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

davsclaus pushed a commit to branch camel-4.8.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.8.x by this push:
     new 351d06c6a6d Camel 21386 4.8.x (#16098)
351d06c6a6d is described below

commit 351d06c6a6dafbdbcb78ccd01822e57ed425aefa
Author: Thomas Diesler <tdies...@redhat.com>
AuthorDate: Mon Oct 28 12:09:42 2024 +0100

    Camel 21386 4.8.x (#16098)
    
    * [CAMEL-21381] camel k8s cannot run spring-boot on openshift (#16084)
    
    * [CAMEL-21386] camel k8s run --dev does not work on openshift
---
 dsl/camel-jbang/README.md                          |   4 +-
 .../dsl/jbang/core/commands/CamelCommand.java      |   4 +-
 .../dsl/jbang/core/commands/CommandHelper.java     |  11 ++
 .../core/commands/action/CamelThreadDump.java      |   2 +-
 .../camel/dsl/jbang/core/common/PluginHelper.java  |   2 +-
 .../core/commands/kubernetes/KubernetesDelete.java |   7 +-
 .../core/commands/kubernetes/KubernetesExport.java |   1 -
 .../core/commands/kubernetes/KubernetesHelper.java |  47 ++++---
 .../core/commands/kubernetes/KubernetesRun.java    | 150 ++++++++++++---------
 .../jbang/core/commands/kubernetes/PodLogs.java    | 110 ++++++++-------
 .../commands/kubernetes/KubernetesClientTest.java  |  47 -------
 .../core/commands/kubernetes/PodLogsTest.java      |   7 +-
 12 files changed, 201 insertions(+), 191 deletions(-)

diff --git a/dsl/camel-jbang/README.md b/dsl/camel-jbang/README.md
index 08bb58fa0db..62f17a370e7 100644
--- a/dsl/camel-jbang/README.md
+++ b/dsl/camel-jbang/README.md
@@ -9,10 +9,10 @@ jbang app install camel@apache/camel
 If you however like to install camel-jbang from this project build you create 
an alias to the local entry point.
 
 ```shell
-jbang alias add --name camel -Dcamel.jbang.version=4.8.1-SNAPSHOT 
./dsl/camel-jbang/camel-jbang-main/dist/CamelJBang.java
+jbang alias add --name camel -Dcamel.jbang.version=4.8.2-SNAPSHOT 
./dsl/camel-jbang/camel-jbang-main/dist/CamelJBang.java
 
 jbang camel version  
-Camel JBang version: 4.8.1-SNAPSHOT
+Camel JBang version: 4.8.2-SNAPSHOT
 ```
 
 Alternatively, you can change the version in 
[`CamelJBang.java`](https://github.com/apache/camel/blob/main/dsl/camel-jbang/camel-jbang-main/src/main/jbang/main/CamelJBang.java#L22)
\ No newline at end of file
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
index 41ae9f33f67..eb4b256f024 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
@@ -115,7 +115,9 @@ public abstract class CamelCommand implements 
Callable<Integer> {
     }
 
     protected Printer printer() {
-        return getMain().getOut();
+        var out = getMain().getOut();
+        CommandHelper.SetPrinter(out);
+        return out;
     }
 
     protected void printConfigurationValues(String header) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CommandHelper.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CommandHelper.java
index 1ce40f1a4f8..864da314bec 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CommandHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CommandHelper.java
@@ -18,13 +18,24 @@ package org.apache.camel.dsl.jbang.core.commands;
 
 import java.io.File;
 
+import org.apache.camel.dsl.jbang.core.common.Printer;
 import org.apache.camel.util.FileUtil;
 
 public final class CommandHelper {
 
+    private static ThreadLocal<Printer> printerAssociation = new 
ThreadLocal<>();
+
     private CommandHelper() {
     }
 
+    public static Printer GetPrinter() {
+        return printerAssociation.get();
+    }
+
+    public static void SetPrinter(Printer out) {
+        printerAssociation.set(out);
+    }
+
     public static void cleanExportDir(String dir) {
         File target = new File(dir);
         File[] files = target.listFiles();
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelThreadDump.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelThreadDump.java
index d5666ded944..5bd19d7b36e 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelThreadDump.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelThreadDump.java
@@ -148,7 +148,7 @@ public class CamelThreadDump extends ActionWatchCommand {
         if (!rows.isEmpty()) {
             int total = jo.getInteger("threadCount");
             int peak = jo.getInteger("peakThreadCount");
-            printer().printf("PID: %s\tThreads: %d\tPeak: %d\t\tDisplay: 
%d/%d\n", pid, total, peak, rows.size(), total);
+            printer().printf("PID: %s\tThreads: %d\tPeak: %d\t\tDisplay: 
%d/%d%n", pid, total, peak, rows.size(), total);
 
             if (depth == 1) {
                 singleTable(rows);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java
index fbe0af60b29..fe9e504aa15 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java
@@ -157,7 +157,7 @@ public final class PluginHelper {
                 instance = 
Optional.of(Plugin.class.cast(ObjectHelper.newInstance(pluginClass)));
             } else {
                 String gav = String.join(":", group, "camel-jbang-plugin-" + 
command, version);
-                main.getOut().printf(String.format("ERROR: Failed to read file 
%s in dependency %s.\n", path, gav));
+                main.getOut().printf(String.format("ERROR: Failed to read file 
%s in dependency %s%n", path, gav));
             }
         } catch (IOException e) {
             throw new RuntimeCamelException(String.format("Failed to read the 
file %s.", path), e);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDelete.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDelete.java
index a5c44169177..226f050adb1 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDelete.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDelete.java
@@ -44,6 +44,10 @@ public class KubernetesDelete extends KubernetesBaseCommand {
                         description = "The working directory where to find 
exported project sources.")
     String workingDir;
 
+    @CommandLine.Option(names = { "--cluster-type" },
+                        description = "The target cluster type. Special 
configurations may be applied to different cluster types such as Kind or 
Minikube or Openshift.")
+    protected String clusterType;
+
     public KubernetesDelete(CamelJBangMain main) {
         super(main);
     }
@@ -71,7 +75,8 @@ public class KubernetesDelete extends KubernetesBaseCommand {
             return 1;
         }
 
-        File manifest = KubernetesHelper.resolveKubernetesManifest(new 
File(resolvedWorkingDir, "target/kubernetes"));
+        File resolvedManifestDir = new File(resolvedWorkingDir, 
"target/kubernetes");
+        File manifest = 
KubernetesHelper.resolveKubernetesManifest(clusterType, resolvedManifestDir);
         try (FileInputStream fis = new FileInputStream(manifest)) {
             List<StatusDetails> status;
             if (!ObjectHelper.isEmpty(namespace)) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java
index 6fa5cb92c4c..ef7b0b91962 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExport.java
@@ -125,7 +125,6 @@ public class KubernetesExport extends Export {
 
     public KubernetesExport(CamelJBangMain main, String[] files) {
         super(main);
-
         this.files = Arrays.asList(files);
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelper.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelper.java
index 6f5d6724ab2..976de6a4ae3 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelper.java
@@ -31,8 +31,10 @@ import com.fasterxml.jackson.databind.MapperFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.json.JsonMapper;
+import io.fabric8.kubernetes.api.model.Pod;
 import io.fabric8.kubernetes.client.KubernetesClient;
 import io.fabric8.kubernetes.client.KubernetesClientBuilder;
+import org.apache.camel.dsl.jbang.core.commands.CommandHelper;
 import org.apache.camel.dsl.jbang.core.common.YamlHelper;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.StringHelper;
@@ -74,6 +76,7 @@ public final class KubernetesHelper {
     public static KubernetesClient getKubernetesClient() {
         if (kubernetesClient == null) {
             kubernetesClient = new KubernetesClientBuilder().build();
+            printClientInfo(kubernetesClient);
         }
 
         return kubernetesClient;
@@ -87,7 +90,18 @@ public final class KubernetesHelper {
             return clients.get(config);
         }
 
-        return clients.put(config, new 
KubernetesClientBuilder().withConfig(config).build());
+        var client = new KubernetesClientBuilder().withConfig(config).build();
+        printClientInfo(client);
+        return clients.put(config, client);
+    }
+
+    private static void printClientInfo(KubernetesClient client) {
+        var printer = CommandHelper.GetPrinter();
+        if (printer != null) {
+            var serverUrl = client.getConfiguration().getMasterUrl();
+            var info = client.getKubernetesVersion();
+            printer.println(String.format("Kubernetes v%s.%s %s", 
info.getMajor(), info.getMinor(), serverUrl));
+        }
     }
 
     /**
@@ -146,29 +160,23 @@ public final class KubernetesHelper {
         return json().convertValue(model, Map.class);
     }
 
-    public static File resolveKubernetesManifest(String workingDir) throws 
FileNotFoundException {
-        return resolveKubernetesManifest(new File(workingDir));
+    public static File resolveKubernetesManifest(String clusterType, String 
workingDir) throws FileNotFoundException {
+        return resolveKubernetesManifest(clusterType, new File(workingDir));
     }
 
-    public static File resolveKubernetesManifest(String workingDir, String 
extension) throws FileNotFoundException {
-        return resolveKubernetesManifest(new File(workingDir), extension);
+    public static File resolveKubernetesManifest(String clusterType, String 
workingDir, String extension)
+            throws FileNotFoundException {
+        return resolveKubernetesManifest(clusterType, new File(workingDir), 
extension);
     }
 
-    public static File resolveKubernetesManifest(File workingDir) throws 
FileNotFoundException {
-        return resolveKubernetesManifest(workingDir, "yml");
+    public static File resolveKubernetesManifest(String clusterType, File 
workingDir) throws FileNotFoundException {
+        return resolveKubernetesManifest(clusterType, workingDir, "yml");
     }
 
-    public static File resolveKubernetesManifest(File workingDir, String 
extension) throws FileNotFoundException {
-
-        // Try explicit Kubernetes manifest first
-        String clusterType = ClusterType.KUBERNETES.name();
-        File manifest = getKubernetesManifest(clusterType, workingDir, 
extension);
-        if (manifest.exists()) {
-            return manifest;
-        }
+    public static File resolveKubernetesManifest(String clusterType, File 
workingDir, String extension)
+            throws FileNotFoundException {
 
-        // Try arbitrary Kubernetes manifest first
-        manifest = getKubernetesManifest(clusterType, workingDir);
+        var manifest = getKubernetesManifest(clusterType, workingDir);
         if (manifest.exists()) {
             return manifest;
         }
@@ -178,6 +186,10 @@ public final class KubernetesHelper {
                         .formatted(extension, workingDir.toPath().toString()));
     }
 
+    public static String getPodPhase(Pod pod) {
+        return Optional.ofNullable(pod).map(p -> 
p.getStatus().getPhase()).orElse("Unknown");
+    }
+
     public static File getKubernetesManifest(String clusterType, String 
workingDir) {
         return getKubernetesManifest(clusterType, new File(workingDir));
     }
@@ -193,7 +205,6 @@ public final class KubernetesHelper {
         } else {
             manifestFile = 
Optional.ofNullable(clusterType).map(String::toLowerCase).orElse("kubernetes");
         }
-
         return new File(workingDir, "%s.%s".formatted(manifestFile, 
extension));
     }
 }
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 19319d2b566..a25c8f5cc9d 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
@@ -44,6 +44,8 @@ import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.concurrent.ThreadHelper;
 import picocli.CommandLine;
 
+import static 
org.apache.camel.dsl.jbang.core.commands.kubernetes.KubernetesHelper.getPodPhase;
+
 @CommandLine.Command(name = "run", description = "Run Camel application on 
Kubernetes", sortOptions = false)
 public class KubernetesRun extends KubernetesBaseCommand {
 
@@ -239,8 +241,12 @@ public class KubernetesRun extends KubernetesBaseCommand {
                         description = "Maven/Gradle build properties, ex. 
--build-property=prop1=foo")
     List<String> buildProperties = new ArrayList<>();
 
-    CamelContext reloadContext;
-    int reloadCount;
+    // DevMode/Reload state
+    private CamelContext devModeContext;
+    private Thread devModeShutdownTask;
+    private int devModeReloadCount;
+
+    private PodLogs reusablePodLogs;
 
     public KubernetesRun(CamelJBangMain main) {
         super(main);
@@ -273,9 +279,10 @@ public class KubernetesRun extends KubernetesBaseCommand {
             File manifest;
             switch (output) {
                 case "yaml" ->
-                    manifest = 
KubernetesHelper.resolveKubernetesManifest(workingDir + "/target/kubernetes");
+                    manifest = 
KubernetesHelper.resolveKubernetesManifest(clusterType, workingDir + 
"/target/kubernetes");
                 case "json" ->
-                    manifest = 
KubernetesHelper.resolveKubernetesManifest(workingDir + "/target/kubernetes", 
"json");
+                    manifest = 
KubernetesHelper.resolveKubernetesManifest(clusterType, workingDir + 
"/target/kubernetes",
+                            "json");
                 default -> {
                     printer().printf("Unsupported output format '%s' 
(supported: yaml, json)%n", output);
                     return 1;
@@ -305,7 +312,6 @@ public class KubernetesRun extends KubernetesBaseCommand {
 
         if (dev || logs) {
             startPodLogging(projectName);
-            printer().println("Stopped pod logging!");
         }
 
         return 0;
@@ -313,8 +319,8 @@ public class KubernetesRun extends KubernetesBaseCommand {
 
     private String getIndexedWorkingDir(String projectName) {
         var workingDir = RUN_PLATFORM_DIR + "/" + projectName;
-        if (reloadCount > 0) {
-            workingDir += "-%03d".formatted(reloadCount);
+        if (devModeReloadCount > 0) {
+            workingDir += "-%03d".formatted(devModeReloadCount);
         }
         return workingDir;
     }
@@ -395,51 +401,79 @@ public class KubernetesRun extends KubernetesBaseCommand {
 
         FileWatcherResourceReloadStrategy reloadStrategy = new 
FileWatcherResourceReloadStrategy(watchDir);
         reloadStrategy.setResourceReload((name, resource) -> {
-            reloadCount += 1;
-            reloadContext.close();
-            printer().printf("Reloading project due to file change: %s%n", 
FileUtil.stripPath(name));
-            String reloadWorkingDir = getIndexedWorkingDir(projectName);
-            KubernetesExport export = configureExport(reloadWorkingDir);
-            int exit = export.export();
-            if (exit != 0) {
-                printer().printf("Project reexport failed for: %s%n", 
reloadWorkingDir);
-                return;
-            }
-            exit = deployProject(reloadWorkingDir, true);
-            if (exit != 0) {
-                printer().printf("Project redeploy failed for: %s%n", 
reloadWorkingDir);
-                return;
-            }
-            if (dev || wait || logs) {
-                waitForRunningPod(projectName);
-            }
-            if (dev) {
+            synchronized (this) {
+
+                printer().printf("Reloading project due to file change: %s%n", 
FileUtil.stripPath(name));
+
+                String currentWorkingDir = getIndexedWorkingDir(projectName);
+                devModeReloadCount += 1;
+
+                String reloadWorkingDir = getIndexedWorkingDir(projectName);
+                devModeContext.close();
+
+                // Re-export updated project
+                //
+                KubernetesExport export = configureExport(reloadWorkingDir);
+                int exit = export.export();
+                if (exit != 0) {
+                    printer().printf("Project reexport failed for: %s%n", 
reloadWorkingDir);
+                    return;
+                }
+
+                reusablePodLogs.retryForReload = true;
+                try {
+
+                    // Undeploy/Delete current project
+                    //
+                    KubernetesDelete deleteCommand = new 
KubernetesDelete(getMain());
+                    deleteCommand.workingDir = currentWorkingDir;
+                    deleteCommand.clusterType = clusterType;
+                    deleteCommand.name = projectName;
+                    deleteCommand.doCall();
+
+                    // Re-deploy updated project
+                    //
+                    exit = deployProject(reloadWorkingDir, true);
+                    if (exit != 0) {
+                        printer().printf("Project redeploy failed for: %s%n", 
reloadWorkingDir);
+                        return;
+                    }
+
+                    waitForRunningPod(projectName);
+
+                } finally {
+                    reusablePodLogs.retryForReload = false;
+                }
+
+                // Recursively setup --dev mode for updated project
+                //
+                Runtime.getRuntime().removeShutdownHook(devModeShutdownTask);
                 setupDevMode(projectName, reloadWorkingDir);
+
+                printer().printf("Project reloaded: %s%n", reloadWorkingDir);
             }
-            printer().printf("Project reloaded: %s%n", reloadWorkingDir);
         });
         if (filter != null) {
             reloadStrategy.setFileFilter(filter);
         }
 
-        reloadContext = new DefaultCamelContext(false);
-        reloadContext.addService(reloadStrategy);
-        reloadContext.start();
+        devModeContext = new DefaultCamelContext(false);
+        devModeContext.addService(reloadStrategy);
+        devModeContext.start();
 
         if (cleanup) {
-            installShutdownInterceptor(projectName, workingDir);
+            installShutdownHook(projectName, workingDir);
         }
     }
 
     private void startPodLogging(String projectName) throws Exception {
         try {
-            var podLogs = new PodLogs(getMain());
-            podLogs.withClient(client());
-            podLogs.label = "%s=%s".formatted(BaseTrait.KUBERNETES_NAME_LABEL, 
projectName);
+            reusablePodLogs = new PodLogs(getMain());
             if (!ObjectHelper.isEmpty(namespace)) {
-                podLogs.namespace = namespace;
+                reusablePodLogs.namespace = namespace;
             }
-            podLogs.doCall();
+            reusablePodLogs.name = projectName;
+            reusablePodLogs.doCall();
         } catch (Exception e) {
             printer().println("Failed to read pod logs - " + e);
             throw e;
@@ -455,24 +489,28 @@ public class KubernetesRun extends KubernetesBaseCommand {
             }
             printer().println("Run: " + kubectlCmd);
         }
-        client(Pod.class).withLabel(BaseTrait.KUBERNETES_NAME_LABEL, 
projectName)
-                .waitUntilCondition(it -> 
"Running".equals(it.getStatus().getPhase()), 10, TimeUnit.MINUTES);
+        var pod = client(Pod.class).withLabel(BaseTrait.KUBERNETES_NAME_LABEL, 
projectName)
+                .waitUntilCondition(it -> "Running".equals(getPodPhase(it)), 
10, TimeUnit.MINUTES);
+        if (!quiet) {
+            printer().println(String.format("Pod '%s' in phase %s", 
pod.getMetadata().getName(), getPodPhase(pod)));
+        }
     }
 
-    private void installShutdownInterceptor(String projectName, String 
workingDir) {
+    private void installShutdownHook(String projectName, String workingDir) {
         KubernetesDelete deleteCommand = new KubernetesDelete(getMain());
-        deleteCommand.name = projectName;
+        deleteCommand.clusterType = clusterType;
         deleteCommand.workingDir = workingDir;
+        deleteCommand.name = projectName;
 
-        Thread task = new Thread(() -> {
+        devModeShutdownTask = new Thread(() -> {
             try {
                 deleteCommand.doCall();
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
         });
-        task.setName(ThreadHelper.resolveThreadName(null, 
"CamelShutdownInterceptor"));
-        Runtime.getRuntime().addShutdownHook(task);
+        devModeShutdownTask.setName(ThreadHelper.resolveThreadName(null, 
"CamelShutdownInterceptor"));
+        Runtime.getRuntime().addShutdownHook(devModeShutdownTask);
     }
 
     private Integer buildProject(String workingDir) throws IOException, 
InterruptedException {
@@ -493,17 +531,10 @@ public class KubernetesRun extends KubernetesBaseCommand {
         args.add("--file");
         args.add(workingDir);
 
-        if (runtime == RuntimeType.quarkus) {
-
-            if (ClusterType.KUBERNETES.isEqualTo(clusterType)) {
-                if (!ObjectHelper.isEmpty(namespace)) {
-                    args.add("-Dquarkus.kubernetes.namespace=" + namespace);
-                }
-            }
-
-        } else {
-
-            if (!ObjectHelper.isEmpty(namespace)) {
+        if (!ObjectHelper.isEmpty(namespace)) {
+            if (runtime == RuntimeType.quarkus && 
ClusterType.KUBERNETES.isEqualTo(clusterType)) {
+                args.add("-Dquarkus.kubernetes.namespace=" + namespace);
+            } else {
                 args.add("-Djkube.namespace=%s".formatted(namespace));
             }
         }
@@ -547,6 +578,8 @@ public class KubernetesRun extends KubernetesBaseCommand {
         args.add("--file");
         args.add(workingDir);
 
+        boolean isOpenshift = ClusterType.OPENSHIFT.isEqualTo(clusterType);
+
         if (runtime == RuntimeType.quarkus) {
 
             if (imagePlatforms != null) {
@@ -556,7 +589,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
             args.add("-Dquarkus.container-image.build=" + imageBuild);
             args.add("-Dquarkus.container-image.push=" + imagePush);
 
-            if (ClusterType.OPENSHIFT.isEqualTo(clusterType)) {
+            if (isOpenshift) {
                 args.add("-Dquarkus.openshift.deploy=true");
             } else {
                 args.add("-Dquarkus.kubernetes.deploy=true");
@@ -581,11 +614,8 @@ public class KubernetesRun extends KubernetesBaseCommand {
                 args.add("-Djkube.namespace=%s".formatted(namespace));
             }
 
-            args.add("package");
-            if (reload) {
-                args.add("k8s:undeploy");
-            }
-            args.add("k8s:deploy");
+            var prefix = isOpenshift ? "oc" : "k8s";
+            args.add(prefix + ":deploy");
         }
 
         if (!quiet) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/PodLogs.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/PodLogs.java
index cd6f65d7bf0..42baee9cea7 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/PodLogs.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/PodLogs.java
@@ -19,19 +19,20 @@ package org.apache.camel.dsl.jbang.core.commands.kubernetes;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.Optional;
 
-import io.fabric8.kubernetes.api.model.Pod;
-import io.fabric8.kubernetes.api.model.PodList;
 import io.fabric8.kubernetes.client.dsl.LogWatch;
 import io.fabric8.kubernetes.client.dsl.PodResource;
 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.SourceScheme;
 import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.ObjectHelper;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
 
+import static 
org.apache.camel.dsl.jbang.core.commands.kubernetes.KubernetesHelper.getPodPhase;
+
 @Command(name = "logs", description = "Print the logs of a Kubernetes pod", 
sortOptions = false)
 public class PodLogs extends KubernetesBaseCommand {
 
@@ -56,10 +57,13 @@ public class PodLogs extends KubernetesBaseCommand {
                         description = "The number of lines from the end of the 
logs to show. Defaults to -1 to show all the lines.")
     int tail = -1;
 
-    int maxWaitAttempts = 30; // total timeout of 60 seconds
+    // total timeout of 60s
+    int maxRetryAttempts = 30;
+    boolean retryForReload;
+    private int retryCount;
 
     // used for testing
-    int maxLogMessages = -1;
+    long maxMessageCount = -1;
     long messageCount = 0;
 
     public PodLogs(CamelJBangMain main) {
@@ -67,6 +71,7 @@ public class PodLogs extends KubernetesBaseCommand {
     }
 
     public Integer doCall() throws Exception {
+
         if (name == null && label == null && filePath == null) {
             printer().println("Name or label selector must be set");
             return 1;
@@ -87,76 +92,69 @@ public class PodLogs extends KubernetesBaseCommand {
             printer().println("--label selector must be in syntax: key=value");
         }
 
-        boolean shouldResume = true;
-        AtomicInteger resumeCount = new AtomicInteger();
-        while (shouldResume) {
-            shouldResume = watchLogs(parts[0], parts[1], container, 
resumeCount);
-            printer().printf("PodLogs: [resume=%b, count=%d]%n", shouldResume, 
resumeCount.get());
-            resumeCount.incrementAndGet();
+        var retry = watchLogs();
+        while ((retry || retryForReload) && ++retryCount < maxRetryAttempts) {
             sleepWell();
+            printer().printf("Retry %d/%d Pod log for label %s%n", retryCount, 
maxRetryAttempts, label);
+            retry = watchLogs();
         }
 
+        printer().println("Stopped pod logging!");
         return 0;
     }
 
-    public boolean watchLogs(String label, String labelValue, String 
container, AtomicInteger resumeCount) {
-        PodList pods = pods().withLabel(label, labelValue).list();
+    // Returns true if a retry should be attempted
+    private boolean watchLogs() {
 
-        Pod pod = pods.getItems().stream()
-                .filter(p -> p.getStatus().getPhase() != null && 
!"Terminated".equals(p.getStatus().getPhase()))
+        PodResource podRes = pods().withLabel(label)
+                .resources()
                 .findFirst()
                 .orElse(null);
-
-        if (pod == null) {
-            if (resumeCount.get() == 0) {
-                printer().printf("Pod for label %s=%s not available - Waiting 
...%n".formatted(label, labelValue));
-            }
-
-            // use 2-sec delay in waiting for pod logs mode
-            sleepWell();
-            return resumeCount.get() < maxWaitAttempts;
-        }
-
-        String containerName = null;
-        if (pod.getSpec() != null && pod.getSpec().getContainers() != null) {
-            if (container != null && 
pod.getSpec().getContainers().stream().anyMatch(c -> 
container.equals(c.getName()))) {
-                containerName = container;
-            } else if (!pod.getSpec().getContainers().isEmpty()) {
-                containerName = pod.getSpec().getContainers().get(0).getName();
-            }
+        if (podRes == null) {
+            printer().printf("Pod for label %s not available%n", label);
+            return true;
         }
 
-        PodResource podRes = pods().withName(pod.getMetadata().getName());
+        var terminated = isPodTerminated(podRes);
+        if (!terminated) {
 
-        LogWatch logs;
-        if (tail < 0) {
-            if (containerName != null) {
-                logs = podRes.inContainer(containerName).watchLog();
-            } else {
-                logs = podRes.watchLog();
-            }
-        } else {
-            if (containerName != null) {
-                logs = 
podRes.inContainer(containerName).tailingLines(tail).watchLog();
+            LogWatch logs;
+            if (tail < 0) {
+                if (!ObjectHelper.isEmpty(container)) {
+                    logs = podRes.inContainer(container).watchLog();
+                } else {
+                    logs = podRes.watchLog();
+                }
             } else {
-                logs = podRes.tailingLines(tail).watchLog();
+                if (!ObjectHelper.isEmpty(container)) {
+                    logs = 
podRes.inContainer(container).tailingLines(tail).watchLog();
+                } else {
+                    logs = podRes.tailingLines(tail).watchLog();
+                }
             }
-        }
 
-        try (logs; BufferedReader reader = new BufferedReader(new 
InputStreamReader(logs.getOutput()))) {
-            String line;
-            while ((line = reader.readLine()) != null) {
-                printer().println(line);
-                if (messageCount++ > maxLogMessages && maxLogMessages > 0) {
-                    return false;
+            try (logs; BufferedReader reader = new BufferedReader(new 
InputStreamReader(logs.getOutput()))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    printer().println(line);
+                    if (maxMessageCount > 0 && ++messageCount > 
maxMessageCount) {
+                        return false;
+                    }
+                    retryCount = 0;
                 }
-                resumeCount.set(0);
+            } catch (IOException e) {
+                printer().println("Failed to read pod logs - " + 
e.getMessage());
             }
-        } catch (IOException e) {
-            printer().println("Failed to read pod logs - " + e.getMessage());
+
+            terminated = isPodTerminated(podRes);
         }
 
-        return resumeCount.get() < maxWaitAttempts;
+        return !terminated;
+    }
+
+    private boolean isPodTerminated(PodResource podRes) {
+        var phase = Optional.ofNullable(podRes).map(pr -> 
getPodPhase(pr.get())).orElse("Unknown");
+        return "Terminated".equals(phase);
     }
 
     private void sleepWell() {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesClientTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesClientTest.java
deleted file mode 100644
index d532b3bb99a..00000000000
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesClientTest.java
+++ /dev/null
@@ -1,47 +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.kubernetes;
-
-import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.client.KubernetesClientException;
-import io.fabric8.openshift.client.OpenShiftClient;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class KubernetesClientTest {
-
-    private static final Logger log = 
LoggerFactory.getLogger(KubernetesClientTest.class);
-
-    @Test
-    public void shouldHaveOpenshiftClient() {
-        try (KubernetesClient client = KubernetesHelper.getKubernetesClient()) 
{
-            OpenShiftClient openShiftClient = 
client.adapt(OpenShiftClient.class);
-            try {
-                openShiftClient.projects().list().getItems().forEach(project 
-> {
-                    log.debug("Project: {}", project.getMetadata().getName());
-                });
-                log.info("OpenShiftClient is authenticated and working 
properly.");
-            } catch (KubernetesClientException e) {
-                log.debug("OpenShiftClient is not authenticated: {}", 
e.getMessage());
-            }
-        } catch (Exception e) {
-            log.debug("Cannot construct OpenShiftClient: {}", e.getMessage());
-        }
-    }
-}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/PodLogsTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/PodLogsTest.java
index d031c7d2430..ffb83fe20bc 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/PodLogsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/PodLogsTest.java
@@ -32,8 +32,9 @@ class PodLogsTest extends KubernetesBaseTest {
     public void shouldHandlePodNotFound() throws Exception {
         PodLogs command = createCommand();
         command.name = "mickey-mouse";
-        command.maxWaitAttempts = 2; // total timeout of 4 seconds
-        command.doCall();
+        command.maxRetryAttempts = 2; // total timeout of 4 seconds
+        int exit = command.doCall();
+        Assertions.assertEquals(0, exit);
 
         Assertions.assertTrue(
                 printer.getOutput().contains("Pod for label 
app.kubernetes.io/name=mickey-mouse not available"));
@@ -54,7 +55,7 @@ class PodLogsTest extends KubernetesBaseTest {
         kubernetesClient.pods().resource(pod).create();
 
         var podLog = createCommand();
-        podLog.maxLogMessages = 10;
+        podLog.maxMessageCount = 10;
         podLog.name = "routes";
         int exit = podLog.doCall();
         Assertions.assertEquals(0, exit);


Reply via email to