// 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/kinbiko/jsonassert"
	"github.com/stretchr/testify/assert"
	kube_core_v1 "k8s.io/api/core/v1"
	kube_meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"go.woodpecker-ci.org/woodpecker/v3/pipeline/backend/types"
)

const taskUUID = "11301"

func TestPodName(t *testing.T) {
	name, err := podName(&types.Step{UUID: "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{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: "cache", Type: types.StepTypeCache})
	assert.NoError(t, err)
	assert.EqualValues(t, "wp-01he8bebctabr3kg", name)
	name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "release", Type: types.StepTypePlugin})
	assert.NoError(t, err)
	assert.EqualValues(t, "wp-01he8bebctabr3kg", name)
	name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "prepare-env", Type: types.StepTypeCommands})
	assert.NoError(t, err)
	assert.EqualValues(t, "wp-01he8bebctabr3kg", name)
	name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "postgres", Type: types.StepTypeService, Ports: []types.Port{{Number: 5432}}})
	assert.NoError(t, err)
	assert.EqualValues(t, "wp-svc-01he8bebctabr3kg-postgres", name)
	// Service
	name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "postgres", Detached: true, Type: types.StepTypeService, Ports: []types.Port{{Number: 5432}}})
	assert.NoError(t, err)
	assert.EqualValues(t, "wp-svc-01he8bebctabr3kg-postgres", name)
	// Detached long running container
	name, err = stepToPodName(&types.Step{UUID: "01he8bebctabr3kg", Name: "long running", Detached: true})
	assert.NoError(t, err)
	assert.EqualValues(t, "wp-01he8bebctabr3kg", name)
}

func TestPodMeta(t *testing.T) {
	meta, err := podMeta(&types.Step{
		Name:        "postgres",
		UUID:        "01he8bebctabr3kg",
		Type:        types.StepTypeService,
		Image:       "postgres:16",
		WorkingDir:  "/woodpecker/src",
		Environment: map[string]string{"CI": "woodpecker"},
		Ports:       []types.Port{{Number: 5432}},
	}, &config{
		Namespace: "woodpecker",
	}, BackendOptions{}, "wp-01he8bebctabr3kg-0", taskUUID)
	assert.NoError(t, err)
	assert.EqualValues(t, "wp-svc-01he8bebctabr3kg-postgres", meta.Labels[ServiceLabel])
	assert.EqualValues(t, taskUUID, meta.Labels[TaskUUIDLabel])

	// Service
	meta, err = podMeta(&types.Step{
		Name:        "postgres",
		UUID:        "01he8bebctabr3kg",
		Detached:    true,
		Type:        types.StepTypeService,
		Image:       "postgres:16",
		WorkingDir:  "/woodpecker/src",
		Environment: map[string]string{"CI": "woodpecker"},
		Ports:       []types.Port{{Number: 5432}},
	}, &config{
		Namespace: "woodpecker",
	}, BackendOptions{}, "wp-01he8bebctabr3kg-0", taskUUID)
	assert.NoError(t, err)
	assert.EqualValues(t, "wp-svc-01he8bebctabr3kg-postgres", meta.Labels[ServiceLabel])

	// Detached long running container
	meta, err = podMeta(&types.Step{
		Name:        "long running",
		UUID:        "01he8bebctabr3kg",
		Detached:    true,
		Image:       "postgres:16",
		WorkingDir:  "/woodpecker/src",
		Environment: map[string]string{"CI": "woodpecker"},
	}, &config{
		Namespace: "woodpecker",
	}, BackendOptions{}, "wp-01he8bebctabr3kg-0", taskUUID)
	assert.NoError(t, err)
	assert.EqualValues(t, "", meta.Labels[ServiceLabel])
}

func TestStepLabel(t *testing.T) {
	name, err := stepLabel(&types.Step{Name: "Build image"})
	assert.NoError(t, err)
	assert.EqualValues(t, "build-image", name)

	_, err = stepLabel(&types.Step{Name: ".build.image"})
	assert.ErrorIs(t, err, ErrDNSPatternInvalid)
}

func TestPodHostnameSanitized(t *testing.T) {
	pod, err := mkPod(&types.Step{
		Name:        "Update repos",
		Image:       "alpine:latest",
		UUID:        "01he8bebctabr3kgk0qj36d2me-1",
		WorkingDir:  "/woodpecker/src",
		Environment: map[string]string{},
	}, &config{Namespace: "woodpecker"}, "wp-01he8bebctabr3kgk0qj36d2me-1", "linux/amd64", BackendOptions{}, taskUUID)
	assert.NoError(t, err)
	assert.Equal(t, "update-repos", pod.Spec.Hostname)
}

