From 518c317b3f8693234b7520e1840fcb2f1ee87aba Mon Sep 17 00:00:00 2001 From: Olaf Lessenich Date: Thu, 28 Nov 2024 08:50:48 +0100 Subject: [PATCH] Improve session pod labels (#362) org.eclipse.theia.cloud.common.util.LabelsUtil is added to encapsulate creation of labels. To indicate that a pod is a user session, its 'app.kubernetes.io/component' label is set to 'session'. For custom labels, we use the prefix 'theia-cloud.io'. This is currently used for: - Identifying a session pods associated with a user: 'theia-cloud.io/user' = '$username' - Identifying a session pod by appdefinition name: 'theia-cloud.io/app-definition' = $appdefinitionname Signed-off-by: Olaf Lessenich Co-authored-by: Johannes Faltermeier Co-authored-by: Lucas Koehler --- .../theia/cloud/common/util/LabelsUtil.java | 31 +++++++++++++ .../EagerStartAppDefinitionAddedHandler.java | 29 +++++++----- .../session/EagerStartSessionHandler.java | 22 ++++++++++ .../handler/session/LazySessionHandler.java | 44 ++++++++++++++----- .../theia/cloud/operator/util/K8sUtil.java | 42 +++++++++++++----- 5 files changed, 135 insertions(+), 33 deletions(-) create mode 100644 java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/LabelsUtil.java diff --git a/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/LabelsUtil.java b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/LabelsUtil.java new file mode 100644 index 00000000..7acbfa22 --- /dev/null +++ b/java/common/org.eclipse.theia.cloud.common/src/main/java/org/eclipse/theia/cloud/common/util/LabelsUtil.java @@ -0,0 +1,31 @@ +package org.eclipse.theia.cloud.common.util; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec; +import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec; + +public class LabelsUtil { + public static final String LABEL_CUSTOM_PREFIX = "theia-cloud.io"; + + public static final String LABEL_KEY_SESSION = "app.kubernetes.io/component"; + public static final String LABEL_VALUE_SESSION = "session"; + + public static final String LABEL_KEY_THEIACLOUD = "app.kubernetes.io/part-of"; + public static final String LABEL_VALUE_THEIACLOUD = "theia-cloud"; + + public static final String LABEL_KEY_USER = LABEL_CUSTOM_PREFIX + "/user"; + public static final String LABEL_KEY_APPDEF = LABEL_CUSTOM_PREFIX + "/app-definition"; + + public static Map createSessionLabels(SessionSpec sessionSpec, + AppDefinitionSpec appDefinitionSpec) { + Map labels = new HashMap<>(); + labels.put(LABEL_KEY_SESSION, LABEL_VALUE_SESSION); + labels.put(LABEL_KEY_THEIACLOUD, LABEL_VALUE_THEIACLOUD); + String sanitizedUser = sessionSpec.getUser().replaceAll("@", "_at_").replaceAll("[^a-zA-Z0-9]", "_"); + labels.put(LABEL_KEY_USER, sanitizedUser); + labels.put(LABEL_KEY_APPDEF, appDefinitionSpec.getName()); + return labels; + } +} diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java index fed9a707..9bc7182f 100644 --- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java +++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java @@ -22,6 +22,7 @@ import java.net.URISyntaxException; import java.util.List; import java.util.Map; +import java.util.HashMap; import java.util.Set; import java.util.stream.Collectors; @@ -104,10 +105,12 @@ public boolean appDefinitionAdded(AppDefinition appDefinition, String correlatio Set missingServiceIds = TheiaCloudServiceUtil.computeIdsOfMissingServices(appDefinition, correlationId, instances, existingServices); + Map labelsToAdd = new HashMap(); + /* Create missing services for this app definition */ for (int instance : missingServiceIds) { createAndApplyService(client.kubernetes(), client.namespace(), correlationId, appDefinitionResourceName, - appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak()); + appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak(), labelsToAdd); } if (arguments.isUseKeycloak()) { @@ -130,11 +133,13 @@ public boolean appDefinitionAdded(AppDefinition appDefinition, String correlatio /* Create missing configmaps for this app definition */ for (int instance : missingProxyIds) { createAndApplyProxyConfigMap(client.kubernetes(), client.namespace(), correlationId, - appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition); + appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition, + labelsToAdd); } for (int instance : missingEmailIds) { createAndApplyEmailConfigMap(client.kubernetes(), client.namespace(), correlationId, - appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition); + appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition, + labelsToAdd); } } @@ -149,14 +154,14 @@ public boolean appDefinitionAdded(AppDefinition appDefinition, String correlatio /* Create missing deployments for this app definition */ for (int instance : missingDeploymentIds) { createAndApplyDeployment(client.kubernetes(), client.namespace(), correlationId, appDefinitionResourceName, - appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak()); + appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak(), labelsToAdd); } return true; } protected void createAndApplyService(NamespacedKubernetesClient client, String namespace, String correlationId, String appDefinitionResourceName, String appDefinitionResourceUID, int instance, - AppDefinition appDefinition, boolean useOAuth2Proxy) { + AppDefinition appDefinition, boolean useOAuth2Proxy, Map labelsToAdd) { Map replacements = TheiaCloudServiceUtil.getServiceReplacements(namespace, appDefinition, instance); String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_SERVICE_YAML @@ -172,12 +177,13 @@ protected void createAndApplyService(NamespacedKubernetesClient client, String n return; } K8sUtil.loadAndCreateServiceWithOwnerReference(client, namespace, correlationId, serviceYaml, AppDefinition.API, - AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0); + AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0, + labelsToAdd); } protected void createAndApplyDeployment(NamespacedKubernetesClient client, String namespace, String correlationId, String appDefinitionResourceName, String appDefinitionResourceUID, int instance, - AppDefinition appDefinition, boolean useOAuth2Proxy) { + AppDefinition appDefinition, boolean useOAuth2Proxy, Map labelsToAdd) { Map replacements = deploymentReplacements.getReplacements(namespace, appDefinition, instance); String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_DEPLOYMENT_YAML : AddedHandlerUtil.TEMPLATE_DEPLOYMENT_WITHOUT_AOUTH2_PROXY_YAML; @@ -193,6 +199,7 @@ protected void createAndApplyDeployment(NamespacedKubernetesClient client, Strin } K8sUtil.loadAndCreateDeploymentWithOwnerReference(client, namespace, correlationId, deploymentYaml, AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0, + labelsToAdd, deployment -> { bandwidthLimiter.limit(deployment, appDefinition.getSpec().getDownlinkLimit(), appDefinition.getSpec().getUplinkLimit(), correlationId); @@ -206,7 +213,7 @@ protected void createAndApplyDeployment(NamespacedKubernetesClient client, Strin protected void createAndApplyProxyConfigMap(NamespacedKubernetesClient client, String namespace, String correlationId, String appDefinitionResourceName, String appDefinitionResourceUID, int instance, - AppDefinition appDefinition) { + AppDefinition appDefinition, Map labelsToAdd) { Map replacements = TheiaCloudConfigMapUtil.getProxyConfigMapReplacements(namespace, appDefinition, instance); String configMapYaml; @@ -221,6 +228,7 @@ protected void createAndApplyProxyConfigMap(NamespacedKubernetesClient client, S } K8sUtil.loadAndCreateConfigMapWithOwnerReference(client, namespace, correlationId, configMapYaml, AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0, + labelsToAdd, configMap -> { String host = arguments.getInstancesHost() + ingressPathProvider.getPath(appDefinition, instance); int port = appDefinition.getSpec().getPort(); @@ -230,7 +238,7 @@ protected void createAndApplyProxyConfigMap(NamespacedKubernetesClient client, S protected void createAndApplyEmailConfigMap(NamespacedKubernetesClient client, String namespace, String correlationId, String appDefinitionResourceName, String appDefinitionResourceUID, int instance, - AppDefinition appDefinition) { + AppDefinition appDefinition, Map labelsToAdd) { Map replacements = TheiaCloudConfigMapUtil.getEmailConfigMapReplacements(namespace, appDefinition, instance); String configMapYaml; @@ -244,7 +252,8 @@ protected void createAndApplyEmailConfigMap(NamespacedKubernetesClient client, S return; } K8sUtil.loadAndCreateConfigMapWithOwnerReference(client, namespace, correlationId, configMapYaml, - AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0); + AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0, + labelsToAdd); } } diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerStartSessionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerStartSessionHandler.java index c9ddd205..0cb2a107 100644 --- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerStartSessionHandler.java +++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerStartSessionHandler.java @@ -21,6 +21,8 @@ import java.util.Collections; import java.util.List; import java.util.Map.Entry; +import java.util.Map; +import java.util.HashMap; import java.util.Optional; import org.apache.logging.log4j.LogManager; @@ -30,6 +32,7 @@ import org.eclipse.theia.cloud.common.k8s.resource.session.Session; import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec; import org.eclipse.theia.cloud.common.util.JavaUtil; +import org.eclipse.theia.cloud.common.util.LabelsUtil; import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments; import org.eclipse.theia.cloud.operator.handler.AddedHandlerUtil; import org.eclipse.theia.cloud.operator.ingress.IngressPathProvider; @@ -115,6 +118,25 @@ public boolean sessionAdded(Session session, String correlationId) { return false; } + try { + client.services().inNamespace(client.namespace()).withName(serviceToUse.get().getMetadata().getName()) + .edit(service -> { + LOGGER.debug("Setting session labels"); + Map labels = service.getMetadata().getLabels(); + if (labels == null) { + labels = new HashMap<>(); + service.getMetadata().setLabels(labels); + } + Map newLabels = LabelsUtil.createSessionLabels(spec, appDefinition.get().getSpec()); + labels.putAll(newLabels); + return service; + }); + } catch (KubernetesClientException e) { + LOGGER.error(formatLogMessage(correlationId, + "Error while adding labels to service " + (serviceToUse.get().getMetadata().getName())), e); + return false; + } + /* get the deployment for the service and add as owner */ Integer instance = TheiaCloudServiceUtil.getId(correlationId, appDefinition.get(), serviceToUse.get()); if (instance == null) { diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java index 5cc0fd16..db956608 100644 --- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java +++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.HashMap; import java.util.Optional; import org.apache.logging.log4j.LogManager; @@ -39,6 +40,7 @@ import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec; import org.eclipse.theia.cloud.common.k8s.resource.session.SessionStatus; import org.eclipse.theia.cloud.common.k8s.resource.workspace.Workspace; +import org.eclipse.theia.cloud.common.util.LabelsUtil; import org.eclipse.theia.cloud.common.util.TheiaCloudError; import org.eclipse.theia.cloud.common.util.WorkspaceUtil; import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments; @@ -155,6 +157,10 @@ protected boolean doSessionAdded(Session session, String correlationId) { return false; } AppDefinition appDefinition = optionalAppDefinition.get(); + AppDefinitionSpec appDefinitionSpec = appDefinition.getSpec(); + + /* label maps */ + Map labelsToAdd = LabelsUtil.createSessionLabels(session.getSpec(), appDefinitionSpec); if (hasMaxInstancesReached(appDefinition, session, correlationId)) { client.sessions().updateStatus(correlationId, session, s -> { @@ -200,7 +206,7 @@ protected boolean doSessionAdded(Session session, String correlationId) { } Optional serviceToUse = createAndApplyService(correlationId, sessionResourceName, sessionResourceUID, - session, appDefinition.getSpec(), arguments.isUseKeycloak()); + session, appDefinitionSpec, arguments.isUseKeycloak(), labelsToAdd); if (serviceToUse.isEmpty()) { LOGGER.error(formatLogMessage(correlationId, "Unable to create service for session " + sessionSpec)); client.sessions().updateStatus(correlationId, session, s -> { @@ -226,9 +232,9 @@ protected boolean doSessionAdded(Session session, String correlationId) { // this handler return true; } - createAndApplyEmailConfigMap(correlationId, sessionResourceName, sessionResourceUID, session); + createAndApplyEmailConfigMap(correlationId, sessionResourceName, sessionResourceUID, session, labelsToAdd); createAndApplyProxyConfigMap(correlationId, sessionResourceName, sessionResourceUID, session, - appDefinition); + appDefinition, labelsToAdd); } /* Create deployment for this session */ @@ -247,7 +253,7 @@ protected boolean doSessionAdded(Session session, String correlationId) { Optional storageName = getStorageName(session, correlationId); createAndApplyDeployment(correlationId, sessionResourceName, sessionResourceUID, session, appDefinition, - storageName, arguments.isUseKeycloak()); + storageName, arguments.isUseKeycloak(), labelsToAdd); /* adjust the ingress */ String host; @@ -371,7 +377,8 @@ protected Optional getStorageName(Session session, String correlationId) } protected Optional createAndApplyService(String correlationId, String sessionResourceName, - String sessionResourceUID, Session session, AppDefinitionSpec appDefinitionSpec, boolean useOAuth2Proxy) { + String sessionResourceUID, Session session, AppDefinitionSpec appDefinitionSpec, boolean useOAuth2Proxy, + Map labelsToAdd) { Map replacements = TheiaCloudServiceUtil.getServiceReplacements(client.namespace(), session, appDefinitionSpec); String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_SERVICE_YAML @@ -385,11 +392,11 @@ protected Optional createAndApplyService(String correlationId, String s return Optional.empty(); } return K8sUtil.loadAndCreateServiceWithOwnerReference(client.kubernetes(), client.namespace(), correlationId, - serviceYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0); + serviceYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, labelsToAdd); } protected void createAndApplyEmailConfigMap(String correlationId, String sessionResourceName, - String sessionResourceUID, Session session) { + String sessionResourceUID, Session session, Map labelsToAdd) { Map replacements = TheiaCloudConfigMapUtil.getEmailConfigMapReplacements(client.namespace(), session); String configMapYaml; @@ -401,14 +408,15 @@ protected void createAndApplyEmailConfigMap(String correlationId, String session return; } K8sUtil.loadAndCreateConfigMapWithOwnerReference(client.kubernetes(), client.namespace(), correlationId, - configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, configmap -> { + configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, + labelsToAdd, configmap -> { configmap.setData(Collections.singletonMap(AddedHandlerUtil.FILENAME_AUTHENTICATED_EMAILS_LIST, session.getSpec().getUser())); }); } protected void createAndApplyProxyConfigMap(String correlationId, String sessionResourceName, - String sessionResourceUID, Session session, AppDefinition appDefinition) { + String sessionResourceUID, Session session, AppDefinition appDefinition, Map labelsToAdd) { Map replacements = TheiaCloudConfigMapUtil.getProxyConfigMapReplacements(client.namespace(), session); String configMapYaml; @@ -420,7 +428,8 @@ protected void createAndApplyProxyConfigMap(String correlationId, String session return; } K8sUtil.loadAndCreateConfigMapWithOwnerReference(client.kubernetes(), client.namespace(), correlationId, - configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, configMap -> { + configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, + labelsToAdd, configMap -> { String host = arguments.getInstancesHost() + ingressPathProvider.getPath(appDefinition, session); int port = appDefinition.getSpec().getPort(); AddedHandlerUtil.updateProxyConfigMap(client.kubernetes(), client.namespace(), configMap, host, @@ -429,7 +438,8 @@ protected void createAndApplyProxyConfigMap(String correlationId, String session } protected void createAndApplyDeployment(String correlationId, String sessionResourceName, String sessionResourceUID, - Session session, AppDefinition appDefinition, Optional pvName, boolean useOAuth2Proxy) { + Session session, AppDefinition appDefinition, Optional pvName, boolean useOAuth2Proxy, + Map labelsToAdd) { Map replacements = deploymentReplacements.getReplacements(client.namespace(), appDefinition, session); String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_DEPLOYMENT_YAML @@ -443,7 +453,17 @@ protected void createAndApplyDeployment(String correlationId, String sessionReso return; } K8sUtil.loadAndCreateDeploymentWithOwnerReference(client.kubernetes(), client.namespace(), correlationId, - deploymentYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, deployment -> { + deploymentYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, + labelsToAdd, deployment -> { + + LOGGER.debug("Setting session labels"); + Map labels = deployment.getSpec().getTemplate().getMetadata().getLabels(); + if (labels == null) { + labels = new HashMap<>(); + deployment.getSpec().getTemplate().getMetadata().setLabels(labels); + } + labels.putAll(labelsToAdd); + pvName.ifPresent(name -> addVolumeClaim(deployment, name, appDefinition.getSpec())); bandwidthLimiter.limit(deployment, appDefinition.getSpec().getDownlinkLimit(), appDefinition.getSpec().getUplinkLimit(), correlationId); diff --git a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/util/K8sUtil.java b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/util/K8sUtil.java index 0c2d54b5..8533a3a7 100644 --- a/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/util/K8sUtil.java +++ b/java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/util/K8sUtil.java @@ -21,6 +21,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.List; +import java.util.Map; +import java.util.HashMap; import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -110,50 +112,54 @@ private static boolean hasThisTemplateOwnerReference(List ownerR public static Optional loadAndCreateIngressWithOwnerReference(NamespacedKubernetesClient client, String namespace, String correlationId, String yaml, String ownerAPIVersion, String ownerKind, - String ownerName, String ownerUid, int ownerReferenceIndex) { + String ownerName, String ownerUid, int ownerReferenceIndex, Map labelsToAdd) { return loadAndCreateTypeWithOwnerReference(client, namespace, correlationId, yaml, ownerAPIVersion, ownerKind, ownerName, ownerUid, ownerReferenceIndex, INGRESS, - client.network().v1().ingresses().inNamespace(namespace), item -> { + client.network().v1().ingresses().inNamespace(namespace), labelsToAdd, item -> { }); } public static Optional loadAndCreateServiceWithOwnerReference(NamespacedKubernetesClient client, String namespace, String correlationId, String yaml, String ownerAPIVersion, String ownerKind, - String ownerName, String ownerUid, int ownerReferenceIndex) { + String ownerName, String ownerUid, int ownerReferenceIndex, Map labelsToAdd) { return loadAndCreateTypeWithOwnerReference(client, namespace, correlationId, yaml, ownerAPIVersion, ownerKind, - ownerName, ownerUid, ownerReferenceIndex, SERVICE, client.services().inNamespace(namespace), item -> { + ownerName, ownerUid, ownerReferenceIndex, SERVICE, client.services().inNamespace(namespace), + labelsToAdd, item -> { }); } public static Optional loadAndCreateConfigMapWithOwnerReference(NamespacedKubernetesClient client, String namespace, String correlationId, String yaml, String ownerAPIVersion, String ownerKind, - String ownerName, String ownerUid, int ownerReferenceIndex) { + String ownerName, String ownerUid, int ownerReferenceIndex, Map labelsToAdd) { return loadAndCreateTypeWithOwnerReference(client, namespace, correlationId, yaml, ownerAPIVersion, ownerKind, ownerName, ownerUid, ownerReferenceIndex, CONFIG_MAP, client.configMaps().inNamespace(namespace), - item -> { + labelsToAdd, item -> { }); } public static Optional loadAndCreateDeploymentWithOwnerReference(NamespacedKubernetesClient client, String namespace, String correlationId, String yaml, String ownerAPIVersion, String ownerKind, - String ownerName, String ownerUid, int ownerReferenceIndex, Consumer additionalModification) { + String ownerName, String ownerUid, int ownerReferenceIndex, Map labelsToAdd, + Consumer additionalModification) { return loadAndCreateTypeWithOwnerReference(client, namespace, correlationId, yaml, ownerAPIVersion, ownerKind, ownerName, ownerUid, ownerReferenceIndex, DEPLOYMENT, - client.apps().deployments().inNamespace(namespace), additionalModification); + client.apps().deployments().inNamespace(namespace), labelsToAdd, additionalModification); } public static Optional loadAndCreateConfigMapWithOwnerReference(NamespacedKubernetesClient client, String namespace, String correlationId, String yaml, String ownerAPIVersion, String ownerKind, - String ownerName, String ownerUid, int ownerReferenceIndex, Consumer additionalModification) { + String ownerName, String ownerUid, int ownerReferenceIndex, Map labelsToAdd, + Consumer additionalModification) { return loadAndCreateTypeWithOwnerReference(client, namespace, correlationId, yaml, ownerAPIVersion, ownerKind, ownerName, ownerUid, ownerReferenceIndex, CONFIG_MAP, client.configMaps().inNamespace(namespace), - additionalModification); + labelsToAdd, additionalModification); } private static > Optional loadAndCreateTypeWithOwnerReference( NamespacedKubernetesClient client, String namespace, String correlationId, String yaml, String ownerAPIVersion, String ownerKind, String ownerName, String ownerUid, int ownerReferenceIndex, - String typeName, NonNamespaceOperation items, Consumer additionalModification) { + String typeName, NonNamespaceOperation items, Map labelsToAdd, + Consumer additionalModification) { try (ByteArrayInputStream inputStream = new ByteArrayInputStream(yaml.getBytes())) { @@ -164,6 +170,20 @@ private static > Optional loa return Optional.empty(); } + // Apply labels to the resource metadata + if (newItem.getMetadata().getLabels() == null) { + newItem.getMetadata().setLabels(new HashMap<>()); + } + newItem.getMetadata().getLabels().putAll(labelsToAdd); + + // If the resource is a Deployment, also apply labels to the pod template metadata + if (newItem instanceof Deployment deployment) { + if (deployment.getSpec().getTemplate().getMetadata().getLabels() == null) { + deployment.getSpec().getTemplate().getMetadata().setLabels(new HashMap<>()); + } + deployment.getSpec().getTemplate().getMetadata().getLabels().putAll(labelsToAdd); + } + ResourceEdit. updateOwnerReference(ownerReferenceIndex, ownerAPIVersion, ownerKind, ownerName, ownerUid, correlationId).andThen(additionalModification).accept(newItem);