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 66b30a7ead7e CAMEL-26934: camel-jbang-plugin-kubernetes -
--kube-config works, the client timeouts apply, and logs stops at a finished
pod (#29919)
66b30a7ead7e is described below
commit 66b30a7ead7e408a8198f8f0d9497e02c352f3e3
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 14 19:24:38 2026 +0100
CAMEL-26934: camel-jbang-plugin-kubernetes - --kube-config works, the
client timeouts apply, and logs stops at a finished pod (#29919)
--kube-config: the content of the file went to
KubernetesClientBuilder.withConfig(String),
which reads a fabric8 Config, not a kube config file, so every command used
the default
cluster; it is read with Config.fromKubeconfig now. delete used the default
client, the
cluster detection asked the default client, and the JKube deploy never got
the file:
delete and the detection use the client of the command, and the Maven runs
get KUBECONFIG.
The connection and request timeouts were set after the default client was
built, which
had read them already (40 s instead of 2 s against an unreachable cluster).
The cluster
detection messages go to stderr, as they ended up in the YAML of run
--output yaml.
kubernetes logs checked for the phase Terminated, which no pod has, so the
log of a
finished pod was printed again every 2 seconds: a pod in Succeeded or
Failed gets its
log printed once.
CAMEL-26934: camel-jbang-plugin-kubernetes - a log read after a failed read
is no failure, and the Javadoc of the pod phase check
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../core/commands/kubernetes/KubernetesDelete.java | 6 +-
.../core/commands/kubernetes/KubernetesHelper.java | 36 ++++++---
.../commands/kubernetes/KubernetesPodLogs.java | 90 ++++++++++++++-------
.../core/commands/kubernetes/KubernetesRun.java | 14 +++-
.../kubernetes/KubernetesDeleteKubeConfigTest.java | 94 ++++++++++++++++++++++
.../commands/kubernetes/KubernetesHelperTest.java | 31 +++++++
.../commands/kubernetes/KubernetesPodLogsTest.java | 24 ++++++
7 files changed, 251 insertions(+), 44 deletions(-)
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 7f950e64295d..66050bb39b4b 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
@@ -31,7 +31,6 @@ import
org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.common.CliExitCode;
import org.apache.camel.util.StringHelper;
-import static
org.apache.camel.dsl.jbang.core.commands.kubernetes.KubernetesHelper.getKubernetesClient;
import static
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait.KUBERNETES_LABEL_NAME;
@CommandDefinition(name = "delete",
@@ -48,7 +47,8 @@ public class KubernetesDelete extends KubernetesBaseCommand {
printer().printErr("Missing required --name option or integration
name");
return CliExitCode.USAGE;
}
- var client = getKubernetesClient();
+ // the cluster of --kube-config when it is given (CAMEL-26934: the
default context was used)
+ var client = client();
namespace =
Optional.ofNullable(namespace).orElse(client.getNamespace());
namespace = Optional.ofNullable(namespace).orElse("default");
printer().printf("Deleting resources in namespace '%s' with name:
%s%n", namespace, name);
@@ -84,7 +84,7 @@ public class KubernetesDelete extends KubernetesBaseCommand {
} catch (Exception ex) {
// ignore
}
- ClusterType clusterType = KubernetesHelper.discoverClusterType();
+ ClusterType clusterType =
KubernetesHelper.discoverClusterType(client);
if (ClusterType.OPENSHIFT == clusterType) {
// openshift specific: BuildConfig, ImageStreams, Route -
BuildConfig cascade delete to Build and ConfigMap
OpenShiftClient ocpClient =
client.adapt(OpenShiftClient.class);
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 7db50b25e585..2555a6c5d195 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
@@ -33,6 +33,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.GenericKubernetesResourceList;
import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext;
@@ -84,21 +85,24 @@ public final class KubernetesHelper {
*/
public static KubernetesClient getKubernetesClient() {
if (kubernetesClient == null) {
+ // the timeouts before the client is built, which reads them
(CAMEL-26934: 40 s against an unreachable API)
+ setKubernetesClientProperties();
kubernetesClient = new KubernetesClientBuilder().build();
}
- setKubernetesClientProperties();
return kubernetesClient;
}
/**
- * Create or get Kubernetes client with given config.
+ * Create or get Kubernetes client with given config: the contents of a
kube config file.
*/
public static KubernetesClient getKubernetesClient(String config) {
if (clients.containsKey(config)) {
return clients.get(config);
}
setKubernetesClientProperties();
- var client = new KubernetesClientBuilder().withConfig(config).build();
+ // as a kube config file (CAMEL-26934): withConfig(String) reads the
text as a fabric8 Config, so the clusters
+ // and contexts of a kube config were ignored and the client went to
the default kubernetes.default.svc
+ var client = new
KubernetesClientBuilder().withConfig(Config.fromKubeconfig(config)).build();
clients.put(config, client);
return client;
}
@@ -145,27 +149,37 @@ public final class KubernetesHelper {
* @return The cluster type may be: Openshift, Minikube or Kubernetes.
*/
static ClusterType discoverClusterType() {
+ return discoverClusterType(getKubernetesClient());
+ }
+
+ /**
+ * Verify what cluster the given client is connected to (the cluster of
--kube-config, when it is given).
+ *
+ * @see #discoverClusterType()
+ */
+ static ClusterType discoverClusterType(KubernetesClient client) {
ClusterType cluster = ClusterType.KUBERNETES;
- if (isConnectedToOpenshift()) {
+ if (isConnectedToOpenshift(client)) {
cluster = ClusterType.OPENSHIFT;
- } else if (isConnectedToMinikube()) {
+ } else if (isConnectedToMinikube(client)) {
cluster = ClusterType.MINIKUBE;
}
return cluster;
}
- private static boolean isConnectedToOpenshift() {
+ private static boolean isConnectedToOpenshift(KubernetesClient client) {
boolean ocp = false;
try {
- APIGroup apiGroup =
getKubernetesClient().getApiGroup("route.openshift.io");
+ APIGroup apiGroup = client.getApiGroup("route.openshift.io");
ocp = apiGroup != null;
} catch (RuntimeException e) {
- System.out.println("Failed to detect cluster: " + e.getMessage() +
", default to kubernetes.");
+ // on stderr: the output may be the YAML of run --output yaml
+ System.err.println("Failed to detect cluster: " + e.getMessage() +
", default to kubernetes.");
}
return ocp;
}
- private static boolean isConnectedToMinikube() {
+ private static boolean isConnectedToMinikube(KubernetesClient client) {
boolean minikube = false;
boolean minikubeEnv = false;
try {
@@ -175,12 +189,12 @@ public final class KubernetesHelper {
.withNamespaced(false)
.build();
// if there is a node with minikube label, then it's minikube
- GenericKubernetesResourceList list =
getKubernetesClient().genericKubernetesResources(nodecrd)
+ GenericKubernetesResourceList list =
client.genericKubernetesResources(nodecrd)
.withLabels(Collections.singletonMap("minikube.k8s.io/name", null)).list();
minikube = list.getItems().size() > 0;
minikubeEnv = isMinikubeDockerEnv();
if (minikube && !minikubeEnv) {
- System.out.println(
+ System.err.println(
"It seems you have minikube running but forgot to run
\"eval $(minikube docker-env)\", default cluster to kubernetes.");
}
} catch (Exception e) {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesPodLogs.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesPodLogs.java
index 7a0f9afa3c2f..aa257e695c13 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesPodLogs.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesPodLogs.java
@@ -111,52 +111,84 @@ public class KubernetesPodLogs extends
KubernetesBaseCommand {
podFound = true;
var terminated = isPodTerminated(podRes);
- if (!terminated) {
-
- LogWatch logs;
- if (tail < 0) {
- if (!ObjectHelper.isEmpty(container)) {
- logs = podRes.inContainer(container).watchLog();
- } else {
- logs = podRes.watchLog();
- }
+ if (terminated) {
+ // a pod that has finished, such as the pod of a CronJob: its log
once, as kubectl logs does
+ printLog(podRes);
+ return false;
+ }
+
+ LogWatch logs;
+ if (tail < 0) {
+ if (!ObjectHelper.isEmpty(container)) {
+ logs = podRes.inContainer(container).watchLog();
} else {
- if (!ObjectHelper.isEmpty(container)) {
- logs =
podRes.inContainer(container).tailingLines(tail).watchLog();
- } else {
- logs = podRes.tailingLines(tail).watchLog();
- }
+ logs = podRes.watchLog();
}
+ } else {
+ 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 (maxMessageCount > 0 && ++messageCount >
maxMessageCount) {
- return false;
- }
- retryCount = 0;
+ 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;
}
- readFailed = false;
- } catch (IOException e) {
- printer().printErr("Failed to read pod logs - " +
e.getMessage());
- readFailed = true;
+ retryCount = 0;
}
-
- terminated = isPodTerminated(podRes);
+ readFailed = false;
+ } catch (IOException e) {
+ printer().printErr("Failed to read pod logs - " + e.getMessage());
+ readFailed = true;
}
+ terminated = isPodTerminated(podRes);
+
return !terminated;
}
+ private void printLog(PodResource podRes) {
+ String log;
+ try {
+ if (!ObjectHelper.isEmpty(container)) {
+ log = tail < 0
+ ? podRes.inContainer(container).getLog() :
podRes.inContainer(container).tailingLines(tail).getLog();
+ } else {
+ log = tail < 0 ? podRes.getLog() :
podRes.tailingLines(tail).getLog();
+ }
+ } catch (RuntimeException e) {
+ printer().printErr("Failed to read pod logs - " + e.getMessage());
+ readFailed = true;
+ return;
+ }
+ readFailed = false;
+ if (log != null) {
+ for (String line : log.split("\\R")) {
+ printer().println(line);
+ if (maxMessageCount > 0 && ++messageCount > maxMessageCount) {
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether the pod has finished: the phase {@code Succeeded} or {@code
Failed}.
+ */
private boolean isPodTerminated(PodResource podRes) {
+ // not Terminated, which is no pod phase: the log of a finished pod
was printed again and again (CAMEL-26934)
var phase = "Unknown";
try {
phase = Optional.ofNullable(podRes).map(pr ->
getPodPhase(pr.get())).orElse(phase);
} catch (RuntimeException ex) {
printer().printErr(ex);
}
- return "Terminated".equals(phase);
+ return "Succeeded".equals(phase) || "Failed".equals(phase);
}
private boolean sleepWell() {
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 e0c32b4a315c..aff44f525faf 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
@@ -706,6 +706,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
pb.command(args.toArray(String[]::new));
+ useKubeConfig(pb);
Path log = mavenOutput(pb);
try {
Process p = pb.start();
@@ -782,6 +783,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
pb.command(args.toArray(String[]::new));
+ useKubeConfig(pb);
Path log = mavenOutput(pb);
try {
Process p = pb.start();
@@ -803,6 +805,16 @@ public class KubernetesRun extends KubernetesBaseCommand {
*
* @return the log file, or <tt>null</tt> with --verbose
*/
+ /**
+ * The Kubernetes client of the JKube plugin reads KUBECONFIG: the cluster
of --kube-config, when it is given
+ * (CAMEL-26934: the build deployed to the current context).
+ */
+ void useKubeConfig(ProcessBuilder pb) {
+ if (kubeConfig != null) {
+ pb.environment().put("KUBECONFIG",
Path.of(kubeConfig).toAbsolutePath().toString());
+ }
+ }
+
private Path mavenOutput(ProcessBuilder pb) throws IOException {
if (!quiet && verbose) {
pb.inheritIO(); // run in foreground (with IO so logs are visible)
@@ -850,7 +862,7 @@ public class KubernetesRun extends KubernetesBaseCommand {
if (verbose) {
printer().print("Automatic Kubernetes cluster detection... ");
}
- this.clusterType = KubernetesHelper.discoverClusterType().name();
+ this.clusterType =
KubernetesHelper.discoverClusterType(client()).name();
if (verbose) {
printer().println(this.clusterType);
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDeleteKubeConfigTest.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDeleteKubeConfigTest.java
new file mode 100644
index 000000000000..906d260c48cb
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDeleteKubeConfigTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+import io.fabric8.kubernetes.client.KubernetesClientBuilder;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * camel kubernetes delete --kube-config acts on the cluster of that file:
before, it used the current context
+ * (CAMEL-26934), so a delete aimed at staging acted on the cluster of
KUBECONFIG.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*",
+ disabledReason = "Requires too much network
resources")
+class KubernetesDeleteKubeConfigTest extends KubernetesBaseTestSupport {
+
+ @TempDir
+ Path dir;
+
+ @Test
+ public void deleteUsesTheClusterOfTheKubeConfig() throws Exception {
+ Deployment deployment = new DeploymentBuilder()
+ .withNewMetadata()
+ .withName("routes")
+ .withNamespace("staging")
+ .withLabels(Map.of(BaseTrait.KUBERNETES_LABEL_NAME, "routes"))
+ .endMetadata()
+ .build();
+
kubernetesClient.apps().deployments().inNamespace("staging").resource(deployment).create();
+ Path kubeConfig = dir.resolve("staging.yaml");
+ Files.writeString(kubeConfig, """
+ apiVersion: v1
+ kind: Config
+ clusters:
+ - name: staging
+ cluster:
+ server: %s
+ insecure-skip-tls-verify: true
+ contexts:
+ - name: staging
+ context:
+ cluster: staging
+ user: staging
+ namespace: staging
+ current-context: staging
+ users:
+ - name: staging
+ user:
+ token: secret
+ """.formatted(kubernetesClient.getMasterUrl()));
+
+ KubernetesDelete command = new KubernetesDelete(new
CamelJBangMain().withPrinter(printer));
+ // the current context is another cluster, which cannot be reached
+ command.withClient(new KubernetesClientBuilder()
+ .withConfig(new
ConfigBuilder().withMasterUrl("http://localhost:1").withRequestTimeout(500)
+ .withConnectionTimeout(500).build())
+ .build());
+ command.kubeConfig = kubeConfig.toString();
+ command.name = "routes";
+
+ int exit = command.doCall();
+
+ Assertions.assertEquals(0, exit, printer.getOutput());
+ Assertions.assertTrue(printer.getOutput().contains("Deleting resources
in namespace 'staging' with name: routes"),
+ printer.getOutput());
+ Assertions.assertTrue(printer.getOutput().contains("Deployment
'routes'"), printer.getOutput());
+
Assertions.assertNull(kubernetesClient.apps().deployments().inNamespace("staging").withName("routes").get());
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelperTest.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelperTest.java
index 3b3985e4af02..e520ef7bb6be 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelperTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelperTest.java
@@ -19,7 +19,9 @@ package org.apache.camel.dsl.jbang.core.commands.kubernetes;
import java.io.File;
import java.nio.file.Path;
+import java.util.HashMap;
import java.util.Locale;
+import java.util.Map;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -28,6 +30,35 @@ import org.junit.jupiter.params.provider.EnumSource;
class KubernetesHelperTest {
+ @Test
+ void theDefaultClientHasTheShortTimeouts() {
+ // they were set after the client was built, which had read them
already (CAMEL-26934: run --output yaml took
+ // 40 s against an unreachable API server instead of about 2 s)
+ // the other tests of this JVM may have set them already: as on a
fresh start, none is set
+ String[] keys = {
+ "kubernetes.connection.timeout", "kubernetes.request.timeout",
+ "kubernetes.request.retry.backoffLimit" };
+ Map<String, String> before = new HashMap<>();
+ for (String key : keys) {
+ String value = System.clearProperty(key);
+ if (value != null) {
+ before.put(key, value);
+ }
+ }
+ KubernetesHelper.setKubernetesClient(null);
+ try {
+ var config =
KubernetesHelper.getKubernetesClient().getConfiguration();
+ Assertions.assertEquals(2000, config.getConnectionTimeout());
+ Assertions.assertEquals(2000, config.getRequestTimeout());
+ } finally {
+ KubernetesHelper.setKubernetesClient(null);
+ for (String key : keys) {
+ System.clearProperty(key);
+ }
+ before.forEach(System::setProperty);
+ }
+ }
+
private final Path workingDir = Path.of("target", "kubernetes");
// KubernetesExport builds with the kubernetes-maven-plugin for every
cluster type but OpenShift,
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesPodLogsTest.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesPodLogsTest.java
index b7b8840baf2e..6b9a00c9e590 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesPodLogsTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesPodLogsTest.java
@@ -67,6 +67,30 @@ class KubernetesPodLogsTest extends
KubernetesBaseTestSupport {
Assertions.assertEquals(0, exit);
}
+ @Test
+ public void theLogOfAFinishedPodIsReadOnce() throws Exception {
+ // the phase of a finished pod is Succeeded or Failed: the check for
Terminated, which is no phase, made the
+ // command read the log again every 2 seconds (CAMEL-26934)
+ Pod pod = new PodBuilder()
+ .withNewMetadata()
+ .withName("job")
+ .withLabels(Map.of(BaseTrait.KUBERNETES_LABEL_NAME, "job"))
+ .endMetadata()
+ .withNewStatus()
+ .withPhase("Succeeded")
+ .endStatus()
+ .build();
+ kubernetesClient.pods().resource(pod).create();
+
+ var podLog = createCommand();
+ podLog.name = "job";
+ long start = System.currentTimeMillis();
+ podLog.doCall();
+
+ Assertions.assertTrue(System.currentTimeMillis() - start < 2000, "no
retry for a finished pod");
+ Assertions.assertFalse(printer.getOutput().contains("Retry"),
printer.getOutput());
+ }
+
private KubernetesPodLogs createCommand() {
KubernetesPodLogs command = new KubernetesPodLogs(new
CamelJBangMain().withPrinter(printer));
command.withClient(kubernetesClient);