Skip to content

Commit

Permalink
Merge branch 'main' into linter-gomnd
Browse files Browse the repository at this point in the history
  • Loading branch information
xoxys authored Jan 11, 2024
2 parents d710173 + d1fe86b commit c1683ab
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 108 deletions.
92 changes: 36 additions & 56 deletions pipeline/backend/kubernetes/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,18 @@ import (

const (
StepLabel = "step"
podPrefix = "wp-"
)

func mkPod(namespace, name, image, workDir, goos, serviceAccountName string,
pool, privileged bool,
commands, vols, pullSecretNames []string,
labels, annotations, env, nodeSelector map[string]string,
extraHosts []types.HostAlias, tolerations []types.Toleration, resources types.Resources,
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
) (*v1.Pod, error) {
var err error

meta := podMeta(name, namespace, labels, annotations)
func mkPod(step *types.Step, config *config, podName, goos string) (*v1.Pod, error) {
meta := podMeta(step, config, podName)

spec, err := podSpec(serviceAccountName, vols, pullSecretNames, env, nodeSelector, extraHosts, tolerations,
securityContext, securityContextConfig)
spec, err := podSpec(step, config)
if err != nil {
return nil, err
}

container, err := podContainer(name, image, workDir, goos, pool, privileged, commands, vols, env,
resources, securityContext)
container, err := podContainer(step, podName, goos)
if err != nil {
return nil, err
}
Expand All @@ -74,78 +65,72 @@ func stepToPodName(step *types.Step) (name string, err error) {
}

func podName(step *types.Step) (string, error) {
return dnsName(step.Name)
return dnsName(podPrefix + step.UUID)
}

func podMeta(name, namespace string, labels, annotations map[string]string) metav1.ObjectMeta {
func podMeta(step *types.Step, config *config, podName string) metav1.ObjectMeta {
meta := metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Annotations: annotations,
Name: podName,
Namespace: config.Namespace,
Annotations: config.PodAnnotations,
}

if labels == nil {
labels = make(map[string]string, 1)
}
labels[StepLabel] = name
labels := make(map[string]string, len(config.PodLabels)+1)
// copy to not alter the engine config
maps.Copy(labels, config.PodLabels)
labels[StepLabel] = step.Name
meta.Labels = labels

return meta
}

func podSpec(serviceAccountName string, vols, pullSecretNames []string, env, backendNodeSelector map[string]string,
extraHosts []types.HostAlias, backendTolerations []types.Toleration,
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
) (v1.PodSpec, error) {
func podSpec(step *types.Step, config *config) (v1.PodSpec, error) {
var err error
spec := v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
ServiceAccountName: serviceAccountName,
ImagePullSecrets: imagePullSecretsReferences(pullSecretNames),
}

spec.HostAliases = hostAliases(extraHosts)
spec.NodeSelector = nodeSelector(backendNodeSelector, env["CI_SYSTEM_PLATFORM"])
spec.Tolerations = tolerations(backendTolerations)
spec.SecurityContext = podSecurityContext(securityContext, securityContextConfig)
spec.Volumes, err = volumes(vols)
ServiceAccountName: step.BackendOptions.Kubernetes.ServiceAccountName,
ImagePullSecrets: imagePullSecretsReferences(config.ImagePullSecretNames),
HostAliases: hostAliases(step.ExtraHosts),
NodeSelector: nodeSelector(step.BackendOptions.Kubernetes.NodeSelector, step.Environment["CI_SYSTEM_PLATFORM"]),
Tolerations: tolerations(step.BackendOptions.Kubernetes.Tolerations),
SecurityContext: podSecurityContext(step.BackendOptions.Kubernetes.SecurityContext, config.SecurityContext),
}
spec.Volumes, err = volumes(step.Volumes)
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,
securityContext *types.SecurityContext,
) (v1.Container, error) {
func podContainer(step *types.Step, podName, goos string) (v1.Container, error) {
var err error
container := v1.Container{
Name: name,
Image: image,
WorkingDir: workDir,
Name: podName,
Image: step.Image,
WorkingDir: step.WorkingDir,
}

if pull {
if step.Pull {
container.ImagePullPolicy = v1.PullAlways
}

if len(commands) != 0 {
scriptEnv, command, args := common.GenerateContainerConf(commands, goos)
if len(step.Commands) != 0 {
scriptEnv, command, args := common.GenerateContainerConf(step.Commands, goos)
container.Command = command
container.Args = args
maps.Copy(env, scriptEnv)
maps.Copy(step.Environment, scriptEnv)
}

container.Env = mapToEnvVars(env)
container.SecurityContext = containerSecurityContext(securityContext, privileged)
container.Env = mapToEnvVars(step.Environment)
container.SecurityContext = containerSecurityContext(step.BackendOptions.Kubernetes.SecurityContext, step.Privileged)

container.Resources, err = resourceRequirements(resources)
container.Resources, err = resourceRequirements(step.BackendOptions.Kubernetes.Resources)
if err != nil {
return container, err
}

container.VolumeMounts, err = volumeMounts(volumes)
container.VolumeMounts, err = volumeMounts(step.Volumes)
if err != nil {
return container, err
}
Expand Down Expand Up @@ -378,12 +363,7 @@ func startPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, err
if err != nil {
return nil, err
}

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, engine.config.ImagePullSecretNames,
engine.config.PodLabels, engine.config.PodAnnotations, step.Environment, step.BackendOptions.Kubernetes.NodeSelector,
step.ExtraHosts, step.BackendOptions.Kubernetes.Tolerations, step.BackendOptions.Kubernetes.Resources, step.BackendOptions.Kubernetes.SecurityContext, engine.config.SecurityContext)
pod, err := mkPod(step, engine.config, podName, engine.goos)
if err != nil {
return nil, err
}
Expand Down
94 changes: 71 additions & 23 deletions pipeline/backend/kubernetes/pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,35 @@ import (
)

func TestPodName(t *testing.T) {
name, err := podName(&types.Step{Name: "wp_01he8bebctabr3kgk0qj36d2me_0"})
name, err := podName(&types.Step{UUID: "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{UUID: "01he8bebctabr3kgk0qj36d2me\\0a"})
assert.ErrorIs(t, err, ErrDNSPatternInvalid)

_, err = podName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"})
_, err = podName(&types.Step{UUID: "01he8bebctabr3kgk0qj36d2me-0-services-0..woodpecker-runtime.svc.cluster.local"})
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
}

func TestStepToPodName(t *testing.T) {
name, err := stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "clone", Type: types.StepTypeClone})
assert.NoError(t, err)
assert.EqualValues(t, "wp-01he8bebctabr3kg", name)
name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "clone", Type: types.StepTypeCache})
assert.NoError(t, err)
assert.EqualValues(t, "wp-01he8bebctabr3kg", name)
name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "clone", Type: types.StepTypePlugin})
assert.NoError(t, err)
assert.EqualValues(t, "wp-01he8bebctabr3kg", name)
name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "clone", Type: types.StepTypeCommands})
assert.NoError(t, err)
assert.EqualValues(t, "wp-01he8bebctabr3kg", name)
name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "clone", Type: types.StepTypeService})
assert.NoError(t, err)
assert.EqualValues(t, "clone", name)
}

