diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ef6d1d0..337a23d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,8 +20,8 @@ jobs: - name: Display Go version run: go version - name: Check that all autogenerated files are up to date - run: make manifests generate && git diff --quiet HEAD -- + run: make manifests generate && git diff HEAD -- - name: Check that all docs are up to date - run: make docs && git diff --quiet HEAD -- + run: make docs && git diff HEAD -- - name: Unit Test run: make test diff --git a/api/v1beta1/questdb_types.go b/api/v1beta1/questdb_types.go index b15619e..d92ad13 100644 --- a/api/v1beta1/questdb_types.go +++ b/api/v1beta1/questdb_types.go @@ -58,6 +58,7 @@ type QuestDBSpec struct { PodAnnotations map[string]string `json:"podAnnotations,omitempty"` PodSecurityContext v1.PodSecurityContext `json:"podSecurityContext,omitempty"` Resources v1.ResourceRequirements `json:"resources,omitempty"` + ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` StatefulSetAnnotations map[string]string `json:"statefulSetAnnotations,omitempty"` Tolerations []v1.Toleration `json:"tolerations,omitempty"` } diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 19cc4c9..fcf2abe 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -353,6 +353,13 @@ func (in *QuestDBSpec) DeepCopyInto(out *QuestDBSpec) { } in.PodSecurityContext.DeepCopyInto(&out.PodSecurityContext) in.Resources.DeepCopyInto(&out.Resources) + if in.ServiceAnnotations != nil { + in, out := &in.ServiceAnnotations, &out.ServiceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.StatefulSetAnnotations != nil { in, out := &in.StatefulSetAnnotations, &out.StatefulSetAnnotations *out = make(map[string]string, len(*in)) diff --git a/config/crd/bases/crd.questdb.io_questdbs.yaml b/config/crd/bases/crd.questdb.io_questdbs.yaml index 1f731ac..8983aa7 100644 --- a/config/crd/bases/crd.questdb.io_questdbs.yaml +++ b/config/crd/bases/crd.questdb.io_questdbs.yaml @@ -2827,6 +2827,10 @@ spec: to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object + serviceAnnotations: + additionalProperties: + type: string + type: object statefulSetAnnotations: additionalProperties: type: string diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 54a37be..2459a08 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,4 +5,4 @@ kind: Kustomization images: - name: controller newName: questdb/questdb-operator - newTag: v0.0.0 + newTag: v0.5.1 diff --git a/docs.md b/docs.md index 431d1b7..543c8f4 100644 --- a/docs.md +++ b/docs.md @@ -169,6 +169,7 @@ _Appears in:_ | `podAnnotations` _object (keys:string, values:string)_ | | | `podSecurityContext` _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#podsecuritycontext-v1-core)_ | | | `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#resourcerequirements-v1-core)_ | | +| `serviceAnnotations` _object (keys:string, values:string)_ | | | `statefulSetAnnotations` _object (keys:string, values:string)_ | | | `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#toleration-v1-core) array_ | | diff --git a/internal/controller/questdb_controller.go b/internal/controller/questdb_controller.go index f9f3dd4..45f21ba 100644 --- a/internal/controller/questdb_controller.go +++ b/internal/controller/questdb_controller.go @@ -353,9 +353,10 @@ func (r *QuestDBReconciler) reconcileStatefulSet(ctx context.Context, q *crdv1be func (r *QuestDBReconciler) buildService(q *crdv1beta1.QuestDB) v1.Service { svc := v1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: q.Name, - Namespace: q.Namespace, - Labels: q.Labels, + Name: q.Name, + Namespace: q.Namespace, + Labels: q.Labels, + Annotations: q.Spec.ServiceAnnotations, }, Spec: v1.ServiceSpec{ Ports: []v1.ServicePort{ @@ -391,7 +392,8 @@ func (r *QuestDBReconciler) buildService(q *crdv1beta1.QuestDB) v1.Service { func (r *QuestDBReconciler) reconcileService(ctx context.Context, q *crdv1beta1.QuestDB) error { var ( - err error + err error + needsUpdate bool actual = &v1.Service{} desired = r.buildService(q) @@ -410,7 +412,21 @@ func (r *QuestDBReconciler) reconcileService(ctx context.Context, q *crdv1beta1. *actual = desired } + if !reflect.DeepEqual(actual.Annotations, desired.Annotations) { + actual.Annotations = desired.Annotations + needsUpdate = true + } + + if needsUpdate { + if err = r.Update(ctx, actual); err != nil { + r.Recorder.Event(q, v1.EventTypeWarning, "ServiceUpdateFailed", err.Error()) + return err + } + r.Recorder.Event(q, v1.EventTypeNormal, "ServiceUpdated", "Service updated") + } + return nil + } func (r *QuestDBReconciler) buildPvc(q *crdv1beta1.QuestDB) v1.PersistentVolumeClaim { diff --git a/internal/controller/questdb_controller_test.go b/internal/controller/questdb_controller_test.go index 05854dc..6223f66 100644 --- a/internal/controller/questdb_controller_test.go +++ b/internal/controller/questdb_controller_test.go @@ -82,9 +82,10 @@ var _ = Describe("QuestDB Controller", func() { }) - Context("port allocation", func() { + Context("service updates", func() { var ( - q *crdv1beta1.QuestDB + q *crdv1beta1.QuestDB + svc = &v1.Service{} ) BeforeEach(func() { By("Creating a new QuestDB") @@ -100,7 +101,6 @@ var _ = Describe("QuestDB Controller", func() { }, timeout, interval).Should(Succeed()) By("Check the service port values") - svc := &v1.Service{} Eventually(func() error { return k8sClient.Get(ctx, client.ObjectKey{Name: q.Name, Namespace: q.Namespace}, svc) }, timeout, interval).Should(Succeed()) @@ -127,6 +127,30 @@ var _ = Describe("QuestDB Controller", func() { )) }) + It("should update the service annotations if they change", func() { + By("Getting the service and ensuring that there are no annotations") + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: q.Name, Namespace: q.Namespace}, svc)).Should(Succeed()) + g.Expect(svc.Annotations).Should(BeEmpty()) + }, timeout, interval).Should(Succeed()) + + By("Adding a service annotation") + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(q), q)).To(Succeed()) + q.Spec.ServiceAnnotations = map[string]string{ + "test-key": "test-value", + } + g.Expect(k8sClient.Update(ctx, q)).To(Succeed()) + }, timeout, interval).Should(Succeed()) + + By("Verifying that the service has been updated") + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: q.Name, Namespace: q.Namespace}, svc)).Should(Succeed()) + g.Expect(svc.Annotations).ShouldNot(BeEmpty()) + g.Expect(svc.Annotations).Should(HaveKeyWithValue("test-key", "test-value")) + }, timeout, interval).Should(Succeed()) + }) + }) Context("statefulset updated", func() {