diff --git a/executor/linux/build_test.go b/executor/linux/build_test.go index 1998265a..fa0d0e0e 100644 --- a/executor/linux/build_test.go +++ b/executor/linux/build_test.go @@ -12,6 +12,8 @@ import ( "testing" "time" + v1 "k8s.io/api/core/v1" + "github.com/gin-gonic/gin" "github.com/go-vela/sdk-go/vela" "github.com/go-vela/server/compiler/native" @@ -22,6 +24,7 @@ import ( "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime" "github.com/go-vela/worker/runtime/docker" + "github.com/go-vela/worker/runtime/kubernetes" "github.com/sirupsen/logrus" logrusTest "github.com/sirupsen/logrus/hooks/test" "github.com/urfave/cli/v2" @@ -121,6 +124,12 @@ func TestLinux_CreateBuild(t *testing.T) { var _runtime runtime.Engine switch test.runtime { + case constants.DriverKubernetes: + _pod := testPodFor(_pipeline) + _runtime, err = kubernetes.NewMock(_pod) + if err != nil { + t.Errorf("unable to create kubernetes runtime engine: %v", err) + } case constants.DriverDocker: _runtime, err = docker.NewMock() if err != nil { @@ -1083,6 +1092,12 @@ func TestLinux_PlanBuild(t *testing.T) { var _runtime runtime.Engine switch test.runtime { + case constants.DriverKubernetes: + _pod := testPodFor(_pipeline) + _runtime, err = kubernetes.NewMock(_pod) + if err != nil { + t.Errorf("unable to create kubernetes runtime engine: %v", err) + } case constants.DriverDocker: _runtime, err = docker.NewMock() if err != nil { @@ -1281,6 +1296,12 @@ func TestLinux_AssembleBuild(t *testing.T) { var _runtime runtime.Engine switch test.runtime { + case constants.DriverKubernetes: + _pod := testPodFor(_pipeline) + _runtime, err = kubernetes.NewMock(_pod) + if err != nil { + t.Errorf("unable to create kubernetes runtime engine: %v", err) + } case constants.DriverDocker: _runtime, err = docker.NewMock() if err != nil { @@ -1432,9 +1453,18 @@ func TestLinux_ExecBuild(t *testing.T) { // Docker uses _ while Kubernetes uses - _pipeline = _pipeline.Sanitize(test.runtime) - var _runtime runtime.Engine + var ( + _runtime runtime.Engine + _pod *v1.Pod + ) switch test.runtime { + case constants.DriverKubernetes: + _pod = testPodFor(_pipeline) + _runtime, err = kubernetes.NewMock(_pod) + if err != nil { + t.Errorf("unable to create kubernetes runtime engine: %v", err) + } case constants.DriverDocker: _runtime, err = docker.NewMock() if err != nil { @@ -1818,6 +1848,12 @@ func TestLinux_StreamBuild(t *testing.T) { var _runtime runtime.Engine switch test.runtime { + case constants.DriverKubernetes: + _pod := testPodFor(_pipeline) + _runtime, err = kubernetes.NewMock(_pod) + if err != nil { + t.Errorf("unable to create kubernetes runtime engine: %v", err) + } case constants.DriverDocker: _runtime, err = docker.NewMock() if err != nil { @@ -2024,6 +2060,12 @@ func TestLinux_DestroyBuild(t *testing.T) { var _runtime runtime.Engine switch test.runtime { + case constants.DriverKubernetes: + _pod := testPodFor(_pipeline) + _runtime, err = kubernetes.NewMock(_pod) + if err != nil { + t.Errorf("unable to create kubernetes runtime engine: %v", err) + } case constants.DriverDocker: _runtime, err = docker.NewMock() if err != nil { diff --git a/executor/linux/linux_test.go b/executor/linux/linux_test.go index 2503fc7a..5384e749 100644 --- a/executor/linux/linux_test.go +++ b/executor/linux/linux_test.go @@ -5,9 +5,13 @@ package linux import ( + "math" "net/http/httptest" "testing" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/gin-gonic/gin" "github.com/go-vela/sdk-go/vela" "github.com/go-vela/server/mock/server" @@ -336,3 +340,364 @@ func testSteps(runtime string) *pipeline.Build { // apply any runtime-specific cleanups return steps.Sanitize(runtime) } + +// testPod is a test helper function to create a Pod +// type with all fields set to a fake value. +func testPod(useStages bool) *v1.Pod { + // https://github.com/go-vela/worker/blob/main/runtime/kubernetes/kubernetes_test.go#L83 + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "github-octocat-1", + Namespace: "test", + Labels: map[string]string{ + "pipeline": "github-octocat-1", + }, + }, + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + Status: v1.PodStatus{ + Phase: v1.PodRunning, + }, + Spec: v1.PodSpec{}, + } + + if useStages { + pod.Spec.Containers = []v1.Container{ + { + Name: "github-octocat-1-clone-clone", + Image: "target/vela-git:v0.6.0", + WorkingDir: "/vela/src/github.com/octocat/helloworld", + ImagePullPolicy: v1.PullAlways, + }, + { + Name: "github-octocat-1-echo-echo", + Image: "alpine:latest", + WorkingDir: "/vela/src/github.com/octocat/helloworld", + ImagePullPolicy: v1.PullAlways, + }, + { + Name: "service-github-octocat-1-postgres", + Image: "postgres:12-alpine", + WorkingDir: "/vela/src/github.com/octocat/helloworld", + ImagePullPolicy: v1.PullAlways, + }, + } + pod.Status.ContainerStatuses = []v1.ContainerStatus{ + { + Name: "github-octocat-1-clone-clone", + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + Image: "target/vela-git:v0.6.0", + }, + { + Name: "github-octocat-1-echo-echo", + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + Image: "alpine:latest", + }, + } + } else { // step + pod.Spec.Containers = []v1.Container{ + { + Name: "step-github-octocat-1-clone", + Image: "target/vela-git:v0.6.0", + WorkingDir: "/vela/src/github.com/octocat/helloworld", + ImagePullPolicy: v1.PullAlways, + }, + { + Name: "step-github-octocat-1-echo", + Image: "alpine:latest", + WorkingDir: "/vela/src/github.com/octocat/helloworld", + ImagePullPolicy: v1.PullAlways, + }, + { + Name: "service-github-octocat-1-postgres", + Image: "postgres:12-alpine", + WorkingDir: "/vela/src/github.com/octocat/helloworld", + ImagePullPolicy: v1.PullAlways, + }, + } + pod.Status.ContainerStatuses = []v1.ContainerStatus{ + { + Name: "step-github-octocat-1-clone", + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + Image: "target/vela-git:v0.6.0", + }, + { + Name: "step-github-octocat-1-echo", + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + Image: "alpine:latest", + }, + } + } + + return pod +} + +// testPodFor is a test helper function to create a Pod +// using container names from a test pipeline. +func testPodFor(build *pipeline.Build) *v1.Pod { + var ( + pod *v1.Pod + containers []v1.Container + ) + + workingDir := "/vela/src/github.com/octocat/helloworld" + useStages := len(build.Stages) > 0 + pod = testPod(useStages) + + for _, service := range build.Services { + containers = append(containers, v1.Container{ + Name: service.ID, + Image: service.Image, + WorkingDir: workingDir, + // service.Pull should be one of: Always, Never, IfNotPresent + ImagePullPolicy: v1.PullPolicy(service.Pull), + }) + } + + if useStages { + containers = append(containers, v1.Container{ + Name: "step-github-octocat-1-clone-clone", + Image: "target/vela-git:v0.6.0", + WorkingDir: workingDir, + ImagePullPolicy: v1.PullAlways, + }) + } else { // steps + containers = append(containers, v1.Container{ + Name: "step-github-octocat-1-clone", + Image: "target/vela-git:v0.6.0", + WorkingDir: workingDir, + ImagePullPolicy: v1.PullAlways, + }) + } + + for _, stage := range build.Stages { + for _, step := range stage.Steps { + if step.Name == "init" { + continue + } + + containers = append(containers, v1.Container{ + Name: step.ID, + Image: step.Image, + WorkingDir: workingDir, + // step.Pull should be one of: Always, Never, IfNotPresent + ImagePullPolicy: v1.PullPolicy(step.Pull), + }) + } + } + + for _, step := range build.Steps { + if step.Name == "init" { + continue + } + + containers = append(containers, v1.Container{ + Name: step.ID, + Image: step.Image, + WorkingDir: workingDir, + // step.Pull should be one of: Always, Never, IfNotPresent + ImagePullPolicy: v1.PullPolicy(step.Pull), + }) + } + + for _, secret := range build.Secrets { + if secret.Origin.Empty() { + continue + } + + containers = append(containers, v1.Container{ + Name: secret.Origin.ID, + Image: secret.Origin.Image, + WorkingDir: workingDir, + // secret.Origin.Pull should be one of: Always, Never, IfNotPresent + ImagePullPolicy: v1.PullPolicy(secret.Origin.Pull), + }) + } + + pod.Spec.Containers = containers + pod.Status.ContainerStatuses = testContainerStatuses(build, false, 0, 0) + + return pod +} + +// countBuildSteps counts the steps in the build. +func countBuildSteps(build *pipeline.Build) int { + steps := 0 + + for _, stage := range build.Stages { + for _, step := range stage.Steps { + if step.Name == "init" { + continue + } + + steps++ + } + } + + for _, step := range build.Steps { + if step.Name == "init" { + continue + } + + steps++ + } + + return steps +} + +// testContainerStatuses is a test helper function to create a ContainerStatuses list. +func testContainerStatuses(build *pipeline.Build, servicesRunning bool, stepsRunningCount, stepsCompletedPercent int) []v1.ContainerStatus { + var containerStatuses []v1.ContainerStatus + + useStages := len(build.Stages) > 0 + stepsCompletedCount := 0 + + if stepsCompletedPercent > 0 { + stepsCompletedCount = int(math.Round(float64(stepsCompletedPercent) / 100 * float64(countBuildSteps(build)))) + } + + if servicesRunning { + for _, service := range build.Services { + containerStatuses = append(containerStatuses, v1.ContainerStatus{ + Name: service.ID, + State: v1.ContainerState{ + Running: &v1.ContainerStateRunning{}, + }, + Image: service.Image, + }) + } + } + + if useStages { + containerStatuses = append(containerStatuses, v1.ContainerStatus{ + Name: "step-github-octocat-1-clone-clone", + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + Image: "target/vela-git:v0.6.0", + }) + } else { // steps + containerStatuses = append(containerStatuses, v1.ContainerStatus{ + Name: "step-github-octocat-1-clone", + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + Image: "target/vela-git:v0.6.0", + }) + } + + steps := 0 + + for _, stage := range build.Stages { + for _, step := range stage.Steps { + if step.Name == "init" { + continue + } + + steps++ + if steps > stepsCompletedCount+stepsRunningCount { + break + } + + if stepsRunningCount > 0 && steps > stepsCompletedCount { + containerStatuses = append(containerStatuses, v1.ContainerStatus{ + Name: step.ID, + State: v1.ContainerState{ + Running: &v1.ContainerStateRunning{}, + }, + Image: step.Image, + }) + } else if steps <= stepsCompletedCount { + containerStatuses = append(containerStatuses, v1.ContainerStatus{ + Name: step.ID, + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + Image: step.Image, + }) + } + } + } + + for _, step := range build.Steps { + if step.Name == "init" { + continue + } + + steps++ + if steps > stepsCompletedCount+stepsRunningCount { + break + } + + if stepsRunningCount > 0 && steps > stepsCompletedCount { + containerStatuses = append(containerStatuses, v1.ContainerStatus{ + Name: step.ID, + State: v1.ContainerState{ + Running: &v1.ContainerStateRunning{}, + }, + Image: step.Image, + }) + } else if steps <= stepsCompletedCount { + containerStatuses = append(containerStatuses, v1.ContainerStatus{ + Name: step.ID, + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + Image: step.Image, + }) + } + } + + for _, secret := range build.Secrets { + if secret.Origin.Empty() { + continue + } + + containerStatuses = append(containerStatuses, v1.ContainerStatus{ + Name: secret.Origin.ID, + Image: secret.Origin.Image, + State: v1.ContainerState{ + Terminated: &v1.ContainerStateTerminated{ + Reason: "Completed", + ExitCode: 0, + }, + }, + }) + } + + return containerStatuses +} diff --git a/executor/linux/secret_test.go b/executor/linux/secret_test.go index 86c2efb0..9cdbcffb 100644 --- a/executor/linux/secret_test.go +++ b/executor/linux/secret_test.go @@ -21,6 +21,7 @@ import ( "github.com/go-vela/worker/internal/message" "github.com/go-vela/worker/runtime" "github.com/go-vela/worker/runtime/docker" + "github.com/go-vela/worker/runtime/kubernetes" "github.com/google/go-cmp/cmp" "github.com/urfave/cli/v2" ) @@ -317,6 +318,12 @@ func TestLinux_Secret_exec(t *testing.T) { var _runtime runtime.Engine switch test.runtime { + case constants.DriverKubernetes: + _pod := testPodFor(p) + _runtime, err = kubernetes.NewMock(_pod) + if err != nil { + t.Errorf("unable to create kubernetes runtime engine: %v", err) + } case constants.DriverDocker: _runtime, err = docker.NewMock() if err != nil {