Skip to content

Commit 518c317

Browse files
xaijfaltermeierlucas-koehler
authored
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 <[email protected]> Co-authored-by: Johannes Faltermeier <[email protected]> Co-authored-by: Lucas Koehler <[email protected]>
1 parent 84d00f6 commit 518c317

File tree

5 files changed

+135
-33
lines changed

5 files changed

+135
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.eclipse.theia.cloud.common.util;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import org.eclipse.theia.cloud.common.k8s.resource.appdefinition.AppDefinitionSpec;
7+
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec;
8+
9+
public class LabelsUtil {
10+
public static final String LABEL_CUSTOM_PREFIX = "theia-cloud.io";
11+
12+
public static final String LABEL_KEY_SESSION = "app.kubernetes.io/component";
13+
public static final String LABEL_VALUE_SESSION = "session";
14+
15+
public static final String LABEL_KEY_THEIACLOUD = "app.kubernetes.io/part-of";
16+
public static final String LABEL_VALUE_THEIACLOUD = "theia-cloud";
17+
18+
public static final String LABEL_KEY_USER = LABEL_CUSTOM_PREFIX + "/user";
19+
public static final String LABEL_KEY_APPDEF = LABEL_CUSTOM_PREFIX + "/app-definition";
20+
21+
public static Map<String, String> createSessionLabels(SessionSpec sessionSpec,
22+
AppDefinitionSpec appDefinitionSpec) {
23+
Map<String, String> labels = new HashMap<>();
24+
labels.put(LABEL_KEY_SESSION, LABEL_VALUE_SESSION);
25+
labels.put(LABEL_KEY_THEIACLOUD, LABEL_VALUE_THEIACLOUD);
26+
String sanitizedUser = sessionSpec.getUser().replaceAll("@", "_at_").replaceAll("[^a-zA-Z0-9]", "_");
27+
labels.put(LABEL_KEY_USER, sanitizedUser);
28+
labels.put(LABEL_KEY_APPDEF, appDefinitionSpec.getName());
29+
return labels;
30+
}
31+
}

java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/appdef/EagerStartAppDefinitionAddedHandler.java

+19-10
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.net.URISyntaxException;
2323
import java.util.List;
2424
import java.util.Map;
25+
import java.util.HashMap;
2526
import java.util.Set;
2627
import java.util.stream.Collectors;
2728

@@ -104,10 +105,12 @@ public boolean appDefinitionAdded(AppDefinition appDefinition, String correlatio
104105
Set<Integer> missingServiceIds = TheiaCloudServiceUtil.computeIdsOfMissingServices(appDefinition, correlationId,
105106
instances, existingServices);
106107

108+
Map<String, String> labelsToAdd = new HashMap<String, String>();
109+
107110
/* Create missing services for this app definition */
108111
for (int instance : missingServiceIds) {
109112
createAndApplyService(client.kubernetes(), client.namespace(), correlationId, appDefinitionResourceName,
110-
appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak());
113+
appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak(), labelsToAdd);
111114
}
112115

113116
if (arguments.isUseKeycloak()) {
@@ -130,11 +133,13 @@ public boolean appDefinitionAdded(AppDefinition appDefinition, String correlatio
130133
/* Create missing configmaps for this app definition */
131134
for (int instance : missingProxyIds) {
132135
createAndApplyProxyConfigMap(client.kubernetes(), client.namespace(), correlationId,
133-
appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition);
136+
appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition,
137+
labelsToAdd);
134138
}
135139
for (int instance : missingEmailIds) {
136140
createAndApplyEmailConfigMap(client.kubernetes(), client.namespace(), correlationId,
137-
appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition);
141+
appDefinitionResourceName, appDefinitionResourceUID, instance, appDefinition,
142+
labelsToAdd);
138143
}
139144
}
140145

@@ -149,14 +154,14 @@ public boolean appDefinitionAdded(AppDefinition appDefinition, String correlatio
149154
/* Create missing deployments for this app definition */
150155
for (int instance : missingDeploymentIds) {
151156
createAndApplyDeployment(client.kubernetes(), client.namespace(), correlationId, appDefinitionResourceName,
152-
appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak());
157+
appDefinitionResourceUID, instance, appDefinition, arguments.isUseKeycloak(), labelsToAdd);
153158
}
154159
return true;
155160
}
156161

157162
protected void createAndApplyService(NamespacedKubernetesClient client, String namespace, String correlationId,
158163
String appDefinitionResourceName, String appDefinitionResourceUID, int instance,
159-
AppDefinition appDefinition, boolean useOAuth2Proxy) {
164+
AppDefinition appDefinition, boolean useOAuth2Proxy, Map<String, String> labelsToAdd) {
160165
Map<String, String> replacements = TheiaCloudServiceUtil.getServiceReplacements(namespace, appDefinition,
161166
instance);
162167
String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_SERVICE_YAML
@@ -172,12 +177,13 @@ protected void createAndApplyService(NamespacedKubernetesClient client, String n
172177
return;
173178
}
174179
K8sUtil.loadAndCreateServiceWithOwnerReference(client, namespace, correlationId, serviceYaml, AppDefinition.API,
175-
AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0);
180+
AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0,
181+
labelsToAdd);
176182
}
177183

