internal/utils/controller/appdeployment_validate.go (118 lines of code) (raw):

package controller import ( "errors" "fmt" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" "github.com/Azure/operation-cache-controller/api/v1alpha1" ) type Validater func(*v1alpha1.AppDeployment) error func Validate(ap *v1alpha1.AppDeployment) error { var errs error validaters := []Validater{ validateJobSpec, } for _, v := range validaters { errs = errors.Join(errs, v(ap)) } return errs } // validateJobSpec validates the container count in the AppDeployment Spec // * container count in AppDeployment Spec should be 1 // * initCountainer is not allowed func validateJobSpec(ap *v1alpha1.AppDeployment) error { if equality.Semantic.DeepEqual(ap.Spec, v1alpha1.AppDeploymentSpec{}) { return errors.New("spec of appdeployment is nil") } // provision job must be present if equality.Semantic.DeepEqual(ap.Spec.Provision, batchv1.JobSpec{}) { return errors.New("provision job is nil") } if jobConstraint(ap.Spec.Provision) != nil { return fmt.Errorf("provision: %w", jobConstraint(ap.Spec.Provision)) } // teardown job is optional if !equality.Semantic.DeepEqual(ap.Spec.Teardown, batchv1.JobSpec{}) { if jobConstraint(ap.Spec.Teardown) != nil { return fmt.Errorf("teardown: %w", jobConstraint(ap.Spec.Teardown)) } } return nil } // jobConstraint validates the JobSpec // only container is allowed in the JobSpec func jobConstraint(js batchv1.JobSpec) error { if js.ActiveDeadlineSeconds != nil { return fmt.Errorf("activeDeadlineSeconds is not allowed") } if js.BackoffLimit != nil { return fmt.Errorf("backoffLimit is not allowed") } if js.BackoffLimitPerIndex != nil { return fmt.Errorf("backoffLimitPerIndex is not allowed") } if js.Completions != nil { return fmt.Errorf("completions is not allowed") } if js.CompletionMode != nil { return fmt.Errorf("completionMode is not allowed") } if js.ManagedBy != nil { return fmt.Errorf("managedBy is not allowed") } if js.ManualSelector != nil { return fmt.Errorf("manualSelector is not allowed") } if js.MaxFailedIndexes != nil { return fmt.Errorf("maxFailedIndexes is not allowed") } if js.Parallelism != nil { return fmt.Errorf("parallelism is not allowed") } if js.PodFailurePolicy != nil { return fmt.Errorf("podFailurePolicy is not allowed") } if js.PodReplacementPolicy != nil { return fmt.Errorf("podReplacementPolicy is not allowed") } if js.Selector != nil { return fmt.Errorf("selector is not allowed") } if js.TTLSecondsAfterFinished != nil { return fmt.Errorf("ttlSecondsAfterFinished is not allowed") } if js.SuccessPolicy != nil { return fmt.Errorf("successPolicy is not allowed") } if js.Suspend != nil { return fmt.Errorf("suspend is not allowed") } if podConstraint(js.Template) != nil { return fmt.Errorf("pod template: %w", podConstraint(js.Template)) } return nil } // podConstraint validates the PodSpec func podConstraint(pt corev1.PodTemplateSpec) error { if len(pt.Name) > 0 { return fmt.Errorf("name is not allowed") } if len(pt.Namespace) > 0 { return fmt.Errorf("namespace is not allowed") } if len(pt.Spec.Volumes) > 0 { return fmt.Errorf("volumes are not allowed") } if len(pt.Spec.InitContainers) > 0 { return fmt.Errorf("initContainers are not allowed") } if len(pt.Spec.Containers) != 1 { return fmt.Errorf("container count should be 1") } if containerConstraint(pt.Spec.Containers[0]) != nil { return fmt.Errorf("container: %w", containerConstraint(pt.Spec.Containers[0])) } return nil } // containerConstraint validates the Container func containerConstraint(c corev1.Container) error { if c.Image == "" { return fmt.Errorf("image is empty") } if len(c.VolumeMounts) > 0 { return fmt.Errorf("volumeMounts are not allowed") } return nil }