func TestTinyPod(t *testing.T) {
expected := `
{
Expand All @@ -44,7 +61,7 @@ func TestTinyPod(t *testing.T) {
"namespace": "woodpecker",
"creationTimestamp": null,
"labels": {
"step": "wp-01he8bebctabr3kgk0qj36d2me-0"
"step": "build-via-gradle"
}
},
"spec": {
Expand Down Expand Up @@ -101,13 +118,18 @@ func TestTinyPod(t *testing.T) {
"status": {}
}`

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,
nil, nil,
types.Resources{Requests: nil, Limits: nil}, nil, SecurityContextConfig{},
)
pod, err := mkPod(&types.Step{
Name: "build-via-gradle",
Image: "gradle:8.4.0-jdk21",
WorkingDir: "/woodpecker/src",
Pull: false,
Privileged: false,
Commands: []string{"gradle build"},
Volumes: []string{"workspace:/woodpecker/src"},
Environment: map[string]string{"CI": "woodpecker"},
}, &config{
Namespace: "woodpecker",
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64")
assert.NoError(t, err)

json, err := json.Marshal(pod)
Expand All @@ -126,7 +148,7 @@ func TestFullPod(t *testing.T) {
"creationTimestamp": null,
"labels": {
"app": "test",
"step": "wp-01he8bebctabr3kgk0qj36d2me-0"
"step": "go-test"
},
"annotations": {
"apparmor.security": "runtime/default"
Expand Down Expand Up @@ -242,15 +264,41 @@ func TestFullPod(t *testing.T) {
{Name: "cloudflare", IP: "1.1.1.1"},
{Name: "cf.v6", IP: "2606:4700:4700::64"},
}
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{"regcred", "another-pull-secret"},
map[string]string{"app": "test"}, map[string]string{"apparmor.security": "runtime/default"}, map[string]string{"CGO": "0"}, map[string]string{"storage": "ssd"},
hostAliases, []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},
)
pod, err := mkPod(&types.Step{
Name: "go-test",
Image: "meltwater/drone-cache",
WorkingDir: "/woodpecker/src",
Pull: true,
Privileged: true,
Commands: []string{"go get", "go test"},
Volumes: []string{"woodpecker-cache:/woodpecker/src/cache"},
Environment: map[string]string{"CGO": "0"},
ExtraHosts: hostAliases,
BackendOptions: types.BackendOptions{
Kubernetes: types.KubernetesBackendOptions{
NodeSelector: map[string]string{"storage": "ssd"},
ServiceAccountName: "wp-svc-acc",
Tolerations: []types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}},
Resources: types.Resources{
Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"},
Limits: map[string]string{"memory": "256Mi", "cpu": "2"},
},
SecurityContext: &types.SecurityContext{
Privileged: newBool(true),
RunAsNonRoot: newBool(true),
RunAsUser: newInt64(101),
RunAsGroup: newInt64(101),
FSGroup: newInt64(101),
},
},
},
}, &config{
Namespace: "woodpecker",
ImagePullSecretNames: []string{"regcred", "another-pull-secret"},
PodLabels: map[string]string{"app": "test"},
PodAnnotations: map[string]string{"apparmor.security": "runtime/default"},
SecurityContext: SecurityContextConfig{RunAsNonRoot: false},
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64")
assert.NoError(t, err)

json, err := json.Marshal(pod)
Expand Down
32 changes: 17 additions & 15 deletions pipeline/backend/kubernetes/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,22 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
)

func mkService(namespace, name string, ports []uint16, selector map[string]string) *v1.Service {
log.Trace().Str("name", name).Interface("selector", selector).Interface("ports", ports).Msg("Creating service")
const (
ServiceLabel = "service"
)

func mkService(step *types.Step, namespace string) (*v1.Service, error) {
name, err := serviceName(step)
if err != nil {
return nil, err
}

selector := map[string]string{
ServiceLabel: name,
}

var svcPorts []v1.ServicePort
for _, port := range ports {
for _, port := range step.Ports {
svcPorts = append(svcPorts, v1.ServicePort{
Name: fmt.Sprintf("port-%d", port),
Port: int32(port),
Expand All @@ -48,29 +59,20 @@ func mkService(namespace, name string, ports []uint16, selector map[string]strin
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)
svc, err := mkService(step, engine.config.Namespace)
if err != nil {
return nil, err
}

selector := map[string]string{
StepLabel: podName,
}

svc := mkService(engine.config.Namespace, name, step.Ports, selector)

log.Trace().Str("name", svc.Name).Interface("selector", svc.Spec.Selector).Interface("ports", svc.Spec.Ports).Msg("creating service")
return engine.client.CoreV1().Services(engine.config.Namespace).Create(ctx, svc, metav1.CreateOptions{})
}

Expand Down
21 changes: 13 additions & 8 deletions pipeline/backend/kubernetes/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ import (
)

func TestServiceName(t *testing.T) {
name, err := serviceName(&types.Step{Name: "wp_01he8bebctabr3kgk0qj36d2me_0_services_0"})
name, err := serviceName(&types.Step{Name: "database"})
assert.NoError(t, err)
assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0-services-0", name)
assert.Equal(t, "database", name)

name, err = serviceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0\\services-0"})
name, err = serviceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"})
assert.NoError(t, err)
assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0\\services-0", name)
assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local", name)

_, err = serviceName(&types.Step{Name: "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local"})
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
name, err = serviceName(&types.Step{Name: "awesome_service"})
assert.NoError(t, err)
assert.Equal(t, "awesome-service", name)
}

func TestService(t *testing.T) {
Expand Down Expand Up @@ -62,7 +63,7 @@ func TestService(t *testing.T) {
}
],
"selector": {
"step": "baz"
"service": "bar"
},
"type": "ClusterIP"
},
Expand All @@ -71,7 +72,11 @@ func TestService(t *testing.T) {
}
}`

s := mkService("foo", "bar", []uint16{1, 2, 3}, map[string]string{"step": "baz"})
s, err := mkService(&types.Step{
Name: "bar",
Ports: []uint16{1, 2, 3},
}, "foo")
assert.NoError(t, err)
j, err := json.Marshal(s)
assert.NoError(t, err)
assert.JSONEq(t, expected, string(j))
Expand Down
Loading

0 comments on commit c1683ab

Please sign in to comment.