diff --git a/.env.local b/.env.local index a47dda8..57dfc5b 100644 --- a/.env.local +++ b/.env.local @@ -1,5 +1,10 @@ -ATOM_BASEURL="https://service.pdok.nl" -ATOM_GENERATOR_IMAGE="acrpdokprodman.azurecr.io/mirror/docker.io/pdok/atom-generator:0.6.1" -LIGHTTPD_IMAGE="acrpdokprodman.azurecr.io/mirror/docker.io/pdok/lighttpd:1.4.67" +// Deprecated, should be removed +ATOM_BASEURL="https://base.url" + +// atom-generator does not have a latest tag +ATOM_GENERATOR_IMAGE="docker.io/pdok/atom-generator:0.6.4" +LIGHTTPD_IMAGE="docker.io/pdok/lighttpd:latest" BLOB_ENDPOINT=http://localazurite.blob.azurite + +// generate local certs using smooth-operator: https://github.com/PDOK/smooth-operator/tree/master/pkg/util/certs CERT_DIR=certs \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 63dab75..9baf6c8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,4 +22,4 @@ jobs: - name: Run linter uses: golangci/golangci-lint-action@v8 with: - version: v2.5.0 + version: v2.10.1 diff --git a/Dockerfile b/Dockerfile index c768688..f68d5c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM docker.io/golang:1.25 AS builder +FROM docker.io/golang:1.26 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/go.mod b/go.mod index cb13b97..8a6fc7e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/pdok/atom-operator -go 1.25.0 +go 1.26.3 godebug default=go1.25 @@ -11,7 +11,7 @@ require ( github.com/onsi/ginkgo/v2 v2.22.1 github.com/onsi/gomega v1.36.2 github.com/pdok/atom-generator v0.6.4 - github.com/pdok/smooth-operator v1.2.7 + github.com/pdok/smooth-operator v1.2.10 github.com/peterbourgon/ff v1.7.1 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.11.1 diff --git a/go.sum b/go.sum index 02b803b..b69f8bf 100644 --- a/go.sum +++ b/go.sum @@ -150,8 +150,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pdok/atom-generator v0.6.4 h1:UpTTSKskdWLnxTAhSNOlz8dF2tBousD97V03kWzCV2k= github.com/pdok/atom-generator v0.6.4/go.mod h1:IlPwti5ocXDTjB4xmz0ZpHCOW/suuW5gQMfjfwPX6uM= -github.com/pdok/smooth-operator v1.2.7 h1:tgHLoUBEyO7iBVwN9CN1wxzyWc8S1MWb1Fzxi8O2hPM= -github.com/pdok/smooth-operator v1.2.7/go.mod h1:tqr/CDCXZHNzQzQVlSAnCmsPlx9tWAObsj8hg9mSSEU= +github.com/pdok/smooth-operator v1.2.10 h1:X3B+WsRFOsbDg3xexlr1BsPOx5/GcqBhLnFaGRRoLJ0= +github.com/pdok/smooth-operator v1.2.10/go.mod h1:Z87CdfxCKTvb7lkd19w1q+bGK0XaqHfa/ppDD8Zpnq0= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= github.com/peterbourgon/ff v1.7.1 h1:xt1lxTG+Nr2+tFtysY7abFgPoH3Lug8CwYJMOmJRXhk= github.com/peterbourgon/ff v1.7.1/go.mod h1:fYI5YA+3RDqQRExmFbHnBjEeWzh9TrS8rnRpEq7XIg0= diff --git a/internal/controller/atom_controller.go b/internal/controller/atom_controller.go index 20a25f9..9e2943c 100644 --- a/internal/controller/atom_controller.go +++ b/internal/controller/atom_controller.go @@ -32,6 +32,7 @@ import ( "time" "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" policyv1 "k8s.io/api/policy/v1" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -268,10 +269,16 @@ func (r *AtomReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func getLabels(atom *pdoknlv3.Atom) map[string]string { - labels := smoothutil.CloneOrEmptyMap(atom.GetLabels()) - labels[appLabelKey] = appName - return labels +var defaultLabels = map[string]string{appLabelKey: appName} + +func getLabelSelector(atom *pdoknlv3.Atom) *metav1.LabelSelector { + return &metav1.LabelSelector{ + MatchLabels: smoothutil.CombineLabels(atom.Labels, defaultLabels), + } +} + +func getObjectLabels(atom *pdoknlv3.Atom, objLabels map[string]string) map[string]string { + return smoothutil.CombineLabels(objLabels, atom.Labels, defaultLabels) } func ttlExpired(atom *pdoknlv3.Atom) bool { diff --git a/internal/controller/atom_controller_test.go b/internal/controller/atom_controller_test.go index 13420c6..51e0a2b 100644 --- a/internal/controller/atom_controller_test.go +++ b/internal/controller/atom_controller_test.go @@ -204,6 +204,48 @@ var _ = Describe("Testing Atom Controller", func() { }, "10s", "1s").Should(BeTrue()) }) + It("should maintain labels added externally after a reconcile", func() { + controllerReconciler := &AtomReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + AtomGeneratorImage: testImageName1, + LighttpdImage: testImageName2, + } + + By("Getting the original Deployment") + deployment := getBareDeployment(clusterAtom) + Eventually(func() bool { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(deployment), deployment) + return Expect(err).NotTo(HaveOccurred()) + }, "10s", "1s").Should(BeTrue()) + externalLabel := "external" + _, ok := deployment.Labels[externalLabel] + Expect(ok).To(BeFalse()) + + By("Adding new label to deployment") + err := k8sClient.Patch(ctx, deployment, client.RawPatch(types.MergePatchType, []byte( + fmt.Sprintf(`{"metadata": {"labels": {"%s": "%s"}}}`, externalLabel, externalLabel)))) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying that the Deployment was altered") + Eventually(func() bool { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(deployment), deployment) + val, ok := deployment.Labels[externalLabel] + return Expect(err).NotTo(HaveOccurred()) && Expect(ok).To(BeTrue()) && Expect(val).To(Equal(externalLabel)) + }, "10s", "1s").Should(BeTrue()) + + By("Reconciling the Atom again") + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{NamespacedName: objectKeyAtom}) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying that Deployment still has the external label") + Eventually(func() bool { + err = k8sClient.Get(ctx, client.ObjectKeyFromObject(deployment), deployment) + val, ok := deployment.Labels[externalLabel] + return Expect(err).NotTo(HaveOccurred()) && Expect(ok).To(BeTrue()) && Expect(val).To(Equal(externalLabel)) + }, "10s", "1s").Should(BeTrue()) + }) + It("Respects the TTL of the WMS", func() { By("Creating a new resource for the Kind WMS") controllerReconciler := &AtomReconciler{ diff --git a/internal/controller/atom_generator_configmap.go b/internal/controller/atom_generator_configmap.go index f7002a9..5d2c211 100644 --- a/internal/controller/atom_generator_configmap.go +++ b/internal/controller/atom_generator_configmap.go @@ -23,10 +23,7 @@ func getBareConfigMap(obj metav1.Object) *corev1.ConfigMap { } func (r *AtomReconciler) mutateAtomGeneratorConfigMap(atom *pdoknlv3.Atom, ownerInfo *smoothoperatorv1.OwnerInfo, configMap *corev1.ConfigMap) error { - labels := getLabels(atom) - if err := smoothutil.SetImmutableLabels(r.Client, configMap, labels); err != nil { - return err - } + configMap.Labels = getObjectLabels(atom, configMap.Labels) if len(configMap.Data) == 0 { generatorConfig, err := getGeneratorConfig(atom, ownerInfo) diff --git a/internal/controller/deployment.go b/internal/controller/deployment.go index eb4b2a5..0dfdf8b 100644 --- a/internal/controller/deployment.go +++ b/internal/controller/deployment.go @@ -31,19 +31,14 @@ func getBareDeployment(obj metav1.Object) *appsv1.Deployment { //nolint:funlen func (r *AtomReconciler) mutateDeployment(atom *pdoknlv3.Atom, deployment *appsv1.Deployment, configMapName string) error { - labels := getLabels(atom) - if err := smoothutil.SetImmutableLabels(r.Client, deployment, labels); err != nil { - return err - } + deployment.Labels = getObjectLabels(atom, deployment.Labels) podTemplateAnnotations := smoothutil.CloneOrEmptyMap(deployment.Spec.Template.GetAnnotations()) podTemplateAnnotations[evictAnnotation] = evictValue podTemplateAnnotations[defaultContainerAnnotation] = "atom-service" podTemplateAnnotations[versionCheckerAnnotation] = versionCheckerPriority - deployment.Spec.Selector = &metav1.LabelSelector{ - MatchLabels: labels, - } + deployment.Spec.Selector = getLabelSelector(atom) deployment.Spec.MinReadySeconds = 0 deployment.Spec.Strategy = appsv1.DeploymentStrategy{ @@ -58,7 +53,7 @@ func (r *AtomReconciler) mutateDeployment(atom *pdoknlv3.Atom, deployment *appsv podTemplateSpec := corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: labels, + Labels: getObjectLabels(atom, deployment.Spec.Template.Labels), Annotations: podTemplateAnnotations, }, Spec: corev1.PodSpec{ diff --git a/internal/controller/ingressroute.go b/internal/controller/ingressroute.go index 0990bd9..7b14ea9 100644 --- a/internal/controller/ingressroute.go +++ b/internal/controller/ingressroute.go @@ -28,10 +28,7 @@ func getBareIngressRoute(obj metav1.Object) *traefikiov1alpha1.IngressRoute { } func (r *AtomReconciler) mutateIngressRoute(atom *pdoknlv3.Atom, ingressRoute *traefikiov1alpha1.IngressRoute) error { - labels := getLabels(atom) - if err := smoothutil.SetImmutableLabels(r.Client, ingressRoute, labels); err != nil { - return err - } + ingressRoute.Labels = getObjectLabels(atom, ingressRoute.Labels) baseURL := atom.Spec.Service.BaseURL diff --git a/internal/controller/middleware.go b/internal/controller/middleware.go index f67bf5f..e48be95 100644 --- a/internal/controller/middleware.go +++ b/internal/controller/middleware.go @@ -25,10 +25,8 @@ func getBareStripPrefixMiddleware(obj metav1.Object) *traefikiov1alpha1.Middlewa } func (r *AtomReconciler) mutateStripPrefixMiddleware(atom *pdoknlv3.Atom, middleware *traefikiov1alpha1.Middleware) error { - labels := getLabels(atom) - if err := smoothutil.SetImmutableLabels(r.Client, middleware, labels); err != nil { - return err - } + middleware.Labels = getObjectLabels(atom, middleware.Labels) + middleware.Spec = traefikiov1alpha1.MiddlewareSpec{ StripPrefix: &dynamic.StripPrefix{ Prefixes: []string{atom.Spec.Service.BaseURL.Path + "/"}}, @@ -56,10 +54,8 @@ func getBareHeadersMiddleware(obj metav1.Object) *traefikiov1alpha1.Middleware { } func (r *AtomReconciler) mutateHeadersMiddleware(atom *pdoknlv3.Atom, middleware *traefikiov1alpha1.Middleware, csp string) error { - labels := getLabels(atom) - if err := smoothutil.SetImmutableLabels(r.Client, middleware, labels); err != nil { - return err - } + middleware.Labels = getObjectLabels(atom, middleware.Labels) + middleware.Spec = traefikiov1alpha1.MiddlewareSpec{ Headers: &dynamic.Headers{ // CSP @@ -92,10 +88,7 @@ func getBareDownloadLinkMiddleware(obj metav1.Object, index int) *traefikiov1alp } func (r *AtomReconciler) mutateDownloadLinkMiddleware(atom *pdoknlv3.Atom, prefix string, files []string, middleware *traefikiov1alpha1.Middleware) error { - labels := getLabels(atom) - if err := smoothutil.SetImmutableLabels(r.Client, middleware, labels); err != nil { - return err - } + middleware.Labels = getObjectLabels(atom, middleware.Labels) ingressRouteURLs := atom.Spec.IngressRouteURLs if len(ingressRouteURLs) == 0 { diff --git a/internal/controller/poddisruptionbudget.go b/internal/controller/poddisruptionbudget.go index d3eadc2..4f4f453 100644 --- a/internal/controller/poddisruptionbudget.go +++ b/internal/controller/poddisruptionbudget.go @@ -19,17 +19,11 @@ func getBarePodDisruptionBudget(obj metav1.Object) *policyv1.PodDisruptionBudget } func (r *AtomReconciler) mutatePodDisruptionBudget(atom *pdoknlv3.Atom, podDisruptionBudget *policyv1.PodDisruptionBudget) error { - labels := getLabels(atom) - if err := smoothutil.SetImmutableLabels(r.Client, podDisruptionBudget, labels); err != nil { - return err - } + podDisruptionBudget.Labels = getObjectLabels(atom, podDisruptionBudget.Labels) - matchLabels := smoothutil.CloneOrEmptyMap(labels) podDisruptionBudget.Spec = policyv1.PodDisruptionBudgetSpec{ MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: 1}, - Selector: &metav1.LabelSelector{ - MatchLabels: matchLabels, - }, + Selector: getLabelSelector(atom), } if err := smoothutil.EnsureSetGVK(r.Client, podDisruptionBudget, podDisruptionBudget); err != nil { diff --git a/internal/controller/service.go b/internal/controller/service.go index b6d62f3..6bff4de 100644 --- a/internal/controller/service.go +++ b/internal/controller/service.go @@ -18,11 +18,7 @@ func getBareService(obj metav1.Object) *corev1.Service { } func (r *AtomReconciler) mutateService(atom *pdoknlv3.Atom, service *corev1.Service) error { - labels := getLabels(atom) - selector := labels - if err := smoothutil.SetImmutableLabels(r.Client, service, labels); err != nil { - return err - } + service.Labels = getObjectLabels(atom, service.Labels) service.Spec = corev1.ServiceSpec{ Ports: []corev1.ServicePort{ @@ -32,7 +28,7 @@ func (r *AtomReconciler) mutateService(atom *pdoknlv3.Atom, service *corev1.Serv Protocol: corev1.ProtocolTCP, }, }, - Selector: selector, + Selector: getLabelSelector(atom).MatchLabels, } if err := smoothutil.EnsureSetGVK(r.Client, service, service); err != nil { return err