func TestTinyPod(t *testing.T) {
	const expected = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "build-via-gradle",
				"woodpecker-ci.org/step": "build-via-gradle",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"volumes": [
				{
					"name": "workspace",
					"persistentVolumeClaim": {
						"claimName": "workspace"
					}
				}
			],
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "gradle:8.4.0-jdk21",
					"command": [
						"/bin/sh",
						"-c",
						"echo $CI_SCRIPT | base64 -d | /bin/sh -e"
					],
					"env": [
						"<<UNORDERED>>",
						{
							"name": "CI",
							"value": "woodpecker"
						},
						{
							"name": "SHELL",
							"value": "/bin/sh"
						},
						{
							"name": "CI_SCRIPT",
							"value": "CmlmIFsgLW4gIiRDSV9ORVRSQ19NQUNISU5FIiBdOyB0aGVuCmNhdCA8PEVPRiA+ICRIT01FLy5uZXRyYwptYWNoaW5lICRDSV9ORVRSQ19NQUNISU5FCmxvZ2luICRDSV9ORVRSQ19VU0VSTkFNRQpwYXNzd29yZCAkQ0lfTkVUUkNfUEFTU1dPUkQKRU9GCmNobW9kIDA2MDAgJEhPTUUvLm5ldHJjCmZpCnVuc2V0IENJX05FVFJDX1VTRVJOQU1FCnVuc2V0IENJX05FVFJDX1BBU1NXT1JECnVuc2V0IENJX1NDUklQVApta2RpciAtcCAiL3dvb2RwZWNrZXIvc3JjIgpjZCAiL3dvb2RwZWNrZXIvc3JjIgoKZWNobyArICdncmFkbGUgYnVpbGQnCmdyYWRsZSBidWlsZAo="
						}
					],
					"resources": {},
					"volumeMounts": [
						{
							"name": "workspace",
							"mountPath": "/woodpecker/src"
						}
					]
				}
			],
			"restartPolicy": "Never",
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "build-via-gradle"
		},
		"status": {}
	}`

	pod, err := mkPod(&types.Step{
		Name:        "build-via-gradle",
		Image:       "gradle:8.4.0-jdk21",
		UUID:        "01he8bebctabr3kgk0qj36d2me-0",
		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", BackendOptions{}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expected)
}

func TestServiceWorkspaceVolume(t *testing.T) {
	useWorkspaceVolume := true
	disableWorkspaceVolume := false
	step := &types.Step{
		Name:          "postgres",
		Image:         "postgres:16",
		UUID:          "01he8bebctabr3kgk0qj36d2me-0",
		Type:          types.StepTypeService,
		WorkingDir:    "/woodpecker/src",
		WorkspaceBase: "/woodpecker",
		Environment:   map[string]string{},
		Volumes:       []string{"workspace:/woodpecker", "cache:/cache"},
	}

	pod, err := mkPod(step, &config{Namespace: "woodpecker"}, "wp-svc-postgres", "linux/amd64", BackendOptions{}, taskUUID)
	assert.NoError(t, err)
	assert.Len(t, pod.Spec.Volumes, 2)
	assert.Equal(t, "workspace", pod.Spec.Volumes[0].Name)
	assert.Equal(t, "/woodpecker", pod.Spec.Containers[0].VolumeMounts[0].MountPath)
	assert.Equal(t, "cache", pod.Spec.Volumes[1].Name)
	assert.Equal(t, "/cache", pod.Spec.Containers[0].VolumeMounts[1].MountPath)

	pod, err = mkPod(step, &config{Namespace: "woodpecker"}, "wp-svc-postgres", "linux/amd64", BackendOptions{WorkspaceVolume: &disableWorkspaceVolume}, taskUUID)
	assert.NoError(t, err)
	assert.Len(t, pod.Spec.Volumes, 1)
	assert.Equal(t, "cache", pod.Spec.Volumes[0].Name)
	assert.Len(t, pod.Spec.Containers[0].VolumeMounts, 1)
	assert.Equal(t, "/cache", pod.Spec.Containers[0].VolumeMounts[0].MountPath)

	pod, err = mkPod(step, &config{Namespace: "woodpecker"}, "wp-svc-postgres", "linux/amd64", BackendOptions{WorkspaceVolume: &useWorkspaceVolume}, taskUUID)
	assert.NoError(t, err)
	assert.Len(t, pod.Spec.Volumes, 2)
	assert.Equal(t, "workspace", pod.Spec.Volumes[0].Name)
	assert.Equal(t, "/woodpecker", pod.Spec.Containers[0].VolumeMounts[0].MountPath)
	assert.Equal(t, "cache", pod.Spec.Volumes[1].Name)
	assert.Equal(t, "/cache", pod.Spec.Containers[0].VolumeMounts[1].MountPath)
}

func TestFullPod(t *testing.T) {
	const expected = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"app": "test",
				"part-of": "woodpecker-ci",
				"step": "go-test",
				"woodpecker-ci.org/step": "go-test",
				"woodpecker-ci.org/task-uuid": "11301"
			},
			"annotations": {
				"apps.kubernetes.io/pod-index": "0",
				"kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu, memory request and limit for container"
			}
		},
		"spec": {
			"volumes": [
				{
					"name": "woodpecker-cache",
					"persistentVolumeClaim": {
						"claimName": "woodpecker-cache"
					}
				}
			],
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "meltwater/drone-cache",
					"command": [
						"/bin/sh",
						"-c"
					],
					"ports": [
						{
							"containerPort": 1234
						},
						{
							"containerPort": 2345,
							"protocol": "TCP"
						},
						{
							"containerPort": 3456,
							"protocol": "UDP"
						}
					],
					"env": [
						"<<UNORDERED>>",
						{
							"name": "CGO",
							"value": "0"
						},
						{
							"name": "CI_SCRIPT",
							"value": "CmlmIFsgLW4gIiRDSV9ORVRSQ19NQUNISU5FIiBdOyB0aGVuCmNhdCA8PEVPRiA+ICRIT01FLy5uZXRyYwptYWNoaW5lICRDSV9ORVRSQ19NQUNISU5FCmxvZ2luICRDSV9ORVRSQ19VU0VSTkFNRQpwYXNzd29yZCAkQ0lfTkVUUkNfUEFTU1dPUkQKRU9GCmNobW9kIDA2MDAgJEhPTUUvLm5ldHJjCmZpCnVuc2V0IENJX05FVFJDX1VTRVJOQU1FCnVuc2V0IENJX05FVFJDX1BBU1NXT1JECnVuc2V0IENJX1NDUklQVApta2RpciAtcCAiL3dvb2RwZWNrZXIvc3JjIgpjZCAiL3dvb2RwZWNrZXIvc3JjIgoKZWNobyArICdnbyBnZXQnCmdvIGdldAoKZWNobyArICdnbyB0ZXN0JwpnbyB0ZXN0Cg=="
						},
						{
							"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,
						"allowPrivilegeEscalation": false,
						"capabilities": {
							"drop": ["ALL"]
						}
					}
				}
			],
			"restartPolicy": "Never",
			"nodeSelector": {
				"storage": "ssd",
				"topology.kubernetes.io/region": "eu-central-1"
			},
			"runtimeClassName": "runc",
			"serviceAccountName": "wp-svc-acc",
			"securityContext": {
				"runAsUser": 101,
				"runAsGroup": 101,
				"runAsNonRoot": true,
				"fsGroup": 101,
				"fsGroupChangePolicy": "OnRootMismatch",
				"appArmorProfile": {
					"type": "Localhost",
					"localhostProfile": "k8s-apparmor-example-deny-write"
				},
				"seccompProfile": {
					"type": "Localhost",
					"localhostProfile": "profiles/audit.json"
				}
			},
			"imagePullSecrets": [
				{
					"name": "regcred"
				},
				{
					"name": "another-pull-secret"
				},
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0"
				}
			],
			"tolerations": [
				{
					"key": "net-port",
					"value": "100Mbit",
					"effect": "NoSchedule"
				}
			],
			"hostAliases": [
				{
					"ip": "1.1.1.1",
					"hostnames": [
						"cloudflare"
					]
				},
				{
					"ip": "2606:4700:4700::64",
					"hostnames": [
						"cf.v6"
					]
				}
			],
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]},
			"subdomain": "wp-hsvc-11301",
			"hostname": "go-test"
		},
		"status": {}
	}`

	runtimeClass := "runc"
	hostAliases := []types.HostAlias{
		{Name: "cloudflare", IP: "1.1.1.1"},
		{Name: "cf.v6", IP: "2606:4700:4700::64"},
	}
	ports := []types.Port{
		{Number: 1234},
		{Number: 2345, Protocol: "tcp"},
		{Number: 3456, Protocol: "udp"},
	}
	fsGroupChangePolicy := kube_core_v1.PodFSGroupChangePolicy("OnRootMismatch")
	secCtx := SecurityContext{
		Privileged:               newBool(true),
		RunAsNonRoot:             newBool(true),
		RunAsUser:                newInt64(101),
		RunAsGroup:               newInt64(101),
		FSGroup:                  newInt64(101),
		FsGroupChangePolicy:      &fsGroupChangePolicy,
		AllowPrivilegeEscalation: newBool(false),
		Capabilities: &Capabilities{
			Drop: []string{"ALL"},
		},
		SeccompProfile: &SecProfile{
			Type:             "Localhost",
			LocalhostProfile: "profiles/audit.json",
		},
		ApparmorProfile: &SecProfile{
			Type:             "Localhost",
			LocalhostProfile: "k8s-apparmor-example-deny-write",
		},
	}
	pod, err := mkPod(&types.Step{
		UUID:        "01he8bebctabr3kgk0qj36d2me-0",
		Name:        "go-test",
		Image:       "meltwater/drone-cache",
		WorkingDir:  "/woodpecker/src",
		Pull:        true,
		Privileged:  true,
		Commands:    []string{"go get", "go test"},
		Entrypoint:  []string{"/bin/sh", "-c"},
		Volumes:     []string{"woodpecker-cache:/woodpecker/src/cache"},
		Environment: map[string]string{"CGO": "0"},
		ExtraHosts:  hostAliases,
		Ports:       ports,
		AuthConfig: types.Auth{
			Username: "foo",
			Password: "bar",
		},
	}, &config{
		Namespace:                   "woodpecker",
		ImagePullSecretNames:        []string{"regcred", "another-pull-secret"},
		PodLabels:                   map[string]string{"app": "test"},
		PodLabelsAllowFromStep:      true,
		PodAnnotations:              map[string]string{"apps.kubernetes.io/pod-index": "0"},
		PodAnnotationsAllowFromStep: true,
		PodTolerationsAllowFromStep: true,
		PodNodeSelector:             map[string]string{"topology.kubernetes.io/region": "eu-central-1"},
		SecurityContext:             SecurityContextConfig{RunAsNonRoot: false},
	},
		"wp-01he8bebctabr3kgk0qj36d2me-0",
		"linux/amd64",
		BackendOptions{
			Labels:             map[string]string{"part-of": "woodpecker-ci"},
			Annotations:        map[string]string{"kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu, memory request and limit for container"},
			NodeSelector:       map[string]string{"storage": "ssd"},
			RuntimeClassName:   &runtimeClass,
			ServiceAccountName: "wp-svc-acc",
			Tolerations:        []Toleration{{Key: "net-port", Value: "100Mbit", Effect: TaintEffectNoSchedule}},
			Resources: Resources{
				Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"},
				Limits:   map[string]string{"memory": "256Mi", "cpu": "2"},
			},
			SecurityContext: &secCtx,
		}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expected)
}

