diff --git a/.woodpecker/test.yml b/.woodpecker/test.yml index 1374b0601..17730b72d 100644 --- a/.woodpecker/test.yml +++ b/.woodpecker/test.yml @@ -115,6 +115,7 @@ pipeline: from_secret: codecov_token when: path: *when_path + failure: ignore services: service-postgres: diff --git a/pipeline/backend/kubernetes/kubernetes.go b/pipeline/backend/kubernetes/kubernetes.go index 688f58a92..b4cb6dd85 100644 --- a/pipeline/backend/kubernetes/kubernetes.go +++ b/pipeline/backend/kubernetes/kubernetes.go @@ -119,8 +119,12 @@ func (e *kube) Setup(ctx context.Context, conf *types.Config) error { log.Trace().Msgf("Setting up Kubernetes primitives") for _, vol := range conf.Volumes { - pvc := PersistentVolumeClaim(e.config.Namespace, vol.Name, e.config.StorageClass, e.config.VolumeSize, e.config.StorageRwx) - _, err := e.client.CoreV1().PersistentVolumeClaims(e.config.Namespace).Create(ctx, pvc, metav1.CreateOptions{}) + 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{}) if err != nil { return err } @@ -131,10 +135,14 @@ func (e *kube) Setup(ctx context.Context, conf *types.Config) error { for _, stage := range conf.Stages { if stage.Alias == "services" { for _, step := range stage.Steps { - log.Trace().Str("pod-name", podName(step)).Msgf("Creating service: %s", step.Name) + stepName, err := dnsName(step.Name) + if err != nil { + return err + } + log.Trace().Str("pod-name", stepName).Msgf("Creating service: %s", step.Name) // TODO: support ports setting - // svc, err := Service(e.config.Namespace, step.Name, podName(step), step.Ports) - svc, err := Service(e.config.Namespace, step.Name, podName(step), []string{}) + // svc, err := Service(e.config.Namespace, step.Name, stepName, step.Ports) + svc, err := Service(e.config.Namespace, step.Name, stepName, []string{}) if err != nil { return err } @@ -161,16 +169,23 @@ func (e *kube) Setup(ctx context.Context, conf *types.Config) error { // Start the pipeline step. func (e *kube) Exec(ctx context.Context, step *types.Step) error { - pod := Pod(e.config.Namespace, step, e.config.PodLabels, e.config.PodAnnotations) + pod, err := Pod(e.config.Namespace, step, e.config.PodLabels, e.config.PodAnnotations) + if err != nil { + return err + } + log.Trace().Msgf("Creating pod: %s", pod.Name) - _, err := e.client.CoreV1().Pods(e.config.Namespace).Create(ctx, pod, metav1.CreateOptions{}) + _, err = e.client.CoreV1().Pods(e.config.Namespace).Create(ctx, pod, metav1.CreateOptions{}) return err } // Wait for the pipeline step to complete and returns // the completion results. func (e *kube) Wait(ctx context.Context, step *types.Step) (*types.State, error) { - podName := podName(step) + podName, err := dnsName(step.Name) + if err != nil { + return nil, err + } finished := make(chan bool) @@ -225,7 +240,10 @@ func (e *kube) Wait(ctx context.Context, step *types.Step) (*types.State, error) // Tail the pipeline step logs. func (e *kube) Tail(ctx context.Context, step *types.Step) (io.ReadCloser, error) { - podName := podName(step) + podName, err := dnsName(step.Name) + if err != nil { + return nil, err + } up := make(chan bool) @@ -305,10 +323,14 @@ func (e *kube) Destroy(_ context.Context, conf *types.Config) error { for _, stage := range conf.Stages { for _, step := range stage.Steps { - log.Trace().Msgf("Deleting pod: %s", podName(step)) - if err := e.client.CoreV1().Pods(e.config.Namespace).Delete(noContext, podName(step), deleteOpts); err != nil { + stepName, err := dnsName(step.Name) + 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) { - log.Trace().Err(err).Msgf("Unable to delete pod %s", podName(step)) + log.Trace().Err(err).Msgf("Unable to delete pod %s", stepName) } else { return err } @@ -338,8 +360,11 @@ func (e *kube) Destroy(_ context.Context, conf *types.Config) error { } for _, vol := range conf.Volumes { - pvc := PersistentVolumeClaim(e.config.Namespace, vol.Name, e.config.StorageClass, e.config.VolumeSize, e.config.StorageRwx) - err := e.client.CoreV1().PersistentVolumeClaims(e.config.Namespace).Delete(noContext, pvc.Name, deleteOpts) + 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).Delete(noContext, pvc.Name, deleteOpts) if err != nil { if errors.IsNotFound(err) { log.Trace().Err(err).Msgf("Unable to delete pvc %s", pvc.Name) diff --git a/pipeline/backend/kubernetes/pod.go b/pipeline/backend/kubernetes/pod.go index 1099f7c1a..b3f0650b2 100644 --- a/pipeline/backend/kubernetes/pod.go +++ b/pipeline/backend/kubernetes/pod.go @@ -10,7 +10,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func Pod(namespace string, step *types.Step, labels, annotations map[string]string) *v1.Pod { +func Pod(namespace string, step *types.Step, labels, annotations map[string]string) (*v1.Pod, error) { var ( vols []v1.Volume volMounts []v1.VolumeMount @@ -20,18 +20,23 @@ func Pod(namespace string, step *types.Step, labels, annotations map[string]stri 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(vol), + Name: volumeName, VolumeSource: v1.VolumeSource{ PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ - ClaimName: volumeName(vol), + ClaimName: volumeName, ReadOnly: false, }, }, }) volMounts = append(volMounts, v1.VolumeMount{ - Name: volumeName(vol), + Name: volumeName, MountPath: volumeMountPath(vol), }) } @@ -88,11 +93,16 @@ func Pod(namespace string, step *types.Step, labels, annotations map[string]stri }, } - labels["step"] = podName(step) + podName, err := dnsName(step.Name) + if err != nil { + return nil, err + } - return &v1.Pod{ + labels["step"] = podName + + pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: podName(step), + Name: podName, Namespace: namespace, Labels: labels, Annotations: annotations, @@ -101,7 +111,7 @@ func Pod(namespace string, step *types.Step, labels, annotations map[string]stri RestartPolicy: v1.RestartPolicyNever, HostAliases: hostAliases, Containers: []v1.Container{{ - Name: podName(step), + Name: podName, Image: step.Image, ImagePullPolicy: pullPolicy, Command: entrypoint, @@ -118,10 +128,8 @@ func Pod(namespace string, step *types.Step, labels, annotations map[string]stri Volumes: vols, }, } -} -func podName(s *types.Step) string { - return dnsName(s.Name) + return pod, nil } func mapToEnvVars(m map[string]string) []v1.EnvVar { diff --git a/pipeline/backend/kubernetes/service.go b/pipeline/backend/kubernetes/service.go index 49cc31dc3..6ed0eb108 100644 --- a/pipeline/backend/kubernetes/service.go +++ b/pipeline/backend/kubernetes/service.go @@ -22,9 +22,14 @@ func Service(namespace, name, podName string, ports []string) (*v1.Service, erro }) } + dnsName, err := dnsName(name) + if err != nil { + return nil, err + } + return &v1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: dnsName(name), + Name: dnsName, Namespace: namespace, }, Spec: v1.ServiceSpec{ diff --git a/pipeline/backend/kubernetes/utils.go b/pipeline/backend/kubernetes/utils.go index 6be6b7335..50801febf 100644 --- a/pipeline/backend/kubernetes/utils.go +++ b/pipeline/backend/kubernetes/utils.go @@ -1,7 +1,9 @@ package kubernetes import ( + "errors" "os" + "regexp" "strings" v1 "k8s.io/api/core/v1" @@ -10,8 +12,19 @@ import ( "k8s.io/client-go/tools/clientcmd" ) -func dnsName(i string) string { - return strings.Replace(i, "_", "-", -1) +var ( + dnsPattern = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) + ErrDNSPatternInvalid = errors.New("name is not a valid kubernetes DNS name") +) + +func dnsName(i string) (string, error) { + res := strings.Replace(i, "_", "-", -1) + + if found := dnsPattern.FindStringIndex(res); found == nil { + return "", ErrDNSPatternInvalid + } + + return res, nil } func isImagePullBackOffState(pod *v1.Pod) bool { diff --git a/pipeline/backend/kubernetes/volume.go b/pipeline/backend/kubernetes/volume.go index aeee1eed8..12ebd9cb7 100644 --- a/pipeline/backend/kubernetes/volume.go +++ b/pipeline/backend/kubernetes/volume.go @@ -8,7 +8,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRwx bool) *v1.PersistentVolumeClaim { +func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRwx bool) (*v1.PersistentVolumeClaim, error) { _storageClass := &storageClass if storageClass == "" { _storageClass = nil @@ -22,9 +22,14 @@ func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRw accessMode = v1.ReadWriteOnce } - return &v1.PersistentVolumeClaim{ + volumeName, err := dnsName(strings.Split(name, ":")[0]) + if err != nil { + return nil, err + } + + pvc := &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ - Name: volumeName(name), + Name: volumeName, Namespace: namespace, }, Spec: v1.PersistentVolumeClaimSpec{ @@ -37,8 +42,6 @@ func PersistentVolumeClaim(namespace, name, storageClass, size string, storageRw }, }, } -} -func volumeName(i string) string { - return dnsName(strings.Split(i, ":")[0]) + return pvc, nil } diff --git a/pipeline/backend/kubernetes/volume_test.go b/pipeline/backend/kubernetes/volume_test.go index 22fe2b037..0a8cc8f81 100644 --- a/pipeline/backend/kubernetes/volume_test.go +++ b/pipeline/backend/kubernetes/volume_test.go @@ -11,7 +11,7 @@ func TestPersistentVolumeClaim(t *testing.T) { expectedRwx := ` { "metadata": { - "name": "someName", + "name": "somename", "namespace": "someNamespace", "creationTimestamp": null }, @@ -32,7 +32,7 @@ func TestPersistentVolumeClaim(t *testing.T) { expectedRwo := ` { "metadata": { - "name": "someName", + "name": "somename", "namespace": "someNamespace", "creationTimestamp": null }, @@ -50,13 +50,20 @@ func TestPersistentVolumeClaim(t *testing.T) { "status": {} }` - pvc := PersistentVolumeClaim("someNamespace", "someName", "local-storage", "1Gi", true) + pvc, err := PersistentVolumeClaim("someNamespace", "somename", "local-storage", "1Gi", true) + assert.Nil(t, err) + j, err := json.Marshal(pvc) assert.Nil(t, err) assert.JSONEq(t, expectedRwx, string(j)) - pvc = PersistentVolumeClaim("someNamespace", "someName", "local-storage", "1Gi", false) + pvc, err = PersistentVolumeClaim("someNamespace", "somename", "local-storage", "1Gi", false) + assert.Nil(t, err) + j, err = json.Marshal(pvc) assert.Nil(t, err) assert.JSONEq(t, expectedRwo, string(j)) + + _, err = PersistentVolumeClaim("someNamespace", "some0INVALID3name", "local-storage", "1Gi", false) + assert.NotNil(t, err) } diff --git a/pipeline/stepBuilder.go b/pipeline/stepBuilder.go index 5d7e81e42..6c7e13d37 100644 --- a/pipeline/stepBuilder.go +++ b/pipeline/stepBuilder.go @@ -280,7 +280,7 @@ func (b *StepBuilder) toInternalRepresentation(parsed *yaml.Config, environ map[ compiler.WithPrefix( fmt.Sprintf( "wp_%s_%d", - ulid.Make().String(), + strings.ToLower(ulid.Make().String()), stepID, ), ),