This is an automated email from the ASF dual-hosted git repository.

davsclaus 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 e5134e1fc7d [CAMEL-21068] Add support for `camel kubernetes run` for 
all runtimes (#15514)
e5134e1fc7d is described below

commit e5134e1fc7d0296c671e54593dc417a44f2e3bc2
Author: Thomas Diesler <[email protected]>
AuthorDate: Wed Sep 11 13:45:45 2024 +0200

    [CAMEL-21068] Add support for `camel kubernetes run` for all runtimes 
(#15514)
---
 .../dsl/jbang/core/commands/ExportCamelMain.java   |  11 +-
 .../dsl/jbang/core/commands/ExportSpringBoot.java  |   9 +-
 .../commands/kubernetes/KubernetesBaseCommand.java |   4 -
 .../core/commands/kubernetes/KubernetesRun.java    | 127 ++++++++++-----------
 .../commands/kubernetes/KubernetesRunTest.java     |  72 ++++++++----
 5 files changed, 125 insertions(+), 98 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportCamelMain.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportCamelMain.java
index 1ecac712bba..d47a5b35c1f 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportCamelMain.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportCamelMain.java
@@ -28,10 +28,12 @@ import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.catalog.DefaultCamelCatalog;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import org.apache.camel.dsl.jbang.core.common.RuntimeUtil;
+import org.apache.camel.dsl.jbang.core.common.VersionHelper;
 import org.apache.camel.tooling.maven.MavenGav;
 import org.apache.camel.util.CamelCaseOrderedProperties;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
 import org.apache.commons.io.FileUtils;
 
 class ExportCamelMain extends Export {
@@ -165,9 +167,12 @@ class ExportCamelMain extends Export {
         String context = IOHelper.loadText(is);
         IOHelper.close(is);
 
-        if (camelVersion == null) {
-            CamelCatalog catalog = new DefaultCamelCatalog();
-            camelVersion = catalog.getCatalogVersion();
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        if (ObjectHelper.isEmpty(camelVersion)) {
+            camelVersion = catalog.getLoadedVersion();
+        }
+        if (ObjectHelper.isEmpty(camelVersion)) {
+            camelVersion = VersionHelper.extractCamelVersion();
         }
 
         context = context.replaceAll("\\{\\{ \\.GroupId }}", ids[0]);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java
index 68a2af582ba..07cbe64ff9d 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportSpringBoot.java
@@ -36,6 +36,7 @@ import org.apache.camel.tooling.model.ArtifactModel;
 import org.apache.camel.util.CamelCaseOrderedProperties;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
 import org.apache.commons.io.FileUtils;
 
 class ExportSpringBoot extends Export {
@@ -158,6 +159,12 @@ class ExportSpringBoot extends Export {
         String repos = getMavenRepositories(settings, prop, 
camelSpringBootVersion);
 
         CamelCatalog catalog = CatalogLoader.loadSpringBootCatalog(repos, 
camelSpringBootVersion);
+        if (ObjectHelper.isEmpty(camelVersion)) {
+            camelVersion = catalog.getLoadedVersion();
+        }
+        if (ObjectHelper.isEmpty(camelVersion)) {
+            camelVersion = VersionHelper.extractCamelVersion();
+        }
 
         // First try to load a specialized template from the catalog, if the 
catalog does not provide it
         // fallback to the template defined in camel-jbang-core
@@ -169,8 +176,6 @@ class ExportSpringBoot extends Export {
             context = readResourceTemplate("templates/" + pomTemplateName);
         }
 
-        String camelVersion = catalog.getLoadedVersion();
-
         context = context.replaceAll("\\{\\{ \\.GroupId }}", ids[0]);
         context = context.replaceAll("\\{\\{ \\.ArtifactId }}", ids[1]);
         context = context.replaceAll("\\{\\{ \\.Version }}", ids[2]);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesBaseCommand.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesBaseCommand.java
index e51633dd5b5..2a3d8ebbe07 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesBaseCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesBaseCommand.java
@@ -83,8 +83,6 @@ public abstract class KubernetesBaseCommand extends 
CamelCommand {
     /**
      * Gets Kubernetes client. In case custom kubeConfig option is set 
initializes the client with the config otherwise
      * uses default client.
-     *
-     * @return
      */
     protected KubernetesClient client() {
         if (kubernetesClient == null) {
@@ -100,8 +98,6 @@ public abstract class KubernetesBaseCommand extends 
CamelCommand {
 
     /**
      * Sets the Kubernetes client.
-     *
-     * @param kubernetesClient
      */
     public KubernetesBaseCommand withClient(KubernetesClient kubernetesClient) 
{
         this.kubernetesClient = kubernetesClient;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
index b1dd1092c71..f61c65d7bc3 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
@@ -138,15 +138,13 @@ public class KubernetesRun extends KubernetesBaseCommand {
                         description = "The target cluster type. Special 
configurations may be applied to different cluster types such as Kind or 
Minikube.")
     String clusterType;
 
-    @CommandLine.Option(names = { "--image-build" },
-                        defaultValue = "true",
-                        description = "Weather to build container image as 
part of the run.")
+    @CommandLine.Option(names = { "--image-build" }, defaultValue = "true",
+                        description = "Whether to build container image as 
part of the run.")
     boolean imageBuild = true;
 
-    @CommandLine.Option(names = { "--image-push" },
-                        defaultValue = "true",
-                        description = "Weather to push image to given image 
registry as part of the run.")
-    boolean imagePush = true;
+    @CommandLine.Option(names = { "--image-push" }, defaultValue = "false",
+                        description = "Whether to push image to given image 
registry as part of the run.")
+    boolean imagePush = false;
 
     @CommandLine.Option(names = { "--image-platforms" },
                         description = "List of target platforms. Each platform 
is defined using the pattern.")
@@ -242,6 +240,11 @@ public class KubernetesRun extends KubernetesBaseCommand {
         super(main);
     }
 
+    public KubernetesRun(CamelJBangMain main, String[] files) {
+        super(main);
+        filePaths = files;
+    }
+
     public Integer doCall() throws Exception {
         String projectName = getProjectName();
 
@@ -266,11 +269,8 @@ public class KubernetesRun extends KubernetesBaseCommand {
         }
 
         if (output != null) {
-            if (RuntimeType.quarkus == runtime) {
-                exit = buildQuarkus(workingDir);
-            } else if (RuntimeType.springBoot == runtime) {
-                exit = buildSpringBoot(workingDir);
-            }
+
+            exit = buildProject(workingDir);
 
             if (exit != 0) {
                 printer().println("Project build failed!");
@@ -295,11 +295,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
             return 0;
         }
 
-        if (RuntimeType.quarkus == runtime) {
-            exit = deployQuarkus(workingDir);
-        } else if (RuntimeType.springBoot == runtime) {
-            exit = deploySpringBoot(workingDir);
-        }
+        exit = deployProject(workingDir);
 
         if (exit != 0) {
             printer().println("Deployment to %s 
failed!".formatted(Optional.ofNullable(clusterType)
@@ -309,7 +305,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
 
         if (dev) {
             DefaultCamelContext reloadContext = new DefaultCamelContext(false);
-            configureFileWatch(reloadContext, workingDir);
+            configureFileWatch(reloadContext, export, workingDir);
             reloadContext.start();
 
             if (cleanup) {
@@ -318,6 +314,14 @@ public class KubernetesRun extends KubernetesBaseCommand {
         }
 
         if (dev || wait || logs) {
+
+            String kubectlCmd = "kubectl get pod";
+            if (namespace != null) {
+                kubectlCmd += " -n %s".formatted(namespace);
+            }
+            kubectlCmd += " -l %s=%s".formatted(BaseTrait.INTEGRATION_LABEL, 
projectName);
+            printer().println(kubectlCmd);
+
             client(Pod.class).withLabel(BaseTrait.INTEGRATION_LABEL, 
projectName)
                     .waitUntilCondition(it -> 
"Running".equals(it.getStatus().getPhase()), 10, TimeUnit.MINUTES);
         }
@@ -407,10 +411,10 @@ public class KubernetesRun extends KubernetesBaseCommand {
         Runtime.getRuntime().addShutdownHook(task);
     }
 
-    private Integer buildQuarkus(String workingDir) throws IOException, 
InterruptedException {
-        printer().println("Building Quarkus application ...");
+    private Integer buildProject(String workingDir) throws IOException, 
InterruptedException {
+        printer().println("Building Camel application ...");
 
-        // Run Quarkus build via Maven
+        // Run build via Maven
         String mvnw = "/mvnw";
         if (FileUtil.isWindows()) {
             mvnw = "/mvnw.cmd";
@@ -437,11 +441,11 @@ public class KubernetesRun extends KubernetesBaseCommand {
         return 0;
     }
 
-    private Integer deployQuarkus(String workingDir) throws IOException, 
InterruptedException {
+    private Integer deployProject(String workingDir) throws IOException, 
InterruptedException {
         printer().println("Deploying to %s 
...".formatted(Optional.ofNullable(clusterType)
                 .map(StringHelper::capitalize).orElse("Kubernetes")));
 
-        // Run Quarkus build via Maven
+        // Run build via Maven
         String mvnw = "/mvnw";
         if (FileUtil.isWindows()) {
             mvnw = "/mvnw.cmd";
@@ -454,29 +458,46 @@ public class KubernetesRun extends KubernetesBaseCommand {
         args.add("--file");
         args.add(workingDir);
 
-        if (imagePlatforms != null) {
-            args.add("-Dquarkus.jib.platforms=%s".formatted(imagePlatforms));
-        }
+        if (runtime == RuntimeType.quarkus) {
 
-        if (imageBuild) {
-            args.add("-Dquarkus.container-image.build=true");
-        }
+            if (imagePlatforms != null) {
+                
args.add("-Dquarkus.jib.platforms=%s".formatted(imagePlatforms));
+            }
 
-        if (imagePush) {
-            args.add("-Dquarkus.container-image.push=true");
-        }
+            if (imageBuild) {
+                args.add("-Dquarkus.container-image.build=true");
+            }
+
+            if (imagePush) {
+                args.add("-Dquarkus.container-image.push=true");
+            }
+
+            if (ClusterType.OPENSHIFT.isEqualTo(clusterType)) {
+                args.add("-Dquarkus.openshift.deploy=true");
+            } else {
+                args.add("-Dquarkus.kubernetes.deploy=true");
+            }
+
+            args.add("package");
 
-        if (ClusterType.OPENSHIFT.isEqualTo(clusterType)) {
-            args.add("-Dquarkus.openshift.deploy=true");
         } else {
-            args.add("-Dquarkus.kubernetes.deploy=true");
-        }
 
-        if (namespace != null) {
-            args.add("-Dquarkus.kubernetes.namespace=%s".formatted(namespace));
+            if (!imageBuild) {
+                args.add("-Djkube.skip.build=true");
+            }
+
+            if (imagePush) {
+                args.add("-Djkube.%s.push=true".formatted(imageBuilder));
+            }
+
+            if (namespace != null) {
+                args.add("-Djkube.namespace=%s".formatted(namespace));
+            }
+
+            args.add("package");
+            args.add("k8s:deploy");
         }
 
-        args.add("package");
         pb.command(args.toArray(String[]::new));
 
         pb.inheritIO(); // run in foreground (with IO so logs are visible)
@@ -491,22 +512,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
         return 0;
     }
 
-    private Integer buildSpringBoot(String workingDir) {
-        printer().println("Building Spring Boot application ...");
-
-        // TODO: implement SpringBoot project build
-        return 0;
-    }
-
-    private Integer deploySpringBoot(String workingDir) {
-        printer().println("Deploying to %s 
...".formatted(Optional.ofNullable(clusterType)
-                .map(StringHelper::capitalize).orElse("Kubernetes")));
-
-        // TODO: implement SpringBoot Kubernetes deployment
-        return 0;
-    }
-
-    private void configureFileWatch(DefaultCamelContext camelContext, String 
workingDir)
+    private void configureFileWatch(DefaultCamelContext camelContext, 
KubernetesExport export, String workingDir)
             throws Exception {
         String watchDir = ".";
         FileFilter filter = null;
@@ -525,17 +531,9 @@ public class KubernetesRun extends KubernetesBaseCommand {
                 = new FileWatcherResourceReloadStrategy(watchDir);
         reloadStrategy.setResourceReload((name, resource) -> {
             printer().printf("Reloading project due to file change: %s%n", 
FileUtil.stripPath(name));
-            KubernetesExport export = configureExport(workingDir);
             int refresh = export.export();
             if (refresh == 0) {
-                if (RuntimeType.quarkus == runtime) {
-                    deployQuarkus(workingDir);
-                } else if (RuntimeType.springBoot == runtime) {
-                    deploySpringBoot(workingDir);
-                }
-            } else {
-                // print export command output with error details
-                printer().printf("Reloading project failed - export failed 
with code: %d%n", refresh);
+                deployProject(workingDir);
             }
         });
         if (filter != null) {
@@ -563,5 +561,4 @@ public class KubernetesRun extends KubernetesBaseCommand {
         throw new RuntimeCamelException(
                 "Failed to resolve project name - please provide --gav, 
--image option or at least one source file");
     }
-
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunTest.java
index b2d7f934765..afcee4a94a5 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunTest.java
@@ -17,17 +17,27 @@
 
 package org.apache.camel.dsl.jbang.core.commands.kubernetes;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
 
+import io.fabric8.kubernetes.api.model.Container;
 import io.fabric8.kubernetes.api.model.HasMetadata;
 import io.fabric8.kubernetes.api.model.apps.Deployment;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait;
+import org.apache.camel.dsl.jbang.core.common.RuntimeType;
 import org.apache.camel.dsl.jbang.core.common.StringPrinter;
+import org.apache.camel.dsl.jbang.core.common.VersionHelper;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import picocli.CommandLine;
 
 class KubernetesRunTest extends KubernetesBaseTest {
 
@@ -35,13 +45,24 @@ class KubernetesRunTest extends KubernetesBaseTest {
 
     @BeforeEach
     public void setup() {
+        // Set Camel version with system property value, usually set via Maven 
surefire plugin
+        // In case you run this test via local Java IDE you need to provide 
the system property or a default value here
+        VersionHelper.setCamelVersion(System.getProperty("camel.version", ""));
         printer = new StringPrinter();
     }
 
-    @Test
-    public void shouldHandleMissingSourceFile() throws Exception {
-        KubernetesRun command = createCommand();
-        command.filePaths = new String[] { "mickey-mouse.groovy" };
+    private static Stream<Arguments> runtimeProvider() {
+        return Stream.of(
+                Arguments.of(RuntimeType.main),
+                Arguments.of(RuntimeType.springBoot),
+                Arguments.of(RuntimeType.quarkus));
+    }
+
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldHandleMissingSourceFile(RuntimeType rt) throws Exception 
{
+        KubernetesRun command = createCommand(new String[] { 
"mickey-mouse.groovy" },
+                "--output=yaml", "--runtime=" + rt.runtime());
         int exit = command.doCall();
 
         Assertions.assertEquals(1, exit);
@@ -49,10 +70,13 @@ class KubernetesRunTest extends KubernetesBaseTest {
         Assertions.assertTrue(printer.getOutput().contains("Project export 
failed"));
     }
 
-    @Test
-    public void shouldGenerateKubernetesManifest() throws Exception {
-        KubernetesRun command = createCommand();
-        command.filePaths = new String[] { "classpath:route.yaml" };
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldGenerateKubernetesManifest(RuntimeType rt) throws 
Exception {
+        KubernetesRun command = createCommand(new String[] { 
"classpath:route.yaml" },
+                "--image-registry=quay.io", "--image-group=camel-test", 
"--output=yaml",
+                "--trait", "container.image-pull-policy=IfNotPresent",
+                "--runtime=" + rt.runtime());
         int exit = command.doCall();
 
         Assertions.assertEquals(0, exit);
@@ -68,30 +92,30 @@ class KubernetesRunTest extends KubernetesBaseTest {
 
         Assertions.assertEquals("route", deployment.getMetadata().getName());
         Assertions.assertEquals(1, 
deployment.getSpec().getTemplate().getSpec().getContainers().size());
+        Container container = 
deployment.getSpec().getTemplate().getSpec().getContainers().get(0);
+        Assertions.assertEquals("route", container.getName());
         Assertions.assertEquals("route", 
deployment.getMetadata().getLabels().get(BaseTrait.INTEGRATION_LABEL));
-        Assertions.assertEquals("route", 
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getName());
-        Assertions.assertEquals(3, 
deployment.getSpec().getSelector().getMatchLabels().size());
         Assertions.assertEquals("route", 
deployment.getSpec().getSelector().getMatchLabels().get(BaseTrait.INTEGRATION_LABEL));
-        Assertions.assertEquals("docker.io/camel-test/route:1.0-SNAPSHOT",
-                
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage());
-        Assertions.assertEquals("Always",
-                
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImagePullPolicy());
+        Assertions.assertEquals("quay.io/camel-test/route:1.0-SNAPSHOT", 
container.getImage());
+        Assertions.assertEquals("IfNotPresent", 
container.getImagePullPolicy());
     }
 
-    @Test
-    public void shouldHandleUnsupportedOutputFormat() throws Exception {
-        KubernetesRun command = createCommand();
-        command.filePaths = new String[] { "classpath:route.yaml" };
-        command.output = "wrong";
+    @ParameterizedTest
+    @MethodSource("runtimeProvider")
+    public void shouldHandleUnsupportedOutputFormat(RuntimeType rt) throws 
Exception {
+        KubernetesRun command = createCommand(new String[] { 
"classpath:route.yaml" },
+                "--output=wrong", "--runtime=" + rt.runtime());
 
         Assertions.assertEquals(1, command.doCall());
         Assertions.assertTrue(printer.getOutput().endsWith("Unsupported output 
format 'wrong' (supported: yaml, json)"));
     }
 
-    private KubernetesRun createCommand() {
-        KubernetesRun command = new KubernetesRun(new 
CamelJBangMain().withPrinter(printer));
-        command.output = "yaml";
-        command.imageGroup = "camel-test";
+    private KubernetesRun createCommand(String[] files, String... args) {
+        var argsArr = Optional.ofNullable(args).orElse(new String[0]);
+        var argsLst = new ArrayList<>(Arrays.asList(argsArr));
+        var jbangMain = new CamelJBangMain().withPrinter(printer);
+        KubernetesRun command = new KubernetesRun(jbangMain, files);
+        CommandLine.populateCommand(command, argsLst.toArray(new String[0]));
         command.imageBuild = false;
         command.imagePush = false;
         return command;

Reply via email to