func TestPodPrivilege(t *testing.T) {
	createTestPod := func(stepPrivileged, globalRunAsRoot bool, secCtx SecurityContext) (*kube_core_v1.Pod, error) {
		return mkPod(&types.Step{
			Name:       "go-test",
			Image:      "golang:1.16",
			UUID:       "01he8bebctabr3kgk0qj36d2me-0",
			Privileged: stepPrivileged,
		}, &config{
			Namespace:       "woodpecker",
			SecurityContext: SecurityContextConfig{RunAsNonRoot: globalRunAsRoot},
		}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
			SecurityContext: &secCtx,
		}, "11301")
	}

	// securty context is requesting user and group 101 (non-root)
	secCtx := SecurityContext{
		RunAsUser:  newInt64(101),
		RunAsGroup: newInt64(101),
		FSGroup:    newInt64(101),
	}
	pod, err := createTestPod(false, false, secCtx)
	assert.NoError(t, err)
	assert.Equal(t, int64(101), *pod.Spec.SecurityContext.RunAsUser)
	assert.Equal(t, int64(101), *pod.Spec.SecurityContext.RunAsGroup)
	assert.Equal(t, int64(101), *pod.Spec.SecurityContext.FSGroup)

	// securty context is requesting root, but step is not privileged
	secCtx = SecurityContext{
		RunAsUser:  newInt64(0),
		RunAsGroup: newInt64(0),
		FSGroup:    newInt64(0),
	}
	pod, err = createTestPod(false, false, secCtx)
	assert.NoError(t, err)
	assert.Equal(t, &kube_core_v1.PodSecurityContext{
		SELinuxOptions:           (*kube_core_v1.SELinuxOptions)(nil),
		WindowsOptions:           (*kube_core_v1.WindowsSecurityContextOptions)(nil),
		RunAsUser:                (*int64)(nil),
		RunAsGroup:               (*int64)(nil),
		RunAsNonRoot:             (*bool)(nil),
		SupplementalGroups:       []int64(nil),
		SupplementalGroupsPolicy: (*kube_core_v1.SupplementalGroupsPolicy)(nil),
		FSGroup:                  newInt64(0),
		Sysctls:                  []kube_core_v1.Sysctl(nil),
		FSGroupChangePolicy:      (*kube_core_v1.PodFSGroupChangePolicy)(nil),
		SeccompProfile:           (*kube_core_v1.SeccompProfile)(nil),
		AppArmorProfile:          (*kube_core_v1.AppArmorProfile)(nil),
	}, pod.Spec.SecurityContext)
	assert.Nil(t, pod.Spec.Containers[0].SecurityContext)

	// step is not privileged, but security context is requesting privileged
	secCtx = SecurityContext{
		Privileged: newBool(true),
	}
	pod, err = createTestPod(false, false, secCtx)
	assert.NoError(t, err)
	assert.Nil(t, pod.Spec.SecurityContext)
	assert.Equal(t, (*kube_core_v1.PodSecurityContext)(nil), pod.Spec.SecurityContext)

	// step is privileged and security context is requesting privileged
	secCtx = SecurityContext{
		Privileged: newBool(true),
	}
	pod, err = createTestPod(true, false, secCtx)
	assert.NoError(t, err)
	assert.True(t, *pod.Spec.Containers[0].SecurityContext.Privileged)

	// step is privileged and no security context is provided
	secCtx = SecurityContext{}
	pod, err = createTestPod(true, false, secCtx)
	assert.NoError(t, err)
	assert.True(t, *pod.Spec.Containers[0].SecurityContext.Privileged)

	// global runAsNonRoot is true and override is requested value by security context
	secCtx = SecurityContext{
		RunAsNonRoot: newBool(false),
	}
	pod, err = createTestPod(false, true, secCtx)
	assert.NoError(t, err)
	assert.True(t, *pod.Spec.SecurityContext.RunAsNonRoot)

	// non-privileged step with allowPrivilegeEscalation=false: applied
	secCtx = SecurityContext{
		AllowPrivilegeEscalation: newBool(false),
	}
	pod, err = createTestPod(false, false, secCtx)
	assert.NoError(t, err)
	assert.NotNil(t, pod.Spec.Containers[0].SecurityContext)
	assert.False(t, *pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation)
	assert.Nil(t, pod.Spec.Containers[0].SecurityContext.Privileged)

	// non-privileged step with allowPrivilegeEscalation=true: ignored
	secCtx = SecurityContext{
		AllowPrivilegeEscalation: newBool(true),
	}
	pod, err = createTestPod(false, false, secCtx)
	assert.NoError(t, err)
	assert.Nil(t, pod.Spec.Containers[0].SecurityContext)

	// privileged step with allowPrivilegeEscalation=true: ignored
	secCtx = SecurityContext{
		AllowPrivilegeEscalation: newBool(true),
	}
	pod, err = createTestPod(true, false, secCtx)
	assert.NoError(t, err)
	assert.Nil(t, pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation)

	// non-privileged step with capabilities drop: applied
	secCtx = SecurityContext{
		Capabilities: &Capabilities{Drop: []string{"ALL"}},
	}
	pod, err = createTestPod(false, false, secCtx)
	assert.NoError(t, err)
	assert.NotNil(t, pod.Spec.Containers[0].SecurityContext)
	assert.Equal(t, []kube_core_v1.Capability{"ALL"}, pod.Spec.Containers[0].SecurityContext.Capabilities.Drop)
	assert.Nil(t, pod.Spec.Containers[0].SecurityContext.Capabilities.Add)
	assert.Nil(t, pod.Spec.Containers[0].SecurityContext.Privileged)

	// non-privileged step with drop capabilities and allowPrivilegeEscalation=false: both applied
	secCtx = SecurityContext{
		AllowPrivilegeEscalation: newBool(false),
		Capabilities:             &Capabilities{Drop: []string{"ALL"}},
	}
	pod, err = createTestPod(false, false, secCtx)
	assert.NoError(t, err)
	assert.NotNil(t, pod.Spec.Containers[0].SecurityContext)
	assert.Nil(t, pod.Spec.Containers[0].SecurityContext.Privileged)
	assert.False(t, *pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation)
	assert.Equal(t, []kube_core_v1.Capability{"ALL"}, pod.Spec.Containers[0].SecurityContext.Capabilities.Drop)
}

func TestScratchPod(t *testing.T) {
	const expected = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "curl-google",
				"woodpecker-ci.org/step": "curl-google",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "quay.io/curl/curl",
					"command": [
						"/usr/bin/curl",
						"-v",
						"google.com"
					],
					"resources": {}
				}
			],
			"restartPolicy": "Never",
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "curl-google"
		},
		"status": {}
	}`

	pod, err := mkPod(&types.Step{
		Name:       "curl-google",
		Image:      "quay.io/curl/curl",
		UUID:       "01he8bebctabr3kgk0qj36d2me-0",
		Entrypoint: []string{"/usr/bin/curl", "-v", "google.com"},
	}, &config{
		Namespace: "woodpecker",
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expected)
}

func TestSecrets(t *testing.T) {
	const expected = `
	{
		"metadata": {
			"name": "wp-3kgk0qj36d2me01he8bebctabr-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "test-secrets",
				"woodpecker-ci.org/step": "test-secrets",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"volumes": [
				{
					"name": "workspace",
					"persistentVolumeClaim": {
						"claimName": "workspace"
					}
				},
				{
					"name": "reg-cred",
					"secret": {
						"secretName": "reg-cred"
					}
				}
			],
			"containers": [
				{
					"name": "wp-3kgk0qj36d2me01he8bebctabr-0",
					"image": "alpine",
					"envFrom": [
						{
							"secretRef": {
								"name": "ghcr-push-secret"
							}
						}
					],
					"env": [
						{
							"name": "CGO",
							"value": "0"
						},
						{
							"name": "AWS_ACCESS_KEY_ID",
							"valueFrom": {
								"secretKeyRef": {
									"name": "aws-ecr",
									"key": "AWS_ACCESS_KEY_ID"
								}
							}
						},
						{
							"name": "AWS_SECRET_ACCESS_KEY",
							"valueFrom": {
								"secretKeyRef": {
									"name": "aws-ecr",
									"key": "access-key"
								}
							}
						}
					],
					"resources": {},
					"volumeMounts": [
						{
							"name": "workspace",
							"mountPath": "/woodpecker/src"
						},
						{
							"name": "reg-cred",
							"mountPath": "~/.docker/config.json",
							"subPath": ".dockerconfigjson",
							"readOnly": true
						}
					]
				}
			],
			"restartPolicy": "Never",
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "test-secrets"
		},
		"status": {}
	}`

	pod, err := mkPod(&types.Step{
		Name:        "test-secrets",
		Image:       "alpine",
		UUID:        "01he8bebctabr3kgk0qj36d2me-0",
		Environment: map[string]string{"CGO": "0"},
		Volumes:     []string{"workspace:/woodpecker/src"},
	}, &config{
		Namespace:                  "woodpecker",
		NativeSecretsAllowFromStep: true,
	}, "wp-3kgk0qj36d2me01he8bebctabr-0", "linux/amd64", BackendOptions{
		Secrets: []SecretRef{
			{
				Name: "ghcr-push-secret",
			},
			{
				Name: "aws-ecr",
				Key:  "AWS_ACCESS_KEY_ID",
			},
			{
				Name:   "aws-ecr",
				Key:    "access-key",
				Target: SecretTarget{Env: "AWS_SECRET_ACCESS_KEY"},
			},
			{
				Name:   "reg-cred",
				Key:    ".dockerconfigjson",
				Target: SecretTarget{File: "~/.docker/config.json"},
			},
		},
	}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expected)
}

func TestPodTolerations(t *testing.T) {
	const expected = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "toleration-test",
				"woodpecker-ci.org/step": "toleration-test",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "alpine",
					"resources": {}
				}
			],
			"restartPolicy": "Never",
			"tolerations": [
				{
					"key": "foo",
					"value": "bar",
					"effect": "NoSchedule"
				},
				{
					"key": "baz",
					"value": "qux",
					"effect": "NoExecute"
				}
			],
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "toleration-test"
		},
		"status": {}
	}`

	globalTolerations := []Toleration{
		{Key: "foo", Value: "bar", Effect: TaintEffectNoSchedule},
		{Key: "baz", Value: "qux", Effect: TaintEffectNoExecute},
	}

	pod, err := mkPod(&types.Step{
		Name:  "toleration-test",
		Image: "alpine",
		UUID:  "01he8bebctabr3kgk0qj36d2me-0",
	}, &config{
		Namespace:                   "woodpecker",
		PodTolerations:              globalTolerations,
		PodTolerationsAllowFromStep: false,
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expected)
}

