This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit 9f9715f9479b526685880309247a060085b55e0d Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Thu Oct 24 09:00:17 2024 -0400 Kubernetes Improvements --- .../karavan/kubernetes/KubernetesService.java | 187 ++++++++++++++++++--- .../kubernetes/KubernetesStatusService.java | 6 +- .../listener/KubernetesCommandListener.java | 40 +++++ .../camel/karavan/model/KubernetesConfigMap.java | 33 ++++ .../camel/karavan/model/KubernetesSecret.java | 33 ++++ 5 files changed, 274 insertions(+), 25 deletions(-) diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java b/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java index b5e5cba8..8e5b5967 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesService.java @@ -28,7 +28,10 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Default; import jakarta.enterprise.inject.Produces; import jakarta.inject.Inject; -import org.apache.camel.karavan.model.PodContainerStatus; +import org.apache.camel.karavan.KaravanConstants; +import org.apache.camel.karavan.model.ContainerType; +import org.apache.camel.karavan.model.KubernetesConfigMap; +import org.apache.camel.karavan.model.KubernetesSecret; import org.apache.camel.karavan.model.Project; import org.apache.camel.karavan.service.CodeService; import org.apache.camel.karavan.service.ConfigService; @@ -48,6 +51,9 @@ public class KubernetesService { private static final Logger LOGGER = Logger.getLogger(KubernetesService.class.getName()); + @ConfigProperty(name = "karavan.environment", defaultValue = KaravanConstants.DEV) + private String environment; + @Inject CodeService codeService; @@ -111,7 +117,7 @@ public class KubernetesService { public void runBuildProject(Project project, String podFragment) { try (KubernetesClient client = kubernetesClient()) { String containerName = project.getProjectId() + BUILDER_SUFFIX; - Map<String, String> labels = getLabels(containerName, project, PodContainerStatus.ContainerType.build); + Map<String, String> labels = getLabels(containerName, project, ContainerType.build); // Delete old build pod Pod old = client.pods().inNamespace(getNamespace()).withName(containerName).get(); @@ -128,17 +134,17 @@ public class KubernetesService { } } - private Map<String, String> getLabels(String name, Project project, PodContainerStatus.ContainerType type) { + private Map<String, String> getLabels(String name, Project project, ContainerType type) { Map<String, String> labels = new HashMap<>(); - labels.putAll(getRuntimeLabels()); labels.putAll(getPartOfLabels()); labels.put("app.kubernetes.io/name", name); labels.put(LABEL_PROJECT_ID, project.getProjectId()); if (type != null) { labels.put(LABEL_TYPE, type.name()); } - if (Objects.equals(type, PodContainerStatus.ContainerType.devmode)) { + if (Objects.equals(type, ContainerType.devmode)) { labels.put(LABEL_CAMEL_RUNTIME, CamelRuntime.CAMEL_MAIN.getValue()); + labels.putAll(getRuntimeLabels()); } return labels; } @@ -229,7 +235,7 @@ public class KubernetesService { public boolean hasDockerConfigSecret() { try (KubernetesClient client = kubernetesClient()) { - return client.secrets().inNamespace(namespace).withName(BUILD_DOCKER_CONFIG_SECRET).get() != null; + return client.secrets().inNamespace(getNamespace()).withName(BUILD_DOCKER_CONFIG_SECRET).get() != null; } catch (Exception ex) { LOGGER.error(ex.getMessage()); return false; @@ -244,16 +250,21 @@ public class KubernetesService { public void rolloutDeployment(String name) { try (KubernetesClient client = kubernetesClient()) { - client.apps().deployments().inNamespace(namespace).withName(name).rolling().restart(); + client.apps().deployments().inNamespace(getNamespace()).withName(name).rolling().restart(); } catch (Exception ex) { LOGGER.error(ex.getMessage()); } } - public void startDeployment(String resources) { + public void startDeployment(String resources, Map<String, String> labels) { try (KubernetesClient client = kubernetesClient()) { KubernetesList list = Serialization.unmarshal(resources, KubernetesList.class); - list.getItems().forEach(item -> client.resource(item).serverSideApply()); + list.getItems().forEach(item -> { + if (labels != null ) { + item.getMetadata().getLabels().putAll(labels); + } + client.resource(item).inNamespace(getNamespace()).serverSideApply(); + }); } catch (Exception ex) { LOGGER.error(ex.getMessage()); } @@ -261,9 +272,9 @@ public class KubernetesService { public void deleteDeployment(String name) { try (KubernetesClient client = kubernetesClient()) { - LOGGER.info("Delete deployment: " + name + " in the namespace: " + namespace); - client.apps().deployments().inNamespace(namespace).withName(name).delete(); - client.services().inNamespace(namespace).withName(name).delete(); + LOGGER.info("Delete deployment: " + name + " in the namespace: " + getNamespace()); + client.apps().deployments().inNamespace(getNamespace()).withName(name).delete(); + client.services().inNamespace(getNamespace()).withName(name).delete(); } catch (Exception ex) { LOGGER.error(ex.getMessage()); } @@ -322,9 +333,10 @@ public class KubernetesService { return result; } - public void runDevModeContainer(Project project, String jBangOptions, Map<String, String> files, String projectDevmodeImage, String deploymentFragment) { + public void runDevModeContainer(Project project, String jBangOptions, Map<String, String> files, String projectDevmodeImage, String deploymentFragment, Map<String, String> labels, Map<String, String> envVars) { String name = project.getProjectId(); - Map<String, String> labels = getLabels(name, project, PodContainerStatus.ContainerType.devmode); + Map<String, String> podLabels = new HashMap<>(labels); + podLabels.putAll(getLabels(name, project, ContainerType.devmode)); try (KubernetesClient client = kubernetesClient()) { if (devmodePVC.orElse(false)) { @@ -332,13 +344,13 @@ public class KubernetesService { } Pod old = client.pods().inNamespace(getNamespace()).withName(name).get(); if (old == null) { - Pod pod = getDevModePod(name, jBangOptions, labels, projectDevmodeImage, deploymentFragment); + Pod pod = getDevModePod(name, jBangOptions, podLabels, projectDevmodeImage, deploymentFragment, envVars); Pod result = client.resource(pod).serverSideApply(); copyFilesToContainer(result, files, "/karavan/code"); LOGGER.info("Created pod " + result.getMetadata().getName()); } } - createService(name, labels); + createService(name, podLabels); } private void copyFilesToContainer(Pod pod, Map<String, String> files, String dirName) { @@ -375,7 +387,7 @@ public class KubernetesService { .build(); } - private Pod getDevModePod(String name, String jbangOptions, Map<String, String> labels, String projectDevmodeImage, String deploymentFragment) { + private Pod getDevModePod(String name, String jbangOptions, Map<String, String> labels, String projectDevmodeImage, String deploymentFragment, Map<String, String> envVars) { Deployment deployment = Serialization.unmarshal(deploymentFragment, Deployment.class); PodSpec podSpec = null; @@ -404,13 +416,17 @@ public class KubernetesService { .withProtocol("TCP") .build(); + List<EnvVar> environmentVariables = new ArrayList<>(); + envVars.forEach((k, v) -> environmentVariables.add(new EnvVarBuilder().withName(k).withValue(v).build())); + environmentVariables.add(new EnvVarBuilder().withName(ENV_VAR_JBANG_OPTIONS).withValue(jbangOptions).build()); + Container container = new ContainerBuilder() .withName(name) .withImage(projectDevmodeImage != null ? projectDevmodeImage : devmodeImage) .withPorts(port) .withResources(resources) .withImagePullPolicy(devmodeImagePullPolicy.orElse("IfNotPresent")) - .withEnv(new EnvVarBuilder().withName(ENV_VAR_JBANG_OPTIONS).withValue(jbangOptions).build()) + .withEnv(environmentVariables) .withVolumeMounts(volumeMounts) .build(); @@ -486,6 +502,20 @@ public class KubernetesService { } } + public void createConfigMap(String name, Map<String, String> data, Map<String, String> labels) { + try (KubernetesClient client = kubernetesClient()) { + ConfigMap configMap = new ConfigMapBuilder() + .withNewMetadata() + .withName(name) + .withNamespace(getNamespace()) + .withLabels(labels) + .endMetadata() + .withData(data) + .build(); + client.resource(configMap).serverSideApply(); + } + } + public Secret getSecret(String name) { try (KubernetesClient client = kubernetesClient()) { return client.secrets().inNamespace(getNamespace()).withName(name).get(); @@ -521,16 +551,16 @@ public class KubernetesService { return null; } - public boolean isOpenshift() { - return isOpenShift.isPresent() && isOpenShift.get(); - } - - public String getCluster() { + public ConfigMap getConfigMap(String name) { try (KubernetesClient client = kubernetesClient()) { - return client.getMasterUrl().getHost(); + return client.configMaps().inNamespace(getNamespace()).withName(name).get(); } } + public boolean isOpenshift() { + return isOpenShift.isPresent() && isOpenShift.get(); + } + public String getNamespace() { if (namespace == null) { try (KubernetesClient client = kubernetesClient()) { @@ -545,4 +575,113 @@ public class KubernetesService { client.resource(secret).update(); } } + + public void updateConfigMap(ConfigMap configMap) { + try (KubernetesClient client = kubernetesClient()) { + client.resource(configMap).update(); + } + } + + public String getSecretValue(String secretName, String secretKey) { + return getSecret(secretName).getData().get(secretKey); + } + + public void setSecretValue(String secretName, String secretKey, String value) { + Secret secret = getSecret(secretName); + if (secret != null) { + secret.getData().put(secretKey, value); + updateSecret(secret); + } + } + + public void createSecret(String secretName) { + Secret secret = getSecret(secretName); + if (secret == null) { + createSecret(secretName, Map.of(), Map.of()); + } + } + + public void deleteSecretValue(String secretName, String secretKey) { + Secret secret = getSecret(secretName); + if (secret != null) { + secret.getData().remove(secretKey); + updateSecret(secret); + } + } + + public List<KubernetesSecret> getSecrets() { + List<KubernetesSecret> result = new ArrayList<>(); + try (KubernetesClient client = kubernetesClient()) { + client.secrets().inNamespace(getNamespace()).list().getItems().forEach(secret -> { + Map<String, String> data = new HashMap<>(secret.getData()); + data.replaceAll((s, s2) -> ""); + result.add(new KubernetesSecret(secret.getMetadata().getName(), data)); + }); + } catch (Exception e) { + LOGGER.error(e); + } + return result; + } + + public void deleteSecret(String secretName) { + Secret secret = getSecret(secretName); + if (secret != null) { + try (KubernetesClient client = kubernetesClient()) { + client.secrets().inNamespace(getNamespace()).withName(secretName).delete(); + } + } + } + + public List<KubernetesConfigMap> getConfigMaps() { + List<KubernetesConfigMap> result = new ArrayList<>(); + try (KubernetesClient client = kubernetesClient()) { + client.configMaps().inNamespace(getNamespace()).list().getItems() + .forEach(secret -> result.add(new KubernetesConfigMap(secret.getMetadata().getName(), new HashMap<>(secret.getData())))); + } catch (Exception e) { + LOGGER.error(e); + } + return result; + } + + public void deleteConfigMap(String configMapName) { + ConfigMap configMap = getConfigMap(configMapName); + if (configMap != null) { + try (KubernetesClient client = kubernetesClient()) { + client.configMaps().inNamespace(getNamespace()).withName(configMapName).delete(); + } + } + } + + public void setConfigMapValue(String configMapName, String configMapKey, String value) { + ConfigMap configMap = getConfigMap(configMapName); + if (configMap != null) { + configMap.getData().put(configMapKey, value); + updateConfigMap(configMap); + } + } + + public void createConfigMap(String configMapName) { + ConfigMap configMap = getConfigMap(configMapName); + if (configMap == null) { + createConfigMap(configMapName, Map.of(), Map.of()); + } + } + + public void deleteConfigMapValue(String configMapName, String configMapKey) { + ConfigMap configMap = getConfigMap(configMapName); + if (configMap != null) { + configMap.getData().remove(configMapKey); + updateConfigMap(configMap); + } + } + + public String getCluster() { + try (KubernetesClient client = kubernetesClient()) { + return client.getMasterUrl().getHost(); + } + } + + public String getEnvironment() { + return environment; + } } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesStatusService.java b/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesStatusService.java index 35716a87..40cf3de9 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesStatusService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/kubernetes/KubernetesStatusService.java @@ -62,7 +62,7 @@ public class KubernetesStatusService implements HealthCheck { } @ConfigProperty(name = "karavan.environment", defaultValue = KaravanConstants.DEV) - public String environment; + private String environment; @ConfigProperty(name = "karavan.openshift") Optional<Boolean> isOpenShift; @@ -169,4 +169,8 @@ public class KubernetesStatusService implements HealthCheck { .addToLimits("memory", new Quantity(containerResources.get("limits.memory"))) .build(); } + + public String getEnvironment() { + return environment; + } } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/listener/KubernetesCommandListener.java b/karavan-app/src/main/java/org/apache/camel/karavan/listener/KubernetesCommandListener.java new file mode 100644 index 00000000..c47f91c6 --- /dev/null +++ b/karavan-app/src/main/java/org/apache/camel/karavan/listener/KubernetesCommandListener.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.camel.karavan.listener; + +import io.quarkus.vertx.ConsumeEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.camel.karavan.kubernetes.KubernetesStatusService; +import org.apache.camel.karavan.service.ConfigService; + +import static org.apache.camel.karavan.KaravanEvents.CMD_RESTART_INFORMERS; + +@ApplicationScoped +public class KubernetesCommandListener { + + @Inject + KubernetesStatusService kubernetesStatusService; + + @ConsumeEvent(value = CMD_RESTART_INFORMERS, blocking = true) + public void restartInformers(String dummy) { + if (ConfigService.inKubernetes()) { + kubernetesStatusService.startInformers(); + } + } +} \ No newline at end of file diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/KubernetesConfigMap.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/KubernetesConfigMap.java new file mode 100644 index 00000000..9929ddbc --- /dev/null +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/KubernetesConfigMap.java @@ -0,0 +1,33 @@ +package org.apache.camel.karavan.model; + +import java.util.Map; + +public class KubernetesConfigMap { + + private String name; + private Map<String, String> data; + + public KubernetesConfigMap() { + } + + public KubernetesConfigMap(String name, Map<String, String> data) { + this.name = name; + this.data = data; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map<String, String> getData() { + return data; + } + + public void setData(Map<String, String> data) { + this.data = data; + } +} diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/KubernetesSecret.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/KubernetesSecret.java new file mode 100644 index 00000000..9159d1b3 --- /dev/null +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/KubernetesSecret.java @@ -0,0 +1,33 @@ +package org.apache.camel.karavan.model; + +import java.util.Map; + +public class KubernetesSecret { + + private String name; + private Map<String, String> data; + + public KubernetesSecret() { + } + + public KubernetesSecret(String name, Map<String, String> data) { + this.name = name; + this.data = data; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map<String, String> getData() { + return data; + } + + public void setData(Map<String, String> data) { + this.data = data; + } +}