From 109af9097f5f8a5f836be85e5ef05e022b0b5669 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Fri, 31 Jul 2020 08:20:46 +0000 Subject: [PATCH 1/6] Customizable Annotations and Labels settings --- pkg/apis/deployment/v1/const.go | 40 +++ pkg/apis/deployment/v1/deployment_spec.go | 12 +- pkg/apis/deployment/v1/server_group_spec.go | 8 + pkg/deployment/resources/annotations.go | 316 ++++++++------------ pkg/deployment/resources/labels.go | 211 ++----------- pkg/util/collection/map.go | 185 ++++++++++++ pkg/util/k8sutil/map.go | 182 ----------- tests/annotations_test.go | 3 +- 8 files changed, 397 insertions(+), 560 deletions(-) create mode 100644 pkg/apis/deployment/v1/const.go create mode 100644 pkg/util/collection/map.go delete mode 100644 pkg/util/k8sutil/map.go diff --git a/pkg/apis/deployment/v1/const.go b/pkg/apis/deployment/v1/const.go new file mode 100644 index 000000000..bc90dc412 --- /dev/null +++ b/pkg/apis/deployment/v1/const.go @@ -0,0 +1,40 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// 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. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package v1 + +type LabelsMode string + +const ( + // LabelsDisabledMode disable annotations/labels override. Default if there is no annotations/labels set in ArangoDeployment + LabelsDisabledMode = "disabled" + // LabelsAppendMode add new annotations/labels without affecting old ones + LabelsAppendMode = "append" + // LabelsReplaceMode replace existing annotations/labels + LabelsReplaceMode = "replace" +) + +func (a *LabelsMode) Get(def LabelsMode) LabelsMode { + if a == nil { + return def + } + + return *a +} \ No newline at end of file diff --git a/pkg/apis/deployment/v1/deployment_spec.go b/pkg/apis/deployment/v1/deployment_spec.go index 6fe3e9f3b..0cf49bf6a 100644 --- a/pkg/apis/deployment/v1/deployment_spec.go +++ b/pkg/apis/deployment/v1/deployment_spec.go @@ -62,10 +62,18 @@ type DeploymentSpec struct { NetworkAttachedVolumes *bool `json:"networkAttachedVolumes,omitempty"` - // Annotations specified the annotations added to all resources + // Annotations specified the annotations added to Pods in this group. Annotations map[string]string `json:"annotations,omitempty"` - // Labels specified the labels added to all resources + // AnnotationsIgnoreList list regexp or plain definitions which annotations should be ignored + AnnotationsIgnoreList []string `json:"annotationsIgnoreList,omitempty"` + // AnnotationsMode Define annotations mode which should be use while overriding annotations + AnnotationsMode *LabelsMode `json:"annotationsMode,omitempty"` + // Labels specified the labels added to Pods in this group. Labels map[string]string `json:"labels,omitempty"` + // LabelsIgnoreList list regexp or plain definitions which labels should be ignored + LabelsIgnoreList []string `json:"labelsIgnoreList,omitempty"` + // LabelsMode Define labels mode which should be use while overriding labels + LabelsMode *LabelsMode `json:"labelsMode,omitempty"` RestoreFrom *string `json:"restoreFrom,omitempty"` diff --git a/pkg/apis/deployment/v1/server_group_spec.go b/pkg/apis/deployment/v1/server_group_spec.go index 4dc67ce53..dc85bc0c4 100644 --- a/pkg/apis/deployment/v1/server_group_spec.go +++ b/pkg/apis/deployment/v1/server_group_spec.go @@ -58,8 +58,16 @@ type ServerGroupSpec struct { Tolerations []core.Toleration `json:"tolerations,omitempty"` // Annotations specified the annotations added to Pods in this group. Annotations map[string]string `json:"annotations,omitempty"` + // AnnotationsIgnoreList list regexp or plain definitions which annotations should be ignored + AnnotationsIgnoreList []string `json:"annotationsIgnoreList,omitempty"` + // AnnotationsMode Define annotations mode which should be use while overriding annotations + AnnotationsMode *LabelsMode `json:"annotationsMode,omitempty"` // Labels specified the labels added to Pods in this group. Labels map[string]string `json:"labels,omitempty"` + // LabelsIgnoreList list regexp or plain definitions which labels should be ignored + LabelsIgnoreList []string `json:"labelsIgnoreList,omitempty"` + // LabelsMode Define labels mode which should be use while overriding labels + LabelsMode *LabelsMode `json:"labelsMode,omitempty"` // Envs allow to specify additional envs in this group. Envs ServerGroupEnvVars `json:"envs,omitempty"` // ServiceAccountName specifies the name of the service account used for Pods in this group. diff --git a/pkg/deployment/resources/annotations.go b/pkg/deployment/resources/annotations.go index 0b60a90a3..b28b8265f 100644 --- a/pkg/deployment/resources/annotations.go +++ b/pkg/deployment/resources/annotations.go @@ -23,16 +23,16 @@ package resources import ( - "time" - + "github.com/arangodb/kube-arangodb/pkg/deployment/patch" + "github.com/arangodb/kube-arangodb/pkg/util/collection" monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1" monitoringTypedClient "github.com/coreos/prometheus-operator/pkg/client/versioned/typed/monitoring/v1" + "k8s.io/apimachinery/pkg/types" "github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector" "github.com/arangodb/kube-arangodb/pkg/apis/deployment" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" - "github.com/arangodb/kube-arangodb/pkg/backup/utils" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/rs/zerolog/log" core "k8s.io/api/core/v1" @@ -53,7 +53,7 @@ func (r *Resources) EnsureAnnotations(cachedStatus inspector.Inspector) error { deployment.ArangoDeploymentResourceKind, r.context.GetAPIObject().GetName(), r.context.GetAPIObject().GetNamespace(), - r.context.GetSpec().Annotations); err != nil { + r.context.GetSpec()); err != nil { return err } @@ -62,7 +62,7 @@ func (r *Resources) EnsureAnnotations(cachedStatus inspector.Inspector) error { deployment.ArangoDeploymentResourceKind, r.context.GetAPIObject().GetName(), r.context.GetAPIObject().GetNamespace(), - r.context.GetSpec().Annotations); err != nil { + r.context.GetSpec()); err != nil { return err } @@ -71,7 +71,7 @@ func (r *Resources) EnsureAnnotations(cachedStatus inspector.Inspector) error { deployment.ArangoDeploymentResourceKind, r.context.GetAPIObject().GetName(), r.context.GetAPIObject().GetNamespace(), - r.context.GetSpec().Annotations); err != nil { + r.context.GetSpec()); err != nil { return err } @@ -80,7 +80,7 @@ func (r *Resources) EnsureAnnotations(cachedStatus inspector.Inspector) error { deployment.ArangoDeploymentResourceKind, r.context.GetAPIObject().GetName(), r.context.GetAPIObject().GetNamespace(), - r.context.GetSpec().Annotations); err != nil { + r.context.GetSpec()); err != nil { return err } @@ -89,7 +89,7 @@ func (r *Resources) EnsureAnnotations(cachedStatus inspector.Inspector) error { deployment.ArangoDeploymentResourceKind, r.context.GetAPIObject().GetName(), r.context.GetAPIObject().GetNamespace(), - r.context.GetSpec().Annotations); err != nil { + r.context.GetSpec()); err != nil { return err } @@ -108,22 +108,19 @@ func (r *Resources) EnsureAnnotations(cachedStatus inspector.Inspector) error { deployment.ArangoDeploymentResourceKind, r.context.GetAPIObject().GetName(), r.context.GetAPIObject().GetNamespace(), - r.context.GetSpec().Annotations); err != nil { + r.context.GetSpec()); err != nil { return err } return nil } -func ensureSecretsAnnotations(client typedCore.SecretInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string) error { +func ensureSecretsAnnotations(client typedCore.SecretInterface, cachedStatus inspector.Inspector, kind, name, namespace string, spec api.DeploymentSpec) error { if err := cachedStatus.IterateSecrets(func(secret *core.Secret) error { - if !k8sutil.CompareAnnotations(secret.GetAnnotations(), annotations) { - log.Info().Msgf("Replacing annotations for Secret %s", secret.Name) - if err := setSecretAnnotations(client, secret, annotations); err != nil { - return err - } - } - + ensureAnnotationsMap(secret.Kind, secret, spec, func(name string, d []byte) error { + _, err := client.Patch(name, types.JSONPatchType, d) + return err + }) return nil }, func(secret *core.Secret) bool { return k8sutil.IsChildResource(kind, name, namespace, secret) @@ -134,33 +131,12 @@ func ensureSecretsAnnotations(client typedCore.SecretInterface, cachedStatus ins return nil } -func setSecretAnnotations(client typedCore.SecretInterface, secret *core.Secret, annotations map[string]string) error { - return utils.Retry(5, 200*time.Millisecond, func() error { - currentSecret, err := client.Get(secret.Name, meta.GetOptions{}) - if err != nil { - return err - } - - currentSecret.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentSecret.Annotations), annotations) - - _, err = client.Update(currentSecret) - if err != nil { - return err - } - - return nil - }) -} - -func ensureServiceAccountsAnnotations(client typedCore.ServiceAccountInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string) error { +func ensureServiceAccountsAnnotations(client typedCore.ServiceAccountInterface, cachedStatus inspector.Inspector, kind, name, namespace string, spec api.DeploymentSpec) error { if err := cachedStatus.IterateServiceAccounts(func(serviceAccount *core.ServiceAccount) error { - if !k8sutil.CompareAnnotations(serviceAccount.GetAnnotations(), annotations) { - log.Info().Msgf("Replacing annotations for ServiceAccount %s", serviceAccount.Name) - if err := setServiceAccountAnnotations(client, serviceAccount, annotations); err != nil { - return err - } - } - + ensureAnnotationsMap(serviceAccount.Kind, serviceAccount, spec, func(name string, d []byte) error { + _, err := client.Patch(name, types.JSONPatchType, d) + return err + }) return nil }, func(serviceAccount *core.ServiceAccount) bool { return k8sutil.IsChildResource(kind, name, namespace, serviceAccount) @@ -171,33 +147,12 @@ func ensureServiceAccountsAnnotations(client typedCore.ServiceAccountInterface, return nil } -func setServiceAccountAnnotations(client typedCore.ServiceAccountInterface, serviceAccount *core.ServiceAccount, annotations map[string]string) error { - return utils.Retry(5, 200*time.Millisecond, func() error { - currentServiceAccount, err := client.Get(serviceAccount.Name, meta.GetOptions{}) - if err != nil { - return err - } - - currentServiceAccount.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentServiceAccount.Annotations), annotations) - - _, err = client.Update(currentServiceAccount) - if err != nil { - return err - } - - return nil - }) -} - -func ensureServicesAnnotations(client typedCore.ServiceInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string) error { +func ensureServicesAnnotations(client typedCore.ServiceInterface, cachedStatus inspector.Inspector, kind, name, namespace string, spec api.DeploymentSpec) error { if err := cachedStatus.IterateServices(func(service *core.Service) error { - if !k8sutil.CompareAnnotations(service.GetAnnotations(), annotations) { - log.Info().Msgf("Replacing annotations for Service %s", service.Name) - if err := setServiceAnnotations(client, service, annotations); err != nil { - return err - } - } - + ensureAnnotationsMap(service.Kind, service, spec, func(name string, d []byte) error { + _, err := client.Patch(name, types.JSONPatchType, d) + return err + }) return nil }, func(service *core.Service) bool { return k8sutil.IsChildResource(kind, name, namespace, service) @@ -208,33 +163,12 @@ func ensureServicesAnnotations(client typedCore.ServiceInterface, cachedStatus i return nil } -func setServiceAnnotations(client typedCore.ServiceInterface, service *core.Service, annotations map[string]string) error { - return utils.Retry(5, 200*time.Millisecond, func() error { - currentService, err := client.Get(service.Name, meta.GetOptions{}) - if err != nil { - return err - } - - currentService.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentService.Annotations), annotations) - - _, err = client.Update(currentService) - if err != nil { - return err - } - - return nil - }) -} - -func ensurePdbsAnnotations(client policyTyped.PodDisruptionBudgetInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string) error { +func ensurePdbsAnnotations(client policyTyped.PodDisruptionBudgetInterface, cachedStatus inspector.Inspector, kind, name, namespace string, spec api.DeploymentSpec) error { if err := cachedStatus.IteratePodDisruptionBudgets(func(podDisruptionBudget *policy.PodDisruptionBudget) error { - if !k8sutil.CompareAnnotations(podDisruptionBudget.GetAnnotations(), annotations) { - log.Info().Msgf("Replacing annotations for PodDisruptionBudget %s", podDisruptionBudget.Name) - if err := setPdbAnnotations(client, podDisruptionBudget, annotations); err != nil { - return err - } - } - + ensureAnnotationsMap(podDisruptionBudget.Kind, podDisruptionBudget, spec, func(name string, d []byte) error { + _, err := client.Patch(name, types.JSONPatchType, d) + return err + }) return nil }, func(podDisruptionBudget *policy.PodDisruptionBudget) bool { return k8sutil.IsChildResource(kind, name, namespace, podDisruptionBudget) @@ -245,33 +179,12 @@ func ensurePdbsAnnotations(client policyTyped.PodDisruptionBudgetInterface, cach return nil } -func setPdbAnnotations(client policyTyped.PodDisruptionBudgetInterface, podDisruptionBudget *policy.PodDisruptionBudget, annotations map[string]string) error { - return utils.Retry(5, 200*time.Millisecond, func() error { - currentPodDistributionBudget, err := client.Get(podDisruptionBudget.Name, meta.GetOptions{}) - if err != nil { - return err - } - - currentPodDistributionBudget.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentPodDistributionBudget.Annotations), annotations) - - _, err = client.Update(currentPodDistributionBudget) - if err != nil { - return err - } - - return nil - }) -} - -func ensurePvcsAnnotations(client typedCore.PersistentVolumeClaimInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string) error { +func ensurePvcsAnnotations(client typedCore.PersistentVolumeClaimInterface, cachedStatus inspector.Inspector, kind, name, namespace string, spec api.DeploymentSpec) error { if err := cachedStatus.IteratePersistentVolumeClaims(func(persistentVolumeClaim *core.PersistentVolumeClaim) error { - if !k8sutil.CompareAnnotations(persistentVolumeClaim.GetAnnotations(), annotations) { - log.Info().Msgf("Replacing annotations for PVC %s", persistentVolumeClaim.Name) - if err := setPvcAnnotations(client, persistentVolumeClaim, annotations); err != nil { - return err - } - } - + ensureGroupAnnotationsMap(persistentVolumeClaim.Kind, persistentVolumeClaim, spec, func(name string, d []byte) error { + _, err := client.Patch(name, types.JSONPatchType, d) + return err + }) return nil }, func(persistentVolumeClaim *core.PersistentVolumeClaim) bool { return k8sutil.IsChildResource(kind, name, namespace, persistentVolumeClaim) @@ -282,33 +195,12 @@ func ensurePvcsAnnotations(client typedCore.PersistentVolumeClaimInterface, cach return nil } -func setPvcAnnotations(client typedCore.PersistentVolumeClaimInterface, persistentVolumeClaim *core.PersistentVolumeClaim, annotations map[string]string) error { - return utils.Retry(5, 200*time.Millisecond, func() error { - currentVolumeClaim, err := client.Get(persistentVolumeClaim.Name, meta.GetOptions{}) - if err != nil { - return err - } - - currentVolumeClaim.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentVolumeClaim.Annotations), annotations) - - _, err = client.Update(currentVolumeClaim) - if err != nil { - return err - } - - return nil - }) -} - -func ensureServiceMonitorsAnnotations(client monitoringTypedClient.ServiceMonitorInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string) error { +func ensureServiceMonitorsAnnotations(client monitoringTypedClient.ServiceMonitorInterface, cachedStatus inspector.Inspector, kind, name, namespace string, spec api.DeploymentSpec) error { if err := cachedStatus.IterateServiceMonitors(func(serviceMonitor *monitoring.ServiceMonitor) error { - if !k8sutil.CompareAnnotations(serviceMonitor.GetAnnotations(), annotations) { - log.Info().Msgf("Replacing annotations for ServiceMonitors %s", serviceMonitor.Name) - if err := setServiceMonitorAnnotations(client, serviceMonitor, annotations); err != nil { - return err - } - } - + ensureAnnotationsMap(serviceMonitor.Kind, serviceMonitor, spec, func(name string, d []byte) error { + _, err := client.Patch(name, types.JSONPatchType, d) + return err + }) return nil }, func(serviceMonitor *monitoring.ServiceMonitor) bool { return k8sutil.IsChildResource(kind, name, namespace, serviceMonitor) @@ -319,24 +211,6 @@ func ensureServiceMonitorsAnnotations(client monitoringTypedClient.ServiceMonito return nil } -func setServiceMonitorAnnotations(client monitoringTypedClient.ServiceMonitorInterface, serviceMonitor *monitoring.ServiceMonitor, annotations map[string]string) error { - return utils.Retry(5, 200*time.Millisecond, func() error { - currentServiceMonitor, err := client.Get(serviceMonitor.Name, meta.GetOptions{}) - if err != nil { - return err - } - - currentServiceMonitor.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentServiceMonitor.Annotations), annotations) - - _, err = client.Update(currentServiceMonitor) - if err != nil { - return err - } - - return nil - }) -} - func getObjectGroup(obj meta.Object) api.ServerGroup { l := obj.GetLabels() if len(l) == 0 { @@ -353,16 +227,10 @@ func getObjectGroup(obj meta.Object) api.ServerGroup { func ensurePodsAnnotations(client typedCore.PodInterface, cachedStatus inspector.Inspector, kind, name, namespace string, annotations map[string]string, spec api.DeploymentSpec) error { if err := cachedStatus.IteratePods(func(pod *core.Pod) error { - group := getObjectGroup(pod) - mergedAnnotations := k8sutil.MergeAnnotations(annotations, spec.GetServerGroupSpec(group).Annotations) - - if !k8sutil.CompareAnnotations(pod.GetAnnotations(), mergedAnnotations) { - log.Info().Msgf("Replacing annotations for Pod %s", pod.Name) - if err := setPodAnnotations(client, pod, mergedAnnotations); err != nil { - return err - } - } - + ensureGroupAnnotationsMap(pod.Kind, pod, spec, func(name string, d []byte) error { + _, err := client.Patch(name, types.JSONPatchType, d) + return err + }) return nil }, func(pod *core.Pod) bool { return k8sutil.IsChildResource(kind, name, namespace, pod) @@ -373,27 +241,89 @@ func ensurePodsAnnotations(client typedCore.PodInterface, cachedStatus inspector return nil } -func setPodAnnotations(client typedCore.PodInterface, pod *core.Pod, annotations map[string]string) error { - return utils.Retry(5, 200*time.Millisecond, func() error { - currentPod, err := client.Get(pod.Name, meta.GetOptions{}) - if err != nil { - return err - } - - currentPod.Annotations = k8sutil.MergeAnnotations(k8sutil.GetSecuredAnnotations(currentPod.Annotations), annotations) - - _, err = client.Update(currentPod) - if err != nil { - return err - } - - return nil - }) -} - func (r *Resources) isChildResource(obj meta.Object) bool { return k8sutil.IsChildResource(deployment.ArangoDeploymentResourceKind, r.context.GetAPIObject().GetName(), r.context.GetAPIObject().GetNamespace(), obj) } + +func getDefaultMode(annotations map[string]string) api.LabelsMode { + if len(annotations) == 0 { + return api.LabelsDisabledMode + } + return api.LabelsReplaceMode +} + +func ensureGroupLabelsMap(kind string, obj meta.Object, spec api.DeploymentSpec, + patchCmd func(name string, d []byte) error) bool { + group := getObjectGroup(obj) + groupSpec := spec.GetServerGroupSpec(group) + expected := collection.MergeAnnotations(spec.Labels, groupSpec.Labels) + + ignoredList := append(spec.LabelsIgnoreList, groupSpec.LabelsIgnoreList...) + + mode := groupSpec.LabelsMode.Get(spec.LabelsMode.Get(getDefaultMode(expected))) + + return ensureObjectMap(kind, obj, mode, expected, obj.GetLabels(), collection.LabelsPatch, patchCmd, ignoredList...) +} + +func ensureLabelsMap(kind string, obj meta.Object, spec api.DeploymentSpec, + patchCmd func(name string, d []byte) error ) bool { + expected := spec.Labels + ignored := spec.AnnotationsIgnoreList + + mode := spec.LabelsMode.Get(getDefaultMode(expected)) + + return ensureObjectMap(kind, obj, mode, expected, obj.GetLabels(), collection.LabelsPatch, patchCmd, ignored...) +} + +func ensureGroupAnnotationsMap(kind string, obj meta.Object, spec api.DeploymentSpec, + patchCmd func(name string, d []byte) error) bool { + group := getObjectGroup(obj) + groupSpec := spec.GetServerGroupSpec(group) + expected := collection.MergeAnnotations(spec.Annotations, groupSpec.Annotations) + + ignoredList := append(spec.AnnotationsIgnoreList, groupSpec.AnnotationsIgnoreList...) + + mode := groupSpec.AnnotationsMode.Get(spec.AnnotationsMode.Get(getDefaultMode(expected))) + + return ensureObjectMap(kind, obj, mode, expected, obj.GetAnnotations(), collection.AnnotationsPatch, patchCmd, ignoredList...) +} + +func ensureAnnotationsMap(kind string, obj meta.Object, spec api.DeploymentSpec, + patchCmd func(name string, d []byte) error ) bool { + expected := spec.Annotations + ignored := spec.AnnotationsIgnoreList + + mode := spec.AnnotationsMode.Get(getDefaultMode(expected)) + + return ensureObjectMap(kind, obj, mode, expected, obj.GetAnnotations(), collection.AnnotationsPatch, patchCmd, ignored...) +} + +func ensureObjectMap(kind string, obj meta.Object, mode api.LabelsMode, + expected, actual map[string]string, + patchGetter func(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ... string) patch.Patch, + patchCmd func(name string, d []byte) error, + ignored ... string) bool { + p := patchGetter(mode, expected, actual, ignored...) + + if len(p) == 0 { + return false + } + + log.Info().Msgf("Replacing annotations for %s %s", kind, obj.GetName()) + + d, err := p.Marshal() + if err != nil { + log.Warn().Err(err).Msgf("Unable to marshal kubernetes patch instruction") + return false + } + + if err := patchCmd(obj.GetName(), d); err != nil { + log.Warn().Err(err).Msgf("Unable to patch Pod") + return false + } + + return true +} \ No newline at end of file diff --git a/pkg/deployment/resources/labels.go b/pkg/deployment/resources/labels.go index a08dce8e0..3598a4fd9 100644 --- a/pkg/deployment/resources/labels.go +++ b/pkg/deployment/resources/labels.go @@ -23,30 +23,15 @@ package resources import ( - "encoding/json" - monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1" - "github.com/arangodb/kube-arangodb/pkg/deployment/patch" "github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector" "github.com/arangodb/kube-arangodb/pkg/util/errors" - "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" core "k8s.io/api/core/v1" policy "k8s.io/api/policy/v1beta1" - meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) -var ( - reservedLabels = RestrictedList{ - k8sutil.LabelKeyArangoDeployment, - k8sutil.LabelKeyArangoLocalStorage, - k8sutil.LabelKeyApp, - k8sutil.LabelKeyRole, - k8sutil.LabelKeyArangoExporter, - } -) - func (r *Resources) EnsureLabels(cachedStatus inspector.Inspector) error { r.log.Info().Msgf("Ensuring labels") @@ -84,19 +69,11 @@ func (r *Resources) EnsureLabels(cachedStatus inspector.Inspector) error { func (r *Resources) EnsureSecretLabels(cachedStatus inspector.Inspector) error { changed := false if err := cachedStatus.IterateSecrets(func(secret *core.Secret) error { - if p := ensureLabelsFromMaps(secret, r.context.GetSpec().Labels, r.context.GetSpec().GetServerGroupSpec(getObjectGroup(secret)).Labels); len(p) != 0 { - patch, err := json.Marshal(p) - if err != nil { - return err - } - r.log.Info().Int("changes", len(p)).Msgf("Updating labels for secret %s", secret.GetName()) - - if _, err = r.context.GetKubeCli().CoreV1().Secrets(r.context.GetAPIObject().GetNamespace()).Patch(secret.GetName(), types.JSONPatchType, patch); err != nil { - return err - } - + if ensureLabelsMap(secret.Kind, secret, r.context.GetSpec(), func(name string, d []byte) error { + _, err := r.context.GetKubeCli().CoreV1().Secrets(r.context.GetAPIObject().GetNamespace()).Patch(name, types.JSONPatchType, d) + return err + }) { changed = true - return nil } return nil @@ -116,19 +93,11 @@ func (r *Resources) EnsureSecretLabels(cachedStatus inspector.Inspector) error { func (r *Resources) EnsureServiceAccountsLabels(cachedStatus inspector.Inspector) error { changed := false if err := cachedStatus.IterateServiceAccounts(func(serviceAccount *core.ServiceAccount) error { - if p := ensureLabelsFromMaps(serviceAccount, r.context.GetSpec().Labels, r.context.GetSpec().GetServerGroupSpec(getObjectGroup(serviceAccount)).Labels); len(p) != 0 { - patch, err := json.Marshal(p) - if err != nil { - return err - } - r.log.Info().Int("changes", len(p)).Msgf("Updating labels for ServiceAccount %s", serviceAccount.GetName()) - - if _, err = r.context.GetKubeCli().CoreV1().ServiceAccounts(r.context.GetAPIObject().GetNamespace()).Patch(serviceAccount.GetName(), types.JSONPatchType, patch); err != nil { - return err - } - + if ensureLabelsMap(serviceAccount.Kind, serviceAccount, r.context.GetSpec(), func(name string, d []byte) error { + _, err := r.context.GetKubeCli().CoreV1().ServiceAccounts(r.context.GetAPIObject().GetNamespace()).Patch(name, types.JSONPatchType, d) + return err + }) { changed = true - return nil } return nil @@ -148,19 +117,11 @@ func (r *Resources) EnsureServiceAccountsLabels(cachedStatus inspector.Inspector func (r *Resources) EnsureServicesLabels(cachedStatus inspector.Inspector) error { changed := false if err := cachedStatus.IterateServices(func(service *core.Service) error { - if p := ensureLabelsFromMaps(service, r.context.GetSpec().Labels, r.context.GetSpec().GetServerGroupSpec(getObjectGroup(service)).Labels); len(p) != 0 { - patch, err := json.Marshal(p) - if err != nil { - return err - } - r.log.Info().Int("changes", len(p)).Msgf("Updating labels for Service %s", service.GetName()) - - if _, err = r.context.GetKubeCli().CoreV1().Services(r.context.GetAPIObject().GetNamespace()).Patch(service.GetName(), types.JSONPatchType, patch); err != nil { - return err - } - + if ensureLabelsMap(service.Kind, service, r.context.GetSpec(), func(name string, d []byte) error { + _, err := r.context.GetKubeCli().CoreV1().Services(r.context.GetAPIObject().GetNamespace()).Patch(name, types.JSONPatchType, d) + return err + }) { changed = true - return nil } return nil @@ -180,19 +141,11 @@ func (r *Resources) EnsureServicesLabels(cachedStatus inspector.Inspector) error func (r *Resources) EnsureServiceMonitorsLabels(cachedStatus inspector.Inspector) error { changed := false if err := cachedStatus.IterateServiceMonitors(func(serviceMonitor *monitoring.ServiceMonitor) error { - if p := ensureLabelsFromMaps(serviceMonitor, r.context.GetSpec().Labels, r.context.GetSpec().GetServerGroupSpec(getObjectGroup(serviceMonitor)).Labels); len(p) != 0 { - patch, err := json.Marshal(p) - if err != nil { - return err - } - r.log.Info().Int("changes", len(p)).Msgf("Updating labels for ServiceMonitor %s", serviceMonitor.GetName()) - - if _, err = r.context.GetMonitoringV1Cli().ServiceMonitors(r.context.GetAPIObject().GetNamespace()).Patch(serviceMonitor.GetName(), types.JSONPatchType, patch); err != nil { - return err - } - + if ensureLabelsMap(serviceMonitor.Kind, serviceMonitor, r.context.GetSpec(), func(name string, d []byte) error { + _, err := r.context.GetMonitoringV1Cli().ServiceMonitors(r.context.GetAPIObject().GetNamespace()).Patch(name, types.JSONPatchType, d) + return err + }) { changed = true - return nil } return nil @@ -212,19 +165,11 @@ func (r *Resources) EnsureServiceMonitorsLabels(cachedStatus inspector.Inspector func (r *Resources) EnsurePodsLabels(cachedStatus inspector.Inspector) error { changed := false if err := cachedStatus.IteratePods(func(pod *core.Pod) error { - if p := ensureLabelsFromMaps(pod, r.context.GetSpec().Labels, r.context.GetSpec().GetServerGroupSpec(getObjectGroup(pod)).Labels); len(p) != 0 { - patch, err := json.Marshal(p) - if err != nil { - return err - } - r.log.Info().Int("changes", len(p)).Msgf("Updating labels for Pod %s", pod.GetName()) - - if _, err = r.context.GetKubeCli().CoreV1().Pods(r.context.GetAPIObject().GetNamespace()).Patch(pod.GetName(), types.JSONPatchType, patch); err != nil { - return err - } - + if ensureGroupLabelsMap(pod.Kind, pod, r.context.GetSpec(), func(name string, d []byte) error { + _, err := r.context.GetKubeCli().CoreV1().Pods(r.context.GetAPIObject().GetNamespace()).Patch(name, types.JSONPatchType, d) + return err + }) { changed = true - return nil } return nil @@ -244,19 +189,11 @@ func (r *Resources) EnsurePodsLabels(cachedStatus inspector.Inspector) error { func (r *Resources) EnsurePersistentVolumeClaimsLabels(cachedStatus inspector.Inspector) error { changed := false if err := cachedStatus.IteratePersistentVolumeClaims(func(persistentVolumeClaim *core.PersistentVolumeClaim) error { - if p := ensureLabelsFromMaps(persistentVolumeClaim, r.context.GetSpec().Labels, r.context.GetSpec().GetServerGroupSpec(getObjectGroup(persistentVolumeClaim)).Labels); len(p) != 0 { - patch, err := json.Marshal(p) - if err != nil { - return err - } - r.log.Info().Int("changes", len(p)).Msgf("Updating labels for PersistentVolumeClaim %s", persistentVolumeClaim.GetName()) - - if _, err = r.context.GetKubeCli().CoreV1().PersistentVolumeClaims(r.context.GetAPIObject().GetNamespace()).Patch(persistentVolumeClaim.GetName(), types.JSONPatchType, patch); err != nil { - return err - } - + if ensureGroupLabelsMap(persistentVolumeClaim.Kind, persistentVolumeClaim, r.context.GetSpec(), func(name string, d []byte) error { + _, err := r.context.GetKubeCli().CoreV1().PersistentVolumeClaims(r.context.GetAPIObject().GetNamespace()).Patch(name, types.JSONPatchType, d) + return err + }) { changed = true - return nil } return nil @@ -276,19 +213,11 @@ func (r *Resources) EnsurePersistentVolumeClaimsLabels(cachedStatus inspector.In func (r *Resources) EnsurePodDisruptionBudgetsLabels(cachedStatus inspector.Inspector) error { changed := false if err := cachedStatus.IteratePodDisruptionBudgets(func(budget *policy.PodDisruptionBudget) error { - if p := ensureLabelsFromMaps(budget, r.context.GetSpec().Labels, r.context.GetSpec().GetServerGroupSpec(getObjectGroup(budget)).Labels); len(p) != 0 { - patch, err := json.Marshal(p) - if err != nil { - return err - } - r.log.Info().Int("changes", len(p)).Msgf("Updating labels for PodDisruptionBudget %s", budget.GetName()) - - if _, err = r.context.GetKubeCli().PolicyV1beta1().PodDisruptionBudgets(r.context.GetAPIObject().GetNamespace()).Patch(budget.GetName(), types.JSONPatchType, patch); err != nil { - return err - } - + if ensureLabelsMap(budget.Kind, budget, r.context.GetSpec(), func(name string, d []byte) error { + _, err := r.context.GetKubeCli().PolicyV1beta1().PodDisruptionBudgets(r.context.GetAPIObject().GetNamespace()).Patch(name, types.JSONPatchType, d) + return err + }) { changed = true - return nil } return nil @@ -303,86 +232,4 @@ func (r *Resources) EnsurePodDisruptionBudgetsLabels(cachedStatus inspector.Insp } return nil -} - -type RestrictedList []string - -func (r RestrictedList) IsRestricted(s string) bool { - for _, i := range r { - if i == s { - return true - } - } - - return false -} - -func ensureLabelsFromMaps(obj meta.Object, labels ...map[string]string) patch.Patch { - m := map[string]string{} - for _, labelMap := range labels { - if len(labelMap) == 0 { - continue - } - for k, v := range labelMap { - m[k] = v - } - } - - return ensureLabels(obj, m) -} - -func ensureLabels(obj meta.Object, labels map[string]string) patch.Patch { - objLabels := obj.GetLabels() - if objLabels == nil { - objLabels = map[string]string{} - } - - if labels == nil { - labels = map[string]string{} - } - - m := ensureMap(objLabels, labels, reservedLabels) - if len(m) == 0 { - return m - } - - // Map not present, we need to fix this - if obj.GetLabels() == nil { - p := patch.NewPatch() - p.ItemAdd(patch.NewPath("metadata", "labels"), map[string]string{}) - p.Add(m...) - return p - } - - return m -} - -func ensureMap(obj map[string]string, labels map[string]string, restricted RestrictedList) patch.Patch { - p := patch.Patch{} - - for k := range obj { - if restricted.IsRestricted(k) { - continue - } - - if _, ok := labels[k]; !ok { - p.ItemRemove(patch.NewPath("metadata", "labels", k)) - } - } - - for k, v := range labels { - if restricted.IsRestricted(k) { - continue - } - - if objV, ok := obj[k]; !ok { - p.ItemAdd(patch.NewPath("metadata", "labels", k), v) - continue - } else if objV != v { - p.ItemReplace(patch.NewPath("metadata", "labels", k), v) - continue - } - } - - return p -} +} \ No newline at end of file diff --git a/pkg/util/collection/map.go b/pkg/util/collection/map.go new file mode 100644 index 000000000..13bb3cbc4 --- /dev/null +++ b/pkg/util/collection/map.go @@ -0,0 +1,185 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// 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. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package collection + +import ( + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/deployment/patch" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + "regexp" +) + +const ( + kubernetesAnnotationMatch = ".*kubernetes\\.io/.*" + arangoAnnotationMatch = ".*arangodb\\.com/.*" +) + +var ( + kubernetesAnnotationRegex *regexp.Regexp + arangoAnnotationRegex *regexp.Regexp + + reservedLabels = RestrictedList{ + k8sutil.LabelKeyArangoDeployment, + k8sutil.LabelKeyArangoLocalStorage, + k8sutil.LabelKeyApp, + k8sutil.LabelKeyRole, + k8sutil.LabelKeyArangoExporter, + } +) + +// MergeAnnotations into one annotations map +func MergeAnnotations(annotations ...map[string]string) map[string]string { + ret := map[string]string{} + + for _, annotationMap := range annotations { + if annotationMap == nil { + continue + } + + for annotation, value := range annotationMap { + ret[annotation] = value + } + } + + return ret +} + +func NewRestrictedList(param ... string) RestrictedList { + return param +} + +type RestrictedList []string + +func (r RestrictedList) IsRestricted(s string) bool { + for _, i := range r { + if match, err := regexp.MatchString(i, s); err != nil { + continue + } else if match { + return true + } + + if i == s { + return true + } + } + + return false +} + +func init() { + r, err := regexp.Compile(kubernetesAnnotationMatch) + if err != nil { + panic(err) + } + + kubernetesAnnotationRegex = r + + r, err = regexp.Compile(arangoAnnotationMatch) + if err != nil { + panic(err) + } + + arangoAnnotationRegex = r +} + +func LabelsPatch(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ... string) patch.Patch { + return getFieldPatch(mode, "labels", expected, actual, func(k, v string) bool { + if reservedLabels.IsRestricted(k) { + return true + } + + if NewRestrictedList(ignored...).IsRestricted(k) { + return true + } + + return false + }) +} + +func AnnotationsPatch(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ... string) patch.Patch { + return getFieldPatch(mode, "annotations", expected, actual, func(k, v string) bool { + if kubernetesAnnotationRegex.MatchString(k) { + return true + } + + if arangoAnnotationRegex.MatchString(k) { + return true + } + + if NewRestrictedList(ignored...).IsRestricted(k) { + return true + } + + return false + }) +} + +func getFieldPatch(mode api.LabelsMode, section string, expected map[string]string, actual map[string]string, filtered func(k ,v string) bool) patch.Patch { + p := patch.NewPatch() + + switch mode { + case api.LabelsDisabledMode: + break + case api.LabelsAppendMode: + for e, v := range expected { + if a, ok := actual[e]; !ok { + p.ItemAdd(patch.NewPath("metadata", section, e), v) + } else if v != a { + p.ItemReplace(patch.NewPath("metadata", section, e), v) + } + } + case api.LabelsReplaceMode: + for e, v := range expected { + if a, ok := actual[e]; !ok { + p.ItemAdd(patch.NewPath("metadata", section, e), v) + } else if v != a { + p.ItemReplace(patch.NewPath("metadata", section, e), v) + } + } + + for a, v := range actual { + if filtered != nil { + if filtered(a, v) { + continue + } + } + + if _, ok := expected[a]; !ok { + p.ItemRemove(patch.NewPath("metadata", section, a)) + } + } + } + + if len(p) == 0 { + return nil + } + + // Add map init + if actual == nil { + newP := patch.NewPatch() + newP.ItemAdd(patch.NewPath("metadata", section), []string{}) + p = append(newP, p...) + } + + return p +} \ No newline at end of file diff --git a/pkg/util/k8sutil/map.go b/pkg/util/k8sutil/map.go deleted file mode 100644 index 010a672ed..000000000 --- a/pkg/util/k8sutil/map.go +++ /dev/null @@ -1,182 +0,0 @@ -// -// DISCLAIMER -// -// Copyright 2020 ArangoDB GmbH, Cologne, Germany -// -// 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. -// -// Copyright holder is ArangoDB GmbH, Cologne, Germany -// -// Author Adam Janikowski -// - -package k8sutil - -import ( - "regexp" - - "github.com/arangodb/kube-arangodb/pkg/apis/deployment" -) - -const ( - kubernetesAnnotationMatch = ".*kubernetes\\.io/.*" - arangoAnnotationMatch = ".*arangodb\\.com/.*" -) - -var ( - kubernetesAnnotationRegex *regexp.Regexp - arangoAnnotationRegex *regexp.Regexp -) - -func init() { - r, err := regexp.Compile(kubernetesAnnotationMatch) - if err != nil { - panic(err) - } - - kubernetesAnnotationRegex = r - - r, err = regexp.Compile(arangoAnnotationMatch) - if err != nil { - panic(err) - } - - arangoAnnotationRegex = r -} - -func isFilteredBlockedAnnotation(key string) bool { - switch key { - case deployment.ArangoDeploymentPodRotateAnnotation: - return true - default: - return false - } -} - -func filterBlockedAnnotations(m map[string]string) map[string]string { - n := map[string]string{} - - for key, value := range m { - if isFilteredBlockedAnnotation(key) { - continue - } - - n[key] = value - } - - return n -} - -// MergeAnnotations into one annotations map -func MergeAnnotations(annotations ...map[string]string) map[string]string { - ret := map[string]string{} - - for _, annotationMap := range annotations { - if annotationMap == nil { - continue - } - - for annotation, value := range annotationMap { - ret[annotation] = value - } - } - - return ret -} - -// IsSecuredAnnotation check if annotation key is from secured namespace -func IsSecuredAnnotation(key string) bool { - return kubernetesAnnotationRegex.MatchString(key) || arangoAnnotationRegex.MatchString(key) -} - -func GetSecuredAnnotations(annotations map[string]string) map[string]string { - if annotations == nil { - return map[string]string{} - } - - filteredAnnotations := map[string]string{} - - for key, value := range annotations { - if !IsSecuredAnnotation(key) { - continue - } - - filteredAnnotations[key] = value - } - - return filteredAnnotations -} - -func filterActualAnnotations(actual, expected map[string]string) map[string]string { - if actual == nil { - return nil - } - - if expected == nil { - expected = map[string]string{} - } - - actualFiltered := map[string]string{} - - for key, value := range actual { - if _, ok := expected[key]; IsSecuredAnnotation(key) && !ok { - continue - } - - actualFiltered[key] = value - } - - return actualFiltered -} - -// CompareAnnotations will compare annotations, but will ignore secured annotations which are present in -// actual but not specified in expected map -// It will also filter out blocked annotations -func CompareAnnotations(actual, expected map[string]string) bool { - return compareAnnotations(filterBlockedAnnotations(actual), filterBlockedAnnotations(expected)) -} - -func compareAnnotations(actual, expected map[string]string) bool { - actualFiltered := filterActualAnnotations(actual, expected) - - if actualFiltered == nil && expected == nil { - return true - } - - if (actualFiltered == nil && expected != nil && len(expected) == 0) || - (expected == nil && actualFiltered != nil && len(actualFiltered) == 0) { - return true - } - - if actualFiltered == nil || expected == nil { - return false - } - - if len(actualFiltered) != len(expected) { - return false - } - - for key, value := range expected { - existingValue, existing := actualFiltered[key] - - if !existing { - return false - } - - if existingValue != value { - return false - } - } - - return true -} diff --git a/tests/annotations_test.go b/tests/annotations_test.go index c446c1d1b..7d439ff83 100644 --- a/tests/annotations_test.go +++ b/tests/annotations_test.go @@ -23,6 +23,7 @@ package tests import ( + "github.com/arangodb/kube-arangodb/pkg/util/map" "testing" "time" @@ -111,7 +112,7 @@ func ensurePodAnnotations(t *testing.T, client kubernetes.Interface, depl *api.A require.True(t, len(pods) > 0) for _, pod := range pods { group := getPodGroup(pod) - combinedAnnotations := k8sutil.MergeAnnotations(depl.Spec.Annotations, depl.Spec.GetServerGroupSpec(group).Annotations) + combinedAnnotations := collection.MergeAnnotations(depl.Spec.Annotations, depl.Spec.GetServerGroupSpec(group).Annotations) if !k8sutil.CompareAnnotations(pod.GetAnnotations(), combinedAnnotations) { log.Info().Msgf("Annotations for Pod does not match on %s", pod.Name) return nil From 4f14b20392e56454759f4352d066ef80c56d71f2 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Fri, 31 Jul 2020 12:51:06 +0000 Subject: [PATCH 2/6] Fix FMT --- pkg/apis/deployment/v1/const.go | 2 +- .../deployment/v1/zz_generated.deepcopy.go | 40 +++++++++++++++++++ pkg/deployment/resources/annotations.go | 14 +++---- pkg/deployment/resources/labels.go | 2 +- pkg/util/collection/map.go | 13 +++--- 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/pkg/apis/deployment/v1/const.go b/pkg/apis/deployment/v1/const.go index bc90dc412..68c2aa689 100644 --- a/pkg/apis/deployment/v1/const.go +++ b/pkg/apis/deployment/v1/const.go @@ -37,4 +37,4 @@ func (a *LabelsMode) Get(def LabelsMode) LabelsMode { } return *a -} \ No newline at end of file +} diff --git a/pkg/apis/deployment/v1/zz_generated.deepcopy.go b/pkg/apis/deployment/v1/zz_generated.deepcopy.go index b896d58d6..e76cc32b4 100644 --- a/pkg/apis/deployment/v1/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v1/zz_generated.deepcopy.go @@ -334,6 +334,16 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { (*out)[key] = val } } + if in.AnnotationsIgnoreList != nil { + in, out := &in.AnnotationsIgnoreList, &out.AnnotationsIgnoreList + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AnnotationsMode != nil { + in, out := &in.AnnotationsMode, &out.AnnotationsMode + *out = new(LabelsMode) + **out = **in + } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -341,6 +351,16 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { (*out)[key] = val } } + if in.LabelsIgnoreList != nil { + in, out := &in.LabelsIgnoreList, &out.LabelsIgnoreList + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.LabelsMode != nil { + in, out := &in.LabelsMode, &out.LabelsMode + *out = new(LabelsMode) + **out = **in + } if in.RestoreFrom != nil { in, out := &in.RestoreFrom, &out.RestoreFrom *out = new(string) @@ -1133,6 +1153,16 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) { (*out)[key] = val } } + if in.AnnotationsIgnoreList != nil { + in, out := &in.AnnotationsIgnoreList, &out.AnnotationsIgnoreList + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AnnotationsMode != nil { + in, out := &in.AnnotationsMode, &out.AnnotationsMode + *out = new(LabelsMode) + **out = **in + } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -1140,6 +1170,16 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) { (*out)[key] = val } } + if in.LabelsIgnoreList != nil { + in, out := &in.LabelsIgnoreList, &out.LabelsIgnoreList + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.LabelsMode != nil { + in, out := &in.LabelsMode, &out.LabelsMode + *out = new(LabelsMode) + **out = **in + } if in.Envs != nil { in, out := &in.Envs, &out.Envs *out = make(ServerGroupEnvVars, len(*in)) diff --git a/pkg/deployment/resources/annotations.go b/pkg/deployment/resources/annotations.go index b28b8265f..f0ee20880 100644 --- a/pkg/deployment/resources/annotations.go +++ b/pkg/deployment/resources/annotations.go @@ -258,7 +258,7 @@ func getDefaultMode(annotations map[string]string) api.LabelsMode { func ensureGroupLabelsMap(kind string, obj meta.Object, spec api.DeploymentSpec, patchCmd func(name string, d []byte) error) bool { group := getObjectGroup(obj) - groupSpec := spec.GetServerGroupSpec(group) + groupSpec := spec.GetServerGroupSpec(group) expected := collection.MergeAnnotations(spec.Labels, groupSpec.Labels) ignoredList := append(spec.LabelsIgnoreList, groupSpec.LabelsIgnoreList...) @@ -269,7 +269,7 @@ func ensureGroupLabelsMap(kind string, obj meta.Object, spec api.DeploymentSpec, } func ensureLabelsMap(kind string, obj meta.Object, spec api.DeploymentSpec, - patchCmd func(name string, d []byte) error ) bool { + patchCmd func(name string, d []byte) error) bool { expected := spec.Labels ignored := spec.AnnotationsIgnoreList @@ -281,7 +281,7 @@ func ensureLabelsMap(kind string, obj meta.Object, spec api.DeploymentSpec, func ensureGroupAnnotationsMap(kind string, obj meta.Object, spec api.DeploymentSpec, patchCmd func(name string, d []byte) error) bool { group := getObjectGroup(obj) - groupSpec := spec.GetServerGroupSpec(group) + groupSpec := spec.GetServerGroupSpec(group) expected := collection.MergeAnnotations(spec.Annotations, groupSpec.Annotations) ignoredList := append(spec.AnnotationsIgnoreList, groupSpec.AnnotationsIgnoreList...) @@ -292,7 +292,7 @@ func ensureGroupAnnotationsMap(kind string, obj meta.Object, spec api.Deployment } func ensureAnnotationsMap(kind string, obj meta.Object, spec api.DeploymentSpec, - patchCmd func(name string, d []byte) error ) bool { + patchCmd func(name string, d []byte) error) bool { expected := spec.Annotations ignored := spec.AnnotationsIgnoreList @@ -303,9 +303,9 @@ func ensureAnnotationsMap(kind string, obj meta.Object, spec api.DeploymentSpec, func ensureObjectMap(kind string, obj meta.Object, mode api.LabelsMode, expected, actual map[string]string, - patchGetter func(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ... string) patch.Patch, + patchGetter func(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ...string) patch.Patch, patchCmd func(name string, d []byte) error, - ignored ... string) bool { + ignored ...string) bool { p := patchGetter(mode, expected, actual, ignored...) if len(p) == 0 { @@ -326,4 +326,4 @@ func ensureObjectMap(kind string, obj meta.Object, mode api.LabelsMode, } return true -} \ No newline at end of file +} diff --git a/pkg/deployment/resources/labels.go b/pkg/deployment/resources/labels.go index 3598a4fd9..34212dc5a 100644 --- a/pkg/deployment/resources/labels.go +++ b/pkg/deployment/resources/labels.go @@ -232,4 +232,4 @@ func (r *Resources) EnsurePodDisruptionBudgetsLabels(cachedStatus inspector.Insp } return nil -} \ No newline at end of file +} diff --git a/pkg/util/collection/map.go b/pkg/util/collection/map.go index 13bb3cbc4..0ffd65840 100644 --- a/pkg/util/collection/map.go +++ b/pkg/util/collection/map.go @@ -23,10 +23,11 @@ package collection import ( + "regexp" + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" "github.com/arangodb/kube-arangodb/pkg/deployment/patch" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" - "regexp" ) const ( @@ -64,7 +65,7 @@ func MergeAnnotations(annotations ...map[string]string) map[string]string { return ret } -func NewRestrictedList(param ... string) RestrictedList { +func NewRestrictedList(param ...string) RestrictedList { return param } @@ -102,7 +103,7 @@ func init() { arangoAnnotationRegex = r } -func LabelsPatch(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ... string) patch.Patch { +func LabelsPatch(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ...string) patch.Patch { return getFieldPatch(mode, "labels", expected, actual, func(k, v string) bool { if reservedLabels.IsRestricted(k) { return true @@ -116,7 +117,7 @@ func LabelsPatch(mode api.LabelsMode, expected map[string]string, actual map[str }) } -func AnnotationsPatch(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ... string) patch.Patch { +func AnnotationsPatch(mode api.LabelsMode, expected map[string]string, actual map[string]string, ignored ...string) patch.Patch { return getFieldPatch(mode, "annotations", expected, actual, func(k, v string) bool { if kubernetesAnnotationRegex.MatchString(k) { return true @@ -134,7 +135,7 @@ func AnnotationsPatch(mode api.LabelsMode, expected map[string]string, actual ma }) } -func getFieldPatch(mode api.LabelsMode, section string, expected map[string]string, actual map[string]string, filtered func(k ,v string) bool) patch.Patch { +func getFieldPatch(mode api.LabelsMode, section string, expected map[string]string, actual map[string]string, filtered func(k, v string) bool) patch.Patch { p := patch.NewPatch() switch mode { @@ -182,4 +183,4 @@ func getFieldPatch(mode api.LabelsMode, section string, expected map[string]stri } return p -} \ No newline at end of file +} From 3f08a90c196ac52faf8da74e15e22bc8042af73c Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Mon, 3 Aug 2020 14:33:35 +0000 Subject: [PATCH 3/6] Fix types --- pkg/apis/deployment/v1/const.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/apis/deployment/v1/const.go b/pkg/apis/deployment/v1/const.go index 68c2aa689..c7cd27979 100644 --- a/pkg/apis/deployment/v1/const.go +++ b/pkg/apis/deployment/v1/const.go @@ -24,13 +24,17 @@ type LabelsMode string const ( // LabelsDisabledMode disable annotations/labels override. Default if there is no annotations/labels set in ArangoDeployment - LabelsDisabledMode = "disabled" + LabelsDisabledMode LabelsMode = "disabled" // LabelsAppendMode add new annotations/labels without affecting old ones - LabelsAppendMode = "append" + LabelsAppendMode LabelsMode = "append" // LabelsReplaceMode replace existing annotations/labels - LabelsReplaceMode = "replace" + LabelsReplaceMode LabelsMode = "replace" ) +func (a LabelsMode) New() *LabelsMode { + return &a +} + func (a *LabelsMode) Get(def LabelsMode) LabelsMode { if a == nil { return def From 736f475047e5e2ed2c125b31d2b9bbe9698f68a3 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Tue, 4 Aug 2020 14:46:31 +0000 Subject: [PATCH 4/6] Update vendor --- tests/annotations_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/annotations_test.go b/tests/annotations_test.go index 7d439ff83..97a44a867 100644 --- a/tests/annotations_test.go +++ b/tests/annotations_test.go @@ -23,7 +23,6 @@ package tests import ( - "github.com/arangodb/kube-arangodb/pkg/util/map" "testing" "time" From 7b5bc9f4563043f13b706f57ea91a95b9dd17ba2 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Tue, 4 Aug 2020 20:39:57 +0000 Subject: [PATCH 5/6] Fix IT --- Makefile | 3 +++ pkg/deployment/deployment_suite_test.go | 2 +- pkg/util/collection/map.go | 18 ++++++++++++++++++ tests/annotations_test.go | 13 +++++++------ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index cc8b2ad49..b2bd637bb 100644 --- a/Makefile +++ b/Makefile @@ -268,6 +268,9 @@ dashboard/assets.go: $(DASHBOARDSOURCES) $(DASHBOARDDIR)/Dockerfile.build .PHONY: bin bin: $(BIN) +.PHONY: test-bin +test-bin: $(TESTBIN) + $(BIN): $(SOURCES) dashboard/assets.go VERSION @mkdir -p $(BINDIR) CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -installsuffix netgo -ldflags "-X main.projectVersion=$(VERSION) -X main.projectBuild=$(COMMIT)" -o $(BIN) $(REPOPATH) diff --git a/pkg/deployment/deployment_suite_test.go b/pkg/deployment/deployment_suite_test.go index bff6e82a1..670b7d75a 100644 --- a/pkg/deployment/deployment_suite_test.go +++ b/pkg/deployment/deployment_suite_test.go @@ -285,7 +285,7 @@ func createTestCommandForSingleMode(name string, tls, auth, encryptionRocksDB bo command = append(command, "--database.directory=/data", "--foxx.queues=true", "--log.level=INFO", "--log.output=+") - if encryptionRocksDB { + if encryptionRocksDB { command = append(command, "--rocksdb.encryption-keyfile=/secrets/rocksdb/encryption/key") } diff --git a/pkg/util/collection/map.go b/pkg/util/collection/map.go index 0ffd65840..b8b644a23 100644 --- a/pkg/util/collection/map.go +++ b/pkg/util/collection/map.go @@ -65,6 +65,24 @@ func MergeAnnotations(annotations ...map[string]string) map[string]string { return ret } +func Compare(a, b map[string]string) bool { + if len(a) != len(b) { + return false + } + + for ak, av := range a { + bv, ok := b[ak] + if !ok { + return false + } + if av != bv { + return false + } + } + + return true +} + func NewRestrictedList(param ...string) RestrictedList { return param } diff --git a/tests/annotations_test.go b/tests/annotations_test.go index 97a44a867..9dd0eb87b 100644 --- a/tests/annotations_test.go +++ b/tests/annotations_test.go @@ -23,6 +23,7 @@ package tests import ( + "github.com/arangodb/kube-arangodb/pkg/util/collection" "testing" "time" @@ -88,7 +89,7 @@ func ensureSecretAnnotations(t *testing.T, client kubernetes.Interface, depl *ap require.NoError(t, err) require.True(t, len(secrets) > 0) for _, secret := range secrets { - if !k8sutil.CompareAnnotations(secret.GetAnnotations(), depl.Spec.Annotations) { + if !collection.Compare(secret.GetAnnotations(), depl.Spec.Annotations) { log.Info().Msgf("Annotations for Secret does not match on %s", secret.Name) return nil } @@ -112,7 +113,7 @@ func ensurePodAnnotations(t *testing.T, client kubernetes.Interface, depl *api.A for _, pod := range pods { group := getPodGroup(pod) combinedAnnotations := collection.MergeAnnotations(depl.Spec.Annotations, depl.Spec.GetServerGroupSpec(group).Annotations) - if !k8sutil.CompareAnnotations(pod.GetAnnotations(), combinedAnnotations) { + if !collection.Compare(pod.GetAnnotations(), combinedAnnotations) { log.Info().Msgf("Annotations for Pod does not match on %s", pod.Name) return nil } @@ -126,7 +127,7 @@ func ensurePDBAnnotation(t *testing.T, client kubernetes.Interface, depl *api.Ar require.NoError(t, err) require.True(t, len(podDisruptionBudgets) > 0) for _, podDisruptionBudget := range podDisruptionBudgets { - if !k8sutil.CompareAnnotations(podDisruptionBudget.GetAnnotations(), depl.Spec.Annotations) { + if !collection.Compare(podDisruptionBudget.GetAnnotations(), depl.Spec.Annotations) { log.Info().Msgf("Annotations for PDB does not match on %s", podDisruptionBudget.Name) return nil } @@ -140,7 +141,7 @@ func ensurePVCAnnotation(t *testing.T, client kubernetes.Interface, depl *api.Ar require.NoError(t, err) require.True(t, len(persistentVolumeClaims) > 0) for _, persistentVolumeClaim := range persistentVolumeClaims { - if !k8sutil.CompareAnnotations(persistentVolumeClaim.GetAnnotations(), depl.Spec.Annotations) { + if !collection.Compare(persistentVolumeClaim.GetAnnotations(), depl.Spec.Annotations) { log.Info().Msgf("Annotations for PVC does not match on %s", persistentVolumeClaim.Name) return nil } @@ -154,7 +155,7 @@ func ensureServiceAnnotation(t *testing.T, client kubernetes.Interface, depl *ap require.NoError(t, err) require.True(t, len(services) > 0) for _, service := range services { - if !k8sutil.CompareAnnotations(service.GetAnnotations(), depl.Spec.Annotations) { + if !collection.Compare(service.GetAnnotations(), depl.Spec.Annotations) { log.Info().Msgf("Annotations for Service does not match on %s", service.Name) return nil } @@ -167,7 +168,7 @@ func ensureServiceAccountAnnotation(t *testing.T, client kubernetes.Interface, d serviceAccounts, err := k8sutil.GetServiceAccountsForParent(client.CoreV1().ServiceAccounts(depl.Namespace), deployment.ArangoDeploymentResourceKind, depl.Name, depl.Namespace) require.NoError(t, err) for _, serviceAccount := range serviceAccounts { - if !k8sutil.CompareAnnotations(serviceAccount.GetAnnotations(), depl.Spec.Annotations) { + if !collection.Compare(serviceAccount.GetAnnotations(), depl.Spec.Annotations) { log.Info().Msgf("Annotations for Service Account does not match on %s", serviceAccount.Name) return nil } From 5ff3a853a71c6464d6ad353b7c21d931a19307be Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Tue, 4 Aug 2020 20:49:37 +0000 Subject: [PATCH 6/6] FMT --- pkg/deployment/deployment_suite_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deployment/deployment_suite_test.go b/pkg/deployment/deployment_suite_test.go index 670b7d75a..bff6e82a1 100644 --- a/pkg/deployment/deployment_suite_test.go +++ b/pkg/deployment/deployment_suite_test.go @@ -285,7 +285,7 @@ func createTestCommandForSingleMode(name string, tls, auth, encryptionRocksDB bo command = append(command, "--database.directory=/data", "--foxx.queues=true", "--log.level=INFO", "--log.output=+") - if encryptionRocksDB { + if encryptionRocksDB { command = append(command, "--rocksdb.encryption-keyfile=/secrets/rocksdb/encryption/key") }