internal/workload/workload.go (253 lines of code) (raw):

// Copyright 2022 Google LLC // // 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 workload import ( "fmt" cloudsqlapi "github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/internal/api/v1" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/client" ) // Workload is a standard interface to access the pod definition for the // 7 major kinds of interfaces: Deployment, Pod, StatefulSet, ReplicaSet, // DaemonSet, Job, and Cronjob. // These methods are used by the ModifierStore to update the contents of the // workload's pod template (or the pod itself) so that it will contain // necessary configuration and other details before it starts, or if the // configuration changes. type Workload interface { PodSpec() corev1.PodSpec PodTemplateAnnotations() map[string]string Object() client.Object } // WithMutablePodTemplate interface applies only to workload types where the pod // template can be changed. type WithMutablePodTemplate interface { SetPodSpec(spec corev1.PodSpec) SetPodTemplateAnnotations(map[string]string) } // WorkloadList is a standard way to access the lists of the // 7 major kinds of interfaces: DeploymentList, DaemonSetList, PodList, // ReplicaSetList, StatefulSetList, JobList, and CronJobList. type WorkloadList interface { // List returns a pointer to the ObjectList ready to be passed to client.List() List() client.ObjectList // Workloads transforms the contents of the List into a slice of Workloads Workloads() []Workload } // realWorkloadList is a generic implementation of WorkloadList that enables // easy extension for new workload list types. type realWorkloadList[L client.ObjectList, T client.Object] struct { objectList L itemsAccessor func(L) []T wlCreator func(T) Workload } func (l *realWorkloadList[L, T]) List() client.ObjectList { return l.objectList } func (l *realWorkloadList[L, T]) Workloads() []Workload { items := l.itemsAccessor(l.objectList) wls := make([]Workload, len(items)) for i := range items { wls[i] = l.wlCreator(items[i]) } return wls } // ptrSlice takes a slice and returns slice with the pointer to each element. func ptrSlice[T any](l []T) []*T { p := make([]*T, len(l)) for i := 0; i < len(l); i++ { p[i] = &l[i] } return p } // WorkloadListForKind returns a new WorkloadList initialized for a particular // kubernetes Kind. func WorkloadListForKind(kind string) (WorkloadList, error) { switch kind { case "Deployment": return &realWorkloadList[*appsv1.DeploymentList, *appsv1.Deployment]{ objectList: &appsv1.DeploymentList{}, itemsAccessor: func(list *appsv1.DeploymentList) []*appsv1.Deployment { return ptrSlice[appsv1.Deployment](list.Items) }, wlCreator: func(v *appsv1.Deployment) Workload { return &DeploymentWorkload{Deployment: v} }, }, nil case "Pod": return &realWorkloadList[*corev1.PodList, *corev1.Pod]{ objectList: &corev1.PodList{}, itemsAccessor: func(list *corev1.PodList) []*corev1.Pod { return ptrSlice[corev1.Pod](list.Items) }, wlCreator: func(v *corev1.Pod) Workload { return &PodWorkload{Pod: v} }, }, nil case "StatefulSet": return &realWorkloadList[*appsv1.StatefulSetList, *appsv1.StatefulSet]{ objectList: &appsv1.StatefulSetList{}, itemsAccessor: func(list *appsv1.StatefulSetList) []*appsv1.StatefulSet { return ptrSlice[appsv1.StatefulSet](list.Items) }, wlCreator: func(v *appsv1.StatefulSet) Workload { return &StatefulSetWorkload{StatefulSet: v} }, }, nil case "ReplicaSet": return &realWorkloadList[*appsv1.ReplicaSetList, *appsv1.ReplicaSet]{ objectList: &appsv1.ReplicaSetList{}, itemsAccessor: func(list *appsv1.ReplicaSetList) []*appsv1.ReplicaSet { return ptrSlice[appsv1.ReplicaSet](list.Items) }, wlCreator: func(v *appsv1.ReplicaSet) Workload { return &ReplicaSetWorkload{ReplicaSet: v} }, }, nil case "Job": return &realWorkloadList[*batchv1.JobList, *batchv1.Job]{ objectList: &batchv1.JobList{}, itemsAccessor: func(list *batchv1.JobList) []*batchv1.Job { return ptrSlice[batchv1.Job](list.Items) }, wlCreator: func(v *batchv1.Job) Workload { return &JobWorkload{Job: v} }, }, nil case "CronJob": return &realWorkloadList[*batchv1.CronJobList, *batchv1.CronJob]{ objectList: &batchv1.CronJobList{}, itemsAccessor: func(list *batchv1.CronJobList) []*batchv1.CronJob { return ptrSlice[batchv1.CronJob](list.Items) }, wlCreator: func(v *batchv1.CronJob) Workload { return &CronJobWorkload{CronJob: v} }, }, nil case "DaemonSet": return &realWorkloadList[*appsv1.DaemonSetList, *appsv1.DaemonSet]{ objectList: &appsv1.DaemonSetList{}, itemsAccessor: func(list *appsv1.DaemonSetList) []*appsv1.DaemonSet { return ptrSlice[appsv1.DaemonSet](list.Items) }, wlCreator: func(v *appsv1.DaemonSet) Workload { return &DaemonSetWorkload{DaemonSet: v} }, }, nil default: return nil, fmt.Errorf("unknown kind for pod workloadSelector: %s", kind) } } // WorkloadForKind returns a workload for a particular Kind func WorkloadForKind(kind string) (Workload, error) { _, gk := schema.ParseKindArg(kind) switch gk.Kind { case "Deployment": return &DeploymentWorkload{Deployment: &appsv1.Deployment{}}, nil case "Pod": return &PodWorkload{Pod: &corev1.Pod{}}, nil case "StatefulSet": return &StatefulSetWorkload{StatefulSet: &appsv1.StatefulSet{}}, nil case "Job": return &JobWorkload{Job: &batchv1.Job{}}, nil case "CronJob": return &CronJobWorkload{CronJob: &batchv1.CronJob{}}, nil case "DaemonSet": return &DaemonSetWorkload{DaemonSet: &appsv1.DaemonSet{}}, nil case "ReplicaSet": return &ReplicaSetWorkload{ReplicaSet: &appsv1.ReplicaSet{}}, nil default: return nil, fmt.Errorf("unknown kind %s", kind) } } // workloadMatches tests if a workload matches a modifier based on its name, kind, and selectors. func workloadMatches(wl client.Object, workloadSelector cloudsqlapi.WorkloadSelectorSpec, ns string) bool { if workloadSelector.Kind != "" && wl.GetObjectKind().GroupVersionKind().Kind != workloadSelector.Kind { return false } if workloadSelector.Name != "" && wl.GetName() != workloadSelector.Name { return false } if ns != "" && wl.GetNamespace() != ns { return false } sel, err := workloadSelector.LabelsSelector() if err != nil { return false } if !sel.Empty() && !sel.Matches(labels.Set(wl.GetLabels())) { return false } return true } type DeploymentWorkload struct { Deployment *appsv1.Deployment } func (d *DeploymentWorkload) PodSpec() corev1.PodSpec { return d.Deployment.Spec.Template.Spec } func (d *DeploymentWorkload) PodTemplateAnnotations() map[string]string { return d.Deployment.Spec.Template.Annotations } func (d *DeploymentWorkload) SetPodTemplateAnnotations(v map[string]string) { d.Deployment.Spec.Template.Annotations = v } func (d *DeploymentWorkload) SetPodSpec(spec corev1.PodSpec) { d.Deployment.Spec.Template.Spec = spec } func (d *DeploymentWorkload) Object() client.Object { return d.Deployment } type StatefulSetWorkload struct { StatefulSet *appsv1.StatefulSet } func (d *StatefulSetWorkload) PodSpec() corev1.PodSpec { return d.StatefulSet.Spec.Template.Spec } func (d *StatefulSetWorkload) PodTemplateAnnotations() map[string]string { return d.StatefulSet.Spec.Template.Annotations } func (d *StatefulSetWorkload) SetPodTemplateAnnotations(v map[string]string) { d.StatefulSet.Spec.Template.Annotations = v } func (d *StatefulSetWorkload) SetPodSpec(spec corev1.PodSpec) { d.StatefulSet.Spec.Template.Spec = spec } func (d *StatefulSetWorkload) Object() client.Object { return d.StatefulSet } type PodWorkload struct { Pod *corev1.Pod } func (d *PodWorkload) PodSpec() corev1.PodSpec { return d.Pod.Spec } func (d *PodWorkload) PodTemplateAnnotations() map[string]string { return d.Pod.Annotations } func (d *PodWorkload) SetPodTemplateAnnotations(v map[string]string) { d.Pod.Annotations = v } func (d *PodWorkload) SetPodSpec(spec corev1.PodSpec) { d.Pod.Spec = spec } func (d *PodWorkload) Object() client.Object { return d.Pod } type JobWorkload struct { Job *batchv1.Job } func (d *JobWorkload) PodSpec() corev1.PodSpec { return d.Job.Spec.Template.Spec } func (d *JobWorkload) PodTemplateAnnotations() map[string]string { return d.Job.Spec.Template.Annotations } func (d *JobWorkload) Object() client.Object { return d.Job } type CronJobWorkload struct { CronJob *batchv1.CronJob } func (d *CronJobWorkload) PodSpec() corev1.PodSpec { return d.CronJob.Spec.JobTemplate.Spec.Template.Spec } func (d *CronJobWorkload) PodTemplateAnnotations() map[string]string { return d.CronJob.Spec.JobTemplate.Spec.Template.Annotations } func (d *CronJobWorkload) Object() client.Object { return d.CronJob } type DaemonSetWorkload struct { DaemonSet *appsv1.DaemonSet } func (d *DaemonSetWorkload) PodSpec() corev1.PodSpec { return d.DaemonSet.Spec.Template.Spec } func (d *DaemonSetWorkload) PodTemplateAnnotations() map[string]string { return d.DaemonSet.Spec.Template.Annotations } func (d *DaemonSetWorkload) SetPodTemplateAnnotations(v map[string]string) { d.DaemonSet.Spec.Template.Annotations = v } func (d *DaemonSetWorkload) SetPodSpec(spec corev1.PodSpec) { d.DaemonSet.Spec.Template.Spec = spec } func (d *DaemonSetWorkload) Object() client.Object { return d.DaemonSet } type ReplicaSetWorkload struct { ReplicaSet *appsv1.ReplicaSet } func (d *ReplicaSetWorkload) PodSpec() corev1.PodSpec { return d.ReplicaSet.Spec.Template.Spec } func (d *ReplicaSetWorkload) PodTemplateAnnotations() map[string]string { return d.ReplicaSet.Spec.Template.Annotations } func (d *ReplicaSetWorkload) SetPodTemplateAnnotations(v map[string]string) { d.ReplicaSet.Spec.Template.Annotations = v } func (d *ReplicaSetWorkload) SetPodSpec(spec corev1.PodSpec) { d.ReplicaSet.Spec.Template.Spec = spec } func (d *ReplicaSetWorkload) Object() client.Object { return d.ReplicaSet }