You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2022/04/27 22:00:45 UTC
[camel] branch main updated: CAMEL-18008. camel-jbang deploy/undeploy command for openshift (#7510)
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.git
The following commit(s) were added to refs/heads/main by this push:
new 3593a5da8f3 CAMEL-18008. camel-jbang deploy/undeploy command for openshift (#7510)
3593a5da8f3 is described below
commit 3593a5da8f394d1e766d99c03d1c92fde604f93b
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Wed Apr 27 18:00:35 2022 -0400
CAMEL-18008. camel-jbang deploy/undeploy command for openshift (#7510)
* Deploy to OpenShift (prototype)
Openshift deploy/updeploy
* Default jar files
---
dsl/camel-jbang/camel-jbang-core/pom.xml | 6 +
.../dsl/jbang/core/commands/CamelJBangMain.java | 2 +-
.../camel/dsl/jbang/core/commands/Deploy.java | 76 ++++++++--
.../camel/dsl/jbang/core/commands/Image.java | 168 ++++++++++++++++++++-
.../dsl/jbang/core/commands/KubernetesHelper.java | 96 +++++++++++-
.../camel/dsl/jbang/core/commands/Manifest.java | 115 ++++++++++++++
.../camel/dsl/jbang/core/commands/Resource.java | 85 -----------
.../camel/dsl/jbang/core/commands/Undeploy.java | 49 ++++--
8 files changed, 475 insertions(+), 122 deletions(-)
diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml b/dsl/camel-jbang/camel-jbang-core/pom.xml
index bf34270abab..be76c83cdf0 100644
--- a/dsl/camel-jbang/camel-jbang-core/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-core/pom.xml
@@ -100,6 +100,12 @@
<artifactId>kubernetes-client</artifactId>
<version>${kubernetes-client-version}</version>
</dependency>
+ <!-- OpenShift client -->
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>openshift-client</artifactId>
+ <version>${kubernetes-client-version}</version>
+ </dependency>
<!-- Docker generator -->
<dependency>
<groupId>com.google.cloud.tools</groupId>
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index 2370ada9b05..551917ccc79 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -36,7 +36,7 @@ public class CamelJBangMain implements Callable<Integer> {
.addSubcommand("generate", new CommandLine(new CodeGenerator())
.addSubcommand("rest", new CodeRestGenerator()))
.addSubcommand("build", new CommandLine(new Build())
- .addSubcommand("resources", new Resource())
+ .addSubcommand("manifests", new Manifest())
.addSubcommand("image", new Image()))
.addSubcommand("deploy", new CommandLine(new Deploy()))
.addSubcommand("undeploy", new CommandLine(new Undeploy()))
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Deploy.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Deploy.java
index 2df300e86ec..bae755ee9a0 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Deploy.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Deploy.java
@@ -19,9 +19,16 @@ package org.apache.camel.dsl.jbang.core.commands;
import java.util.concurrent.Callable;
import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.Status;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClientException;
+import io.fabric8.openshift.api.model.Route;
+import io.fabric8.openshift.client.DefaultOpenShiftClient;
+import io.fabric8.openshift.client.OpenShiftClient;
+import io.fabric8.openshift.client.OpenShiftConfig;
+import io.fabric8.openshift.client.OpenShiftConfigBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
@@ -37,8 +44,8 @@ public class Deploy implements Callable<Integer> {
private String namespace;
@CommandLine.Option(names = { "--name" }, description = "Application name", required = true)
private String name;
- @CommandLine.Option(names = { "--version" }, description = "Application version (label)", required = true)
- private String version;
+ @CommandLine.Option(names = { "--version" }, description = "Application version (label)")
+ private String version = "latest";
@CommandLine.Option(names = { "--image" }, description = "Deployment container image name", required = true)
private String image;
@CommandLine.Option(names = { "--container-port" }, description = "Container port", defaultValue = "8080")
@@ -51,25 +58,62 @@ public class Deploy implements Callable<Integer> {
private int replicas;
@CommandLine.Option(names = { "--minikube" }, description = "Target is minikube")
private boolean minikube;
+ @CommandLine.Option(names = { "--openshift" }, description = "Target is openshift")
+ private boolean openshift;
+ @CommandLine.Option(names = { "--server" }, description = "Master URL")
+ private String server;
+ @CommandLine.Option(names = { "--token" }, description = "Token")
+ private String token;
@Override
public Integer call() throws Exception {
- LOG.info("Generating Deployment...");
- Deployment deployment = KubernetesHelper.createDeployment(namespace, name, image, version, containerPort, replicas);
- LOG.info("Generating Service...");
- Service service
- = KubernetesHelper.createService(namespace, name, version, servicePort, containerPort, minikube, nodePort);
+ if (minikube) {
+ LOG.info("Generating Deployment...");
+ Deployment deployment = KubernetesHelper.createDeployment(namespace, name, image, version, containerPort, replicas);
+ LOG.info("Generating Service...");
+ Service service
+ = KubernetesHelper.createService(namespace, name, version, servicePort, containerPort, minikube, nodePort);
- try (KubernetesClient client = new DefaultKubernetesClient()) {
- LOG.info("Creating Deployment in " + (minikube ? "Minikube" : "Kubernetes"));
- client.apps().deployments().inNamespace(namespace).createOrReplace(deployment);
- client.services().inNamespace(namespace).delete(service);
- LOG.info("Creating Service in " + (minikube ? "Minikube" : "Kubernetes"));
- client.services().inNamespace(namespace).createOrReplace(service);
- } catch (Exception ex) {
- LOG.error("Error", ex.getMessage());
+ try (KubernetesClient client = new DefaultKubernetesClient()) {
+ LOG.info("Creating Deployment in " + (minikube ? "Minikube" : "Kubernetes"));
+ client.apps().deployments().inNamespace(namespace).createOrReplace(deployment);
+ client.services().inNamespace(namespace).delete(service);
+ LOG.info("Creating Service in " + (minikube ? "Minikube" : "Kubernetes"));
+ client.services().inNamespace(namespace).createOrReplace(service);
+ } catch (Exception ex) {
+ LOG.error("Error", ex.getMessage());
+ }
+ } else if (openshift) {
+ if (!image.startsWith("image-registry.openshift-image-registry.svc:5000") && image.split("/").length != 3) {
+ image = "image-registry.openshift-image-registry.svc:5000/" + image;
+ }
+ LOG.info("Generating Deployment...");
+ Deployment deployment = KubernetesHelper.createDeployment(namespace, name, image, version, containerPort, replicas);
+ LOG.info("Generating Service...");
+ Service service
+ = KubernetesHelper.createService(namespace, name, version, servicePort, containerPort, minikube, nodePort);
+ LOG.info("Generating Route...");
+ Route route = KubernetesHelper.createRoute(namespace, name, version, containerPort);
+
+ OpenShiftConfig config = new OpenShiftConfigBuilder().withMasterUrl(server).withOauthToken(token).build();
+ try (OpenShiftClient client = new DefaultOpenShiftClient(config)) {
+ LOG.info("Creating Deployment in Openshift");
+ client.apps().deployments().inNamespace(namespace).createOrReplace(deployment);
+ client.services().inNamespace(namespace).delete(service);
+ LOG.info("Creating Service in Openshift");
+ client.services().inNamespace(namespace).createOrReplace(service);
+ LOG.info("Creating Route in Openshift");
+ client.routes().inNamespace(namespace).createOrReplace(route);
+ } catch (KubernetesClientException ex) {
+ Status status = ex.getStatus();
+ if (status != null) {
+ LOG.error("Error: [%d %s] [%s] %s", status.getCode(), status.getStatus(), status.getReason(),
+ status.getMessage());
+ } else {
+ LOG.error(ex.getMessage());
+ }
+ }
}
return 0;
}
-
}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Image.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Image.java
index 4cf886bf55b..a56a0e1b796 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Image.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Image.java
@@ -16,10 +16,15 @@
*/
package org.apache.camel.dsl.jbang.core.commands;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import com.google.cloud.tools.jib.api.Containerizer;
@@ -28,7 +33,16 @@ import com.google.cloud.tools.jib.api.InvalidImageReferenceException;
import com.google.cloud.tools.jib.api.Jib;
import com.google.cloud.tools.jib.api.LogEvent;
import com.google.cloud.tools.jib.api.RegistryImage;
+import com.google.cloud.tools.jib.api.TarImage;
import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath;
+import io.fabric8.kubernetes.client.dsl.LogWatch;
+import io.fabric8.openshift.api.model.Build;
+import io.fabric8.openshift.api.model.BuildConfig;
+import io.fabric8.openshift.api.model.ImageStream;
+import io.fabric8.openshift.client.DefaultOpenShiftClient;
+import io.fabric8.openshift.client.OpenShiftClient;
+import io.fabric8.openshift.client.OpenShiftConfig;
+import io.fabric8.openshift.client.OpenShiftConfigBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
@@ -37,12 +51,13 @@ import picocli.CommandLine;
public class Image implements Callable<Integer> {
private static final Logger LOG = LoggerFactory.getLogger(Image.class);
+ private static final int LOG_TAIL_SIZE = 10;
@CommandLine.Option(names = { "-h", "--help" }, usageHelp = true, description = "Display the help and sub-commands")
private boolean helpRequested;
@CommandLine.Option(names = { "-f", "--from" }, description = "Base Image", defaultValue = "gcr.io/distroless/java:11")
private String from;
- @CommandLine.Option(names = { "-j", "--jar" }, required = true, description = "Jar filename")
+ @CommandLine.Option(names = { "-j", "--jar" }, description = "Jar file", defaultValue = "target/camel-runner.jar")
private String jar;
@CommandLine.Option(names = { "-t", "--tag" }, description = "Image tag")
private String tag;
@@ -54,15 +69,93 @@ public class Image implements Callable<Integer> {
private String username;
@CommandLine.Option(names = { "-p", "--password" }, description = "Registry password")
private String password;
+ @CommandLine.Option(names = { "--tar" }, description = "Create tar")
+ private boolean tar;
+ @CommandLine.Option(names = { "--tar-name" }, description = "Tar filename")
+ private String tarname;
+ @CommandLine.Option(names = { "--openshift" }, description = "Target is openshift")
+ private boolean openshift;
+ @CommandLine.Option(names = { "--server" }, description = "Master URL")
+ private String server;
+ @CommandLine.Option(names = { "--token" }, description = "Token")
+ private String token;
+ @CommandLine.Option(names = { "--namespace" }, description = "Namespace", defaultValue = "default")
+ private String namespace;
+ @CommandLine.Option(names = { "--name" }, description = "Application name", required = true)
+ private String name;
+ @CommandLine.Option(names = { "--version" }, description = "Application version (label)", required = true)
+ private String version;
+ @CommandLine.Option(names = { "--source-image" }, description = "Source image name (for OpenShift buildConfig)",
+ defaultValue = "java:openjdk-11-ubi8")
+ private String sourceImage;
@Override
public Integer call() throws Exception {
File jarFile = Paths.get(jar).toFile();
- Jib.from(from)
- .addLayer(Arrays.asList(Paths.get(jar)), "/deployments/")
- .setWorkingDirectory(AbsoluteUnixPath.get("/deployments"))
- .setEntrypoint("java", "-jar", jarFile.getName())
- .containerize(push ? getRegistry() : getDockerImage());
+ if (openshift) {
+ LOG.info("Generating resources...");
+ OpenShiftConfig config
+ = new OpenShiftConfigBuilder().withMasterUrl(server).withOauthToken(token).withNamespace(namespace).build();
+ try (OpenShiftClient client = new DefaultOpenShiftClient(config)) {
+ ImageStream imageStream = KubernetesHelper.createImageStream(namespace, name, version);
+ BuildConfig buildConfig
+ = KubernetesHelper.createBuildConfig(namespace, name, version, jarFile.getName(), sourceImage);
+ LOG.info("Creating ImageStream...");
+ client.imageStreams().createOrReplace(imageStream);
+ LOG.info("Creating BuildConfig...");
+ client.buildConfigs().createOrReplace(buildConfig);
+ LOG.info("Creating Build...");
+ Build build = client.buildConfigs()
+ .inNamespace(namespace)
+ .withName(buildConfig.getMetadata().getName())
+ .instantiateBinary()
+ .asFile(jarFile.getName())
+ .withTimeout(5, TimeUnit.MINUTES)
+ .fromFile(jarFile);
+ while (isNew(build) || isPending(build) || isRunning(build)) {
+ final String buildName = build.getMetadata().getName();
+ Build updated = client.builds().withName(buildName).get();
+ if (updated == null) {
+ throw new IllegalStateException("Build:" + build.getMetadata().getName() + " is no longer present!");
+ } else if (updated.getStatus() == null) {
+ throw new IllegalStateException("Build:" + build.getMetadata().getName() + " has no status!");
+ } else if (isNew(updated) || isPending(updated) || isRunning(updated)) {
+ build = updated;
+ try (LogWatch w
+ = client.builds().withName(buildName).withPrettyOutput().watchLog();
+ Reader reader = new InputStreamReader(w.getOutput())) {
+ display(reader);
+ } catch (IOException e) {
+ // This may happen if the LogWatch is closed while we are still reading.
+ // We shouldn't let the build fail, so let's log a warning and display last few lines of the log
+ LOG.warn("Log stream closed, redisplaying last " + LOG_TAIL_SIZE + " entries:");
+ try {
+ display(client.builds().withName(buildName).tailingLines(LOG_TAIL_SIZE)
+ .getLogReader());
+ } catch (IOException ex) {
+ // Let's ignore this.
+ }
+ }
+ } else if (isComplete(updated)) {
+ break;
+ } else if (isCancelled(updated)) {
+ throw new IllegalStateException("Build:" + buildName + " cancelled!");
+ } else if (isFailed(updated)) {
+ throw new IllegalStateException(
+ "Build:" + buildName + " failed! " + updated.getStatus().getMessage());
+ } else if (isError(updated)) {
+ throw new IllegalStateException(
+ "Build:" + buildName + " encountered error! " + updated.getStatus().getMessage());
+ }
+ }
+ }
+ } else {
+ Jib.from(from)
+ .addLayer(Arrays.asList(Paths.get(jar)), "/deployments/")
+ .setWorkingDirectory(AbsoluteUnixPath.get("/deployments"))
+ .setEntrypoint("java", "-jar", jarFile.getName())
+ .containerize(push ? getRegistry() : (tar ? getTarImage() : getDockerImage()));
+ }
return 0;
}
@@ -70,9 +163,18 @@ public class Image implements Callable<Integer> {
return Containerizer.to(DockerDaemonImage.named(tag)).addEventHandler(LogEvent.class, getEventConsumer());
}
+ private Containerizer getTarImage() throws InvalidImageReferenceException {
+ if (tarname == null) {
+ String filename = jar.contains(File.separator) ? jar.substring(jar.lastIndexOf(File.separator) + 1) : jar;
+ tarname = filename.substring(0, filename.lastIndexOf('.')) + ".tar";
+ }
+ return Containerizer.to(TarImage.at(Paths.get(tarname)).named(tag)).addEventHandler(LogEvent.class, getEventConsumer());
+ }
+
private Containerizer getRegistry() throws InvalidImageReferenceException {
return Containerizer.to(
RegistryImage.named(registry).addCredential(username, password))
+ .withAdditionalTag(tag)
.addEventHandler(LogEvent.class, getEventConsumer());
}
@@ -94,4 +196,58 @@ public class Image implements Callable<Integer> {
}
};
}
+
+ private static void display(Reader logReader) throws IOException {
+ BufferedReader reader = new BufferedReader(logReader);
+ for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+ LOG.info(line);
+ }
+ }
+
+ static boolean isNew(Build build) {
+ return build != null && build.getStatus() != null
+ && BuildStatus.New.name().equalsIgnoreCase(build.getStatus().getPhase());
+ }
+
+ static boolean isPending(Build build) {
+ return build != null && build.getStatus() != null
+ && BuildStatus.Pending.name().equalsIgnoreCase(build.getStatus().getPhase());
+ }
+
+ static boolean isRunning(Build build) {
+ return build != null && build.getStatus() != null
+ && BuildStatus.Running.name().equalsIgnoreCase(build.getStatus().getPhase());
+ }
+
+ static boolean isComplete(Build build) {
+ return build != null && build.getStatus() != null
+ && BuildStatus.Complete.name().equalsIgnoreCase(build.getStatus().getPhase());
+ }
+
+ static boolean isFailed(Build build) {
+ return build != null && build.getStatus() != null
+ && BuildStatus.Failed.name().equalsIgnoreCase(build.getStatus().getPhase());
+ }
+
+ static boolean isError(Build build) {
+ return build != null && build.getStatus() != null
+ && BuildStatus.Error.name().equalsIgnoreCase(build.getStatus().getPhase());
+ }
+
+ static boolean isCancelled(Build build) {
+ return build != null && build.getStatus() != null
+ && BuildStatus.Cancelled.name().equalsIgnoreCase(build.getStatus().getPhase());
+ }
+
+ public enum BuildStatus {
+
+ New,
+ Pending,
+ Running,
+ Complete,
+ Failed,
+ Error,
+ Cancelled;
+
+ }
}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/KubernetesHelper.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/KubernetesHelper.java
index 718c6e6dc5d..d79c2e870d4 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/KubernetesHelper.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/KubernetesHelper.java
@@ -27,12 +27,85 @@ import io.fabric8.kubernetes.api.model.ServicePortBuilder;
import io.fabric8.kubernetes.api.model.ServiceSpecBuilder;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
+import io.fabric8.openshift.api.model.BinaryBuildSource;
+import io.fabric8.openshift.api.model.BuildConfig;
+import io.fabric8.openshift.api.model.BuildConfigBuilder;
+import io.fabric8.openshift.api.model.ImageStream;
+import io.fabric8.openshift.api.model.ImageStreamBuilder;
+import io.fabric8.openshift.api.model.Route;
+import io.fabric8.openshift.api.model.RouteBuilder;
+import io.fabric8.openshift.api.model.RoutePortBuilder;
public final class KubernetesHelper {
private KubernetesHelper() {
}
+ public static BuildConfig createBuildConfig(
+ String namespace, String name, String version, String filename, String sourceImage) {
+
+ ObjectMetaBuilder metadata = new ObjectMetaBuilder()
+ .withName(name)
+ .withAnnotations(Map.of("jarFileName", filename))
+ .withLabels(getLabels(name, version));
+ if (namespace != null) {
+ metadata.withNamespace(namespace);
+ }
+
+ return new BuildConfigBuilder()
+ .withMetadata(metadata.build())
+ .withNewSpec()
+ .withNewSource().withType("Binary").withBinary(new BinaryBuildSource(filename)).endSource()
+ .withNewOutput()
+ .withNewTo().withKind("ImageStreamTag").withName(name + ":" + version).endTo()
+ .endOutput()
+ .withNewStrategy().withType("Source")
+ .withNewSourceStrategy().withNewFrom().withKind("ImageStreamTag").withNamespace("openshift")
+ .withName(sourceImage).endFrom()
+ .endSourceStrategy()
+ .endStrategy()
+ .withNewSource().withType("Binary")
+ .endSource()
+ .endSpec()
+ .build();
+ }
+
+ public static ImageStream createImageStream(String namespace, String name, String version) {
+
+ ObjectMetaBuilder metadata = new ObjectMetaBuilder()
+ .withName(name)
+ .withLabels(getLabels(name, version));
+ if (namespace != null) {
+ metadata.withNamespace(namespace);
+ }
+
+ return new ImageStreamBuilder()
+ .withMetadata(metadata.build())
+ .withNewSpec()
+ .withNewLookupPolicy(false)
+ .endSpec()
+ .build();
+ }
+
+ public static Route createRoute(String namespace, String name, String version, int targetPort) {
+
+ ObjectMetaBuilder metadata = new ObjectMetaBuilder()
+ .withName(name)
+ .withLabels(getLabels(name, version));
+ if (namespace != null) {
+ metadata.withNamespace(namespace);
+ }
+
+ return new RouteBuilder()
+ .withMetadata(metadata.build())
+ .withNewSpec()
+ .withPort(new RoutePortBuilder().withNewTargetPort(targetPort).build())
+ .withNewTo().withKind("Service").withName(name)
+ .endTo()
+ .endSpec()
+ .build();
+ }
+
public static Service createService(
String namespace, String name, String version, int port, int targetPort, boolean minikube, int nodePort) {
@@ -52,6 +125,7 @@ public final class KubernetesHelper {
}
ServiceSpecBuilder spec = new ServiceSpecBuilder()
+ .withSelector(getSelector(name, version))
.withPorts(servicePort.build());
if (minikube) {
spec.withType("NodePort");
@@ -66,6 +140,10 @@ public final class KubernetesHelper {
public static Deployment createDeployment(
String namespace, String name, String image, String version, int containerPort, int replica) {
+ if (image == null) {
+ image = namespace + "/" + name + ":" + version;
+ }
+
EnvVar envVar = new EnvVarBuilder()
.withName("KUBERNETES_NAMESPACE")
.withNewValueFrom()
@@ -87,7 +165,7 @@ public final class KubernetesHelper {
.withNewSpec()
.withReplicas(replica)
.withNewSelector()
- .addToMatchLabels(getLabels(name, version))
+ .addToMatchLabels(getMatchLabels(name))
.endSelector()
.withNewTemplate()
.withNewMetadata()
@@ -118,8 +196,22 @@ public final class KubernetesHelper {
return Map.of(
"app", name,
"app.kubernetes.io/name", name,
+ "app.kubernetes.io/component", name,
+ "app.kubernetes.io/instance", name,
"app.kubernetes.io/version", version,
"app.kubernetes.io/part-of", name,
- "runtime", "camel");
+ "app.openshift.io/runtime", "camel",
+ "app.kubernetes.io/runtime", "camel");
+ }
+
+ public static Map<String, String> getMatchLabels(String name) {
+ return Map.of(
+ "app", name);
+ }
+
+ public static Map<String, String> getSelector(String name, String version) {
+ return Map.of(
+ "app.kubernetes.io/name", name,
+ "app.kubernetes.io/version", version);
}
}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Manifest.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Manifest.java
new file mode 100644
index 00000000000..a95220749a1
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Manifest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.dsl.jbang.core.commands;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.Callable;
+
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.client.utils.Serialization;
+import io.fabric8.openshift.api.model.BuildConfig;
+import io.fabric8.openshift.api.model.ImageStream;
+import io.fabric8.openshift.api.model.Route;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "manifests", description = "Create Kubernetes resources")
+public class Manifest implements Callable<Integer> {
+ private static final Logger LOG = LoggerFactory.getLogger(Manifest.class);
+
+ @CommandLine.Option(names = { "-h", "--help" }, usageHelp = true, description = "Display the help and sub-commands")
+ private boolean helpRequested;
+ @CommandLine.Option(names = { "--path" }, description = "Output folder path", defaultValue = "manifests")
+ private String path;
+ @CommandLine.Option(names = { "--namespace" }, description = "Namespace")
+ private String namespace;
+ @CommandLine.Option(names = { "--name" }, description = "Application name", required = true)
+ private String name;
+ @CommandLine.Option(names = { "--version" }, description = "Application version (label)", required = true)
+ private String version;
+ @CommandLine.Option(names = { "--image" }, description = "Deployment container image name", required = true)
+ private String image;
+ @CommandLine.Option(names = { "--source-image" }, description = "Source image name (for OpenShift buildConfig)",
+ defaultValue = "java:openjdk-11-ubi8")
+ private String sourceImage;
+ @CommandLine.Option(names = { "--container-port" }, description = "Container port", defaultValue = "8080")
+ private int containerPort;
+ @CommandLine.Option(names = { "--service-port" }, description = "Service port", defaultValue = "80")
+ private int servicePort;
+ @CommandLine.Option(names = { "--node-port" }, description = "Node port (minikube)", defaultValue = "30777")
+ private int nodePort;
+ @CommandLine.Option(names = { "--replicas" }, description = "Number of replicas of the application", defaultValue = "1")
+ private int replicas;
+ @CommandLine.Option(names = { "--minikube" }, description = "Target is minikube")
+ private boolean minikube;
+ @CommandLine.Option(names = { "--openshift" }, description = "Target is openshift")
+ private boolean openshift;
+ @CommandLine.Option(names = { "-j", "--jar" }, description = "Jar file", defaultValue = "target/camel-runner.jar")
+ private String jar;
+
+ @Override
+ public Integer call() throws Exception {
+ try {
+ LOG.info("Generating resources...");
+ if (minikube) {
+ Deployment deployment
+ = KubernetesHelper.createDeployment(namespace, name, image, version, containerPort, replicas);
+ Service service = KubernetesHelper.createService(namespace, name, version, servicePort, containerPort, minikube,
+ nodePort);
+ write(deployment, "deployment.yaml");
+ write(service, "service.yaml");
+ } else if (openshift) {
+ Deployment deployment
+ = KubernetesHelper.createDeployment(namespace, name, image, version, containerPort, replicas);
+ Service service = KubernetesHelper.createService(namespace, name, version, servicePort, containerPort, minikube,
+ nodePort);
+ Route route = KubernetesHelper.createRoute(namespace, name, version, containerPort);
+ ImageStream imageStream = KubernetesHelper.createImageStream(namespace, name, version);
+ File jarFile = Paths.get(jar).toFile();
+ BuildConfig buildConfig
+ = KubernetesHelper.createBuildConfig(namespace, name, version, jarFile.getName(), sourceImage);
+ write(deployment, "deployment.yaml");
+ write(service, "service.yaml");
+ write(route, "route.yaml");
+ write(imageStream, "image-stream.yaml");
+ write(buildConfig, "build-config.yaml");
+ }
+ } catch (Exception ex) {
+ LOG.error("Error", ex.getMessage());
+ }
+ return 0;
+ }
+
+ private void write(Object object, String filename) throws IOException {
+ Path output = Paths.get(path != null ? path : System.getProperty("user.dir"));
+ if (!Files.exists(output)) {
+ LOG.info("Creating output folder " + output);
+ Files.createDirectories(output);
+ }
+ LOG.info("Writing {}...", filename);
+ Files.write(Paths.get(output.toString(), filename),
+ Serialization.asYaml(object).getBytes(StandardCharsets.UTF_8));
+ }
+
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Resource.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Resource.java
deleted file mode 100644
index 2903d4b7b14..00000000000
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Resource.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.dsl.jbang.core.commands;
-
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.concurrent.Callable;
-
-import io.fabric8.kubernetes.api.model.Service;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.client.utils.Serialization;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import picocli.CommandLine;
-
-@CommandLine.Command(name = "resources", description = "Create Kubernetes resources")
-public class Resource implements Callable<Integer> {
- private static final Logger LOG = LoggerFactory.getLogger(Resource.class);
-
- @CommandLine.Option(names = { "-h", "--help" }, usageHelp = true, description = "Display the help and sub-commands")
- private boolean helpRequested;
- @CommandLine.Option(names = { "--path" }, description = "Output folder path")
- private String path;
- @CommandLine.Option(names = { "--namespace" }, description = "Namespace")
- private String namespace;
- @CommandLine.Option(names = { "--name" }, description = "Application name", required = true)
- private String name;
- @CommandLine.Option(names = { "--version" }, description = "Application version (label)", required = true)
- private String version;
- @CommandLine.Option(names = { "--image" }, description = "Deployment container image name", required = true)
- private String image;
- @CommandLine.Option(names = { "--container-port" }, description = "Container port", defaultValue = "8080")
- private int containerPort;
- @CommandLine.Option(names = { "--service-port" }, description = "Service port", defaultValue = "80")
- private int servicePort;
- @CommandLine.Option(names = { "--node-port" }, description = "Node port (minikube)", defaultValue = "30777")
- private int nodePort;
- @CommandLine.Option(names = { "--replicas" }, description = "Number of replicas of the application", defaultValue = "1")
- private int replicas;
- @CommandLine.Option(names = { "--minikube" }, description = "Target is minikube")
- private boolean minikube;
-
- @Override
- public Integer call() throws Exception {
- try {
- LOG.info("Generating Deployment...");
- Deployment deployment = KubernetesHelper.createDeployment(namespace, name, image, version, containerPort, replicas);
- LOG.info("Generating Service...");
- Service service
- = KubernetesHelper.createService(namespace, name, version, servicePort, containerPort, minikube, nodePort);
- Path output = Paths.get(path != null ? path : System.getProperty("user.dir"));
- if (!Files.exists(output)) {
- LOG.info("Creating output folder " + output);
- Files.createDirectories(output);
- }
- LOG.info("writing deployment.yaml...");
- Files.write(Paths.get(output.toString(), "deployment.yaml"),
- Serialization.asYaml(deployment).getBytes(StandardCharsets.UTF_8));
- LOG.info("writing service.yaml...");
- Files.write(Paths.get(output.toString(), "service.yaml"),
- Serialization.asYaml(service).getBytes(StandardCharsets.UTF_8));
- } catch (Exception ex) {
- LOG.error("Error", ex.getMessage());
- }
-
- return 0;
- }
-
-}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Undeploy.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Undeploy.java
index e0c46289f8e..838b9b8aefb 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Undeploy.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Undeploy.java
@@ -16,12 +16,15 @@
*/
package org.apache.camel.dsl.jbang.core.commands;
+import java.util.Map;
import java.util.concurrent.Callable;
-import io.fabric8.kubernetes.api.model.Service;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.openshift.client.DefaultOpenShiftClient;
+import io.fabric8.openshift.client.OpenShiftClient;
+import io.fabric8.openshift.client.OpenShiftConfig;
+import io.fabric8.openshift.client.OpenShiftConfigBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
@@ -36,19 +39,41 @@ public class Undeploy implements Callable<Integer> {
private String namespace;
@CommandLine.Option(names = { "--name" }, description = "Application name", required = true)
private String name;
+ @CommandLine.Option(names = { "--version" }, description = "Application version", required = true)
+ private String version;
+ @CommandLine.Option(names = { "--openshift" }, description = "Target is openshift")
+ private boolean openshift;
+ @CommandLine.Option(names = { "--server" }, description = "Master URL")
+ private String server;
+ @CommandLine.Option(names = { "--token" }, description = "Token")
+ private String token;
@Override
public Integer call() throws Exception {
- Deployment deployment = KubernetesHelper.createDeployment(namespace, name, "", "", 0, 0);
- Service service = KubernetesHelper.createService(namespace, name, "", 0, 0, false, 0);
-
- try (KubernetesClient client = new DefaultKubernetesClient()) {
- LOG.info("Deleting Service...");
- client.services().inNamespace(namespace).delete(service);
- LOG.info("Deleting Deployment...");
- client.apps().deployments().inNamespace(namespace).delete(deployment);
- } catch (Exception ex) {
- LOG.error("Error", ex.getMessage());
+ Map labels = KubernetesHelper.getLabels(name, version);
+ if (openshift) {
+ OpenShiftConfig config = new OpenShiftConfigBuilder().withMasterUrl(server).withOauthToken(token).build();
+ try (OpenShiftClient client = new DefaultOpenShiftClient(config)) {
+ LOG.info("Deleting Routes...");
+ client.routes().inNamespace(namespace).withLabels(labels).delete();
+ LOG.info("Deleting Service...");
+ client.services().inNamespace(namespace).withLabels(labels).delete();
+ LOG.info("Deleting Deployment...");
+ client.apps().deployments().inNamespace(namespace).withLabels(labels).delete();
+ LOG.info("Deleting ImageStream...");
+ client.imageStreams().inNamespace(namespace).withLabels(labels).delete();
+ LOG.info("Deleting BuildConfig...");
+ client.buildConfigs().inNamespace(namespace).withLabels(labels).delete();
+ }
+ } else {
+ try (KubernetesClient client = new DefaultKubernetesClient()) {
+ LOG.info("Deleting Service...");
+ client.services().inNamespace(namespace).withLabels(labels).delete();
+ LOG.info("Deleting Deployment...");
+ client.apps().deployments().inNamespace(namespace).withLabels(labels).delete();
+ } catch (Exception ex) {
+ LOG.error("Error", ex.getMessage());
+ }
}
return 0;
}