diff --git a/README.md b/README.md index a09cbe3..aa2aa4f 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,8 @@ $ kubectl -n kube-system apply -f https://github.com/emberstack/kubernetes-refle - Add `reflector.v1.k8s.emberstack.com/reflection-allowed: "true"` to the resource annotations to permit reflection to mirrors. - Add `reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: ""` to the resource annotations to permit reflection from only the list of comma separated namespaces or regular expressions. Note: If this annotation is omitted or is empty, all namespaces are allowed. + - Add `reflector.v1.k8s.emberstack.com/reflection-labels: "true"` to the resource annotations to permit reflection of labels to mirrors. Note: If this annotation is ommitted or empty no labels are mirrored. + - Add `reflector.v1.k8s.emberstack.com/reflection-labels-included: ""` to the resource annotations to permit reflection of only the list of comma separated labels or regular expressions. Note: If this annotation is ommitted or empty all labels are reflected. #### Automatic mirror creation: Reflector can create mirrors with the same name in other namespaces automatically. The following annotations control if and how the mirrors are created: @@ -110,6 +112,24 @@ $ kubectl -n kube-system apply -f https://github.com/emberstack/kubernetes-refle data: ... ``` + + Example source configmap with labels: + ```yaml + apiVersion: v1 + kind: ConfigMap + metadata: + name: source-config-map + labels: + emberstack.com/included-label: to-be-included + example.com/excluded-label: to-be-excluded + annotations: + reflector.v1.k8s.emberstack.com/reflection-allowed: "true" + reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: "namespace-1,namespace-2,namespace-[0-9]*" + reflector.v1.k8s.emberstack.com/reflection-labels: "true" + reflector.v1.k8s.emberstack.com/reflection-labels-included: "emberstack\\.com/.*" + data: + ... + ``` ### 2. Annotate the mirror secret or configmap diff --git a/src/ES.Kubernetes.Reflector/Mirroring/Core/Annotations.cs b/src/ES.Kubernetes.Reflector/Mirroring/Core/Annotations.cs index 41d22c5..d202ce4 100644 --- a/src/ES.Kubernetes.Reflector/Mirroring/Core/Annotations.cs +++ b/src/ES.Kubernetes.Reflector/Mirroring/Core/Annotations.cs @@ -1,4 +1,4 @@ -namespace ES.Kubernetes.Reflector.Mirroring.Core; +namespace ES.Kubernetes.Reflector.Mirroring.Core; public static class Annotations { @@ -10,6 +10,8 @@ public static class Reflection public static string AllowedNamespaces => $"{Prefix}/reflection-allowed-namespaces"; public static string AutoEnabled => $"{Prefix}/reflection-auto-enabled"; public static string AutoNamespaces => $"{Prefix}/reflection-auto-namespaces"; + public static string Labels => $"{Prefix}/reflection-labels"; + public static string LabelsIncluded => $"{Prefix}/reflection-labels-included"; public static string Reflects => $"{Prefix}/reflects"; diff --git a/src/ES.Kubernetes.Reflector/Mirroring/Core/MirroringProperties.cs b/src/ES.Kubernetes.Reflector/Mirroring/Core/MirroringProperties.cs index 12fdc1d..4ca4999 100644 --- a/src/ES.Kubernetes.Reflector/Mirroring/Core/MirroringProperties.cs +++ b/src/ES.Kubernetes.Reflector/Mirroring/Core/MirroringProperties.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using ES.FX.KubernetesClient.Models; namespace ES.Kubernetes.Reflector.Mirroring.Core; @@ -9,6 +9,8 @@ public class MirroringProperties public string AllowedNamespaces { get; set; } = string.Empty; public bool AutoEnabled { get; set; } public string AutoNamespaces { get; set; } = string.Empty; + public bool Labels { get; set; } + public string LabelsIncluded { get; set; } = string.Empty; public NamespacedName? Reflects { get; set; } public string ResourceVersion { get; set; } = string.Empty; diff --git a/src/ES.Kubernetes.Reflector/Mirroring/Core/MirroringPropertiesExtensions.cs b/src/ES.Kubernetes.Reflector/Mirroring/Core/MirroringPropertiesExtensions.cs index 51094d8..47b9f43 100644 --- a/src/ES.Kubernetes.Reflector/Mirroring/Core/MirroringPropertiesExtensions.cs +++ b/src/ES.Kubernetes.Reflector/Mirroring/Core/MirroringPropertiesExtensions.cs @@ -32,6 +32,14 @@ public static MirroringProperties GetMirroringProperties(this V1ObjectMeta metad ? autoNamespaces ?? string.Empty : string.Empty, + Labels = metadata + .TryGetAnnotationValue(Annotations.Reflection.Labels, out bool labels) && labels, + + LabelsIncluded = metadata + .TryGetAnnotationValue(Annotations.Reflection.LabelsIncluded, out string? labelsIncluded) + ? labelsIncluded ?? string.Empty + : string.Empty, + Reflects = metadata .TryGetAnnotationValue(Annotations.Reflection.Reflects, out string? metaReflects) ? NamespacedName.TryParse(metaReflects, out var id) ? id : null @@ -63,6 +71,8 @@ public static bool CanBeAutoReflectedToNamespace(this MirroringProperties proper properties.CanBeReflectedToNamespace(ns) && properties.AutoEnabled && PatternListMatch(properties.AutoNamespaces, ns); + public static bool CanLabelBeReflected(this MirroringProperties properties, string label) => + properties.Labels && PatternListMatch(properties.LabelsIncluded, label); private static bool PatternListMatch(string patternList, string value) { diff --git a/src/ES.Kubernetes.Reflector/Mirroring/Core/ResourceMirror.cs b/src/ES.Kubernetes.Reflector/Mirroring/Core/ResourceMirror.cs index 0e48142..4c3a7ca 100644 --- a/src/ES.Kubernetes.Reflector/Mirroring/Core/ResourceMirror.cs +++ b/src/ES.Kubernetes.Reflector/Mirroring/Core/ResourceMirror.cs @@ -459,6 +459,7 @@ private async Task ResourceReflect(NamespacedName sourceNsName, NamespacedName r source = sourceObj; } + var sourceProperties = source.GetMirroringProperties(); var patchAnnotations = new Dictionary { @@ -487,6 +488,17 @@ private async Task ResourceReflect(NamespacedName sourceNsName, NamespacedName r newResourceAnnotations[Annotations.Reflection.MetaReflectedVersion] = source.Metadata.ResourceVersion; newResourceAnnotations[Annotations.Reflection.MetaReflectedAt] = DateTimeOffset.UtcNow.ToString("O"); + if (sourceProperties.Labels) + { + newResource.Metadata.Labels ??= new Dictionary(); + var newResourceLabels = newResource.Metadata.Labels; + foreach (var label in source.Metadata.Labels) + { + if (sourceProperties.CanLabelBeReflected(label.Key)) + newResourceLabels[label.Key] = label.Value; + } + } + try { await OnResourceCreate(newResource, reflectionNsName.Namespace); @@ -515,6 +527,19 @@ private async Task ResourceReflect(NamespacedName sourceNsName, NamespacedName r annotations[patchAnnotation.Key] = patchAnnotation.Value; patchDoc.Replace(e => e.Metadata.Annotations, annotations); + if (sourceProperties.Labels && source.Metadata.Labels != null) + { + var labels = new Dictionary(); + if (reflectionObj.Metadata.Labels != null) + labels = new Dictionary(reflectionObj.Metadata.Labels); + foreach (var label in source.Metadata.Labels) + { + if (sourceProperties.CanLabelBeReflected(label.Key)) + labels[label.Key] = label.Value; + } + patchDoc.Replace(e => e.Metadata.Labels, labels); + } + await OnResourceConfigurePatch(source, patchDoc); var patch = JsonConvert.SerializeObject(patchDoc, Formatting.Indented);