package utils import ( "context" "fmt" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/utils/ptr" ) const ( // DefaultImage is the default container image used for test workloads. DefaultImage = "busybox:1.36" // DefaultCommand is the default command for test containers. DefaultCommand = "sleep 3600" ) // CreateNamespace creates a namespace with the given name. func CreateNamespace(ctx context.Context, client kubernetes.Interface, name string) error { ns := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, } _, err := client.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{}) return err } // CreateNamespaceWithLabels creates a namespace with the given name and labels. func CreateNamespaceWithLabels(ctx context.Context, client kubernetes.Interface, name string, labels map[string]string) error { ns := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: name, Labels: labels, }, } _, err := client.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{}) return err } // DeleteNamespace deletes the namespace with the given name. func DeleteNamespace(ctx context.Context, client kubernetes.Interface, name string) error { return client.CoreV1().Namespaces().Delete(ctx, name, metav1.DeleteOptions{}) } // CreateConfigMap creates a ConfigMap with the given name, data, and optional annotations. func CreateConfigMap(ctx context.Context, client kubernetes.Interface, namespace, name string, data map[string]string, annotations map[string]string) (*corev1.ConfigMap, error) { cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Annotations: annotations, }, Data: data, } return client.CoreV1().ConfigMaps(namespace).Create(ctx, cm, metav1.CreateOptions{}) } // CreateConfigMapWithLabels creates a ConfigMap with the given name, data, labels, and optional annotations. func CreateConfigMapWithLabels(ctx context.Context, client kubernetes.Interface, namespace, name string, data map[string]string, labels, annotations map[string]string) (*corev1.ConfigMap, error) { cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Labels: labels, Annotations: annotations, }, Data: data, } return client.CoreV1().ConfigMaps(namespace).Create(ctx, cm, metav1.CreateOptions{}) } // CreateSecret creates a Secret with the given name, data, and optional annotations. func CreateSecret(ctx context.Context, client kubernetes.Interface, namespace, name string, data map[string][]byte, annotations map[string]string) (*corev1.Secret, error) { secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Annotations: annotations, }, Data: data, } return client.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{}) } // UpdateConfigMap updates a ConfigMap's data. func UpdateConfigMap(ctx context.Context, client kubernetes.Interface, namespace, name string, data map[string]string) error { cm, err := client.CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return err } cm.Data = data _, err = client.CoreV1().ConfigMaps(namespace).Update(ctx, cm, metav1.UpdateOptions{}) return err } // UpdateConfigMapLabels updates a ConfigMap's labels. func UpdateConfigMapLabels(ctx context.Context, client kubernetes.Interface, namespace, name string, labels map[string]string) error { cm, err := client.CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return err } if cm.Labels == nil { cm.Labels = make(map[string]string) } for k, v := range labels { cm.Labels[k] = v } _, err = client.CoreV1().ConfigMaps(namespace).Update(ctx, cm, metav1.UpdateOptions{}) return err } // UpdateSecret updates a Secret's data. func UpdateSecret(ctx context.Context, client kubernetes.Interface, namespace, name string, data map[string][]byte) error { secret, err := client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return err } secret.Data = data _, err = client.CoreV1().Secrets(namespace).Update(ctx, secret, metav1.UpdateOptions{}) return err } // UpdateSecretLabels updates a Secret's labels. func UpdateSecretLabels(ctx context.Context, client kubernetes.Interface, namespace, name string, labels map[string]string) error { secret, err := client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return err } if secret.Labels == nil { secret.Labels = make(map[string]string) } for k, v := range labels { secret.Labels[k] = v } _, err = client.CoreV1().Secrets(namespace).Update(ctx, secret, metav1.UpdateOptions{}) return err } // stringToByteMap converts a string map to a byte map for Secret data. func stringToByteMap(data map[string]string) map[string][]byte { result := make(map[string][]byte) for k, v := range data { result[k] = []byte(v) } return result } // CreateSecretFromStrings creates a Secret with string data (convenience wrapper). func CreateSecretFromStrings(ctx context.Context, client kubernetes.Interface, namespace, name string, data map[string]string, annotations map[string]string) (*corev1.Secret, error) { return CreateSecret(ctx, client, namespace, name, stringToByteMap(data), annotations) } // UpdateSecretFromStrings updates a Secret's data using string values. func UpdateSecretFromStrings(ctx context.Context, client kubernetes.Interface, namespace, name string, data map[string]string) error { return UpdateSecret(ctx, client, namespace, name, stringToByteMap(data)) } // DeleteConfigMap deletes a ConfigMap. func DeleteConfigMap(ctx context.Context, client kubernetes.Interface, namespace, name string) error { return client.CoreV1().ConfigMaps(namespace).Delete(ctx, name, metav1.DeleteOptions{}) } // DeleteSecret deletes a Secret. func DeleteSecret(ctx context.Context, client kubernetes.Interface, namespace, name string) error { return client.CoreV1().Secrets(namespace).Delete(ctx, name, metav1.DeleteOptions{}) } // DeploymentOption is a functional option for configuring a Deployment. type DeploymentOption func(*appsv1.Deployment) // CreateDeployment creates a Deployment with the given options. func CreateDeployment(ctx context.Context, client kubernetes.Interface, namespace, name string, opts ...DeploymentOption) (*appsv1.Deployment, error) { deploy := baseDeployment(namespace, name) for _, opt := range opts { opt(deploy) } return client.AppsV1().Deployments(namespace).Create(ctx, deploy, metav1.CreateOptions{}) } // WithAnnotations adds annotations to the Deployment metadata. func WithAnnotations(annotations map[string]string) DeploymentOption { return func(d *appsv1.Deployment) { if d.Annotations == nil { d.Annotations = make(map[string]string) } for k, v := range annotations { d.Annotations[k] = v } } } // WithConfigMapEnvFrom adds an envFrom reference to a ConfigMap. func WithConfigMapEnvFrom(name string) DeploymentOption { return func(d *appsv1.Deployment) { d.Spec.Template.Spec.Containers[0].EnvFrom = append( d.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // WithSecretEnvFrom adds an envFrom reference to a Secret. func WithSecretEnvFrom(name string) DeploymentOption { return func(d *appsv1.Deployment) { d.Spec.Template.Spec.Containers[0].EnvFrom = append( d.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ SecretRef: &corev1.SecretEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // WithConfigMapVolume adds a volume mount for a ConfigMap. func WithConfigMapVolume(name string) DeploymentOption { return func(d *appsv1.Deployment) { volumeName := fmt.Sprintf("cm-%s", name) d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, }) d.Spec.Template.Spec.Containers[0].VolumeMounts = append( d.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ Name: volumeName, MountPath: fmt.Sprintf("/etc/config/%s", name), }, ) } } // WithSecretVolume adds a volume mount for a Secret. func WithSecretVolume(name string) DeploymentOption { return func(d *appsv1.Deployment) { volumeName := fmt.Sprintf("secret-%s", name) d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: name, }, }, }) d.Spec.Template.Spec.Containers[0].VolumeMounts = append( d.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ Name: volumeName, MountPath: fmt.Sprintf("/etc/secrets/%s", name), }, ) } } // WithProjectedVolume adds a projected volume with ConfigMap and/or Secret sources. func WithProjectedVolume(cmName, secretName string) DeploymentOption { return func(d *appsv1.Deployment) { volumeName := "projected-config" sources := []corev1.VolumeProjection{} if cmName != "" { sources = append(sources, corev1.VolumeProjection{ ConfigMap: &corev1.ConfigMapProjection{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, }, }) } if secretName != "" { sources = append(sources, corev1.VolumeProjection{ Secret: &corev1.SecretProjection{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, }, }) } d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Projected: &corev1.ProjectedVolumeSource{ Sources: sources, }, }, }) d.Spec.Template.Spec.Containers[0].VolumeMounts = append( d.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ Name: volumeName, MountPath: "/etc/projected", }, ) } } // WithInitContainer adds an init container that references ConfigMap and/or Secret. func WithInitContainer(cmName, secretName string) DeploymentOption { return func(d *appsv1.Deployment) { initContainer := corev1.Container{ Name: "init", Image: DefaultImage, Command: []string{"sh", "-c", "echo init done"}, } if cmName != "" { initContainer.EnvFrom = append(initContainer.EnvFrom, corev1.EnvFromSource{ ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, }, }) } if secretName != "" { initContainer.EnvFrom = append(initContainer.EnvFrom, corev1.EnvFromSource{ SecretRef: &corev1.SecretEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, }, }) } d.Spec.Template.Spec.InitContainers = append(d.Spec.Template.Spec.InitContainers, initContainer) } } // WithMultipleContainers adds additional containers to the pod. func WithMultipleContainers(count int) DeploymentOption { return func(d *appsv1.Deployment) { for i := 1; i < count; i++ { d.Spec.Template.Spec.Containers = append(d.Spec.Template.Spec.Containers, corev1.Container{ Name: fmt.Sprintf("container-%d", i), Image: DefaultImage, Command: []string{"sh", "-c", DefaultCommand}, }) } } } // WithMultipleContainersAndEnv creates two containers, each with a different ConfigMap envFrom. func WithMultipleContainersAndEnv(cm1Name, cm2Name string) DeploymentOption { return func(d *appsv1.Deployment) { // First container gets the first ConfigMap d.Spec.Template.Spec.Containers[0].EnvFrom = append(d.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: cm1Name}, }, }) // Add second container with second ConfigMap d.Spec.Template.Spec.Containers = append(d.Spec.Template.Spec.Containers, corev1.Container{ Name: "container-1", Image: DefaultImage, Command: []string{"sh", "-c", DefaultCommand}, EnvFrom: []corev1.EnvFromSource{ { ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: cm2Name}, }, }, }, }) } } // WithReplicas sets the number of replicas. func WithReplicas(replicas int32) DeploymentOption { return func(d *appsv1.Deployment) { d.Spec.Replicas = ptr.To(replicas) } } // baseDeployment creates a base Deployment template. func baseDeployment(namespace, name string) *appsv1.Deployment { labels := map[string]string{"app": name} return &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Spec: appsv1.DeploymentSpec{ Replicas: ptr.To(int32(1)), Selector: &metav1.LabelSelector{ MatchLabels: labels, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "app", Image: DefaultImage, Command: []string{"sh", "-c", DefaultCommand}, }, }, }, }, }, } } // DeleteDeployment deletes a Deployment. func DeleteDeployment(ctx context.Context, client kubernetes.Interface, namespace, name string) error { return client.AppsV1().Deployments(namespace).Delete(ctx, name, metav1.DeleteOptions{}) } // DaemonSetOption is a functional option for configuring a DaemonSet. type DaemonSetOption func(*appsv1.DaemonSet) // CreateDaemonSet creates a DaemonSet with the given options. func CreateDaemonSet(ctx context.Context, client kubernetes.Interface, namespace, name string, opts ...DaemonSetOption) (*appsv1.DaemonSet, error) { ds := baseDaemonSet(namespace, name) for _, opt := range opts { opt(ds) } return client.AppsV1().DaemonSets(namespace).Create(ctx, ds, metav1.CreateOptions{}) } // WithDaemonSetAnnotations adds annotations to the DaemonSet metadata. func WithDaemonSetAnnotations(annotations map[string]string) DaemonSetOption { return func(ds *appsv1.DaemonSet) { if ds.Annotations == nil { ds.Annotations = make(map[string]string) } for k, v := range annotations { ds.Annotations[k] = v } } } // WithDaemonSetConfigMapEnvFrom adds an envFrom reference to a ConfigMap. func WithDaemonSetConfigMapEnvFrom(name string) DaemonSetOption { return func(ds *appsv1.DaemonSet) { ds.Spec.Template.Spec.Containers[0].EnvFrom = append( ds.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // WithDaemonSetSecretEnvFrom adds an envFrom reference to a Secret. func WithDaemonSetSecretEnvFrom(name string) DaemonSetOption { return func(ds *appsv1.DaemonSet) { ds.Spec.Template.Spec.Containers[0].EnvFrom = append( ds.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ SecretRef: &corev1.SecretEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // baseDaemonSet creates a base DaemonSet template. func baseDaemonSet(namespace, name string) *appsv1.DaemonSet { labels := map[string]string{"app": name} return &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Spec: appsv1.DaemonSetSpec{ Selector: &metav1.LabelSelector{ MatchLabels: labels, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "app", Image: DefaultImage, Command: []string{"sh", "-c", DefaultCommand}, }, }, }, }, }, } } // DeleteDaemonSet deletes a DaemonSet. func DeleteDaemonSet(ctx context.Context, client kubernetes.Interface, namespace, name string) error { return client.AppsV1().DaemonSets(namespace).Delete(ctx, name, metav1.DeleteOptions{}) } // StatefulSetOption is a functional option for configuring a StatefulSet. type StatefulSetOption func(*appsv1.StatefulSet) // CreateStatefulSet creates a StatefulSet with the given options. func CreateStatefulSet(ctx context.Context, client kubernetes.Interface, namespace, name string, opts ...StatefulSetOption) (*appsv1.StatefulSet, error) { ss := baseStatefulSet(namespace, name) for _, opt := range opts { opt(ss) } return client.AppsV1().StatefulSets(namespace).Create(ctx, ss, metav1.CreateOptions{}) } // WithStatefulSetAnnotations adds annotations to the StatefulSet metadata. func WithStatefulSetAnnotations(annotations map[string]string) StatefulSetOption { return func(ss *appsv1.StatefulSet) { if ss.Annotations == nil { ss.Annotations = make(map[string]string) } for k, v := range annotations { ss.Annotations[k] = v } } } // WithStatefulSetConfigMapEnvFrom adds an envFrom reference to a ConfigMap. func WithStatefulSetConfigMapEnvFrom(name string) StatefulSetOption { return func(ss *appsv1.StatefulSet) { ss.Spec.Template.Spec.Containers[0].EnvFrom = append( ss.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // WithStatefulSetSecretEnvFrom adds an envFrom reference to a Secret. func WithStatefulSetSecretEnvFrom(name string) StatefulSetOption { return func(ss *appsv1.StatefulSet) { ss.Spec.Template.Spec.Containers[0].EnvFrom = append( ss.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ SecretRef: &corev1.SecretEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // baseStatefulSet creates a base StatefulSet template. func baseStatefulSet(namespace, name string) *appsv1.StatefulSet { labels := map[string]string{"app": name} return &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Spec: appsv1.StatefulSetSpec{ ServiceName: name, Replicas: ptr.To(int32(1)), Selector: &metav1.LabelSelector{ MatchLabels: labels, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "app", Image: DefaultImage, Command: []string{"sh", "-c", DefaultCommand}, }, }, }, }, }, } } // DeleteStatefulSet deletes a StatefulSet. func DeleteStatefulSet(ctx context.Context, client kubernetes.Interface, namespace, name string) error { return client.AppsV1().StatefulSets(namespace).Delete(ctx, name, metav1.DeleteOptions{}) } // CronJobOption is a functional option for configuring a CronJob. type CronJobOption func(*batchv1.CronJob) // CreateCronJob creates a CronJob with the given options. func CreateCronJob(ctx context.Context, client kubernetes.Interface, namespace, name string, opts ...CronJobOption) (*batchv1.CronJob, error) { cj := baseCronJob(namespace, name) for _, opt := range opts { opt(cj) } return client.BatchV1().CronJobs(namespace).Create(ctx, cj, metav1.CreateOptions{}) } // WithCronJobAnnotations adds annotations to the CronJob metadata. func WithCronJobAnnotations(annotations map[string]string) CronJobOption { return func(cj *batchv1.CronJob) { if cj.Annotations == nil { cj.Annotations = make(map[string]string) } for k, v := range annotations { cj.Annotations[k] = v } } } // WithCronJobConfigMapEnvFrom adds an envFrom reference to a ConfigMap. func WithCronJobConfigMapEnvFrom(name string) CronJobOption { return func(cj *batchv1.CronJob) { cj.Spec.JobTemplate.Spec.Template.Spec.Containers[0].EnvFrom = append( cj.Spec.JobTemplate.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // WithCronJobSecretEnvFrom adds an envFrom reference to a Secret. func WithCronJobSecretEnvFrom(name string) CronJobOption { return func(cj *batchv1.CronJob) { cj.Spec.JobTemplate.Spec.Template.Spec.Containers[0].EnvFrom = append( cj.Spec.JobTemplate.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ SecretRef: &corev1.SecretEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // baseCronJob creates a base CronJob template. func baseCronJob(namespace, name string) *batchv1.CronJob { labels := map[string]string{"app": name} return &batchv1.CronJob{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Spec: batchv1.CronJobSpec{ Schedule: "* * * * *", // Every minute JobTemplate: batchv1.JobTemplateSpec{ Spec: batchv1.JobSpec{ Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, Spec: corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyOnFailure, Containers: []corev1.Container{ { Name: "job", Image: DefaultImage, Command: []string{"sh", "-c", "echo done"}, }, }, }, }, }, }, }, } } // DeleteCronJob deletes a CronJob. func DeleteCronJob(ctx context.Context, client kubernetes.Interface, namespace, name string) error { return client.BatchV1().CronJobs(namespace).Delete(ctx, name, metav1.DeleteOptions{}) } // JobOption is a functional option for configuring a Job. type JobOption func(*batchv1.Job) // CreateJob creates a Job with the given options. func CreateJob(ctx context.Context, client kubernetes.Interface, namespace, name string, opts ...JobOption) (*batchv1.Job, error) { job := baseJob(namespace, name) for _, opt := range opts { opt(job) } return client.BatchV1().Jobs(namespace).Create(ctx, job, metav1.CreateOptions{}) } // WithJobAnnotations adds annotations to the Job metadata. func WithJobAnnotations(annotations map[string]string) JobOption { return func(j *batchv1.Job) { if j.Annotations == nil { j.Annotations = make(map[string]string) } for k, v := range annotations { j.Annotations[k] = v } } } // WithJobConfigMapEnvFrom adds an envFrom reference to a ConfigMap. func WithJobConfigMapEnvFrom(name string) JobOption { return func(j *batchv1.Job) { j.Spec.Template.Spec.Containers[0].EnvFrom = append( j.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ ConfigMapRef: &corev1.ConfigMapEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // WithJobSecretEnvFrom adds an envFrom reference to a Secret. func WithJobSecretEnvFrom(name string) JobOption { return func(j *batchv1.Job) { j.Spec.Template.Spec.Containers[0].EnvFrom = append( j.Spec.Template.Spec.Containers[0].EnvFrom, corev1.EnvFromSource{ SecretRef: &corev1.SecretEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: name}, }, }, ) } } // baseJob creates a base Job template. func baseJob(namespace, name string) *batchv1.Job { labels := map[string]string{"app": name} return &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Spec: batchv1.JobSpec{ Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, Spec: corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, Containers: []corev1.Container{ { Name: "job", Image: DefaultImage, Command: []string{"sh", "-c", "echo done"}, }, }, }, }, }, } } // DeleteJob deletes a Job. func DeleteJob(ctx context.Context, client kubernetes.Interface, namespace, name string) error { propagation := metav1.DeletePropagationBackground return client.BatchV1().Jobs(namespace).Delete(ctx, name, metav1.DeleteOptions{ PropagationPolicy: &propagation, }) } // WithConfigMapKeyRef adds a valueFrom.configMapKeyRef env var to the container. func WithConfigMapKeyRef(cmName, key, envVarName string) DeploymentOption { return func(d *appsv1.Deployment) { d.Spec.Template.Spec.Containers[0].Env = append( d.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ Name: envVarName, ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, Key: key, }, }, }, ) } } // WithSecretKeyRef adds a valueFrom.secretKeyRef env var to the container. func WithSecretKeyRef(secretName, key, envVarName string) DeploymentOption { return func(d *appsv1.Deployment) { d.Spec.Template.Spec.Containers[0].Env = append( d.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ Name: envVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, Key: key, }, }, }, ) } } // WithPodTemplateAnnotations adds annotations to the pod template metadata (not deployment metadata). func WithPodTemplateAnnotations(annotations map[string]string) DeploymentOption { return func(d *appsv1.Deployment) { if d.Spec.Template.Annotations == nil { d.Spec.Template.Annotations = make(map[string]string) } for k, v := range annotations { d.Spec.Template.Annotations[k] = v } } } // WithInitContainerVolume adds an init container with ConfigMap/Secret volume mounts. func WithInitContainerVolume(cmName, secretName string) DeploymentOption { return func(d *appsv1.Deployment) { initContainer := corev1.Container{ Name: "init", Image: DefaultImage, Command: []string{"sh", "-c", "echo init done"}, } if cmName != "" { volumeName := fmt.Sprintf("init-cm-%s", cmName) d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, }, }, }) initContainer.VolumeMounts = append(initContainer.VolumeMounts, corev1.VolumeMount{ Name: volumeName, MountPath: fmt.Sprintf("/etc/init-config/%s", cmName), }) } if secretName != "" { volumeName := fmt.Sprintf("init-secret-%s", secretName) d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: secretName, }, }, }) initContainer.VolumeMounts = append(initContainer.VolumeMounts, corev1.VolumeMount{ Name: volumeName, MountPath: fmt.Sprintf("/etc/init-secrets/%s", secretName), }) } d.Spec.Template.Spec.InitContainers = append(d.Spec.Template.Spec.InitContainers, initContainer) } } // WithInitContainerProjectedVolume adds an init container with projected volume. func WithInitContainerProjectedVolume(cmName, secretName string) DeploymentOption { return func(d *appsv1.Deployment) { volumeName := "init-projected-config" sources := []corev1.VolumeProjection{} if cmName != "" { sources = append(sources, corev1.VolumeProjection{ ConfigMap: &corev1.ConfigMapProjection{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, }, }) } if secretName != "" { sources = append(sources, corev1.VolumeProjection{ Secret: &corev1.SecretProjection{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, }, }) } d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Projected: &corev1.ProjectedVolumeSource{ Sources: sources, }, }, }) initContainer := corev1.Container{ Name: "init", Image: DefaultImage, Command: []string{"sh", "-c", "echo init done"}, VolumeMounts: []corev1.VolumeMount{ { Name: volumeName, MountPath: "/etc/init-projected", }, }, } d.Spec.Template.Spec.InitContainers = append(d.Spec.Template.Spec.InitContainers, initContainer) } } // WithDaemonSetProjectedVolume adds a projected volume with ConfigMap and/or Secret sources to a DaemonSet. func WithDaemonSetProjectedVolume(cmName, secretName string) DaemonSetOption { return func(ds *appsv1.DaemonSet) { volumeName := "projected-config" sources := []corev1.VolumeProjection{} if cmName != "" { sources = append(sources, corev1.VolumeProjection{ ConfigMap: &corev1.ConfigMapProjection{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, }, }) } if secretName != "" { sources = append(sources, corev1.VolumeProjection{ Secret: &corev1.SecretProjection{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, }, }) } ds.Spec.Template.Spec.Volumes = append(ds.Spec.Template.Spec.Volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Projected: &corev1.ProjectedVolumeSource{ Sources: sources, }, }, }) ds.Spec.Template.Spec.Containers[0].VolumeMounts = append( ds.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ Name: volumeName, MountPath: "/etc/projected", }, ) } } // WithStatefulSetProjectedVolume adds a projected volume with ConfigMap and/or Secret sources to a StatefulSet. func WithStatefulSetProjectedVolume(cmName, secretName string) StatefulSetOption { return func(ss *appsv1.StatefulSet) { volumeName := "projected-config" sources := []corev1.VolumeProjection{} if cmName != "" { sources = append(sources, corev1.VolumeProjection{ ConfigMap: &corev1.ConfigMapProjection{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, }, }) } if secretName != "" { sources = append(sources, corev1.VolumeProjection{ Secret: &corev1.SecretProjection{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, }, }) } ss.Spec.Template.Spec.Volumes = append(ss.Spec.Template.Spec.Volumes, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Projected: &corev1.ProjectedVolumeSource{ Sources: sources, }, }, }) ss.Spec.Template.Spec.Containers[0].VolumeMounts = append( ss.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ Name: volumeName, MountPath: "/etc/projected", }, ) } } // WithDaemonSetConfigMapKeyRef adds a valueFrom.configMapKeyRef env var to a DaemonSet. func WithDaemonSetConfigMapKeyRef(cmName, key, envVarName string) DaemonSetOption { return func(ds *appsv1.DaemonSet) { ds.Spec.Template.Spec.Containers[0].Env = append( ds.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ Name: envVarName, ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, Key: key, }, }, }, ) } } // WithDaemonSetSecretKeyRef adds a valueFrom.secretKeyRef env var to a DaemonSet. func WithDaemonSetSecretKeyRef(secretName, key, envVarName string) DaemonSetOption { return func(ds *appsv1.DaemonSet) { ds.Spec.Template.Spec.Containers[0].Env = append( ds.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ Name: envVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, Key: key, }, }, }, ) } } // WithStatefulSetConfigMapKeyRef adds a valueFrom.configMapKeyRef env var to a StatefulSet. func WithStatefulSetConfigMapKeyRef(cmName, key, envVarName string) StatefulSetOption { return func(ss *appsv1.StatefulSet) { ss.Spec.Template.Spec.Containers[0].Env = append( ss.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ Name: envVarName, ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, Key: key, }, }, }, ) } } // WithStatefulSetSecretKeyRef adds a valueFrom.secretKeyRef env var to a StatefulSet. func WithStatefulSetSecretKeyRef(secretName, key, envVarName string) StatefulSetOption { return func(ss *appsv1.StatefulSet) { ss.Spec.Template.Spec.Containers[0].Env = append( ss.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ Name: envVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, Key: key, }, }, }, ) } } // WithJobConfigMapKeyRef adds a valueFrom.configMapKeyRef env var to a Job. func WithJobConfigMapKeyRef(cmName, key, envVarName string) JobOption { return func(j *batchv1.Job) { j.Spec.Template.Spec.Containers[0].Env = append( j.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ Name: envVarName, ValueFrom: &corev1.EnvVarSource{ ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: cmName}, Key: key, }, }, }, ) } } // WithJobSecretKeyRef adds a valueFrom.secretKeyRef env var to a Job. func WithJobSecretKeyRef(secretName, key, envVarName string) JobOption { return func(j *batchv1.Job) { j.Spec.Template.Spec.Containers[0].Env = append( j.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ Name: envVarName, ValueFrom: &corev1.EnvVarSource{ SecretKeyRef: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, Key: key, }, }, }, ) } }