mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-15 22:41:19 +00:00
Use UUID as podName and cleanup arguments for Kubernetes backend (#3135)
to much args are just horrible to maintain. And we already have it nice structured stored as step.
This commit is contained in:
parent
7756c60a33
commit
d1fe86b7be
7 changed files with 201 additions and 108 deletions
|
@ -32,27 +32,18 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StepLabel = "step"
|
StepLabel = "step"
|
||||||
|
podPrefix = "wp-"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mkPod(namespace, name, image, workDir, goos, serviceAccountName string,
|
func mkPod(step *types.Step, config *config, podName, goos string) (*v1.Pod, error) {
|
||||||
pool, privileged bool,
|
meta := podMeta(step, config, podName)
|
||||||
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)
|
spec, err := podSpec(step, config)
|
||||||
|
|
||||||
spec, err := podSpec(serviceAccountName, vols, pullSecretNames, env, nodeSelector, extraHosts, tolerations,
|
|
||||||
securityContext, securityContextConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := podContainer(name, image, workDir, goos, pool, privileged, commands, vols, env,
|
container, err := podContainer(step, podName, goos)
|
||||||
resources, securityContext)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -74,41 +65,37 @@ func stepToPodName(step *types.Step) (name string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func podName(step *types.Step) (string, 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{
|
meta := metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: podName,
|
||||||
Namespace: namespace,
|
Namespace: config.Namespace,
|
||||||
Annotations: annotations,
|
Annotations: config.PodAnnotations,
|
||||||
}
|
}
|
||||||
|
|
||||||
if labels == nil {
|
labels := make(map[string]string, len(config.PodLabels)+1)
|
||||||
labels = make(map[string]string, 1)
|
// copy to not alter the engine config
|
||||||
}
|
maps.Copy(labels, config.PodLabels)
|
||||||
labels[StepLabel] = name
|
labels[StepLabel] = step.Name
|
||||||
meta.Labels = labels
|
meta.Labels = labels
|
||||||
|
|
||||||
return meta
|
return meta
|
||||||
}
|
}
|
||||||
|
|
||||||
func podSpec(serviceAccountName string, vols, pullSecretNames []string, env, backendNodeSelector map[string]string,
|
func podSpec(step *types.Step, config *config) (v1.PodSpec, error) {
|
||||||
extraHosts []types.HostAlias, backendTolerations []types.Toleration,
|
|
||||||
securityContext *types.SecurityContext, securityContextConfig SecurityContextConfig,
|
|
||||||
) (v1.PodSpec, error) {
|
|
||||||
var err error
|
var err error
|
||||||
spec := v1.PodSpec{
|
spec := v1.PodSpec{
|
||||||
RestartPolicy: v1.RestartPolicyNever,
|
RestartPolicy: v1.RestartPolicyNever,
|
||||||
ServiceAccountName: serviceAccountName,
|
ServiceAccountName: step.BackendOptions.Kubernetes.ServiceAccountName,
|
||||||
ImagePullSecrets: imagePullSecretsReferences(pullSecretNames),
|
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)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return spec, err
|
return spec, err
|
||||||
}
|
}
|
||||||
|
@ -116,36 +103,34 @@ func podSpec(serviceAccountName string, vols, pullSecretNames []string, env, bac
|
||||||
return spec, nil
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func podContainer(name, image, workDir, goos string, pull, privileged bool, commands, volumes []string, env map[string]string, resources types.Resources,
|
func podContainer(step *types.Step, podName, goos string) (v1.Container, error) {
|
||||||
securityContext *types.SecurityContext,
|
|
||||||
) (v1.Container, error) {
|
|
||||||
var err error
|
var err error
|
||||||
container := v1.Container{
|
container := v1.Container{
|
||||||
Name: name,
|
Name: podName,
|
||||||
Image: image,
|
Image: step.Image,
|
||||||
WorkingDir: workDir,
|
WorkingDir: step.WorkingDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
if pull {
|
if step.Pull {
|
||||||
container.ImagePullPolicy = v1.PullAlways
|
container.ImagePullPolicy = v1.PullAlways
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(commands) != 0 {
|
if len(step.Commands) != 0 {
|
||||||
scriptEnv, command, args := common.GenerateContainerConf(commands, goos)
|
scriptEnv, command, args := common.GenerateContainerConf(step.Commands, goos)
|
||||||
container.Command = command
|
container.Command = command
|
||||||
container.Args = args
|
container.Args = args
|
||||||
maps.Copy(env, scriptEnv)
|
maps.Copy(step.Environment, scriptEnv)
|
||||||
}
|
}
|
||||||
|
|
||||||
container.Env = mapToEnvVars(env)
|
container.Env = mapToEnvVars(step.Environment)
|
||||||
container.SecurityContext = containerSecurityContext(securityContext, privileged)
|
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 {
|
if err != nil {
|
||||||
return container, err
|
return container, err
|
||||||
}
|
}
|
||||||
|
|
||||||
container.VolumeMounts, err = volumeMounts(volumes)
|
container.VolumeMounts, err = volumeMounts(step.Volumes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return container, err
|
return container, err
|
||||||
}
|
}
|
||||||
|
@ -378,12 +363,7 @@ func startPod(ctx context.Context, engine *kube, step *types.Step) (*v1.Pod, err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
pod, err := mkPod(step, engine.config, podName, engine.goos)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,16 +24,33 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPodName(t *testing.T) {
|
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.NoError(t, err)
|
||||||
assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0", name)
|
assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0", name)
|
||||||
|
|
||||||
name, err = podName(&types.Step{Name: "wp\\01he8bebctabr3kgk0qj36d2me-0"})
|
_, err = podName(&types.Step{UUID: "01he8bebctabr3kgk0qj36d2me\\0a"})
|
||||||
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)
|
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: "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) {
|
func TestTinyPod(t *testing.T) {
|
||||||
|
@ -44,7 +61,7 @@ func TestTinyPod(t *testing.T) {
|
||||||
"namespace": "woodpecker",
|
"namespace": "woodpecker",
|
||||||
"creationTimestamp": null,
|
"creationTimestamp": null,
|
||||||
"labels": {
|
"labels": {
|
||||||
"step": "wp-01he8bebctabr3kgk0qj36d2me-0"
|
"step": "build-via-gradle"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
|
@ -101,13 +118,18 @@ func TestTinyPod(t *testing.T) {
|
||||||
"status": {}
|
"status": {}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
pod, err := mkPod("woodpecker", "wp-01he8bebctabr3kgk0qj36d2me-0", "gradle:8.4.0-jdk21", "/woodpecker/src", "linux/amd64", "",
|
pod, err := mkPod(&types.Step{
|
||||||
false, false,
|
Name: "build-via-gradle",
|
||||||
[]string{"gradle build"}, []string{"workspace:/woodpecker/src"}, nil,
|
Image: "gradle:8.4.0-jdk21",
|
||||||
nil, nil, map[string]string{"CI": "woodpecker"}, nil,
|
WorkingDir: "/woodpecker/src",
|
||||||
nil, nil,
|
Pull: false,
|
||||||
types.Resources{Requests: nil, Limits: nil}, nil, SecurityContextConfig{},
|
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)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
json, err := json.Marshal(pod)
|
json, err := json.Marshal(pod)
|
||||||
|
@ -126,7 +148,7 @@ func TestFullPod(t *testing.T) {
|
||||||
"creationTimestamp": null,
|
"creationTimestamp": null,
|
||||||
"labels": {
|
"labels": {
|
||||||
"app": "test",
|
"app": "test",
|
||||||
"step": "wp-01he8bebctabr3kgk0qj36d2me-0"
|
"step": "go-test"
|
||||||
},
|
},
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"apparmor.security": "runtime/default"
|
"apparmor.security": "runtime/default"
|
||||||
|
@ -242,15 +264,41 @@ func TestFullPod(t *testing.T) {
|
||||||
{Name: "cloudflare", IP: "1.1.1.1"},
|
{Name: "cloudflare", IP: "1.1.1.1"},
|
||||||
{Name: "cf.v6", IP: "2606:4700:4700::64"},
|
{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",
|
pod, err := mkPod(&types.Step{
|
||||||
true, true,
|
Name: "go-test",
|
||||||
[]string{"go get", "go test"}, []string{"woodpecker-cache:/woodpecker/src/cache"}, []string{"regcred", "another-pull-secret"},
|
Image: "meltwater/drone-cache",
|
||||||
map[string]string{"app": "test"}, map[string]string{"apparmor.security": "runtime/default"}, map[string]string{"CGO": "0"}, map[string]string{"storage": "ssd"},
|
WorkingDir: "/woodpecker/src",
|
||||||
hostAliases, []types.Toleration{{Key: "net-port", Value: "100Mbit", Effect: types.TaintEffectNoSchedule}},
|
Pull: true,
|
||||||
types.Resources{Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"}, Limits: map[string]string{"memory": "256Mi", "cpu": "2"}},
|
Privileged: true,
|
||||||
&types.SecurityContext{Privileged: newBool(true), RunAsNonRoot: newBool(true), RunAsUser: newInt64(101), RunAsGroup: newInt64(101), FSGroup: newInt64(101)},
|
Commands: []string{"go get", "go test"},
|
||||||
SecurityContextConfig{RunAsNonRoot: false},
|
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)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
json, err := json.Marshal(pod)
|
json, err := json.Marshal(pod)
|
||||||
|
|
|
@ -26,11 +26,22 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mkService(namespace, name string, ports []uint16, selector map[string]string) *v1.Service {
|
const (
|
||||||
log.Trace().Str("name", name).Interface("selector", selector).Interface("ports", ports).Msg("Creating service")
|
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
|
var svcPorts []v1.ServicePort
|
||||||
for _, port := range ports {
|
for _, port := range step.Ports {
|
||||||
svcPorts = append(svcPorts, v1.ServicePort{
|
svcPorts = append(svcPorts, v1.ServicePort{
|
||||||
Name: fmt.Sprintf("port-%d", port),
|
Name: fmt.Sprintf("port-%d", port),
|
||||||
Port: int32(port),
|
Port: int32(port),
|
||||||
|
@ -48,7 +59,7 @@ func mkService(namespace, name string, ports []uint16, selector map[string]strin
|
||||||
Selector: selector,
|
Selector: selector,
|
||||||
Ports: svcPorts,
|
Ports: svcPorts,
|
||||||
},
|
},
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serviceName(step *types.Step) (string, error) {
|
func serviceName(step *types.Step) (string, error) {
|
||||||
|
@ -56,21 +67,12 @@ func serviceName(step *types.Step) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
svc, err := mkService(step, engine.config.Namespace)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
podName, err := podName(step)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selector := map[string]string{
|
log.Trace().Str("name", svc.Name).Interface("selector", svc.Spec.Selector).Interface("ports", svc.Spec.Ports).Msg("creating service")
|
||||||
StepLabel: podName,
|
|
||||||
}
|
|
||||||
|
|
||||||
svc := mkService(engine.config.Namespace, name, step.Ports, selector)
|
|
||||||
|
|
||||||
return engine.client.CoreV1().Services(engine.config.Namespace).Create(ctx, svc, metav1.CreateOptions{})
|
return engine.client.CoreV1().Services(engine.config.Namespace).Create(ctx, svc, metav1.CreateOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,16 +23,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServiceName(t *testing.T) {
|
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.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.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"})
|
name, err = serviceName(&types.Step{Name: "awesome_service"})
|
||||||
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "awesome-service", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService(t *testing.T) {
|
func TestService(t *testing.T) {
|
||||||
|
@ -62,7 +63,7 @@ func TestService(t *testing.T) {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"selector": {
|
"selector": {
|
||||||
"step": "baz"
|
"service": "bar"
|
||||||
},
|
},
|
||||||
"type": "ClusterIP"
|
"type": "ClusterIP"
|
||||||
},
|
},
|
||||||
|
@ -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)
|
j, err := json.Marshal(s)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.JSONEq(t, expected, string(j))
|
assert.JSONEq(t, expected, string(j))
|
||||||
|
|
|
@ -27,12 +27,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dnsPattern = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
|
dnsPattern = regexp.MustCompile(`^[a-z0-9]` + // must start with
|
||||||
|
`([-a-z0-9]*[a-z0-9])?` + // inside can als contain -
|
||||||
|
`(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`, // allow the same pattern as before with dots in between but only one dot
|
||||||
|
)
|
||||||
ErrDNSPatternInvalid = errors.New("name is not a valid kubernetes DNS name")
|
ErrDNSPatternInvalid = errors.New("name is not a valid kubernetes DNS name")
|
||||||
)
|
)
|
||||||
|
|
||||||
func dnsName(i string) (string, error) {
|
func dnsName(i string) (string, error) {
|
||||||
res := strings.ReplaceAll(i, "_", "-")
|
res := strings.ToLower(strings.ReplaceAll(i, "_", "-"))
|
||||||
|
|
||||||
if found := dnsPattern.FindStringIndex(res); found == nil {
|
if found := dnsPattern.FindStringIndex(res); found == nil {
|
||||||
return "", ErrDNSPatternInvalid
|
return "", ErrDNSPatternInvalid
|
||||||
|
|
56
pipeline/backend/kubernetes/utils_test.go
Normal file
56
pipeline/backend/kubernetes/utils_test.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2024 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 (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDNSName(t *testing.T) {
|
||||||
|
name, err := dnsName("wp_01he8bebctabr3kgk0qj36d2me_0_services_0")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0-services-0", name)
|
||||||
|
|
||||||
|
name, err = dnsName("a.0-AA")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "a.0-aa", name)
|
||||||
|
|
||||||
|
name, err = dnsName("wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "wp-01he8bebctabr3kgk0qj36d2me-0-services-0.woodpecker-runtime.svc.cluster.local", name)
|
||||||
|
|
||||||
|
_, err = dnsName(".0-a")
|
||||||
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
|
|
||||||
|
_, err = dnsName("ABC..DEF")
|
||||||
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
|
|
||||||
|
_, err = dnsName("0.-a")
|
||||||
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
|
|
||||||
|
_, err = dnsName("test-")
|
||||||
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
|
|
||||||
|
_, err = dnsName("-test")
|
||||||
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
|
|
||||||
|
_, err = dnsName("0-a.")
|
||||||
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
|
|
||||||
|
_, err = dnsName("abc\\def")
|
||||||
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
|
}
|
|
@ -26,9 +26,8 @@ func TestPvcName(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "woodpecker-cache", name)
|
assert.Equal(t, "woodpecker-cache", name)
|
||||||
|
|
||||||
name, err = volumeName("woodpecker\\cache")
|
_, err = volumeName("woodpecker\\cache")
|
||||||
assert.NoError(t, err)
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
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)
|
assert.ErrorIs(t, err, ErrDNSPatternInvalid)
|
||||||
|
@ -99,6 +98,6 @@ func TestPersistentVolumeClaim(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.JSONEq(t, expectedRwo, string(j))
|
assert.JSONEq(t, expectedRwo, string(j))
|
||||||
|
|
||||||
_, err = mkPersistentVolumeClaim("someNamespace", "some0INVALID3name", "local-storage", "1Gi", false)
|
_, err = mkPersistentVolumeClaim("someNamespace", "some0..INVALID3name", "local-storage", "1Gi", false)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue