From ed4154bab96ca25a688cbd5c276b48e32e5697e6 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Fri, 26 Jan 2024 01:42:41 +0530 Subject: [PATCH] fix (jkube-kit/enricher) : WellKnownLabelEnricher also considers labels added via resource configuration While adding labels and selectors to a resource, WellKnownLabelEnricher and ProjectLabelEnricher would give precedence to label configured via resource label XML/Groovy DSL configuration. This precedence would depend on the type of resource label configuration used. If `all` has been provided in configuration, this means precedence would be given for all resources. However, if some specific resource label configuration is used; precedence would only be given for that specific resource Signed-off-by: Rohan Kumar --- .../it/src/it/well-known-labels/build.gradle | 34 +++++ .../labelsViaResourceConfig/kubernetes.yml | 103 +++++++++++++ .../labelsViaResourceConfig/openshift.yml | 144 ++++++++++++++++++ .../plugin/tests/WellKnownLabelsIT.java | 3 +- .../jkube/kit/common/util/MapUtil.java | 21 +-- .../generic/AbstractLabelEnricher.java | 63 ++++++-- .../generic/ProjectLabelEnricher.java | 25 ++- .../generic/WellKnownLabelsEnricher.java | 23 +-- .../generic/ProjectLabelEnricherTest.java | 110 +++++++++++-- .../generic/WellKnownLabelsEnricherTest.java | 77 +++++++++- 10 files changed, 547 insertions(+), 56 deletions(-) create mode 100644 gradle-plugin/it/src/it/well-known-labels/expected/labelsViaResourceConfig/kubernetes.yml create mode 100644 gradle-plugin/it/src/it/well-known-labels/expected/labelsViaResourceConfig/openshift.yml diff --git a/gradle-plugin/it/src/it/well-known-labels/build.gradle b/gradle-plugin/it/src/it/well-known-labels/build.gradle index 10ed873d9e..b94c3deb4f 100644 --- a/gradle-plugin/it/src/it/well-known-labels/build.gradle +++ b/gradle-plugin/it/src/it/well-known-labels/build.gradle @@ -25,6 +25,32 @@ repositories { mavenCentral() } +def allProperties = new Properties(); +allProperties.put("team","via-resource-groovy-dsl-labels-all") +allProperties.put("language", "via-resource-groovy-dsl-labels-all") +allProperties.put("provider", "via-resource-groovy-dsl-labels-all") +allProperties.put("app", "via-resource-groovy-dsl-labels-all") +allProperties.put("version", "via-resource-groovy-dsl-labels-all") +allProperties.put("group", "via-resource-groovy-dsl-labels-all") +allProperties.put("app.kubernetes.io/name", "via-resource-groovy-dsl-labels-all") +allProperties.put("app.kubernetes.io/version", "via-resource-groovy-dsl-labels-all") +allProperties.put("app.kubernetes.io/component", "via-resource-groovy-dsl-labels-all") +allProperties.put("app.kubernetes.io/managed-by", "via-resource-groovy-dsl-labels-all") +allProperties.put("app.kubernetes.io/part-of", "via-resource-groovy-dsl-labels-all") + +def serviceProperties = new Properties(); +serviceProperties.put("team", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("language", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("provider", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("app", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("version", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("group", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("app.kubernetes.io/name", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("app.kubernetes.io/version", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("app.kubernetes.io/component", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("app.kubernetes.io/managed-by", "via-resource-groovy-dsl-labels-service") +serviceProperties.put("app.kubernetes.io/part-of", "via-resource-groovy-dsl-labels-service") + def extensionConfig = { offline = true images { @@ -36,6 +62,14 @@ def extensionConfig = { } } } +if (project.hasProperty('labelsViaResourceConfig')) { + resources { + labels { + all = allProperties + service = serviceProperties + } + } +} } kubernetes(extensionConfig) diff --git a/gradle-plugin/it/src/it/well-known-labels/expected/labelsViaResourceConfig/kubernetes.yml b/gradle-plugin/it/src/it/well-known-labels/expected/labelsViaResourceConfig/kubernetes.yml new file mode 100644 index 0000000000..ba0515e75d --- /dev/null +++ b/gradle-plugin/it/src/it/well-known-labels/expected/labelsViaResourceConfig/kubernetes.yml @@ -0,0 +1,103 @@ +--- +apiVersion: v1 +kind: List +items: +- apiVersion: v1 + kind: Service + metadata: + annotations: + jkube.eclipse.org/git-branch: "@ignore@" + jkube.eclipse.org/git-commit: "@ignore@" + jkube.eclipse.org/git-url: "@ignore@" + labels: + app: via-resource-groovy-dsl-labels-service + app.kubernetes.io/component: via-resource-groovy-dsl-labels-service + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-service + app.kubernetes.io/name: via-resource-groovy-dsl-labels-service + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-service + app.kubernetes.io/version: via-resource-groovy-dsl-labels-service + group: via-resource-groovy-dsl-labels-service + provider: via-resource-groovy-dsl-labels-service + version: via-resource-groovy-dsl-labels-service + name: well-known-labels + spec: + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: via-resource-groovy-dsl-labels-service + app.kubernetes.io/component: via-resource-groovy-dsl-labels-service + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-service + app.kubernetes.io/name: via-resource-groovy-dsl-labels-service + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-service + group: via-resource-groovy-dsl-labels-service + provider: via-resource-groovy-dsl-labels-service +- apiVersion: apps/v1 + kind: Deployment + metadata: + annotations: + jkube.eclipse.org/git-branch: "@ignore@" + jkube.eclipse.org/git-commit: "@ignore@" + jkube.eclipse.org/git-url: "@ignore@" + labels: + app: via-resource-groovy-dsl-labels-all + app.kubernetes.io/component: via-resource-groovy-dsl-labels-all + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-all + app.kubernetes.io/name: via-resource-groovy-dsl-labels-all + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-all + app.kubernetes.io/version: via-resource-groovy-dsl-labels-all + group: via-resource-groovy-dsl-labels-all + provider: via-resource-groovy-dsl-labels-all + version: via-resource-groovy-dsl-labels-all + name: well-known-labels + spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: via-resource-groovy-dsl-labels-all + app.kubernetes.io/component: via-resource-groovy-dsl-labels-all + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-all + app.kubernetes.io/name: via-resource-groovy-dsl-labels-all + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-all + group: via-resource-groovy-dsl-labels-all + provider: via-resource-groovy-dsl-labels-all + template: + metadata: + annotations: + jkube.eclipse.org/git-branch: "@ignore@" + jkube.eclipse.org/git-commit: "@ignore@" + jkube.eclipse.org/git-url: "@ignore@" + labels: + app: via-resource-groovy-dsl-labels-all + app.kubernetes.io/component: via-resource-groovy-dsl-labels-all + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-all + app.kubernetes.io/name: via-resource-groovy-dsl-labels-all + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-all + app.kubernetes.io/version: via-resource-groovy-dsl-labels-all + group: via-resource-groovy-dsl-labels-all + provider: via-resource-groovy-dsl-labels-all + version: via-resource-groovy-dsl-labels-all + name: well-known-labels + spec: + containers: + - env: + - name: KUBERNETES_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + image: repository/well-known-labels:latest + imagePullPolicy: IfNotPresent + name: repository-well-known-labels + ports: + - containerPort: 8080 + name: http + protocol: TCP + securityContext: + privileged: false diff --git a/gradle-plugin/it/src/it/well-known-labels/expected/labelsViaResourceConfig/openshift.yml b/gradle-plugin/it/src/it/well-known-labels/expected/labelsViaResourceConfig/openshift.yml new file mode 100644 index 0000000000..b93be39e80 --- /dev/null +++ b/gradle-plugin/it/src/it/well-known-labels/expected/labelsViaResourceConfig/openshift.yml @@ -0,0 +1,144 @@ +--- +apiVersion: v1 +kind: List +items: +- apiVersion: v1 + kind: Service + metadata: + annotations: + app.openshift.io/vcs-ref: "@ignore@" + app.openshift.io/vcs-uri: "@ignore@" + jkube.eclipse.org/git-branch: "@ignore@" + jkube.eclipse.org/git-commit: "@ignore@" + jkube.eclipse.org/git-url: "@ignore@" + labels: + app: via-resource-groovy-dsl-labels-service + app.kubernetes.io/component: via-resource-groovy-dsl-labels-service + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-service + app.kubernetes.io/name: via-resource-groovy-dsl-labels-service + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-service + app.kubernetes.io/version: via-resource-groovy-dsl-labels-service + group: via-resource-groovy-dsl-labels-service + provider: via-resource-groovy-dsl-labels-service + version: via-resource-groovy-dsl-labels-service + name: well-known-labels + spec: + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: via-resource-groovy-dsl-labels-service + app.kubernetes.io/component: via-resource-groovy-dsl-labels-service + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-service + app.kubernetes.io/name: via-resource-groovy-dsl-labels-service + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-service + group: via-resource-groovy-dsl-labels-service + provider: via-resource-groovy-dsl-labels-service +- apiVersion: apps.openshift.io/v1 + kind: DeploymentConfig + metadata: + annotations: + app.openshift.io/vcs-ref: "@ignore@" + app.openshift.io/vcs-uri: "@ignore@" + jkube.eclipse.org/git-branch: "@ignore@" + jkube.eclipse.org/git-commit: "@ignore@" + jkube.eclipse.org/git-url: "@ignore@" + labels: + app: via-resource-groovy-dsl-labels-all + app.kubernetes.io/component: via-resource-groovy-dsl-labels-all + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-all + app.kubernetes.io/name: via-resource-groovy-dsl-labels-all + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-all + app.kubernetes.io/version: via-resource-groovy-dsl-labels-all + group: via-resource-groovy-dsl-labels-all + provider: via-resource-groovy-dsl-labels-all + version: via-resource-groovy-dsl-labels-all + name: well-known-labels + spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + app: via-resource-groovy-dsl-labels-all + app.kubernetes.io/component: via-resource-groovy-dsl-labels-all + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-all + app.kubernetes.io/name: via-resource-groovy-dsl-labels-all + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-all + group: via-resource-groovy-dsl-labels-all + provider: via-resource-groovy-dsl-labels-all + strategy: + rollingParams: + timeoutSeconds: 3600 + type: Rolling + template: + metadata: + annotations: + app.openshift.io/vcs-ref: "@ignore@" + app.openshift.io/vcs-uri: "@ignore@" + jkube.eclipse.org/git-branch: "@ignore@" + jkube.eclipse.org/git-commit: "@ignore@" + jkube.eclipse.org/git-url: "@ignore@" + labels: + app: via-resource-groovy-dsl-labels-all + app.kubernetes.io/component: via-resource-groovy-dsl-labels-all + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-all + app.kubernetes.io/name: via-resource-groovy-dsl-labels-all + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-all + app.kubernetes.io/version: via-resource-groovy-dsl-labels-all + group: via-resource-groovy-dsl-labels-all + provider: via-resource-groovy-dsl-labels-all + version: via-resource-groovy-dsl-labels-all + name: well-known-labels + spec: + containers: + - env: + - name: KUBERNETES_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: repository/well-known-labels:latest + imagePullPolicy: IfNotPresent + name: repository-well-known-labels + ports: + - containerPort: 8080 + name: http + protocol: TCP + securityContext: + privileged: false + triggers: + - type: ConfigChange + - imageChangeParams: + automatic: true + containerNames: + - repository-well-known-labels + from: + kind: ImageStreamTag + name: well-known-labels:latest + type: ImageChange +- apiVersion: route.openshift.io/v1 + kind: Route + metadata: + annotations: + app.openshift.io/vcs-ref: "@ignore@" + app.openshift.io/vcs-uri: "@ignore@" + jkube.eclipse.org/git-branch: "@ignore@" + jkube.eclipse.org/git-commit: "@ignore@" + jkube.eclipse.org/git-url: "@ignore@" + labels: + app: via-resource-groovy-dsl-labels-all + app.kubernetes.io/component: via-resource-groovy-dsl-labels-all + app.kubernetes.io/managed-by: via-resource-groovy-dsl-labels-all + app.kubernetes.io/name: via-resource-groovy-dsl-labels-all + app.kubernetes.io/part-of: via-resource-groovy-dsl-labels-all + app.kubernetes.io/version: via-resource-groovy-dsl-labels-all + group: via-resource-groovy-dsl-labels-all + provider: via-resource-groovy-dsl-labels-all + version: via-resource-groovy-dsl-labels-all + name: well-known-labels + spec: + port: + targetPort: 8080 + to: + kind: Service + name: well-known-labels diff --git a/gradle-plugin/it/src/test/java/org/eclipse/jkube/gradle/plugin/tests/WellKnownLabelsIT.java b/gradle-plugin/it/src/test/java/org/eclipse/jkube/gradle/plugin/tests/WellKnownLabelsIT.java index af44d3d3c8..62a8f770d9 100644 --- a/gradle-plugin/it/src/test/java/org/eclipse/jkube/gradle/plugin/tests/WellKnownLabelsIT.java +++ b/gradle-plugin/it/src/test/java/org/eclipse/jkube/gradle/plugin/tests/WellKnownLabelsIT.java @@ -43,7 +43,8 @@ static Stream data() { "-Pjkube.enricher.jkube-well-known-labels.component=custom-component", "-Pjkube.enricher.jkube-well-known-labels.partOf=custom-part-of", "-Pjkube.enricher.jkube-well-known-labels.managedBy=custom-managed-by", - }) + }), + arguments("labelsViaResourceConfig", new String[] {"-PlabelsViaResourceConfig=true"}) ); } diff --git a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/MapUtil.java b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/MapUtil.java index dc6e6b75fa..0aba67da2e 100644 --- a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/MapUtil.java +++ b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/MapUtil.java @@ -42,23 +42,24 @@ public static void mergeIfAbsent(Map map, Map to } /** - * Returns a new map with all the entries of map1 and any from map2 which don't override map1. + * Returns a new map with all the entries of first map with rest map entries which don't override map1. * - * Can handle either maps being null. Always returns a new mutable map + * Can handle either maps being null. Always returns a new mutable map. * - * @param map1 first hash map - * @param map2 second hash map + * Note: Be careful about the ordering of maps passed here. First map passed in the var args + * would always be given precedence over other maps in case there are colliding entries with same key values. + * + * @param maps var arg for maps * @param first type * @param second type * @return merged hash map */ - public static Map mergeMaps(Map map1, Map map2) { + public static Map mergeMaps(Map ...maps) { Map answer = new HashMap<>(); - if (map2 != null) { - answer.putAll(map2); - } - if (map1 != null) { - answer.putAll(map1); + for (int i = maps.length-1; i >= 0; i--) { + if (maps[i] != null) { + answer.putAll(maps[i]); + } } return answer; diff --git a/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/AbstractLabelEnricher.java b/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/AbstractLabelEnricher.java index 92b8b82f3d..ec2e6c7e37 100644 --- a/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/AbstractLabelEnricher.java +++ b/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/AbstractLabelEnricher.java @@ -29,31 +29,45 @@ import io.fabric8.openshift.api.model.DeploymentConfigBuilder; import io.fabric8.openshift.api.model.DeploymentConfigSpec; import org.eclipse.jkube.kit.common.util.MapUtil; +import org.eclipse.jkube.kit.common.util.PropertiesUtil; +import org.eclipse.jkube.kit.config.resource.MetaDataConfig; import org.eclipse.jkube.kit.config.resource.PlatformMode; +import org.eclipse.jkube.kit.config.resource.ResourceConfig; import org.eclipse.jkube.kit.enricher.api.BaseEnricher; import org.eclipse.jkube.kit.enricher.api.EnricherContext; +import org.eclipse.jkube.kit.enricher.api.model.Configuration; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Properties; + +import static org.eclipse.jkube.kit.common.util.PropertiesUtil.toMap; public abstract class AbstractLabelEnricher extends BaseEnricher { protected AbstractLabelEnricher(EnricherContext enricherContext, String name) { super(enricherContext, name); } - abstract Map createLabels(boolean withoutVersion); + abstract Map createLabels(boolean withoutVersion, Map labelsViaResourceConfig); @Override + @SuppressWarnings("unchecked") public void create(PlatformMode platformMode, KubernetesListBuilder builder) { builder.accept(new TypedVisitor() { @Override public void visit(ServiceBuilder serviceBuilder) { Map selectors = new HashMap<>(); + Map labelsConfiguredViaResourceConfig = MapUtil.mergeMaps( + toMap(getResourceConfigLabels().getService()), + toMap(getResourceConfigLabels().getAll())); if(serviceBuilder.buildSpec() != null && serviceBuilder.buildSpec().getSelector() != null) { selectors.putAll(serviceBuilder.buildSpec().getSelector()); } - MapUtil.mergeIfAbsent(selectors, createLabels(true)); + MapUtil.mergeIfAbsent(selectors, createLabels(true, labelsConfiguredViaResourceConfig)); serviceBuilder.editOrNewSpec().addToSelector(selectors).endSpec(); } }); @@ -64,7 +78,10 @@ public void visit(DeploymentBuilder builder) { final Map selectors = mergedSelectors(Optional.ofNullable(builder.buildSpec()) .map(DeploymentSpec::getSelector) .map(LabelSelector::getMatchLabels) - .orElse(new HashMap<>())); + .orElse(new HashMap<>()), Arrays.asList( + getResourceConfigLabels().getDeployment(), + getResourceConfigLabels().getPod(), + getResourceConfigLabels().getAll())); builder.editOrNewSpec().editOrNewSelector().withMatchLabels(selectors).endSelector().endSpec(); } }); @@ -74,7 +91,9 @@ public void visit(DeploymentBuilder builder) { public void visit(DeploymentConfigBuilder builder) { final Map selectors = mergedSelectors(Optional.ofNullable(builder.buildSpec()) .map(DeploymentConfigSpec::getSelector) - .orElse(new HashMap<>())); + .orElse(new HashMap<>()), Arrays.asList( + getResourceConfigLabels().getPod(), + getResourceConfigLabels().getAll())); builder.editOrNewSpec().addToSelector(selectors).endSpec(); } }); @@ -83,10 +102,13 @@ public void visit(DeploymentConfigBuilder builder) { @Override public void visit(DaemonSetBuilder builder) { Map selectors = new HashMap<>(); + Map labelsFromResourceConfig = MapUtil.mergeMaps( + toMap(getResourceConfigLabels().getPod()), + toMap(getResourceConfigLabels().getAll())); if(builder.buildSpec() != null && builder.buildSpec().getSelector() != null && builder.buildSpec().getSelector().getMatchLabels() != null) { selectors.putAll(builder.buildSpec().getSelector().getMatchLabels()); } - MapUtil.mergeIfAbsent(selectors, createLabels(false)); + MapUtil.mergeIfAbsent(selectors, createLabels(false, labelsFromResourceConfig)); builder.editOrNewSpec().editOrNewSelector().withMatchLabels(selectors).endSelector().endSpec(); } }); @@ -96,7 +118,9 @@ public void visit(DaemonSetBuilder builder) { public void visit(ReplicationControllerBuilder builder) { final Map selectors = mergedSelectors(Optional.ofNullable(builder.buildSpec()) .map(ReplicationControllerSpec::getSelector) - .orElse(new HashMap<>())); + .orElse(new HashMap<>()), Arrays.asList( + getResourceConfigLabels().getPod(), + getResourceConfigLabels().getAll())); builder.editOrNewSpec().addToSelector(selectors).endSpec(); } }); @@ -107,7 +131,10 @@ public void visit(ReplicaSetBuilder builder) { final Map selectors = mergedSelectors(Optional.ofNullable(builder.buildSpec()) .map(ReplicaSetSpec::getSelector) .map(LabelSelector::getMatchLabels) - .orElse(new HashMap<>())); + .orElse(new HashMap<>()), Arrays.asList( + getResourceConfigLabels().getReplicaSet(), + getResourceConfigLabels().getPod(), + getResourceConfigLabels().getAll())); builder.editOrNewSpec().editOrNewSelector().withMatchLabels(selectors).endSelector().endSpec(); } }); @@ -116,10 +143,13 @@ public void visit(ReplicaSetBuilder builder) { @Override public void visit(StatefulSetBuilder builder) { Map selectors = new HashMap<>(); + Map labelsFromResourceConfig = MapUtil.mergeMaps( + toMap(getResourceConfigLabels().getPod()), + toMap(getResourceConfigLabels().getAll())); if(builder.buildSpec() != null && builder.buildSpec().getSelector() != null && builder.buildSpec().getSelector().getMatchLabels() != null) { selectors.putAll(builder.buildSpec().getSelector().getMatchLabels()); } - MapUtil.mergeIfAbsent(selectors, createLabels(false)); + MapUtil.mergeIfAbsent(selectors, createLabels(false, labelsFromResourceConfig)); builder.editOrNewSpec().editOrNewSelector().withMatchLabels(selectors).endSelector().endSpec(); } }); @@ -133,14 +163,25 @@ public void enrich(PlatformMode platformMode, KubernetesListBuilder builder) { @Override public void visit(ObjectMetaBuilder element) { Map labels = Optional.ofNullable(element.getLabels()).orElse(new HashMap<>()); - MapUtil.mergeIfAbsent(labels, createLabels(false)); + MapUtil.mergeIfAbsent(labels, createLabels(false, Collections.emptyMap())); element.withLabels(labels); } }); } - private Map mergedSelectors(Map originalSelectors) { - MapUtil.mergeIfAbsent(originalSelectors, createLabels(true)); + @SuppressWarnings("unchecked") + private Map mergedSelectors(Map originalSelectors, List labelPropertyList) { + Map labelsFromResourceConfig = MapUtil.mergeMaps(labelPropertyList.stream() + .map(PropertiesUtil::toMap) + .toArray(Map[]::new)); + MapUtil.mergeIfAbsent(originalSelectors, createLabels(true, labelsFromResourceConfig)); return originalSelectors; } + + protected MetaDataConfig getResourceConfigLabels() { + return Optional.ofNullable(getConfiguration()) + .map(Configuration::getResource) + .map(ResourceConfig::getLabels) + .orElse(MetaDataConfig.builder().build()); + } } diff --git a/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/ProjectLabelEnricher.java b/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/ProjectLabelEnricher.java index c4b09bbf30..1e52df372d 100644 --- a/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/ProjectLabelEnricher.java +++ b/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/ProjectLabelEnricher.java @@ -15,7 +15,9 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jkube.kit.common.Configs; import org.eclipse.jkube.kit.config.resource.GroupArtifactVersion; import org.eclipse.jkube.kit.enricher.api.JKubeEnricherContext; @@ -50,7 +52,7 @@ private enum Config implements Configs.Config { APP("app", null), GROUP("group", null), VERSION("version", null), - PROVIDER("provider", "jkube"); + PROVIDER(LABEL_PROVIDER, "jkube"); @Getter protected String key; @@ -63,22 +65,31 @@ public ProjectLabelEnricher(JKubeEnricherContext buildContext) { } @Override - public Map createLabels(boolean withoutVersion) { + public Map createLabels(boolean withoutVersion, Map labelsViaResourceConfig) { Map ret = new HashMap<>(); boolean enableProjectLabel = Configs.asBoolean(getConfig(Config.USE_PROJECT_LABEL)); final GroupArtifactVersion groupArtifactVersion = getContext().getGav(); if (enableProjectLabel) { - ret.put("project", groupArtifactVersion.getArtifactId()); + ret.putAll(addProjectLabelFromApplicableSource(null, "project", groupArtifactVersion.getArtifactId(), labelsViaResourceConfig)); } else { - ret.put("app", getConfig(Config.APP, groupArtifactVersion.getArtifactId())); + ret.putAll(addProjectLabelFromApplicableSource(Config.APP, "app", groupArtifactVersion.getArtifactId(), labelsViaResourceConfig)); } - ret.put("group", getConfig(Config.GROUP, groupArtifactVersion.getGroupId())); - ret.put(LABEL_PROVIDER, getConfig(Config.PROVIDER)); + ret.putAll(addProjectLabelFromApplicableSource(Config.GROUP, "group", groupArtifactVersion.getGroupId(), labelsViaResourceConfig)); + ret.putAll(addProjectLabelFromApplicableSource(Config.PROVIDER, LABEL_PROVIDER, null, labelsViaResourceConfig)); if (!withoutVersion) { - ret.put("version", getConfig(Config.VERSION, groupArtifactVersion.getVersion())); + ret.putAll(addProjectLabelFromApplicableSource(Config.VERSION, "version", groupArtifactVersion.getVersion(), labelsViaResourceConfig)); } return ret; } + + private Map addProjectLabelFromApplicableSource(Configs.Config key, String labelKey, String defaultValue, Map labelsViaResourceConfig) { + Map entryMap = new HashMap<>(); + String appLabelValueFromConfig = Optional.ofNullable(key) + .map(k -> getConfig(k, defaultValue)) + .orElse(defaultValue); + entryMap.put(labelKey, labelsViaResourceConfig.getOrDefault(labelKey, appLabelValueFromConfig)); + return entryMap; + } } diff --git a/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/WellKnownLabelsEnricher.java b/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/WellKnownLabelsEnricher.java index da7fc9128a..b532a97354 100644 --- a/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/WellKnownLabelsEnricher.java +++ b/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/WellKnownLabelsEnricher.java @@ -52,28 +52,31 @@ private boolean shouldAddWellKnownLabels() { } @Override - public Map createLabels(boolean withoutVersion) { + public Map createLabels(boolean withoutVersion, Map labelsViaResourceConfig) { Map ret = new HashMap<>(); if (!shouldAddWellKnownLabels()) { return ret; } final GroupArtifactVersion groupArtifactVersion = getContext().getGav(); - ret.putAll(addWellKnownLabelFromConfig(Config.APP_NAME, "name", groupArtifactVersion.getArtifactId())); + ret.putAll(addWellKnownLabelFromApplicableSource(Config.APP_NAME, "name", groupArtifactVersion.getArtifactId(), labelsViaResourceConfig)); if (!withoutVersion) { - ret.putAll(addWellKnownLabelFromConfig(Config.APP_VERSION, "version", groupArtifactVersion.getVersion())); + ret.putAll(addWellKnownLabelFromApplicableSource(Config.APP_VERSION, "version", groupArtifactVersion.getVersion(), labelsViaResourceConfig)); } - ret.putAll(addWellKnownLabelFromConfig(Config.APP_PART_OF, "part-of", groupArtifactVersion.getGroupId())); - ret.putAll(addWellKnownLabelFromConfig(Config.APP_MANAGED_BY, "managed-by", "jkube")); - ret.putAll(addWellKnownLabelFromConfig(Config.APP_COMPONENT, "component", null)); + ret.putAll(addWellKnownLabelFromApplicableSource(Config.APP_PART_OF, "part-of", groupArtifactVersion.getGroupId(), labelsViaResourceConfig)); + ret.putAll(addWellKnownLabelFromApplicableSource(Config.APP_MANAGED_BY, "managed-by", "jkube", labelsViaResourceConfig)); + ret.putAll(addWellKnownLabelFromApplicableSource(Config.APP_COMPONENT, "component", null, labelsViaResourceConfig)); return ret; } - private Map addWellKnownLabelFromConfig(Configs.Config key, String labelKey, String defaultValue) { + private Map addWellKnownLabelFromApplicableSource(Configs.Config key, String labelKey, String defaultValue, Map labelsViaResourceConfig) { Map entryMap = new HashMap<>(); - String value = getConfig(key, defaultValue); - if (StringUtils.isNotBlank(value)) { - entryMap.put(KUBERNETES_APP_LABEL + labelKey, value); + String appLabel = KUBERNETES_APP_LABEL + labelKey; + String appLabelValueFromConfig = getConfig(key, defaultValue); + if (labelsViaResourceConfig.containsKey(appLabel)) { + entryMap.put(appLabel, labelsViaResourceConfig.get(appLabel)); + } else if (StringUtils.isNotBlank(appLabelValueFromConfig)) { + entryMap.put(appLabel, appLabelValueFromConfig); } return entryMap; } diff --git a/jkube-kit/enricher/generic/src/test/java/org/eclipse/jkube/enricher/generic/ProjectLabelEnricherTest.java b/jkube-kit/enricher/generic/src/test/java/org/eclipse/jkube/enricher/generic/ProjectLabelEnricherTest.java index 4ece1f1b91..4e4aa42816 100644 --- a/jkube-kit/enricher/generic/src/test/java/org/eclipse/jkube/enricher/generic/ProjectLabelEnricherTest.java +++ b/jkube-kit/enricher/generic/src/test/java/org/eclipse/jkube/enricher/generic/ProjectLabelEnricherTest.java @@ -13,33 +13,34 @@ */ package org.eclipse.jkube.enricher.generic; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - import java.util.Map; import java.util.Properties; -import io.fabric8.kubernetes.api.model.LabelSelector; -import io.fabric8.kubernetes.api.model.apps.DeploymentSpec; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder; -import io.fabric8.kubernetes.api.model.apps.StatefulSetSpec; -import org.assertj.core.api.InstanceOfAssertFactories; -import org.eclipse.jkube.kit.config.resource.GroupArtifactVersion; +import io.fabric8.kubernetes.api.model.ServiceBuilder; +import org.eclipse.jkube.kit.common.JavaProject; +import org.eclipse.jkube.kit.config.resource.MetaDataConfig; import org.eclipse.jkube.kit.config.resource.PlatformMode; +import org.eclipse.jkube.kit.config.resource.ResourceConfig; import org.eclipse.jkube.kit.enricher.api.JKubeEnricherContext; + import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.api.model.KubernetesListBuilder; +import io.fabric8.kubernetes.api.model.LabelSelector; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; +import io.fabric8.kubernetes.api.model.apps.DeploymentSpec; +import io.fabric8.kubernetes.api.model.apps.StatefulSet; +import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder; +import io.fabric8.kubernetes.api.model.apps.StatefulSetSpec; import io.fabric8.openshift.api.model.DeploymentConfigBuilder; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test label generation. * @@ -49,14 +50,20 @@ class ProjectLabelEnricherTest { private Properties properties; private ProjectLabelEnricher projectLabelEnricher; + private JKubeEnricherContext context; @BeforeEach void setupExpectations() { - JKubeEnricherContext context = mock(JKubeEnricherContext.class, RETURNS_DEEP_STUBS); - projectLabelEnricher = new ProjectLabelEnricher(context); properties = new Properties(); - when(context.getProperties()).thenReturn(properties); - when(context.getGav()).thenReturn(new GroupArtifactVersion("groupId", "artifactId", "version")); + context = JKubeEnricherContext.builder() + .project(JavaProject.builder() + .properties(properties) + .groupId("groupId") + .artifactId("artifactId") + .version("version") + .build()) + .build(); + projectLabelEnricher = new ProjectLabelEnricher(context); } @Test @@ -211,6 +218,77 @@ void create_withConfiguredVersion_shouldAddConfiguredVersionInSelector() { .containsEntry("version", "0.0.1"); } + @Test + @DisplayName("when labels configured via resource config's labels > all, then use configured labels") + void create_whenProjectLabelConfiguredViaResourceConfigLabelAll_thenUseLabelsViaResourceConfig() { + // Given + Properties allLabels = new Properties(); + allLabels.put("app", "app-via-resource-config"); + allLabels.put("provider", "provider-via-resource-config"); + allLabels.put("group", "group-via-resource-config"); + context = context.toBuilder() + .resources(ResourceConfig.builder() + .labels(MetaDataConfig.builder() + .all(allLabels) + .build()) + .build()) + .build(); + KubernetesListBuilder kubernetesListBuilder = new KubernetesListBuilder().withItems(new DeploymentBuilder().build()); + projectLabelEnricher = new ProjectLabelEnricher(context); + + // When + projectLabelEnricher.create(PlatformMode.kubernetes, kubernetesListBuilder); + + // Then + Deployment deployment = (Deployment) kubernetesListBuilder.buildFirstItem(); + assertThat(deployment) + .extracting("spec.selector.matchLabels") + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("app", "app-via-resource-config") + .containsEntry("provider", "provider-via-resource-config") + .containsEntry("group", "group-via-resource-config"); + } + + @Test + @DisplayName("when labels configured via resource config's labels > deployment, then use configured labels for Deployment only") + void create_whenProjectLabelConfiguredViaResourceConfigLabelForSpecificResource_thenUseLabelsViaResourceConfigForSpecificResourceOnly() { + // Given + Properties deploymentLabels = new Properties(); + deploymentLabels.put("app", "app-via-resource-config-labels-deployment"); + deploymentLabels.put("provider", "provider-via-resource-config-labels-deployment"); + deploymentLabels.put("group", "group-via-resource-config-labels-deployment"); + context = context.toBuilder() + .resources(ResourceConfig.builder() + .labels(MetaDataConfig.builder() + .deployment(deploymentLabels) + .build()) + .build()) + .build(); + KubernetesListBuilder kubernetesListBuilder = new KubernetesListBuilder() + .addToItems(new DeploymentBuilder().build()) + .addToItems(new ServiceBuilder().build()); + projectLabelEnricher = new ProjectLabelEnricher(context); + + // When + projectLabelEnricher.create(PlatformMode.kubernetes, kubernetesListBuilder); + + // Then + assertThat(kubernetesListBuilder.buildItems()) + .hasSize(2) + .satisfies(items -> assertThat(items.get(0)) + .extracting("spec.selector.matchLabels") + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("app", "app-via-resource-config-labels-deployment") + .containsEntry("provider", "provider-via-resource-config-labels-deployment") + .containsEntry("group", "group-via-resource-config-labels-deployment")) + .satisfies(items -> assertThat(items.get(1)) + .extracting("spec.selector") + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("app", "artifactId") + .containsEntry("group", "groupId") + .containsEntry("provider", "jkube")); + } + @Nested @DisplayName("enrich with") class Enrich { diff --git a/jkube-kit/enricher/generic/src/test/java/org/eclipse/jkube/enricher/generic/WellKnownLabelsEnricherTest.java b/jkube-kit/enricher/generic/src/test/java/org/eclipse/jkube/enricher/generic/WellKnownLabelsEnricherTest.java index 1fe01e2ff4..6629a3fd5a 100644 --- a/jkube-kit/enricher/generic/src/test/java/org/eclipse/jkube/enricher/generic/WellKnownLabelsEnricherTest.java +++ b/jkube-kit/enricher/generic/src/test/java/org/eclipse/jkube/enricher/generic/WellKnownLabelsEnricherTest.java @@ -26,7 +26,9 @@ import io.fabric8.openshift.api.model.DeploymentConfigBuilder; import org.assertj.core.api.InstanceOfAssertFactories; import org.eclipse.jkube.kit.common.JavaProject; +import org.eclipse.jkube.kit.config.resource.MetaDataConfig; import org.eclipse.jkube.kit.config.resource.PlatformMode; +import org.eclipse.jkube.kit.config.resource.ResourceConfig; import org.eclipse.jkube.kit.enricher.api.JKubeEnricherContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -46,11 +48,12 @@ class WellKnownLabelsEnricherTest { private WellKnownLabelsEnricher wellKnownLabelsEnricher; private Properties properties; private KubernetesListBuilder kubernetesListBuilder; + private JKubeEnricherContext context; @BeforeEach void setup() { properties = new Properties(); - JKubeEnricherContext context = JKubeEnricherContext.builder() + context = JKubeEnricherContext.builder() .project(JavaProject.builder() .groupId("org.example") .artifactId("test-project") @@ -139,6 +142,78 @@ void whenWellKnownLabelAlreadyPresent_thenDoNotOverwriteLabel() { .containsEntry("app.kubernetes.io/version", "1.0.0-already-present"); } + @Test + @DisplayName("when labels configured via resource config's labels > all, then use configured labels") + void whenWellKnownLabelConfiguredViaResourceConfigLabelAll_thenUseLabelsViaResourceConfig() { + // Given + Properties allLabels = new Properties(); + allLabels.put("app.kubernetes.io/part-of", "part-of-via-resource-config"); + allLabels.put("app.kubernetes.io/component", "component-via-resource-config"); + allLabels.put("app.kubernetes.io/managed-by", "managed-by-via-resource-config"); + allLabels.put("app.kubernetes.io/name", "name-via-resource-config"); + context = context.toBuilder() + .resources(ResourceConfig.builder() + .labels(MetaDataConfig.builder() + .all(allLabels) + .build()) + .build()) + .build(); + wellKnownLabelsEnricher = new WellKnownLabelsEnricher(context); + + // When + wellKnownLabelsEnricher.create(PlatformMode.kubernetes, kubernetesListBuilder); + + // Then + Deployment deployment = (Deployment) kubernetesListBuilder.buildFirstItem(); + assertThat(deployment) + .extracting("spec.selector.matchLabels") + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("app.kubernetes.io/part-of", "part-of-via-resource-config") + .containsEntry("app.kubernetes.io/component", "component-via-resource-config") + .containsEntry("app.kubernetes.io/managed-by", "managed-by-via-resource-config") + .containsEntry("app.kubernetes.io/name", "name-via-resource-config"); + } + + @Test + @DisplayName("when labels configured via resource config's labels > deployment, then use configured labels for Deployment only") + void whenWellKnownLabelConfiguredViaResourceConfigLabelForSpecificResource_thenUseLabelsViaResourceConfig() { + // Given + Properties deploymentLabels = new Properties(); + deploymentLabels.put("app.kubernetes.io/part-of", "part-of-via-resource-config-labels-deployment"); + deploymentLabels.put("app.kubernetes.io/component", "component-via-resource-config-labels-deployment"); + deploymentLabels.put("app.kubernetes.io/managed-by", "managed-by-via-resource-config-labels-deployment"); + deploymentLabels.put("app.kubernetes.io/name", "name-via-resource-config-labels-deployment"); + context = context.toBuilder() + .resources(ResourceConfig.builder() + .labels(MetaDataConfig.builder() + .deployment(deploymentLabels) + .build()) + .build()) + .build(); + kubernetesListBuilder.addToItems(new ServiceBuilder().build()); + wellKnownLabelsEnricher = new WellKnownLabelsEnricher(context); + + // When + wellKnownLabelsEnricher.create(PlatformMode.kubernetes, kubernetesListBuilder); + + // Then + assertThat(kubernetesListBuilder.buildItems()) + .hasSize(2) + .satisfies(items -> assertThat(items.get(0)) + .extracting("spec.selector.matchLabels") + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("app.kubernetes.io/part-of", "part-of-via-resource-config-labels-deployment") + .containsEntry("app.kubernetes.io/component", "component-via-resource-config-labels-deployment") + .containsEntry("app.kubernetes.io/managed-by", "managed-by-via-resource-config-labels-deployment") + .containsEntry("app.kubernetes.io/name", "name-via-resource-config-labels-deployment")) + .satisfies(items -> assertThat(items.get(1)) + .extracting("spec.selector") + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("app.kubernetes.io/part-of", "org.example") + .containsEntry("app.kubernetes.io/managed-by", "jkube") + .containsEntry("app.kubernetes.io/name", "test-project")); + } + @Test @DisplayName("labels provided via enricher configuration, then add labels") void whenLabelsProvidedViaEnricherConfiguration_thenAddLabels() {