mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-22 16:36:30 +00:00
fix(backend/kubernetes): Ensure valid naming of name field (#1661)
- Kubernetes v1.26 on VKE causes error when creating persistent volume claim because of uppercase characters in name field This patch is trivial just in order to get it working - happy to implement differently. The error in question: ``` The PersistentVolumeClaim "wp-01G1131R63FWBSPMA4ZAZTKLE-0-clone-0" is invalid: metadata.name: Invalid value: "wp-01G1131R63FWBSPMA4ZAZTKLE-0-clone-0": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*') ```
This commit is contained in:
parent
7c56d7246d
commit
a95a5b43bf
8 changed files with 101 additions and 39 deletions
|
@ -115,6 +115,7 @@ pipeline:
|
|||
from_secret: codecov_token
|
||||
when:
|
||||
path: *when_path
|
||||
failure: ignore
|
||||
|
||||
services:
|
||||
service-postgres:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue