From 29f514030445b1894b460cf4cd0b4f3d81be39e2 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <127358482+zc-devs@users.noreply.github.com> Date: Fri, 10 Nov 2023 00:49:26 +0300 Subject: [PATCH 1/9] Refactoring --- pipeline/backend/kubernetes/kubernetes.go | 115 ++---- pipeline/backend/kubernetes/pod.go | 368 +++++++++++++------- pipeline/backend/kubernetes/service.go | 66 +++- pipeline/backend/kubernetes/service_test.go | 4 +- pipeline/backend/kubernetes/volume.go | 43 ++- 5 files changed, 374 insertions(+), 222 deletions(-) diff --git a/pipeline/backend/kubernetes/kubernetes.go b/pipeline/backend/kubernetes/kubernetes.go index ff394c0e14..23e6e1a6d0 100644 --- a/pipeline/backend/kubernetes/kubernetes.go +++ b/pipeline/backend/kubernetes/kubernetes.go @@ -29,7 +29,6 @@ import ( "github.com/urfave/cli/v2" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" @@ -45,7 +44,10 @@ const ( EngineName = "kubernetes" ) -var noContext = context.Background() +var ( + noContext = context.Background() + defaultDeleteOptions = newDefaultDeleteOptions() +) type kube struct { ctx context.Context @@ -63,6 +65,16 @@ type Config struct { PodAnnotations map[string]string } +func newDefaultDeleteOptions() metav1.DeleteOptions { + gracePeriodSeconds := int64(0) // immediately + propagationPolicy := metav1.DeletePropagationBackground + + return metav1.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, + PropagationPolicy: &propagationPolicy, + } +} + func configFromCliContext(ctx context.Context) (*Config, error) { if ctx != nil { if c, ok := ctx.Value(types.CliContext).(*cli.Context); ok { @@ -143,12 +155,7 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s log.Trace().Str("taskUUID", taskUUID).Msgf("Setting up Kubernetes primitives") for _, vol := range conf.Volumes { - pvc, err := PersistentVolumeClaim(e.config.Namespace, vol.Name, e.config.StorageClass, e.config.VolumeSize, e.config.StorageRwx) - if err != nil { - return err - } - - _, err = e.client.CoreV1().PersistentVolumeClaims(e.config.Namespace).Create(ctx, pvc, metav1.CreateOptions{}) + _, err := StartVolume(ctx, e, vol.Name) if err != nil { return err } @@ -159,21 +166,10 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s for _, stage := range conf.Stages { if stage.Alias == "services" { for _, step := range stage.Steps { - stepName, err := dnsName(step.Name) - if err != nil { - return err - } - log.Trace().Str("pod-name", stepName).Msgf("Creating service: %s", step.Name) - svc, err := Service(e.config.Namespace, step.Name, step.Ports) + svc, err := StartService(ctx, e, step) if err != nil { return err } - - svc, err = e.client.CoreV1().Services(e.config.Namespace).Create(ctx, svc, metav1.CreateOptions{}) - if err != nil { - return err - } - extraHosts = append(extraHosts, step.Networks[0].Aliases[0]+":"+svc.Spec.ClusterIP) } } @@ -191,13 +187,8 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s // Start the pipeline step. func (e *kube) StartStep(ctx context.Context, step *types.Step, taskUUID string) error { - pod, err := Pod(e.config.Namespace, step, e.config.PodLabels, e.config.PodAnnotations, e.goos) - if err != nil { - return err - } - - log.Trace().Str("taskUUID", taskUUID).Msgf("Creating pod: %s", pod.Name) - _, err = e.client.CoreV1().Pods(e.config.Namespace).Create(ctx, pod, metav1.CreateOptions{}) + log.Trace().Str("taskUUID", taskUUID).Msgf("Starting step: %s", step.Name) + _, err := StartPod(ctx, e, step) return err } @@ -333,92 +324,38 @@ func (e *kube) TailStep(ctx context.Context, step *types.Step, taskUUID string) // return rc, nil } -func (e *kube) DestroyStep(ctx context.Context, step *types.Step, taskUUID string) error { - podName, err := dnsName(step.Name) - if err != nil { - return err - } - - log.Trace().Str("taskUUID", taskUUID).Msgf("Stopping pod: %s", podName) - - gracePeriodSeconds := int64(0) // immediately - dpb := metav1.DeletePropagationBackground - - deleteOpts := metav1.DeleteOptions{ - GracePeriodSeconds: &gracePeriodSeconds, - PropagationPolicy: &dpb, - } - - if err := e.client.CoreV1().Pods(e.config.Namespace).Delete(ctx, podName, deleteOpts); err != nil && !errors.IsNotFound(err) { - return err - } - - return nil +func (e *kube) DestroyStep(_ context.Context, step *types.Step, taskUUID string) error { + log.Trace().Str("taskUUID", taskUUID).Msgf("Stopping step: %s", step.Name) + err := StopPod(noContext, e, step, defaultDeleteOptions) + return err } // Destroy the pipeline environment. func (e *kube) DestroyWorkflow(_ context.Context, conf *types.Config, taskUUID string) error { log.Trace().Str("taskUUID", taskUUID).Msg("Deleting Kubernetes primitives") - gracePeriodSeconds := int64(0) // immediately - dpb := metav1.DeletePropagationBackground - - deleteOpts := metav1.DeleteOptions{ - GracePeriodSeconds: &gracePeriodSeconds, - PropagationPolicy: &dpb, - } - // Use noContext because the ctx sent to this function will be canceled/done in case of error or canceled by user. - // Don't abort on 404 errors from k8s, they most likely mean that the pod hasn't been created yet, usually because pipeline was canceled before running all steps. - // Trace log them in case the info could be useful when troubleshooting. - for _, stage := range conf.Stages { for _, step := range stage.Steps { - stepName, err := dnsName(step.Name) + err := StopPod(noContext, e, step, defaultDeleteOptions) if err != nil { return err } - log.Trace().Msgf("Deleting pod: %s", stepName) - if err := e.client.CoreV1().Pods(e.config.Namespace).Delete(noContext, stepName, deleteOpts); err != nil { - if !errors.IsNotFound(err) { - return err - } - } - } - } - for _, stage := range conf.Stages { - if stage.Alias == "services" { - for _, step := range stage.Steps { - log.Trace().Msgf("Deleting service: %s", step.Name) - svc, err := Service(e.config.Namespace, step.Name, step.Ports) + if step.Type == types.StepTypeService { + err := StopService(noContext, e, step, defaultDeleteOptions) if err != nil { return err } - if err := e.client.CoreV1().Services(e.config.Namespace).Delete(noContext, svc.Name, deleteOpts); err != nil { - if errors.IsNotFound(err) { - log.Trace().Err(err).Msgf("Unable to delete service %s", svc.Name) - } else { - return err - } - } } } } for _, vol := range conf.Volumes { - pvc, err := PersistentVolumeClaim(e.config.Namespace, vol.Name, e.config.StorageClass, e.config.VolumeSize, e.config.StorageRwx) + err := StopVolume(noContext, e, vol.Name, defaultDeleteOptions) if err != nil { return err } - err = e.client.CoreV1().PersistentVolumeClaims(e.config.Namespace).Delete(noContext, pvc.Name, deleteOpts) - if err != nil { - if errors.IsNotFound(err) { - log.Trace().Err(err).Msgf("Unable to delete pvc %s", pvc.Name) - } else { - return err - } - } } return nil diff --git a/pipeline/backend/kubernetes/pod.go b/pipeline/backend/kubernetes/pod.go index 0e96134482..a157fe6e67 100644 --- a/pipeline/backend/kubernetes/pod.go +++ b/pipeline/backend/kubernetes/pod.go @@ -15,12 +15,14 @@ package kubernetes import ( + "context" "fmt" "maps" "strings" "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,153 +30,257 @@ import ( "go.woodpecker-ci.org/woodpecker/pipeline/backend/types" ) -func Pod(namespace string, step *types.Step, labels, annotations map[string]string, goos string) (*v1.Pod, error) { - var ( - vols []v1.Volume - volMounts []v1.VolumeMount - entrypoint []string - args []string - ) - - if step.WorkingDir != "" { - for _, vol := range step.Volumes { - volumeName, err := dnsName(strings.Split(vol, ":")[0]) - if err != nil { - return nil, err - } - - vols = append(vols, v1.Volume{ - Name: volumeName, - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ - ClaimName: volumeName, - ReadOnly: false, - }, - }, - }) - - volMounts = append(volMounts, v1.VolumeMount{ - Name: volumeName, - MountPath: volumeMountPath(vol), - }) - } +const ( + StepLabel = "step" +) + +func Pod(namespace, name, image, workDir, goos, serviceAccountName string, + pool, privileged bool, + commands, vols, extraHosts []string, + labels, annotations, env, backendNodeSelector map[string]string, + backendTolerations []types.Toleration, resources types.Resources, +) (*v1.Pod, error) { + var err error + + meta := podMeta(name, namespace, labels, annotations) + + spec, err := podSpec(serviceAccountName, vols, extraHosts, env, + backendNodeSelector, backendTolerations) + if err != nil { + return nil, err } - var pullPolicy v1.PullPolicy - if step.Pull { - pullPolicy = v1.PullAlways + container, err := podContainer(name, image, workDir, goos, pool, privileged, + commands, vols, env, resources) + if err != nil { + return nil, err } + spec.Containers = append(spec.Containers, container) - if len(step.Commands) != 0 { - scriptEnv, entry, cmds := common.GenerateContainerConf(step.Commands, goos) - for k, v := range scriptEnv { - step.Environment[k] = v - } - entrypoint = entry - args = cmds + pod := &v1.Pod{ + ObjectMeta: meta, + Spec: spec, } - hostAliases := []v1.HostAlias{} - for _, extraHost := range step.ExtraHosts { - host := strings.Split(extraHost, ":") - hostAliases = append(hostAliases, v1.HostAlias{IP: host[1], Hostnames: []string{host[0]}}) + return pod, nil +} + +func PodName(step *types.Step) (string, error) { + return dnsName(step.Name) +} + +func podMeta(name, namespace string, labels, annotations map[string]string) metav1.ObjectMeta { + meta := metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Annotations: annotations, } - resourceRequirements := v1.ResourceRequirements{Requests: v1.ResourceList{}, Limits: v1.ResourceList{}} + if labels == nil { + labels = make(map[string]string, 1) + } + labels[StepLabel] = name + meta.Labels = labels + + return meta +} + +func podSpec(serviceAccountName string, vols, extraHosts []string, env, backendNodeSelector map[string]string, backendTolerations []types.Toleration) (v1.PodSpec, error) { var err error - for key, val := range step.BackendOptions.Kubernetes.Resources.Requests { - resourceKey := v1.ResourceName(key) - resourceRequirements.Requests[resourceKey], err = resource.ParseQuantity(val) + spec := v1.PodSpec{ + RestartPolicy: v1.RestartPolicyNever, + ServiceAccountName: serviceAccountName, + ImagePullSecrets: []v1.LocalObjectReference{{Name: "regcred"}}, + } + + spec.HostAliases = hostAliases(extraHosts) + spec.NodeSelector = nodeSelector(backendNodeSelector, env["CI_SYSTEM_PLATFORM"]) + spec.Tolerations = tolerations(backendTolerations) + spec.Volumes, err = volumes(vols) + if err != nil { + return spec, err + } + + return spec, nil +} + +func podContainer(name, image, workDir, goos string, pull, privileged bool, commands, volumes []string, env map[string]string, resources types.Resources) (v1.Container, error) { + var err error + container := v1.Container{ + Name: name, + Image: image, + WorkingDir: workDir, + } + + if pull { + container.ImagePullPolicy = v1.PullAlways + } + + if len(commands) != 0 { + scriptEnv, command, args := common.GenerateContainerConf(commands, goos) + container.Command = command + container.Args = args + maps.Copy(env, scriptEnv) + } + + container.Env = mapToEnvVars(env) + container.SecurityContext = securityContext(privileged) + + container.Resources, err = resourceRequirements(resources) + if err != nil { + return container, err + } + + container.VolumeMounts, err = volumeMounts(volumes) + if err != nil { + return container, err + } + + return container, nil +} + +func volumes(volumes []string) ([]v1.Volume, error) { + var vols []v1.Volume + + for _, v := range volumes { + volumeName, err := VolumeName(v) if err != nil { - return nil, fmt.Errorf("resource request '%v' quantity '%v': %w", key, val, err) + return nil, err } + vols = append(vols, volume(volumeName)) + } + + return vols, nil +} + +func volume(name string) v1.Volume { + pvcSource := v1.PersistentVolumeClaimVolumeSource{ + ClaimName: name, + ReadOnly: false, + } + return v1.Volume{ + Name: name, + VolumeSource: v1.VolumeSource{ + PersistentVolumeClaim: &pvcSource, + }, } - for key, val := range step.BackendOptions.Kubernetes.Resources.Limits { - resourceKey := v1.ResourceName(key) - resourceRequirements.Limits[resourceKey], err = resource.ParseQuantity(val) +} + +func volumeMounts(volumes []string) ([]v1.VolumeMount, error) { + var mounts []v1.VolumeMount + + for _, v := range volumes { + volumeName, err := VolumeName(v) if err != nil { - return nil, fmt.Errorf("resource limit '%v' quantity '%v': %w", key, val, err) + return nil, err } + + mount := volumeMount(volumeName, VolumeMountPath(v)) + mounts = append(mounts, mount) + } + return mounts, nil +} + +func volumeMount(name, path string) v1.VolumeMount { + return v1.VolumeMount{ + Name: name, + MountPath: path, } +} - var serviceAccountName string - if step.BackendOptions.Kubernetes.ServiceAccountName != "" { - serviceAccountName = step.BackendOptions.Kubernetes.ServiceAccountName +// Here is the service IPs (placed in /etc/hosts in the Pod) +func hostAliases(extraHosts []string) []v1.HostAlias { + hostAliases := []v1.HostAlias{} + for _, extraHost := range extraHosts { + hostAlias := hostAlias(extraHost) + hostAliases = append(hostAliases, hostAlias) } + return hostAliases +} - podName, err := dnsName(step.Name) +func hostAlias(extraHost string) v1.HostAlias { + host := strings.Split(extraHost, ":") + return v1.HostAlias{ + IP: host[1], + Hostnames: []string{host[0]}, + } +} + +func resourceRequirements(resources types.Resources) (v1.ResourceRequirements, error) { + var err error + requirements := v1.ResourceRequirements{} + + requirements.Requests, err = resourceList(resources.Requests) if err != nil { - return nil, err + return requirements, err } - labels["step"] = podName + requirements.Limits, err = resourceList(resources.Limits) + if err != nil { + return requirements, err + } + + return requirements, nil +} + +func resourceList(resources map[string]string) (v1.ResourceList, error) { + requestResources := v1.ResourceList{} + for key, val := range resources { + resName := v1.ResourceName(key) + resVal, err := resource.ParseQuantity(val) + if err != nil { + return nil, fmt.Errorf("resource request '%v' quantity '%v': %w", key, val, err) + } + requestResources[resName] = resVal + } + return requestResources, nil +} + +func nodeSelector(backendNodeSelector map[string]string, platform string) map[string]string { + nodeSelector := make(map[string]string) - var nodeSelector map[string]string - platform, exist := step.Environment["CI_SYSTEM_PLATFORM"] - if exist && platform != "" { + if platform != "" { arch := strings.Split(platform, "/")[1] - nodeSelector = map[string]string{v1.LabelArchStable: arch} + nodeSelector[v1.LabelArchStable] = arch log.Trace().Msgf("Using the node selector from the Agent's platform: %v", nodeSelector) } - beOptNodeSelector := step.BackendOptions.Kubernetes.NodeSelector - if len(beOptNodeSelector) > 0 { - if len(nodeSelector) == 0 { - nodeSelector = beOptNodeSelector - } else { - log.Trace().Msgf("Appending labels to the node selector from the backend options: %v", beOptNodeSelector) - maps.Copy(nodeSelector, beOptNodeSelector) - } + + if len(backendNodeSelector) > 0 { + log.Trace().Msgf("Appending labels to the node selector from the backend options: %v", backendNodeSelector) + maps.Copy(nodeSelector, backendNodeSelector) } + return nodeSelector +} + +func tolerations(backendTolerations []types.Toleration) []v1.Toleration { var tolerations []v1.Toleration - beTolerations := step.BackendOptions.Kubernetes.Tolerations - if len(beTolerations) > 0 { - for _, t := range step.BackendOptions.Kubernetes.Tolerations { - toleration := v1.Toleration{ - Key: t.Key, - Operator: v1.TolerationOperator(t.Operator), - Value: t.Value, - Effect: v1.TaintEffect(t.Effect), - TolerationSeconds: t.TolerationSeconds, - } + + if len(backendTolerations) > 0 { + log.Trace().Msgf("Tolerations that will be used in the backend options: %v", backendTolerations) + for _, backendToleration := range backendTolerations { + toleration := toleration(backendToleration) tolerations = append(tolerations, toleration) } - log.Trace().Msgf("Tolerations that will be used in the backend options: %v", beTolerations) } - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Namespace: namespace, - Labels: labels, - Annotations: annotations, - }, - Spec: v1.PodSpec{ - RestartPolicy: v1.RestartPolicyNever, - HostAliases: hostAliases, - NodeSelector: nodeSelector, - Tolerations: tolerations, - ServiceAccountName: serviceAccountName, - Containers: []v1.Container{{ - Name: podName, - Image: step.Image, - ImagePullPolicy: pullPolicy, - Command: entrypoint, - Args: args, - WorkingDir: step.WorkingDir, - Env: mapToEnvVars(step.Environment), - VolumeMounts: volMounts, - Resources: resourceRequirements, - SecurityContext: &v1.SecurityContext{ - Privileged: &step.Privileged, - }, - }}, - ImagePullSecrets: []v1.LocalObjectReference{{Name: "regcred"}}, - Volumes: vols, - }, + return tolerations +} + +func toleration(backendToleration types.Toleration) v1.Toleration { + return v1.Toleration{ + Key: backendToleration.Key, + Operator: v1.TolerationOperator(backendToleration.Operator), + Value: backendToleration.Value, + Effect: v1.TaintEffect(backendToleration.Effect), + TolerationSeconds: backendToleration.TolerationSeconds, } +} - return pod, nil +func securityContext(privileged bool) *v1.SecurityContext { + return &v1.SecurityContext{ + Privileged: &privileged, + } } func mapToEnvVars(m map[string]string) []v1.EnvVar { @@ -188,10 +294,36 @@ func mapToEnvVars(m map[string]string) []v1.EnvVar { return ev } -func volumeMountPath(i string) string { - s := strings.Split(i, ":") - if len(s) > 1 { - return s[1] +func StartPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, error) { + podName, err := PodName(step) + if err != nil { + return nil, err + } + + pod, err := Pod(engine.config.Namespace, podName, step.Image, step.WorkingDir, engine.goos, step.BackendOptions.Kubernetes.ServiceAccountName, + step.Pull, step.Privileged, + step.Commands, step.Volumes, step.ExtraHosts, + engine.config.PodLabels, engine.config.PodAnnotations, step.Environment, step.BackendOptions.Kubernetes.NodeSelector, + step.BackendOptions.Kubernetes.Tolerations, step.BackendOptions.Kubernetes.Resources) + if err != nil { + return nil, err + } + + log.Trace().Msgf("Creating pod: %s", pod.Name) + return engine.client.CoreV1().Pods(engine.config.Namespace).Create(ctx, pod, metav1.CreateOptions{}) +} + +func StopPod(ctx context.Context, engine *kube, step *types.Step, deleteOpts metav1.DeleteOptions) error { + podName, err := PodName(step) + if err != nil { + return err + } + log.Trace().Str("name", podName).Msg("Deleting pod") + + err = engine.client.CoreV1().Pods(engine.config.Namespace).Delete(ctx, podName, deleteOpts) + if errors.IsNotFound(err) { + // Don't abort on 404 errors from k8s, they most likely mean that the pod hasn't been created yet, usually because pipeline was canceled before running all steps. + return nil } - return s[0] + return err } diff --git a/pipeline/backend/kubernetes/service.go b/pipeline/backend/kubernetes/service.go index 2f5b073eb1..8a563d997d 100644 --- a/pipeline/backend/kubernetes/service.go +++ b/pipeline/backend/kubernetes/service.go @@ -15,12 +15,19 @@ package kubernetes import ( + "context" + + "github.com/rs/zerolog/log" + "go.woodpecker-ci.org/woodpecker/pipeline/backend/types" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" ) -func Service(namespace, name string, ports []uint16) (*v1.Service, error) { +func Service(namespace, name string, ports []uint16, selector map[string]string) (*v1.Service, error) { + log.Trace().Str("name", name).Interface("selector", selector).Interface("ports", ports).Msg("Creating service") + var svcPorts []v1.ServicePort for _, port := range ports { svcPorts = append(svcPorts, v1.ServicePort{ @@ -29,22 +36,57 @@ func Service(namespace, name string, ports []uint16) (*v1.Service, error) { }) } - dnsName, err := dnsName(name) - if err != nil { - return nil, err - } - return &v1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: dnsName, + Name: name, Namespace: namespace, }, Spec: v1.ServiceSpec{ - Type: v1.ServiceTypeClusterIP, - Selector: map[string]string{ - "step": dnsName, - }, - Ports: svcPorts, + Type: v1.ServiceTypeClusterIP, + Selector: selector, + Ports: svcPorts, }, }, nil } + +func ServiceName(step *types.Step) (string, error) { + return dnsName(step.Name) +} + +func StartService(ctx context.Context, engine *kube, step *types.Step) (*v1.Service, error) { + name, err := ServiceName(step) + if err != nil { + return nil, err + } + podName, err := PodName(step) + if err != nil { + return nil, err + } + + selector := map[string]string{ + StepLabel: podName, + } + + svc, err := Service(engine.config.Namespace, name, step.Ports, selector) + if err != nil { + return nil, err + } + + return engine.client.CoreV1().Services(engine.config.Namespace).Create(ctx, svc, metav1.CreateOptions{}) +} + +func StopService(ctx context.Context, engine *kube, step *types.Step, deleteOpts metav1.DeleteOptions) error { + svcName, err := ServiceName(step) + if err != nil { + return err + } + log.Trace().Str("name", svcName).Msg("Deleting service") + + err = engine.client.CoreV1().Services(engine.config.Namespace).Delete(ctx, svcName, deleteOpts) + if errors.IsNotFound(err) { + // Don't abort on 404 errors from k8s, they most likely mean that the pod hasn't been created yet, usually because pipeline was canceled before running all steps. + log.Trace().Err(err).Msgf("Unable to delete service %s", svcName) + return nil + } + return err +} diff --git a/pipeline/backend/kubernetes/service_test.go b/pipeline/backend/kubernetes/service_test.go index 9604bee8d0..97b7d47d6a 100644 --- a/pipeline/backend/kubernetes/service_test.go +++ b/pipeline/backend/kubernetes/service_test.go @@ -45,7 +45,7 @@ func TestService(t *testing.T) { } ], "selector": { - "step": "bar" + "step": "baz" }, "type": "ClusterIP" }, @@ -54,7 +54,7 @@ func TestService(t *testing.T) { } }` - s, _ := Service("foo", "bar", []uint16{1, 2, 3}) + s, _ := Service("foo", "bar", []uint16{1, 2, 3}, map[string]string{"step": "baz"}) j, err := json.Marshal(s) assert.NoError(t, err) assert.JSONEq(t, expected, string(j)) diff --git a/pipeline/backend/kubernetes/volume.go b/pipeline/backend/kubernetes/volume.go index d8c9ee72d2..22624c47d7 100644 --- a/pipeline/backend/kubernetes/volume.go +++ b/pipeline/backend/kubernetes/volume.go @@ -15,9 +15,12 @@ package kubernetes import ( + "context" "strings" + "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -36,7 +39,7 @@ func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRw accessMode = v1.ReadWriteOnce } - volumeName, err := dnsName(strings.Split(name, ":")[0]) + volumeName, err := VolumeName(name) if err != nil { return nil, err } @@ -59,3 +62,41 @@ func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRw return pvc, nil } + +func VolumeName(name string) (string, error) { + return dnsName(strings.Split(name, ":")[0]) +} + +func VolumeMountPath(name string) string { + s := strings.Split(name, ":") + if len(s) > 1 { + return s[1] + } + return s[0] +} + +func StartVolume(ctx context.Context, engine *kube, name string) (*v1.PersistentVolumeClaim, error) { + pvc, err := PersistentVolumeClaim(engine.config.Namespace, name, engine.config.StorageClass, engine.config.VolumeSize, engine.config.StorageRwx) + if err != nil { + return nil, err + } + + log.Trace().Msgf("Creating volume: %s", pvc.Name) + return engine.client.CoreV1().PersistentVolumeClaims(engine.config.Namespace).Create(ctx, pvc, metav1.CreateOptions{}) +} + +func StopVolume(ctx context.Context, engine *kube, name string, deleteOpts metav1.DeleteOptions) error { + pvcName, err := VolumeName(name) + if err != nil { + return err + } + log.Trace().Str("name", pvcName).Msg("Deleting volume") + + err = engine.client.CoreV1().PersistentVolumeClaims(engine.config.Namespace).Delete(ctx, pvcName, deleteOpts) + if errors.IsNotFound(err) { + // Don't abort on 404 errors from k8s, they most likely mean that the pod hasn't been created yet, usually because pipeline was canceled before running all steps. + log.Trace().Err(err).Msgf("Unable to delete service %s", pvcName) + return nil + } + return err +} From 4b77add7a276c5d6beac87d043ddbe5baff8a13d Mon Sep 17 00:00:00 2001 From: Thomas Anderson <127358482+zc-devs@users.noreply.github.com> Date: Fri, 10 Nov 2023 00:49:49 +0300 Subject: [PATCH 2/9] Tests --- pipeline/backend/kubernetes/pod_test.go | 239 ++++++++++++++++++++ pipeline/backend/kubernetes/service_test.go | 14 ++ pipeline/backend/kubernetes/volume_test.go | 21 ++ 3 files changed, 274 insertions(+) create mode 100644 pipeline/backend/kubernetes/pod_test.go diff --git a/pipeline/backend/kubernetes/pod_test.go b/pipeline/backend/kubernetes/pod_test.go new file mode 100644 index 0000000000..1fcd577188 --- /dev/null +++ b/pipeline/backend/kubernetes/pod_test.go @@ -0,0 +1,239 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubernetes + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "go.woodpecker-ci.org/woodpecker/pipeline/backend/types" +) + +func TestPodName(t *testing.T) { + name, err := PodName(&types.Step{Name: "wp_01he8bebctabr3kgk0qj36d2me_0"}) + assert.NoError(t, err) + assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0", name) + + name, err = PodName(&types.Step{Name: "wp\\01he8bebctabr3kgk0qj36d2me-0"}) + assert.NoError(t, err) + assert.Equal(t, "wp\\01he8bebctabr3kgk0qj36d2me-0", name) + + _, err = PodName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"}) + assert.ErrorIs(t, err, ErrDNSPatternInvalid) +} + +func TestTinyPod(t *testing.T) { + expected := ` + { + "metadata": { + "name": "wp-01he8bebctabr3kgk0qj36d2me-0", + "namespace": "woodpecker", + "creationTimestamp": null, + "labels": { + "step": "wp-01he8bebctabr3kgk0qj36d2me-0" + } + }, + "spec": { + "volumes": [ + { + "name": "workspace", + "persistentVolumeClaim": { + "claimName": "workspace" + } + } + ], + "containers": [ + { + "name": "wp-01he8bebctabr3kgk0qj36d2me-0", + "image": "gradle:8.4.0-jdk21", + "command": [ + "/bin/sh", + "-c" + ], + "args": [ + "echo $CI_SCRIPT | base64 -d | /bin/sh -e" + ], + "workingDir": "/woodpecker/src", + "env": [ + { + "name": "CI", + "value": "woodpecker" + }, + { + "name": "HOME", + "value": "/root" + }, + { + "name": "SHELL", + "value": "/bin/sh" + }, + { + "name": "CI_SCRIPT", + "value": "CmlmIFsgLW4gIiRDSV9ORVRSQ19NQUNISU5FIiBdOyB0aGVuCmNhdCA8PEVPRiA+ICRIT01FLy5uZXRyYwptYWNoaW5lICRDSV9ORVRSQ19NQUNISU5FCmxvZ2luICRDSV9ORVRSQ19VU0VSTkFNRQpwYXNzd29yZCAkQ0lfTkVUUkNfUEFTU1dPUkQKRU9GCmNobW9kIDA2MDAgJEhPTUUvLm5ldHJjCmZpCnVuc2V0IENJX05FVFJDX1VTRVJOQU1FCnVuc2V0IENJX05FVFJDX1BBU1NXT1JECnVuc2V0IENJX1NDUklQVAoKZWNobyArICdncmFkbGUgYnVpbGQnCmdyYWRsZSBidWlsZAo=" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "workspace", + "mountPath": "/woodpecker/src" + } + ], + "securityContext": { + "privileged": false + } + } + ], + "restartPolicy": "Never", + "imagePullSecrets": [ + { + "name": "regcred" + } + ] + }, + "status": {} + }` + + pod, err := Pod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "gradle:8.4.0-jdk21", "/woodpecker/src", "linux/amd64", "", + false, false, + []string{"gradle build"}, []string{"workspace:/woodpecker/src"}, nil, + nil, nil, map[string]string{"CI": "woodpecker"}, nil, + nil, + types.Resources{Requests: nil, Limits: nil}, + ) + assert.NoError(t, err) + json, err := json.Marshal(pod) + assert.NoError(t, err) + assert.JSONEq(t, expected, string(json)) +} + +func TestFullPod(t *testing.T) { + expected := ` + { + "metadata": { + "name": "wp-01he8bebctabr3kgk0qj36d2me-0", + "namespace": "woodpecker", + "creationTimestamp": null, + "labels": { + "app": "test", + "step": "wp-01he8bebctabr3kgk0qj36d2me-0" + }, + "annotations": { + "apparmor.security": "runtime/default" + } + }, + "spec": { + "volumes": [ + { + "name": "woodpecker-cache", + "persistentVolumeClaim": { + "claimName": "woodpecker-cache" + } + } + ], + "containers": [ + { + "name": "wp-01he8bebctabr3kgk0qj36d2me-0", + "image": "meltwater/drone-cache", + "command": [ + "/bin/sh", + "-c" + ], + "args": [ + "echo $CI_SCRIPT | base64 -d | /bin/sh -e" + ], + "workingDir": "/woodpecker/src", + "env": [ + { + "name": "CGO", + "value": "0" + }, + { + "name": "CI_SCRIPT", + "value": "CmlmIFsgLW4gIiRDSV9ORVRSQ19NQUNISU5FIiBdOyB0aGVuCmNhdCA8PEVPRiA+ICRIT01FLy5uZXRyYwptYWNoaW5lICRDSV9ORVRSQ19NQUNISU5FCmxvZ2luICRDSV9ORVRSQ19VU0VSTkFNRQpwYXNzd29yZCAkQ0lfTkVUUkNfUEFTU1dPUkQKRU9GCmNobW9kIDA2MDAgJEhPTUUvLm5ldHJjCmZpCnVuc2V0IENJX05FVFJDX1VTRVJOQU1FCnVuc2V0IENJX05FVFJDX1BBU1NXT1JECnVuc2V0IENJX1NDUklQVAoKZWNobyArICdnbyBnZXQnCmdvIGdldAoKZWNobyArICdnbyB0ZXN0JwpnbyB0ZXN0Cg==" + }, + { + "name": "HOME", + "value": "/root" + }, + { + "name": "SHELL", + "value": "/bin/sh" + } + ], + "resources": { + "limits": { + "cpu": "2", + "memory": "256Mi" + }, + "requests": { + "cpu": "1", + "memory": "128Mi" + } + }, + "volumeMounts": [ + { + "name": "woodpecker-cache", + "mountPath": "/woodpecker/src/cache" + } + ], + "imagePullPolicy": "Always", + "securityContext": { + "privileged": true + } + } + ], + "restartPolicy": "Never", + "nodeSelector": { + "storage": "ssd" + }, + "serviceAccountName": "wp-svc-acc", + "imagePullSecrets": [ + { + "name": "regcred" + } + ], + "tolerations": [ + { + "key": "net-port", + "value": "100Mbit", + "effect": "NoSchedule" + } + ], + "hostAliases": [ + { + "ip": "1.1.1.1", + "hostnames": [ + "cloudflare" + ] + } + ] + }, + "status": {} + }` + + pod, err := Pod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "meltwater/drone-cache", "/woodpecker/src", "linux/amd64", "wp-svc-acc", + true, true, + []string{"go get", "go test"}, []string{"woodpecker-cache:/woodpecker/src/cache"}, []string{"cloudflare:1.1.1.1"}, + map[string]string{"app": "test"}, map[string]string{"apparmor.security": "runtime/default"}, map[string]string{"CGO": "0"}, map[string]string{"storage": "ssd"}, + []types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}}, + types.Resources{Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"}, Limits: map[string]string{"memory": "256Mi", "cpu": "2"}}, + ) + assert.NoError(t, err) + json, err := json.Marshal(pod) + assert.NoError(t, err) + assert.JSONEq(t, expected, string(json)) +} diff --git a/pipeline/backend/kubernetes/service_test.go b/pipeline/backend/kubernetes/service_test.go index 97b7d47d6a..47d7cecd73 100644 --- a/pipeline/backend/kubernetes/service_test.go +++ b/pipeline/backend/kubernetes/service_test.go @@ -19,8 +19,22 @@ import ( "testing" "github.com/stretchr/testify/assert" + "go.woodpecker-ci.org/woodpecker/pipeline/backend/types" ) +func TestServiceName(t *testing.T) { + name, err := ServiceName(&types.Step{Name: "wp_01he8bebctabr3kgk0qj36d2me_0_services_0"}) + assert.NoError(t, err) + assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0-services-0", name) + + name, err = ServiceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0\\services-0"}) + assert.NoError(t, err) + assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0\\services-0", name) + + _, err = ServiceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"}) + assert.ErrorIs(t, err, ErrDNSPatternInvalid) +} + func TestService(t *testing.T) { expected := ` { diff --git a/pipeline/backend/kubernetes/volume_test.go b/pipeline/backend/kubernetes/volume_test.go index fb38a6825e..344de8cf60 100644 --- a/pipeline/backend/kubernetes/volume_test.go +++ b/pipeline/backend/kubernetes/volume_test.go @@ -21,6 +21,27 @@ import ( "github.com/stretchr/testify/assert" ) +func TestPvcName(t *testing.T) { + name, err := VolumeName("woodpecker_cache:/woodpecker/src/cache") + assert.NoError(t, err) + assert.Equal(t, "woodpecker-cache", name) + + name, err = VolumeName("woodpecker\\cache") + assert.NoError(t, err) + assert.Equal(t, "woodpecker\\cache", name) + + _, err = VolumeName("-woodpecker.cache:/woodpecker/src/cache") + assert.ErrorIs(t, err, ErrDNSPatternInvalid) +} + +func TestPvcMount(t *testing.T) { + mount := VolumeMountPath("woodpecker-cache:/woodpecker/src/cache") + assert.Equal(t, "/woodpecker/src/cache", mount) + + mount = VolumeMountPath("/woodpecker/src/cache") + assert.Equal(t, "/woodpecker/src/cache", mount) +} + func TestPersistentVolumeClaim(t *testing.T) { expectedRwx := ` { From e2686c4d83295a076b61c176353308574f8135d4 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <127358482+zc-devs@users.noreply.github.com> Date: Fri, 10 Nov 2023 20:04:37 +0300 Subject: [PATCH 3/9] Made name methods package private --- pipeline/backend/kubernetes/pod.go | 12 ++++++------ pipeline/backend/kubernetes/pod_test.go | 6 +++--- pipeline/backend/kubernetes/service.go | 8 ++++---- pipeline/backend/kubernetes/service_test.go | 6 +++--- pipeline/backend/kubernetes/volume.go | 8 ++++---- pipeline/backend/kubernetes/volume_test.go | 10 +++++----- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pipeline/backend/kubernetes/pod.go b/pipeline/backend/kubernetes/pod.go index a157fe6e67..3475cf215c 100644 --- a/pipeline/backend/kubernetes/pod.go +++ b/pipeline/backend/kubernetes/pod.go @@ -65,7 +65,7 @@ func Pod(namespace, name, image, workDir, goos, serviceAccountName string, return pod, nil } -func PodName(step *types.Step) (string, error) { +func podName(step *types.Step) (string, error) { return dnsName(step.Name) } @@ -143,7 +143,7 @@ func volumes(volumes []string) ([]v1.Volume, error) { var vols []v1.Volume for _, v := range volumes { - volumeName, err := VolumeName(v) + volumeName, err := volumeName(v) if err != nil { return nil, err } @@ -170,12 +170,12 @@ func volumeMounts(volumes []string) ([]v1.VolumeMount, error) { var mounts []v1.VolumeMount for _, v := range volumes { - volumeName, err := VolumeName(v) + volumeName, err := volumeName(v) if err != nil { return nil, err } - mount := volumeMount(volumeName, VolumeMountPath(v)) + mount := volumeMount(volumeName, volumeMountPath(v)) mounts = append(mounts, mount) } return mounts, nil @@ -295,7 +295,7 @@ func mapToEnvVars(m map[string]string) []v1.EnvVar { } func StartPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, error) { - podName, err := PodName(step) + podName, err := podName(step) if err != nil { return nil, err } @@ -314,7 +314,7 @@ func StartPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, err } func StopPod(ctx context.Context, engine *kube, step *types.Step, deleteOpts metav1.DeleteOptions) error { - podName, err := PodName(step) + podName, err := podName(step) if err != nil { return err } diff --git a/pipeline/backend/kubernetes/pod_test.go b/pipeline/backend/kubernetes/pod_test.go index 1fcd577188..41f538c143 100644 --- a/pipeline/backend/kubernetes/pod_test.go +++ b/pipeline/backend/kubernetes/pod_test.go @@ -23,15 +23,15 @@ import ( ) func TestPodName(t *testing.T) { - name, err := PodName(&types.Step{Name: "wp_01he8bebctabr3kgk0qj36d2me_0"}) + name, err := podName(&types.Step{Name: "wp_01he8bebctabr3kgk0qj36d2me_0"}) assert.NoError(t, err) assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0", name) - name, err = PodName(&types.Step{Name: "wp\\01he8bebctabr3kgk0qj36d2me-0"}) + name, err = podName(&types.Step{Name: "wp\\01he8bebctabr3kgk0qj36d2me-0"}) assert.NoError(t, err) assert.Equal(t, "wp\\01he8bebctabr3kgk0qj36d2me-0", name) - _, err = PodName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"}) + _, err = podName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"}) assert.ErrorIs(t, err, ErrDNSPatternInvalid) } diff --git a/pipeline/backend/kubernetes/service.go b/pipeline/backend/kubernetes/service.go index 8a563d997d..20c7026b13 100644 --- a/pipeline/backend/kubernetes/service.go +++ b/pipeline/backend/kubernetes/service.go @@ -49,16 +49,16 @@ func Service(namespace, name string, ports []uint16, selector map[string]string) }, nil } -func ServiceName(step *types.Step) (string, error) { +func serviceName(step *types.Step) (string, error) { return dnsName(step.Name) } func StartService(ctx context.Context, engine *kube, step *types.Step) (*v1.Service, error) { - name, err := ServiceName(step) + name, err := serviceName(step) if err != nil { return nil, err } - podName, err := PodName(step) + podName, err := podName(step) if err != nil { return nil, err } @@ -76,7 +76,7 @@ func StartService(ctx context.Context, engine *kube, step *types.Step) (*v1.Serv } func StopService(ctx context.Context, engine *kube, step *types.Step, deleteOpts metav1.DeleteOptions) error { - svcName, err := ServiceName(step) + svcName, err := serviceName(step) if err != nil { return err } diff --git a/pipeline/backend/kubernetes/service_test.go b/pipeline/backend/kubernetes/service_test.go index 47d7cecd73..2dd92b249a 100644 --- a/pipeline/backend/kubernetes/service_test.go +++ b/pipeline/backend/kubernetes/service_test.go @@ -23,15 +23,15 @@ import ( ) func TestServiceName(t *testing.T) { - name, err := ServiceName(&types.Step{Name: "wp_01he8bebctabr3kgk0qj36d2me_0_services_0"}) + name, err := serviceName(&types.Step{Name: "wp_01he8bebctabr3kgk0qj36d2me_0_services_0"}) assert.NoError(t, err) assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0-services-0", name) - name, err = ServiceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0\\services-0"}) + name, err = serviceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0\\services-0"}) assert.NoError(t, err) assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0\\services-0", name) - _, err = ServiceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"}) + _, err = serviceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"}) assert.ErrorIs(t, err, ErrDNSPatternInvalid) } diff --git a/pipeline/backend/kubernetes/volume.go b/pipeline/backend/kubernetes/volume.go index 22624c47d7..09fa5f406d 100644 --- a/pipeline/backend/kubernetes/volume.go +++ b/pipeline/backend/kubernetes/volume.go @@ -39,7 +39,7 @@ func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRw accessMode = v1.ReadWriteOnce } - volumeName, err := VolumeName(name) + volumeName, err := volumeName(name) if err != nil { return nil, err } @@ -63,11 +63,11 @@ func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRw return pvc, nil } -func VolumeName(name string) (string, error) { +func volumeName(name string) (string, error) { return dnsName(strings.Split(name, ":")[0]) } -func VolumeMountPath(name string) string { +func volumeMountPath(name string) string { s := strings.Split(name, ":") if len(s) > 1 { return s[1] @@ -86,7 +86,7 @@ func StartVolume(ctx context.Context, engine *kube, name string) (*v1.Persistent } func StopVolume(ctx context.Context, engine *kube, name string, deleteOpts metav1.DeleteOptions) error { - pvcName, err := VolumeName(name) + pvcName, err := volumeName(name) if err != nil { return err } diff --git a/pipeline/backend/kubernetes/volume_test.go b/pipeline/backend/kubernetes/volume_test.go index 344de8cf60..318c415475 100644 --- a/pipeline/backend/kubernetes/volume_test.go +++ b/pipeline/backend/kubernetes/volume_test.go @@ -22,23 +22,23 @@ import ( ) func TestPvcName(t *testing.T) { - name, err := VolumeName("woodpecker_cache:/woodpecker/src/cache") + name, err := volumeName("woodpecker_cache:/woodpecker/src/cache") assert.NoError(t, err) assert.Equal(t, "woodpecker-cache", name) - name, err = VolumeName("woodpecker\\cache") + name, err = volumeName("woodpecker\\cache") assert.NoError(t, err) assert.Equal(t, "woodpecker\\cache", name) - _, err = VolumeName("-woodpecker.cache:/woodpecker/src/cache") + _, err = volumeName("-woodpecker.cache:/woodpecker/src/cache") assert.ErrorIs(t, err, ErrDNSPatternInvalid) } func TestPvcMount(t *testing.T) { - mount := VolumeMountPath("woodpecker-cache:/woodpecker/src/cache") + mount := volumeMountPath("woodpecker-cache:/woodpecker/src/cache") assert.Equal(t, "/woodpecker/src/cache", mount) - mount = VolumeMountPath("/woodpecker/src/cache") + mount = volumeMountPath("/woodpecker/src/cache") assert.Equal(t, "/woodpecker/src/cache", mount) } From 541340652336a6639e07701b6811e30e0c5ce9e3 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <127358482+zc-devs@users.noreply.github.com> Date: Fri, 10 Nov 2023 20:05:06 +0300 Subject: [PATCH 4/9] Using new jsonassert lib in order to pass test with unordered collections --- go.mod | 1 + go.sum | 10 ++-------- pipeline/backend/kubernetes/pod_test.go | 13 +++++++++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 8fdd923577..b5e08515f3 100644 --- a/go.mod +++ b/go.mod @@ -107,6 +107,7 @@ require ( github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kinbiko/jsonassert v1.1.1 github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/libdns/libdns v0.2.1 // indirect diff --git a/go.sum b/go.sum index d6143592da..500bb8b758 100644 --- a/go.sum +++ b/go.sum @@ -179,8 +179,6 @@ github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcg github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -246,6 +244,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kinbiko/jsonassert v1.1.1 h1:DB12divY+YB+cVpHULLuKePSi6+ui4M/shHSzJISkSE= +github.com/kinbiko/jsonassert v1.1.1/go.mod h1:NO4lzrogohtIdNUNzx8sdzB55M4R4Q1bsrWVdqQ7C+A= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -300,8 +300,6 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= -github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -511,8 +509,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -558,8 +554,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= diff --git a/pipeline/backend/kubernetes/pod_test.go b/pipeline/backend/kubernetes/pod_test.go index 41f538c143..44fc296f80 100644 --- a/pipeline/backend/kubernetes/pod_test.go +++ b/pipeline/backend/kubernetes/pod_test.go @@ -18,6 +18,7 @@ import ( "encoding/json" "testing" + "github.com/kinbiko/jsonassert" "github.com/stretchr/testify/assert" "go.woodpecker-ci.org/woodpecker/pipeline/backend/types" ) @@ -68,6 +69,7 @@ func TestTinyPod(t *testing.T) { ], "workingDir": "/woodpecker/src", "env": [ + "<>", { "name": "CI", "value": "woodpecker" @@ -115,9 +117,12 @@ func TestTinyPod(t *testing.T) { types.Resources{Requests: nil, Limits: nil}, ) assert.NoError(t, err) + json, err := json.Marshal(pod) assert.NoError(t, err) - assert.JSONEq(t, expected, string(json)) + + ja := jsonassert.New(t) + ja.Assertf(string(json), expected) } func TestFullPod(t *testing.T) { @@ -157,6 +162,7 @@ func TestFullPod(t *testing.T) { ], "workingDir": "/woodpecker/src", "env": [ + "<>", { "name": "CGO", "value": "0" @@ -233,7 +239,10 @@ func TestFullPod(t *testing.T) { types.Resources{Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"}, Limits: map[string]string{"memory": "256Mi", "cpu": "2"}}, ) assert.NoError(t, err) + json, err := json.Marshal(pod) assert.NoError(t, err) - assert.JSONEq(t, expected, string(json)) + + ja := jsonassert.New(t) + ja.Assertf(string(json), expected) } From 5ab03da972527de9e3870c10a2d7dec6315cd931 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <127358482+zc-devs@users.noreply.github.com> Date: Fri, 10 Nov 2023 20:05:43 +0300 Subject: [PATCH 5/9] Using Engine's context instead Background --- pipeline/backend/kubernetes/kubernetes.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pipeline/backend/kubernetes/kubernetes.go b/pipeline/backend/kubernetes/kubernetes.go index 23e6e1a6d0..408044bc9d 100644 --- a/pipeline/backend/kubernetes/kubernetes.go +++ b/pipeline/backend/kubernetes/kubernetes.go @@ -44,10 +44,7 @@ const ( EngineName = "kubernetes" ) -var ( - noContext = context.Background() - defaultDeleteOptions = newDefaultDeleteOptions() -) +var defaultDeleteOptions = newDefaultDeleteOptions() type kube struct { ctx context.Context @@ -326,7 +323,7 @@ func (e *kube) TailStep(ctx context.Context, step *types.Step, taskUUID string) func (e *kube) DestroyStep(_ context.Context, step *types.Step, taskUUID string) error { log.Trace().Str("taskUUID", taskUUID).Msgf("Stopping step: %s", step.Name) - err := StopPod(noContext, e, step, defaultDeleteOptions) + err := StopPod(e.ctx, e, step, defaultDeleteOptions) return err } @@ -337,13 +334,13 @@ func (e *kube) DestroyWorkflow(_ context.Context, conf *types.Config, taskUUID s // Use noContext because the ctx sent to this function will be canceled/done in case of error or canceled by user. for _, stage := range conf.Stages { for _, step := range stage.Steps { - err := StopPod(noContext, e, step, defaultDeleteOptions) + err := StopPod(e.ctx, e, step, defaultDeleteOptions) if err != nil { return err } if step.Type == types.StepTypeService { - err := StopService(noContext, e, step, defaultDeleteOptions) + err := StopService(e.ctx, e, step, defaultDeleteOptions) if err != nil { return err } @@ -352,7 +349,7 @@ func (e *kube) DestroyWorkflow(_ context.Context, conf *types.Config, taskUUID s } for _, vol := range conf.Volumes { - err := StopVolume(noContext, e, vol.Name, defaultDeleteOptions) + err := StopVolume(e.ctx, e, vol.Name, defaultDeleteOptions) if err != nil { return err } From e5eb0386152ad119df05055b357d8ebaf5edbe02 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <127358482+zc-devs@users.noreply.github.com> Date: Sun, 12 Nov 2023 13:27:44 +0300 Subject: [PATCH 6/9] Fixed go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b5e08515f3..3cf1072d09 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/gorilla/securecookie v1.1.2 github.com/jellydator/ttlcache/v3 v3.1.0 github.com/joho/godotenv v1.5.1 + github.com/kinbiko/jsonassert v1.1.1 github.com/lib/pq v1.10.9 github.com/mattn/go-sqlite3 v1.14.18 github.com/moby/moby v24.0.7+incompatible @@ -107,7 +108,6 @@ require ( github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kinbiko/jsonassert v1.1.1 github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/libdns/libdns v0.2.1 // indirect From 4cacbe1a2a8db48f7c1aa50f2d96eb5c920e7f14 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <127358482+zc-devs@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:23:36 +0300 Subject: [PATCH 7/9] Fixed tests & minor pod changes --- pipeline/backend/kubernetes/pod.go | 20 +++++++++++++------- pipeline/backend/kubernetes/pod_test.go | 15 ++++++++++----- pipeline/backend/kubernetes/utils.go | 12 ++++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/pipeline/backend/kubernetes/pod.go b/pipeline/backend/kubernetes/pod.go index fa6e06a723..f7c6f2e2ac 100644 --- a/pipeline/backend/kubernetes/pod.go +++ b/pipeline/backend/kubernetes/pod.go @@ -37,8 +37,8 @@ const ( func Pod(namespace, name, image, workDir, goos, serviceAccountName string, pool, privileged bool, commands, vols, extraHosts []string, - labels, annotations, env, backendNodeSelector map[string]string, - backendTolerations []types.Toleration, resources types.Resources, + labels, annotations, env, nodeSelector map[string]string, + tolerations []types.Toleration, resources types.Resources, securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig, ) (*v1.Pod, error) { var err error @@ -46,7 +46,7 @@ func Pod(namespace, name, image, workDir, goos, serviceAccountName string, meta := podMeta(name, namespace, labels, annotations) spec, err := podSpec(serviceAccountName, vols, extraHosts, env, - backendNodeSelector, backendTolerations, securityContext, securityContextConfig) + nodeSelector, tolerations, securityContext, securityContextConfig) if err != nil { return nil, err } @@ -87,7 +87,8 @@ func podMeta(name, namespace string, labels, annotations map[string]string) meta } func podSpec(serviceAccountName string, vols, extraHosts []string, env, backendNodeSelector map[string]string, backendTolerations []types.Toleration, - securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig) (v1.PodSpec, error) { + securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig, +) (v1.PodSpec, error) { var err error spec := v1.PodSpec{ RestartPolicy: v1.RestartPolicyNever, @@ -108,7 +109,8 @@ func podSpec(serviceAccountName string, vols, extraHosts []string, env, backendN } func podContainer(name, image, workDir, goos string, pull, privileged bool, commands, volumes []string, env map[string]string, resources types.Resources, - securityContext *types.SecurityContext) (v1.Container, error) { + securityContext *types.SecurityContext, +) (v1.Container, error) { var err error container := v1.Container{ Name: name, @@ -307,12 +309,14 @@ func podSecurityContext(sc *types.SecurityContext, secCtxConf SecurityContextCon return nil } - return &v1.PodSecurityContext{ + securityContext := &v1.PodSecurityContext{ RunAsNonRoot: nonRoot, RunAsUser: user, RunAsGroup: group, FSGroup: fsGroup, } + log.Trace().Msgf("Pod security context that will be used: %v", securityContext) + return securityContext } func containerSecurityContext(sc *types.SecurityContext, stepPrivileged bool) *v1.SecurityContext { @@ -328,9 +332,11 @@ func containerSecurityContext(sc *types.SecurityContext, stepPrivileged bool) *v return nil } - return &v1.SecurityContext{ + securityContext := &v1.SecurityContext{ Privileged: privileged, } + log.Trace().Msgf("Container security context that will be used: %v", securityContext) + return securityContext } func mapToEnvVars(m map[string]string) []v1.EnvVar { diff --git a/pipeline/backend/kubernetes/pod_test.go b/pipeline/backend/kubernetes/pod_test.go index 44fc296f80..372904264d 100644 --- a/pipeline/backend/kubernetes/pod_test.go +++ b/pipeline/backend/kubernetes/pod_test.go @@ -93,10 +93,7 @@ func TestTinyPod(t *testing.T) { "name": "workspace", "mountPath": "/woodpecker/src" } - ], - "securityContext": { - "privileged": false - } + ] } ], "restartPolicy": "Never", @@ -114,7 +111,7 @@ func TestTinyPod(t *testing.T) { []string{"gradle build"}, []string{"workspace:/woodpecker/src"}, nil, nil, nil, map[string]string{"CI": "woodpecker"}, nil, nil, - types.Resources{Requests: nil, Limits: nil}, + types.Resources{Requests: nil, Limits: nil}, nil, SecurityContextConfig{}, ) assert.NoError(t, err) @@ -207,6 +204,12 @@ func TestFullPod(t *testing.T) { "storage": "ssd" }, "serviceAccountName": "wp-svc-acc", + "securityContext": { + "runAsUser": 101, + "runAsGroup": 101, + "runAsNonRoot": true, + "fsGroup": 101 + }, "imagePullSecrets": [ { "name": "regcred" @@ -237,6 +240,8 @@ func TestFullPod(t *testing.T) { map[string]string{"app": "test"}, map[string]string{"apparmor.security": "runtime/default"}, map[string]string{"CGO": "0"}, map[string]string{"storage": "ssd"}, []types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}}, types.Resources{Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"}, Limits: map[string]string{"memory": "256Mi", "cpu": "2"}}, + &types.SecurityContext{Privileged: newBool(true), RunAsNonRoot: newBool(true), RunAsUser: newInt64(101), RunAsGroup: newInt64(101), FSGroup: newInt64(101)}, + SecurityContextConfig{RunAsNonRoot: false}, ) assert.NoError(t, err) diff --git a/pipeline/backend/kubernetes/utils.go b/pipeline/backend/kubernetes/utils.go index 3b177e2fc5..fd924f3570 100644 --- a/pipeline/backend/kubernetes/utils.go +++ b/pipeline/backend/kubernetes/utils.go @@ -78,3 +78,15 @@ func getClientInsideOfCluster() (kubernetes.Interface, error) { return kubernetes.NewForConfig(config) } + +func newBool(val bool) *bool { + ptr := new(bool) + *ptr = val + return ptr +} + +func newInt64(val int64) *int64 { + ptr := new(int64) + *ptr = val + return ptr +} From 1507bc8a895e9fd5497e2c36ef11371a7086155a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 19 Dec 2023 03:41:31 +0100 Subject: [PATCH 8/9] fix imports --- pipeline/backend/kubernetes/pod_test.go | 2 +- pipeline/backend/kubernetes/service.go | 4 ++-- pipeline/backend/kubernetes/service_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pipeline/backend/kubernetes/pod_test.go b/pipeline/backend/kubernetes/pod_test.go index 372904264d..4695257f25 100644 --- a/pipeline/backend/kubernetes/pod_test.go +++ b/pipeline/backend/kubernetes/pod_test.go @@ -20,7 +20,7 @@ import ( "github.com/kinbiko/jsonassert" "github.com/stretchr/testify/assert" - "go.woodpecker-ci.org/woodpecker/pipeline/backend/types" + "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types" ) func TestPodName(t *testing.T) { diff --git a/pipeline/backend/kubernetes/service.go b/pipeline/backend/kubernetes/service.go index 68a808f73d..83e7f30330 100644 --- a/pipeline/backend/kubernetes/service.go +++ b/pipeline/backend/kubernetes/service.go @@ -16,10 +16,10 @@ package kubernetes import ( "context" - "fmt" + "fmt" "github.com/rs/zerolog/log" - "go.woodpecker-ci.org/woodpecker/pipeline/backend/types" + "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pipeline/backend/kubernetes/service_test.go b/pipeline/backend/kubernetes/service_test.go index f5775d3437..cdc89a6251 100644 --- a/pipeline/backend/kubernetes/service_test.go +++ b/pipeline/backend/kubernetes/service_test.go @@ -19,7 +19,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "go.woodpecker-ci.org/woodpecker/pipeline/backend/types" + "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types" ) func TestServiceName(t *testing.T) { From 2f0fcd131c04ad1f740612724303d103cd108347 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 19 Dec 2023 03:52:13 +0100 Subject: [PATCH 9/9] unexport all internal func & struct --- pipeline/backend/kubernetes/kubernetes.go | 22 ++++++++++----------- pipeline/backend/kubernetes/pod.go | 8 ++++---- pipeline/backend/kubernetes/pod_test.go | 4 ++-- pipeline/backend/kubernetes/service.go | 8 ++++---- pipeline/backend/kubernetes/service_test.go | 2 +- pipeline/backend/kubernetes/volume.go | 8 ++++---- pipeline/backend/kubernetes/volume_test.go | 6 +++--- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pipeline/backend/kubernetes/kubernetes.go b/pipeline/backend/kubernetes/kubernetes.go index c7a3841245..c8accfb698 100644 --- a/pipeline/backend/kubernetes/kubernetes.go +++ b/pipeline/backend/kubernetes/kubernetes.go @@ -50,11 +50,11 @@ var defaultDeleteOptions = newDefaultDeleteOptions() type kube struct { ctx context.Context client kubernetes.Interface - config *Config + config *config goos string } -type Config struct { +type config struct { Namespace string StorageClass string VolumeSize string @@ -77,10 +77,10 @@ func newDefaultDeleteOptions() metav1.DeleteOptions { } } -func configFromCliContext(ctx context.Context) (*Config, error) { +func configFromCliContext(ctx context.Context) (*config, error) { if ctx != nil { if c, ok := ctx.Value(types.CliContext).(*cli.Context); ok { - config := Config{ + config := config{ Namespace: c.String("backend-k8s-namespace"), StorageClass: c.String("backend-k8s-storage-class"), VolumeSize: c.String("backend-k8s-volume-size"), @@ -160,7 +160,7 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s log.Trace().Str("taskUUID", taskUUID).Msgf("Setting up Kubernetes primitives") for _, vol := range conf.Volumes { - _, err := StartVolume(ctx, e, vol.Name) + _, err := startVolume(ctx, e, vol.Name) if err != nil { return err } @@ -171,7 +171,7 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s for _, stage := range conf.Stages { if stage.Alias == "services" { for _, step := range stage.Steps { - svc, err := StartService(ctx, e, step) + svc, err := startService(ctx, e, step) if err != nil { return err } @@ -193,7 +193,7 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s // Start the pipeline step. func (e *kube) StartStep(ctx context.Context, step *types.Step, taskUUID string) error { log.Trace().Str("taskUUID", taskUUID).Msgf("Starting step: %s", step.Name) - _, err := StartPod(ctx, e, step) + _, err := startPod(ctx, e, step) return err } @@ -331,7 +331,7 @@ func (e *kube) TailStep(ctx context.Context, step *types.Step, taskUUID string) func (e *kube) DestroyStep(_ context.Context, step *types.Step, taskUUID string) error { log.Trace().Str("taskUUID", taskUUID).Msgf("Stopping step: %s", step.Name) - err := StopPod(e.ctx, e, step, defaultDeleteOptions) + err := stopPod(e.ctx, e, step, defaultDeleteOptions) return err } @@ -342,13 +342,13 @@ func (e *kube) DestroyWorkflow(_ context.Context, conf *types.Config, taskUUID s // Use noContext because the ctx sent to this function will be canceled/done in case of error or canceled by user. for _, stage := range conf.Stages { for _, step := range stage.Steps { - err := StopPod(e.ctx, e, step, defaultDeleteOptions) + err := stopPod(e.ctx, e, step, defaultDeleteOptions) if err != nil { return err } if step.Type == types.StepTypeService { - err := StopService(e.ctx, e, step, defaultDeleteOptions) + err := stopService(e.ctx, e, step, defaultDeleteOptions) if err != nil { return err } @@ -357,7 +357,7 @@ func (e *kube) DestroyWorkflow(_ context.Context, conf *types.Config, taskUUID s } for _, vol := range conf.Volumes { - err := StopVolume(e.ctx, e, vol.Name, defaultDeleteOptions) + err := stopVolume(e.ctx, e, vol.Name, defaultDeleteOptions) if err != nil { return err } diff --git a/pipeline/backend/kubernetes/pod.go b/pipeline/backend/kubernetes/pod.go index c24e346083..5f62bc8b21 100644 --- a/pipeline/backend/kubernetes/pod.go +++ b/pipeline/backend/kubernetes/pod.go @@ -34,7 +34,7 @@ const ( StepLabel = "step" ) -func Pod(namespace, name, image, workDir, goos, serviceAccountName string, +func mkPod(namespace, name, image, workDir, goos, serviceAccountName string, pool, privileged bool, commands, vols, extraHosts []string, labels, annotations, env, nodeSelector map[string]string, @@ -350,13 +350,13 @@ func mapToEnvVars(m map[string]string) []v1.EnvVar { return ev } -func StartPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, error) { +func startPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, error) { podName, err := podName(step) if err != nil { return nil, err } - pod, err := Pod(engine.config.Namespace, podName, step.Image, step.WorkingDir, engine.goos, step.BackendOptions.Kubernetes.ServiceAccountName, + pod, err := mkPod(engine.config.Namespace, podName, step.Image, step.WorkingDir, engine.goos, step.BackendOptions.Kubernetes.ServiceAccountName, step.Pull, step.Privileged, step.Commands, step.Volumes, step.ExtraHosts, engine.config.PodLabels, engine.config.PodAnnotations, step.Environment, step.BackendOptions.Kubernetes.NodeSelector, @@ -369,7 +369,7 @@ func StartPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, err return engine.client.CoreV1().Pods(engine.config.Namespace).Create(ctx, pod, metav1.CreateOptions{}) } -func StopPod(ctx context.Context, engine *kube, step *types.Step, deleteOpts metav1.DeleteOptions) error { +func stopPod(ctx context.Context, engine *kube, step *types.Step, deleteOpts metav1.DeleteOptions) error { podName, err := podName(step) if err != nil { return err diff --git a/pipeline/backend/kubernetes/pod_test.go b/pipeline/backend/kubernetes/pod_test.go index 4695257f25..24f7d64b76 100644 --- a/pipeline/backend/kubernetes/pod_test.go +++ b/pipeline/backend/kubernetes/pod_test.go @@ -106,7 +106,7 @@ func TestTinyPod(t *testing.T) { "status": {} }` - pod, err := Pod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "gradle:8.4.0-jdk21", "/woodpecker/src", "linux/amd64", "", + pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "gradle:8.4.0-jdk21", "/woodpecker/src", "linux/amd64", "", false, false, []string{"gradle build"}, []string{"workspace:/woodpecker/src"}, nil, nil, nil, map[string]string{"CI": "woodpecker"}, nil, @@ -234,7 +234,7 @@ func TestFullPod(t *testing.T) { "status": {} }` - pod, err := Pod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "meltwater/drone-cache", "/woodpecker/src", "linux/amd64", "wp-svc-acc", + pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "meltwater/drone-cache", "/woodpecker/src", "linux/amd64", "wp-svc-acc", true, true, []string{"go get", "go test"}, []string{"woodpecker-cache:/woodpecker/src/cache"}, []string{"cloudflare:1.1.1.1"}, map[string]string{"app": "test"}, map[string]string{"apparmor.security": "runtime/default"}, map[string]string{"CGO": "0"}, map[string]string{"storage": "ssd"}, diff --git a/pipeline/backend/kubernetes/service.go b/pipeline/backend/kubernetes/service.go index 83e7f30330..2fa66a462b 100644 --- a/pipeline/backend/kubernetes/service.go +++ b/pipeline/backend/kubernetes/service.go @@ -26,7 +26,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -func Service(namespace, name string, ports []uint16, selector map[string]string) (*v1.Service, error) { +func mkService(namespace, name string, ports []uint16, selector map[string]string) (*v1.Service, error) { log.Trace().Str("name", name).Interface("selector", selector).Interface("ports", ports).Msg("Creating service") var svcPorts []v1.ServicePort @@ -55,7 +55,7 @@ func serviceName(step *types.Step) (string, error) { return dnsName(step.Name) } -func StartService(ctx context.Context, engine *kube, step *types.Step) (*v1.Service, error) { +func startService(ctx context.Context, engine *kube, step *types.Step) (*v1.Service, error) { name, err := serviceName(step) if err != nil { return nil, err @@ -69,7 +69,7 @@ func StartService(ctx context.Context, engine *kube, step *types.Step) (*v1.Serv StepLabel: podName, } - svc, err := Service(engine.config.Namespace, name, step.Ports, selector) + svc, err := mkService(engine.config.Namespace, name, step.Ports, selector) if err != nil { return nil, err } @@ -77,7 +77,7 @@ func StartService(ctx context.Context, engine *kube, step *types.Step) (*v1.Serv return engine.client.CoreV1().Services(engine.config.Namespace).Create(ctx, svc, metav1.CreateOptions{}) } -func StopService(ctx context.Context, engine *kube, step *types.Step, deleteOpts metav1.DeleteOptions) error { +func stopService(ctx context.Context, engine *kube, step *types.Step, deleteOpts metav1.DeleteOptions) error { svcName, err := serviceName(step) if err != nil { return err diff --git a/pipeline/backend/kubernetes/service_test.go b/pipeline/backend/kubernetes/service_test.go index cdc89a6251..5dc06509ff 100644 --- a/pipeline/backend/kubernetes/service_test.go +++ b/pipeline/backend/kubernetes/service_test.go @@ -71,7 +71,7 @@ func TestService(t *testing.T) { } }` - s, _ := Service("foo", "bar", []uint16{1, 2, 3}, map[string]string{"step": "baz"}) + s, _ := mkService("foo", "bar", []uint16{1, 2, 3}, map[string]string{"step": "baz"}) j, err := json.Marshal(s) assert.NoError(t, err) assert.JSONEq(t, expected, string(j)) diff --git a/pipeline/backend/kubernetes/volume.go b/pipeline/backend/kubernetes/volume.go index 6b07b695a2..92ea532227 100644 --- a/pipeline/backend/kubernetes/volume.go +++ b/pipeline/backend/kubernetes/volume.go @@ -25,7 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRwx bool) (*v1.PersistentVolumeClaim, error) { +func mkPersistentVolumeClaim(namespace, name, storageClass, size string, storageRwx bool) (*v1.PersistentVolumeClaim, error) { _storageClass := &storageClass if storageClass == "" { _storageClass = nil @@ -75,8 +75,8 @@ func volumeMountPath(name string) string { return s[0] } -func StartVolume(ctx context.Context, engine *kube, name string) (*v1.PersistentVolumeClaim, error) { - pvc, err := PersistentVolumeClaim(engine.config.Namespace, name, engine.config.StorageClass, engine.config.VolumeSize, engine.config.StorageRwx) +func startVolume(ctx context.Context, engine *kube, name string) (*v1.PersistentVolumeClaim, error) { + pvc, err := mkPersistentVolumeClaim(engine.config.Namespace, name, engine.config.StorageClass, engine.config.VolumeSize, engine.config.StorageRwx) if err != nil { return nil, err } @@ -85,7 +85,7 @@ func StartVolume(ctx context.Context, engine *kube, name string) (*v1.Persistent return engine.client.CoreV1().PersistentVolumeClaims(engine.config.Namespace).Create(ctx, pvc, metav1.CreateOptions{}) } -func StopVolume(ctx context.Context, engine *kube, name string, deleteOpts metav1.DeleteOptions) error { +func stopVolume(ctx context.Context, engine *kube, name string, deleteOpts metav1.DeleteOptions) error { pvcName, err := volumeName(name) if err != nil { return err diff --git a/pipeline/backend/kubernetes/volume_test.go b/pipeline/backend/kubernetes/volume_test.go index 318c415475..90e180d781 100644 --- a/pipeline/backend/kubernetes/volume_test.go +++ b/pipeline/backend/kubernetes/volume_test.go @@ -85,20 +85,20 @@ func TestPersistentVolumeClaim(t *testing.T) { "status": {} }` - pvc, err := PersistentVolumeClaim("someNamespace", "somename", "local-storage", "1Gi", true) + pvc, err := mkPersistentVolumeClaim("someNamespace", "somename", "local-storage", "1Gi", true) assert.NoError(t, err) j, err := json.Marshal(pvc) assert.NoError(t, err) assert.JSONEq(t, expectedRwx, string(j)) - pvc, err = PersistentVolumeClaim("someNamespace", "somename", "local-storage", "1Gi", false) + pvc, err = mkPersistentVolumeClaim("someNamespace", "somename", "local-storage", "1Gi", false) assert.NoError(t, err) j, err = json.Marshal(pvc) assert.NoError(t, err) assert.JSONEq(t, expectedRwo, string(j)) - _, err = PersistentVolumeClaim("someNamespace", "some0INVALID3name", "local-storage", "1Gi", false) + _, err = mkPersistentVolumeClaim("someNamespace", "some0INVALID3name", "local-storage", "1Gi", false) assert.Error(t, err) }