diff --git a/api/v1alpha1/runnerpool_types.go b/api/v1alpha1/runnerpool_types.go index ca0a1d8a..9530ce68 100644 --- a/api/v1alpha1/runnerpool_types.go +++ b/api/v1alpha1/runnerpool_types.go @@ -57,6 +57,10 @@ type SlackAgentConfig struct { } type RunnerPodTemplateSec struct { + // Standard object's metadata. Only `annotations` and `labels` are valid. + // +optional + ObjectMeta `json:"metadata"` + // Docker image name for the runner container. // +optional Image string `json:"image,omitempty"` @@ -95,6 +99,17 @@ type RunnerPodTemplateSec struct { ServiceAccountName string `json:"serviceAccountName,omitempty"` } +// ObjectMeta is metadata of objects. +// This is partially copied from metav1.ObjectMeta. +type ObjectMeta struct { + // Labels is a map of string keys and values. + // +optional + Labels map[string]string `json:"labels,omitempty"` + // Annotations is a map of string keys and values. + // +optional + Annotations map[string]string `json:"annotations,omitempty"` +} + // RunnerPoolStatus defines status of RunnerPool type RunnerPoolStatus struct { // Bound is true when the child Deployment is created. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index d627b56f..201387c1 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -9,9 +9,39 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectMeta) DeepCopyInto(out *ObjectMeta) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectMeta. +func (in *ObjectMeta) DeepCopy() *ObjectMeta { + if in == nil { + return nil + } + out := new(ObjectMeta) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RunnerPodTemplateSec) DeepCopyInto(out *RunnerPodTemplateSec) { *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) if in.ImagePullSecrets != nil { in, out := &in.ImagePullSecrets, &out.ImagePullSecrets *out = make([]v1.LocalObjectReference, len(*in)) diff --git a/config/crd/bases/meows.cybozu.com_runnerpools.yaml b/config/crd/bases/meows.cybozu.com_runnerpools.yaml index 744bbd41..7c5d001b 100644 --- a/config/crd/bases/meows.cybozu.com_runnerpools.yaml +++ b/config/crd/bases/meows.cybozu.com_runnerpools.yaml @@ -194,6 +194,21 @@ spec: type: string type: object type: array + metadata: + description: Standard object's metadata. Only `annotations` and + `labels` are valid. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is a map of string keys and values. + type: object + labels: + additionalProperties: + type: string + description: Labels is a map of string keys and values. + type: object + type: object resources: description: Compute Resources required by the runner container. properties: diff --git a/controllers/runnerpool_controller.go b/controllers/runnerpool_controller.go index cf30f663..043ece10 100644 --- a/controllers/runnerpool_controller.go +++ b/controllers/runnerpool_controller.go @@ -182,7 +182,10 @@ func (r *RunnerPoolReconciler) reconcileDeployment(ctx context.Context, log logr d.Spec.Selector = &metav1.LabelSelector{ MatchLabels: labelSet(rp, constants.AppComponentRunner), } - d.Spec.Template.Labels = labelSetForRunnerPod(rp, r.organizationName) + + d.Spec.Template.Labels = mergeMap(d.Spec.Template.GetLabels(), rp.Spec.Template.ObjectMeta.Labels) + d.Spec.Template.Labels = mergeMap(d.Spec.Template.GetLabels(), labelSetForRunnerPod(rp, r.organizationName)) + d.Spec.Template.Annotations = mergeMap(d.Spec.Template.GetAnnotations(), rp.Spec.Template.ObjectMeta.Annotations) d.Spec.Replicas = pointer.Int32Ptr(rp.Spec.Replicas) d.Spec.Template.Spec.ServiceAccountName = rp.Spec.Template.ServiceAccountName diff --git a/controllers/runnerpool_controller_test.go b/controllers/runnerpool_controller_test.go index 632ace3c..cfaccf3f 100644 --- a/controllers/runnerpool_controller_test.go +++ b/controllers/runnerpool_controller_test.go @@ -235,6 +235,13 @@ var _ = Describe("RunnerPool reconciler", func() { rp.Spec.SetupCommand = []string{"command", "arg1", "args2"} rp.Spec.SlackAgent.ServiceName = "slack-agent" rp.Spec.SlackAgent.Channel = "#test" + rp.Spec.Template.ObjectMeta.Labels = map[string]string{ + "test-label": "test", + constants.RunnerOrgLabelKey: "should-not-be-updated", + } + rp.Spec.Template.ObjectMeta.Annotations = map[string]string{ + "test-annotation": "test", + } rp.Spec.Template.Image = "sample:devel" rp.Spec.Template.ImagePullPolicy = corev1.PullIfNotPresent rp.Spec.Template.ImagePullSecrets = []corev1.LocalObjectReference{ @@ -310,6 +317,21 @@ var _ = Describe("RunnerPool reconciler", func() { })) // runner container spec + Expect(d.Spec.Template).To(MatchFields(IgnoreExtras, Fields{ + "ObjectMeta": MatchFields(IgnoreExtras, Fields{ + "Labels": MatchAllKeys(Keys{ + constants.AppNameLabelKey: Equal(constants.AppName), + constants.AppComponentLabelKey: Equal(constants.AppComponentRunner), + constants.AppInstanceLabelKey: Equal(rp.Name), + constants.RunnerOrgLabelKey: Equal(organizationName), + constants.RunnerRepoLabelKey: Equal(rp.Spec.RepositoryName), + "test-label": Equal("test"), + }), + "Annotations": MatchAllKeys(Keys{ + "test-annotation": Equal("test"), + }), + }), + })) Expect(d.Spec.Template.Spec.Containers).To(HaveLen(1)) Expect(d.Spec.Template.Spec.Containers[0]).To(MatchFields(IgnoreExtras, Fields{ "Name": Equal(constants.RunnerContainerName),