func TestPodTolerationsAllowFromStep(t *testing.T) {
	const expectedDisallow = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "toleration-test",
				"woodpecker-ci.org/step": "toleration-test",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "alpine",
					"resources": {}
				}
			],
			"restartPolicy": "Never",
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "toleration-test"
		},
		"status": {}
	}`
	const expectedAllow = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "toleration-test",
				"woodpecker-ci.org/step": "toleration-test",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "alpine",
					"resources": {}
				}
			],
			"restartPolicy": "Never",
			"tolerations": [
				{
					"key": "custom",
					"value": "value",
					"effect": "NoSchedule"
				}
			],
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "toleration-test"
		},
		"status": {}
	}`

	stepTolerations := []Toleration{
		{Key: "custom", Value: "value", Effect: TaintEffectNoSchedule},
	}

	step := &types.Step{
		Name:  "toleration-test",
		Image: "alpine",
		UUID:  "01he8bebctabr3kgk0qj36d2me-0",
	}

	pod, err := mkPod(step, &config{
		Namespace:                   "woodpecker",
		PodTolerationsAllowFromStep: false,
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
		Tolerations: stepTolerations,
	}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expectedDisallow)

	pod, err = mkPod(step, &config{
		Namespace:                   "woodpecker",
		PodTolerationsAllowFromStep: true,
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
		Tolerations: stepTolerations,
	}, taskUUID)
	assert.NoError(t, err)

	podJSON, err = json.Marshal(pod)
	assert.NoError(t, err)

	ja = jsonassert.New(t)
	ja.Assertf(string(podJSON), expectedAllow)
}