178184
protected void createAndApplyDeployment(NamespacedKubernetesClient client, String namespace, String correlationId,
179185
String appDefinitionResourceName, String appDefinitionResourceUID, int instance,
180-
AppDefinition appDefinition, boolean useOAuth2Proxy) {
186+
AppDefinition appDefinition, boolean useOAuth2Proxy, Map<String, String> labelsToAdd) {
181187
Map<String, String> replacements = deploymentReplacements.getReplacements(namespace, appDefinition, instance);
182188
String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_DEPLOYMENT_YAML
183189
: AddedHandlerUtil.TEMPLATE_DEPLOYMENT_WITHOUT_AOUTH2_PROXY_YAML;
@@ -193,6 +199,7 @@ protected void createAndApplyDeployment(NamespacedKubernetesClient client, Strin
193199
}
194200
K8sUtil.loadAndCreateDeploymentWithOwnerReference(client, namespace, correlationId, deploymentYaml,
195201
AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0,
202+
labelsToAdd,
196203
deployment -> {
197204
bandwidthLimiter.limit(deployment, appDefinition.getSpec().getDownlinkLimit(),
198205
appDefinition.getSpec().getUplinkLimit(), correlationId);
@@ -206,7 +213,7 @@ protected void createAndApplyDeployment(NamespacedKubernetesClient client, Strin
206213

207214
protected void createAndApplyProxyConfigMap(NamespacedKubernetesClient client, String namespace,
208215
String correlationId, String appDefinitionResourceName, String appDefinitionResourceUID, int instance,
209-
AppDefinition appDefinition) {
216+
AppDefinition appDefinition, Map<String, String> labelsToAdd) {
210217
Map<String, String> replacements = TheiaCloudConfigMapUtil.getProxyConfigMapReplacements(namespace,
211218
appDefinition, instance);
212219
String configMapYaml;
@@ -221,6 +228,7 @@ protected void createAndApplyProxyConfigMap(NamespacedKubernetesClient client, S
221228
}
222229
K8sUtil.loadAndCreateConfigMapWithOwnerReference(client, namespace, correlationId, configMapYaml,
223230
AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0,
231+
labelsToAdd,
224232
configMap -> {
225233
String host = arguments.getInstancesHost() + ingressPathProvider.getPath(appDefinition, instance);
226234
int port = appDefinition.getSpec().getPort();
@@ -230,7 +238,7 @@ protected void createAndApplyProxyConfigMap(NamespacedKubernetesClient client, S
230238

231239
protected void createAndApplyEmailConfigMap(NamespacedKubernetesClient client, String namespace,
232240
String correlationId, String appDefinitionResourceName, String appDefinitionResourceUID, int instance,
233-
AppDefinition appDefinition) {
241+
AppDefinition appDefinition, Map<String, String> labelsToAdd) {
234242
Map<String, String> replacements = TheiaCloudConfigMapUtil.getEmailConfigMapReplacements(namespace,
235243
appDefinition, instance);
236244
String configMapYaml;
@@ -244,7 +252,8 @@ protected void createAndApplyEmailConfigMap(NamespacedKubernetesClient client, S
244252
return;
245253
}
246254
K8sUtil.loadAndCreateConfigMapWithOwnerReference(client, namespace, correlationId, configMapYaml,
247-
AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0);
255+
AppDefinition.API, AppDefinition.KIND, appDefinitionResourceName, appDefinitionResourceUID, 0,
256+
labelsToAdd);
248257
}
249258

250259
}

java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/EagerStartSessionHandler.java

+22
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.Collections;
2222
import java.util.List;
2323
import java.util.Map.Entry;
24+
import java.util.Map;
25+
import java.util.HashMap;
2426
import java.util.Optional;
2527

2628
import org.apache.logging.log4j.LogManager;
@@ -30,6 +32,7 @@
3032
import org.eclipse.theia.cloud.common.k8s.resource.session.Session;
3133
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec;
3234
import org.eclipse.theia.cloud.common.util.JavaUtil;
35+
import org.eclipse.theia.cloud.common.util.LabelsUtil;
3336
import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
3437
import org.eclipse.theia.cloud.operator.handler.AddedHandlerUtil;
3538
import org.eclipse.theia.cloud.operator.ingress.IngressPathProvider;
@@ -115,6 +118,25 @@ public boolean sessionAdded(Session session, String correlationId) {
115118
return false;
116119
}
117120

121+
try {
122+
client.services().inNamespace(client.namespace()).withName(serviceToUse.get().getMetadata().getName())
123+
.edit(service -> {
124+
LOGGER.debug("Setting session labels");
125+
Map<String, String> labels = service.getMetadata().getLabels();
126+
if (labels == null) {
127+
labels = new HashMap<>();
128+
service.getMetadata().setLabels(labels);
129+
}
130+
Map<String, String> newLabels = LabelsUtil.createSessionLabels(spec, appDefinition.get().getSpec());
131+
labels.putAll(newLabels);
132+
return service;
133+
});
134+
} catch (KubernetesClientException e) {
135+
LOGGER.error(formatLogMessage(correlationId,
136+
"Error while adding labels to service " + (serviceToUse.get().getMetadata().getName())), e);
137+
return false;
138+
}
139+
118140
/* get the deployment for the service and add as owner */
119141
Integer instance = TheiaCloudServiceUtil.getId(correlationId, appDefinition.get(), serviceToUse.get());
120142
if (instance == null) {

java/operator/org.eclipse.theia.cloud.operator/src/main/java/org/eclipse/theia/cloud/operator/handler/session/LazySessionHandler.java

+32-12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Collections;
2727
import java.util.List;
2828
import java.util.Map;
29+
import java.util.HashMap;
2930
import java.util.Optional;
3031

3132
import org.apache.logging.log4j.LogManager;
@@ -39,6 +40,7 @@
3940
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionSpec;
4041
import org.eclipse.theia.cloud.common.k8s.resource.session.SessionStatus;
4142
import org.eclipse.theia.cloud.common.k8s.resource.workspace.Workspace;
43+
import org.eclipse.theia.cloud.common.util.LabelsUtil;
4244
import org.eclipse.theia.cloud.common.util.TheiaCloudError;
4345
import org.eclipse.theia.cloud.common.util.WorkspaceUtil;
4446
import org.eclipse.theia.cloud.operator.TheiaCloudOperatorArguments;
@@ -155,6 +157,10 @@ protected boolean doSessionAdded(Session session, String correlationId) {
155157
return false;
156158
}
157159
AppDefinition appDefinition = optionalAppDefinition.get();
160+
AppDefinitionSpec appDefinitionSpec = appDefinition.getSpec();
161+
162+
/* label maps */
163+
Map<String, String> labelsToAdd = LabelsUtil.createSessionLabels(session.getSpec(), appDefinitionSpec);
158164

159165
if (hasMaxInstancesReached(appDefinition, session, correlationId)) {
160166
client.sessions().updateStatus(correlationId, session, s -> {
@@ -200,7 +206,7 @@ protected boolean doSessionAdded(Session session, String correlationId) {
200206
}
201207

202208
Optional<Service> serviceToUse = createAndApplyService(correlationId, sessionResourceName, sessionResourceUID,
203-
session, appDefinition.getSpec(), arguments.isUseKeycloak());
209+
session, appDefinitionSpec, arguments.isUseKeycloak(), labelsToAdd);
204210
if (serviceToUse.isEmpty()) {
205211
LOGGER.error(formatLogMessage(correlationId, "Unable to create service for session " + sessionSpec));
206212
client.sessions().updateStatus(correlationId, session, s -> {
@@ -226,9 +232,9 @@ protected boolean doSessionAdded(Session session, String correlationId) {
226232
// this handler
227233
return true;
228234
}
229-
createAndApplyEmailConfigMap(correlationId, sessionResourceName, sessionResourceUID, session);
235+
createAndApplyEmailConfigMap(correlationId, sessionResourceName, sessionResourceUID, session, labelsToAdd);
230236
createAndApplyProxyConfigMap(correlationId, sessionResourceName, sessionResourceUID, session,
231-
appDefinition);
237+
appDefinition, labelsToAdd);
232238
}
233239

234240
/* Create deployment for this session */
@@ -247,7 +253,7 @@ protected boolean doSessionAdded(Session session, String correlationId) {
247253

248254
Optional<String> storageName = getStorageName(session, correlationId);
249255
createAndApplyDeployment(correlationId, sessionResourceName, sessionResourceUID, session, appDefinition,
250-
storageName, arguments.isUseKeycloak());
256+
storageName, arguments.isUseKeycloak(), labelsToAdd);
251257

252258
/* adjust the ingress */
253259
String host;
@@ -371,7 +377,8 @@ protected Optional<String> getStorageName(Session session, String correlationId)
371377
}
372378

373379
protected Optional<Service> createAndApplyService(String correlationId, String sessionResourceName,
374-
String sessionResourceUID, Session session, AppDefinitionSpec appDefinitionSpec, boolean useOAuth2Proxy) {
380+
String sessionResourceUID, Session session, AppDefinitionSpec appDefinitionSpec, boolean useOAuth2Proxy,
381+
Map<String, String> labelsToAdd) {
375382
Map<String, String> replacements = TheiaCloudServiceUtil.getServiceReplacements(client.namespace(), session,
376383
appDefinitionSpec);
377384
String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_SERVICE_YAML
@@ -385,11 +392,11 @@ protected Optional<Service> createAndApplyService(String correlationId, String s
385392
return Optional.empty();
386393
}
387394
return K8sUtil.loadAndCreateServiceWithOwnerReference(client.kubernetes(), client.namespace(), correlationId,
388-
serviceYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0);
395+
serviceYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, labelsToAdd);
389396
}
390397

391398
protected void createAndApplyEmailConfigMap(String correlationId, String sessionResourceName,
392-
String sessionResourceUID, Session session) {
399+
String sessionResourceUID, Session session, Map<String, String> labelsToAdd) {
393400
Map<String, String> replacements = TheiaCloudConfigMapUtil.getEmailConfigMapReplacements(client.namespace(),
394401
session);
395402
String configMapYaml;
@@ -401,14 +408,15 @@ protected void createAndApplyEmailConfigMap(String correlationId, String session
401408
return;
402409
}
403410
K8sUtil.loadAndCreateConfigMapWithOwnerReference(client.kubernetes(), client.namespace(), correlationId,
404-
configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, configmap -> {
411+
configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0,
412+
labelsToAdd, configmap -> {
405413
configmap.setData(Collections.singletonMap(AddedHandlerUtil.FILENAME_AUTHENTICATED_EMAILS_LIST,
406414
session.getSpec().getUser()));
407415
});
408416
}
409417

410418
protected void createAndApplyProxyConfigMap(String correlationId, String sessionResourceName,
411-
String sessionResourceUID, Session session, AppDefinition appDefinition) {
419+
String sessionResourceUID, Session session, AppDefinition appDefinition, Map<String, String> labelsToAdd) {
412420
Map<String, String> replacements = TheiaCloudConfigMapUtil.getProxyConfigMapReplacements(client.namespace(),
413421
session);
414422
String configMapYaml;
@@ -420,7 +428,8 @@ protected void createAndApplyProxyConfigMap(String correlationId, String session
420428
return;
421429
}
422430
K8sUtil.loadAndCreateConfigMapWithOwnerReference(client.kubernetes(), client.namespace(), correlationId,
423-
configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, configMap -> {
431+
configMapYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0,
432+
labelsToAdd, configMap -> {
424433
String host = arguments.getInstancesHost() + ingressPathProvider.getPath(appDefinition, session);
425434
int port = appDefinition.getSpec().getPort();
426435
AddedHandlerUtil.updateProxyConfigMap(client.kubernetes(), client.namespace(), configMap, host,
@@ -429,7 +438,8 @@ protected void createAndApplyProxyConfigMap(String correlationId, String session
429438
}
430439

431440
protected void createAndApplyDeployment(String correlationId, String sessionResourceName, String sessionResourceUID,
432-
Session session, AppDefinition appDefinition, Optional<String> pvName, boolean useOAuth2Proxy) {
441+
Session session, AppDefinition appDefinition, Optional<String> pvName, boolean useOAuth2Proxy,
442+
Map<String, String> labelsToAdd) {
433443
Map<String, String> replacements = deploymentReplacements.getReplacements(client.namespace(), appDefinition,
434444
session);
435445
String templateYaml = useOAuth2Proxy ? AddedHandlerUtil.TEMPLATE_DEPLOYMENT_YAML
@@ -443,7 +453,17 @@ protected void createAndApplyDeployment(String correlationId, String sessionReso
443453
return;
444454
}
445455
K8sUtil.loadAndCreateDeploymentWithOwnerReference(client.kubernetes(), client.namespace(), correlationId,
446-
deploymentYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0, deployment -> {
456+
deploymentYaml, Session.API, Session.KIND, sessionResourceName, sessionResourceUID, 0,
457+
labelsToAdd, deployment -> {
458+
459+
LOGGER.debug("Setting session labels");
460+
Map<String, String> labels = deployment.getSpec().getTemplate().getMetadata().getLabels();
461+
if (labels == null) {
462+
labels = new HashMap<>();
463+
deployment.getSpec().getTemplate().getMetadata().setLabels(labels);
464+
}
465+
labels.putAll(labelsToAdd);
466+
447467
pvName.ifPresent(name -> addVolumeClaim(deployment, name, appDefinition.getSpec()));
448468
bandwidthLimiter.limit(deployment, appDefinition.getSpec().getDownlinkLimit(),
449469
appDefinition.getSpec().getUplinkLimit(), correlationId);

0 commit comments

Comments
 (0)