func TestStepSecret(t *testing.T) {
	const expected = `{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0-step-secret",
			"namespace": "woodpecker"
		},
		"type": "Opaque",
		"stringData": {
			"VERY_SECRET": "secret_value"
		}
	}`

	secret, err := mkStepSecret(&types.Step{
		UUID:  "01he8bebctabr3kgk0qj36d2me-0",
		Name:  "go-test",
		Image: "meltwater/drone-cache",
		SecretMapping: map[string]string{
			"VERY_SECRET": "secret_value",
		},
	}, &config{
		Namespace: "woodpecker",
	})
	assert.NoError(t, err)

	secretJSON, err := json.Marshal(secret)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(secretJSON), expected)
}

func TestPodAffinity(t *testing.T) {
	const expected = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "affinity-test",
				"woodpecker-ci.org/step": "affinity-test",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "alpine",
					"resources": {}
				}
			],
			"restartPolicy": "Never",
			"affinity": {
				"podAffinity": {
					"requiredDuringSchedulingIgnoredDuringExecution": [
						{
							"labelSelector": {},
							"matchLabelKeys": ["woodpecker-ci.org/task-uuid"],
							"topologyKey": "kubernetes.io/hostname"
						}
					]
				}
			},
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "affinity-test"
		},
		"status": {}
	}`

	agentAffinity := &kube_core_v1.Affinity{
		PodAffinity: &kube_core_v1.PodAffinity{
			RequiredDuringSchedulingIgnoredDuringExecution: []kube_core_v1.PodAffinityTerm{
				{
					LabelSelector:  &kube_meta_v1.LabelSelector{},
					MatchLabelKeys: []string{"woodpecker-ci.org/task-uuid"},
					TopologyKey:    "kubernetes.io/hostname",
				},
			},
		},
	}

	pod, err := mkPod(&types.Step{
		Name:  "affinity-test",
		Image: "alpine",
		UUID:  "01he8bebctabr3kgk0qj36d2me-0",
	}, &config{
		Namespace:                "woodpecker",
		PodAffinity:              agentAffinity,
		PodAffinityAllowFromStep: false,
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expected)
}

func TestPodAffinityAllowFromStep(t *testing.T) {
	const expectedDisallow = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "affinity-test",
				"woodpecker-ci.org/step": "affinity-test",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "alpine",
					"resources": {}
				}
			],
			"restartPolicy": "Never",
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "affinity-test"
		},
		"status": {}
	}`
	const expectedAllow = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "affinity-test",
				"woodpecker-ci.org/step": "affinity-test",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "alpine",
					"resources": {}
				}
			],
			"restartPolicy": "Never",
			"affinity": {
				"podAntiAffinity": {
					"preferredDuringSchedulingIgnoredDuringExecution": [
						{
							"weight": 100,
							"podAffinityTerm": {
								"labelSelector": {
									"matchLabels": {
										"app": "woodpecker"
									}
								},
								"topologyKey": "kubernetes.io/hostname"
							}
						}
					]
				}
			},
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "affinity-test"
		},
		"status": {}
	}`

	stepAffinity := &kube_core_v1.Affinity{
		PodAntiAffinity: &kube_core_v1.PodAntiAffinity{
			PreferredDuringSchedulingIgnoredDuringExecution: []kube_core_v1.WeightedPodAffinityTerm{
				{
					Weight: 100,
					PodAffinityTerm: kube_core_v1.PodAffinityTerm{
						LabelSelector: &kube_meta_v1.LabelSelector{
							MatchLabels: map[string]string{
								"app": "woodpecker",
							},
						},
						TopologyKey: "kubernetes.io/hostname",
					},
				},
			},
		},
	}

	step := &types.Step{
		Name:  "affinity-test",
		Image: "alpine",
		UUID:  "01he8bebctabr3kgk0qj36d2me-0",
	}

	pod, err := mkPod(step, &config{
		Namespace:                "woodpecker",
		PodAffinity:              nil,
		PodAffinityAllowFromStep: false,
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
		Affinity: stepAffinity,
	}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expectedDisallow)

	pod, err = mkPod(step, &config{
		Namespace:                "woodpecker",
		PodAffinity:              nil,
		PodAffinityAllowFromStep: true,
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
		Affinity: stepAffinity,
	}, taskUUID)
	assert.NoError(t, err)

	podJSON, err = json.Marshal(pod)
	assert.NoError(t, err)

	ja = jsonassert.New(t)
	ja.Assertf(string(podJSON), expectedAllow)
}

func TestPodAffinityStepOverridesAgent(t *testing.T) {
	const expected = `
	{
		"metadata": {
			"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
			"namespace": "woodpecker",
			"labels": {
				"step": "affinity-test",
				"woodpecker-ci.org/step": "affinity-test",
				"woodpecker-ci.org/task-uuid": "11301"
			}
		},
		"spec": {
			"containers": [
				{
					"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
					"image": "alpine",
					"resources": {}
				}
			],
			"restartPolicy": "Never",
			"affinity": {
				"nodeAffinity": {
					"requiredDuringSchedulingIgnoredDuringExecution": {
						"nodeSelectorTerms": [
							{
								"matchExpressions": [
									{
										"key": "disk-type",
										"operator": "In",
										"values": ["ssd"]
									}
								]
							}
						]
					}
				}
			},
			"dnsConfig": {
				"searches": ["wp-hsvc-11301.woodpecker.svc.cluster.local"]
			},
			"subdomain": "wp-hsvc-11301",
			"hostname": "affinity-test"
		},
		"status": {}
	}`

	agentAffinity := &kube_core_v1.Affinity{
		NodeAffinity: &kube_core_v1.NodeAffinity{
			RequiredDuringSchedulingIgnoredDuringExecution: &kube_core_v1.NodeSelector{
				NodeSelectorTerms: []kube_core_v1.NodeSelectorTerm{
					{
						MatchExpressions: []kube_core_v1.NodeSelectorRequirement{
							{
								Key:      "topology.kubernetes.io/zone",
								Operator: kube_core_v1.NodeSelectorOpIn,
								Values:   []string{"eu-central-1a"},
							},
						},
					},
				},
			},
		},
	}

	stepAffinity := &kube_core_v1.Affinity{
		NodeAffinity: &kube_core_v1.NodeAffinity{
			RequiredDuringSchedulingIgnoredDuringExecution: &kube_core_v1.NodeSelector{
				NodeSelectorTerms: []kube_core_v1.NodeSelectorTerm{
					{
						MatchExpressions: []kube_core_v1.NodeSelectorRequirement{
							{
								Key:      "disk-type",
								Operator: kube_core_v1.NodeSelectorOpIn,
								Values:   []string{"ssd"},
							},
						},
					},
				},
			},
		},
	}

	pod, err := mkPod(&types.Step{
		Name:  "affinity-test",
		Image: "alpine",
		UUID:  "01he8bebctabr3kgk0qj36d2me-0",
	}, &config{
		Namespace:                "woodpecker",
		PodAffinity:              agentAffinity,
		PodAffinityAllowFromStep: true,
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
		Affinity: stepAffinity,
	}, taskUUID)
	assert.NoError(t, err)

	podJSON, err := json.Marshal(pod)
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expected)
}

func TestInitContainer(t *testing.T) {
	const expected = `
	{
		"name": "init-wp-01he8bebctabr3kgk0qj36d2me-0",
		"image": "busybox:stable-musl",
		"imagePullPolicy": "Always",
		"args": [
			"mkdir",
			"-p",
			"/woodpecker/src/github.com/woodpecker-ci/woodpecker"
		],
		"resources": {
			"requests": {
				"cpu": "5m",
				"memory": "5Mi"
			},
			"limits": {
				"cpu": "5m",
				"memory": "5Mi"
			}
		},
		"securityContext": {
			"allowPrivilegeEscalation": false,
			"capabilities": {"drop": ["ALL"]}
		},
		"volumeMounts": [
			{
				"name": "workspace",
				"mountPath": "/woodpecker/src"
			}
		]
	}`

	pod, err := mkPod(&types.Step{
		Name:       "clone",
		Image:      "docker.io/woodpeckerci/plugin-git",
		UUID:       "01he8bebctabr3kgk0qj36d2me-0",
		WorkingDir: "/woodpecker/src/github.com/woodpecker-ci/woodpecker",
		Volumes:    []string{"workspace:/woodpecker/src", "other:/other"},
	}, &config{
		Namespace: "woodpecker",
	}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
		SecurityContext: &SecurityContext{
			RunAsNonRoot: newBool(true),
			RunAsUser:    newInt64(1000),
		},
	}, taskUUID)
	assert.NoError(t, err)

	assert.NotNil(t, pod.Spec.InitContainers)
	assert.NotEmpty(t, pod.Spec.InitContainers)
	assert.Len(t, pod.Spec.InitContainers, 1)
	podJSON, err := json.Marshal(pod.Spec.InitContainers[0])
	assert.NoError(t, err)

	ja := jsonassert.New(t)
	ja.Assertf(string(podJSON), expected)
}

func TestUnrequiredInitContainer(t *testing.T) {
	createTestPod := func(workingDir string, backendOpts BackendOptions) (*kube_core_v1.Pod, error) {
		return mkPod(&types.Step{
			Name:       "init-container-test",
			Image:      "docker.io/woodpeckerci/plugin-git",
			UUID:       "01he8bebctabr3kgk0qj36d2me-0",
			WorkingDir: workingDir,
			Volumes:    []string{"workspace:/woodpecker/src"},
		}, &config{
			Namespace: "woodpecker",
		}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", backendOpts, taskUUID)
	}
	// no security context (pod running as root), does not need init container
	pod, err := createTestPod("/woodpecker/src/github.com/woodpecker-ci/woodpecker", BackendOptions{})
	assert.NoError(t, err)
	assert.Nil(t, pod.Spec.InitContainers)

	// explicit security context requesting root, does not need init container
	pod, err = createTestPod("/woodpecker/src/github.com/woodpecker-ci/woodpecker", BackendOptions{
		SecurityContext: &SecurityContext{
			RunAsNonRoot: newBool(false),
			RunAsUser:    newInt64(0),
		},
	})
	assert.NoError(t, err)
	assert.Nil(t, pod.Spec.InitContainers)

	// working dir is outside of the workspace volume, does not need init container
	pod, err = createTestPod("/tmp", BackendOptions{
		SecurityContext: &SecurityContext{
			RunAsNonRoot: newBool(true),
			RunAsUser:    newInt64(1000),
		},
	})
	assert.NoError(t, err)
	assert.Nil(t, pod.Spec.InitContainers)

	// workingDir is exactly the same as the workspace volume mount path, does not need init container
	pod, err = createTestPod("/woodpecker/src", BackendOptions{
		SecurityContext: &SecurityContext{
			RunAsNonRoot: newBool(true),
			RunAsUser:    newInt64(1000),
		},
	})
	assert.NoError(t, err)
	assert.Nil(t, pod.Spec.InitContainers)
}
