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 fe14ff190185 CAMEL-25384: handle unconfigured Kubernetes vault
consoles (#27493)
fe14ff190185 is described below
commit fe14ff190185dfb021b4132d5c86589f30d6dbed
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Oct 7 15:02:54 2026 +0200
CAMEL-25384: handle unconfigured Kubernetes vault consoles (#27493)
When camel-kubernetes is present without configured vault resource names,
its developer consoles threw while Camel JBang collected status, so the
integration did not show in camel ps and camel get.
Handle missing vault configurations and null or empty resource-name lists
in both JSON and text output. Read ConfigMaps names and refresh settings from
the ConfigMaps vault, and handle its absent client in local mode. Configured
names stay sorted. The corrected ConfigMaps output is documented in the 4.23
upgrade guide.
Co-authored-by: Codex <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../config_maps/vault/ConfigmapsDevConsole.java | 40 ++++++----
.../secrets/vault/SecretsDevConsole.java | 15 ++--
.../vault/ConfigmapsDevConsoleTest.java | 89 ++++++++++++++++++++--
.../secrets/vault/SecretsDevConsoleTest.java | 89 ++++++++++++++++++++--
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 7 ++
5 files changed, 209 insertions(+), 31 deletions(-)
diff --git
a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/vault/ConfigmapsDevConsole.java
b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/vault/ConfigmapsDevConsole.java
index 1c072f5938bf..11966ebfbd50 100644
---
a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/vault/ConfigmapsDevConsole.java
+++
b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/vault/ConfigmapsDevConsole.java
@@ -22,6 +22,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
+import io.fabric8.kubernetes.client.KubernetesClient;
import
org.apache.camel.component.kubernetes.properties.ConfigMapPropertiesFunction;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.PeriodTaskScheduler;
@@ -32,7 +33,6 @@ import org.apache.camel.support.console.AbstractDevConsole;
import org.apache.camel.util.TimeUtils;
import org.apache.camel.util.json.JsonRecordSupport;
import org.apache.camel.vault.KubernetesConfigMapVaultConfiguration;
-import org.apache.camel.vault.KubernetesVaultConfiguration;
@DevConsole(name = "kubernetes-configmaps", displayName = "Kubernetes Config
Maps",
description = "Kubernetes Cluster Config Maps")
@@ -80,10 +80,13 @@ public class ConfigmapsDevConsole extends
AbstractDevConsole {
if (propertiesFunction != null) {
sb.append("Kubernetes Config maps Cluster:");
- sb.append(String.format("%n Master Url: %s",
propertiesFunction.getClient().getMasterUrl()));
- sb.append("\n Login: OAuth Token");
- KubernetesVaultConfiguration kubernetes
- =
getCamelContext().getVaultConfiguration().getKubernetesVaultConfiguration();
+ KubernetesClient client = propertiesFunction.getClient();
+ if (client != null && client.getMasterUrl() != null) {
+ sb.append(String.format("%n Master Url: %s",
client.getMasterUrl().toString()));
+ sb.append("\n Login: OAuth Token");
+ }
+ KubernetesConfigMapVaultConfiguration kubernetes
+ =
getCamelContext().getVaultConfiguration().getKubernetesConfigMapVaultConfiguration();
if (kubernetes != null) {
sb.append(String.format("%n Refresh Enabled: %s",
kubernetes.isRefreshEnabled()));
}
@@ -94,10 +97,12 @@ public class ConfigmapsDevConsole extends
AbstractDevConsole {
}
List<String> sorted = new ArrayList<>();
if (kubernetes != null) {
- sb.append("\n\nSecrets in use:");
+ sb.append("\n\nConfig maps in use:");
- sorted = new
ArrayList<>(List.of(kubernetes.getSecrets().split(",")));
- Collections.sort(sorted);
+ if (kubernetes.getConfigmaps() != null &&
!kubernetes.getConfigmaps().isEmpty()) {
+ sorted = new
ArrayList<>(List.of(kubernetes.getConfigmaps().split(",")));
+ Collections.sort(sorted);
+ }
}
for (String sec : sorted) {
@@ -113,11 +118,15 @@ public class ConfigmapsDevConsole extends
AbstractDevConsole {
String masterUrl = null;
String login = null;
if (propertiesFunction != null) {
- masterUrl =
propertiesFunction.getClient().getMasterUrl().toString();
- login = "OAuth Token";
+ KubernetesClient client = propertiesFunction.getClient();
+ if (client != null && client.getMasterUrl() != null) {
+ masterUrl = client.getMasterUrl().toString();
+ login = "OAuth Token";
+ }
}
- KubernetesVaultConfiguration kubernetes =
getCamelContext().getVaultConfiguration().getKubernetesVaultConfiguration();
+ KubernetesConfigMapVaultConfiguration kubernetes
+ =
getCamelContext().getVaultConfiguration().getKubernetesConfigMapVaultConfiguration();
Boolean refreshEnabled = kubernetes != null ?
kubernetes.isRefreshEnabled() : null;
Long startCheckTimestamp = null;
@@ -128,10 +137,11 @@ public class ConfigmapsDevConsole extends
AbstractDevConsole {
}
}
- // NOTE: kubernetes is dereferenced unconditionally here, same as the
original code -
- // preserved as-is rather than fixed, since this migration is about
the response contract
- List<String> sorted = new
ArrayList<>(List.of(kubernetes.getSecrets().split(",")));
- Collections.sort(sorted);
+ List<String> sorted = new ArrayList<>();
+ if (kubernetes != null && kubernetes.getConfigmaps() != null &&
!kubernetes.getConfigmaps().isEmpty()) {
+ sorted.addAll(List.of(kubernetes.getConfigmaps().split(",")));
+ Collections.sort(sorted);
+ }
List<ConfigMapEntry> configmaps = new ArrayList<>();
for (String sec : sorted) {
diff --git
a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/secrets/vault/SecretsDevConsole.java
b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/secrets/vault/SecretsDevConsole.java
index 82b6d408cfa0..fc8fc8bde7a2 100644
---
a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/secrets/vault/SecretsDevConsole.java
+++
b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/secrets/vault/SecretsDevConsole.java
@@ -97,8 +97,10 @@ public class SecretsDevConsole extends AbstractDevConsole {
if (kubernetes != null) {
sb.append("\n\nSecrets in use:");
- sorted = new
ArrayList<>(List.of(kubernetes.getSecrets().split(",")));
- Collections.sort(sorted);
+ if (kubernetes.getSecrets() != null &&
!kubernetes.getSecrets().isEmpty()) {
+ sorted = new
ArrayList<>(List.of(kubernetes.getSecrets().split(",")));
+ Collections.sort(sorted);
+ }
}
for (String sec : sorted) {
@@ -133,10 +135,11 @@ public class SecretsDevConsole extends AbstractDevConsole
{
}
}
- // NOTE: kubernetes is dereferenced unconditionally here, same as the
original code - preserved
- // as-is rather than fixed, since this migration is about the response
contract
- List<String> sorted = new
ArrayList<>(List.of(kubernetes.getSecrets().split(",")));
- Collections.sort(sorted);
+ List<String> sorted = new ArrayList<>();
+ if (kubernetes != null && kubernetes.getSecrets() != null &&
!kubernetes.getSecrets().isEmpty()) {
+ sorted.addAll(List.of(kubernetes.getSecrets().split(",")));
+ Collections.sort(sorted);
+ }
List<SecretEntry> secrets = new ArrayList<>();
for (String sec : sorted) {
diff --git
a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/config_maps/vault/ConfigmapsDevConsoleTest.java
b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/config_maps/vault/ConfigmapsDevConsoleTest.java
index 1bedf7ef554f..dafda618a0c5 100644
---
a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/config_maps/vault/ConfigmapsDevConsoleTest.java
+++
b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/config_maps/vault/ConfigmapsDevConsoleTest.java
@@ -16,19 +16,24 @@
*/
package org.apache.camel.component.kubernetes.config_maps.vault;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import
org.apache.camel.component.kubernetes.properties.ConfigMapPropertiesFunction;
import org.apache.camel.console.DevConsole;
import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.test.junit6.CamelTestSupport;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullAndEmptySource;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * ConfigmapsDevConsole unconditionally dereferences the Kubernetes vault
configuration's secrets list (a pre-existing
- * bug, preserved as-is by this migration), so calling it without a real
Kubernetes vault configuration would NPE; this
- * test is limited to verifying the console registers correctly.
- */
public class ConfigmapsDevConsoleTest extends CamelTestSupport {
@Test
@@ -38,4 +43,78 @@ public class ConfigmapsDevConsoleTest extends
CamelTestSupport {
assertEquals("camel", con.getGroup());
assertEquals("kubernetes-configmaps", con.getId());
}
+
+ @ParameterizedTest
+ @NullAndEmptySource
+ public void testJsonWithoutConfiguredNames(String names) throws Exception {
+
context.getVaultConfiguration().kubernetesConfigmaps().setConfigmaps(names);
+ DevConsole con = startConsole();
+ Map<?, ?> out = (Map<?, ?>) con.call(DevConsole.MediaType.JSON);
+ assertEquals(List.of(), out.get("configmaps"));
+ assertFalse(out.containsKey("masterUrl"));
+ assertFalse(out.containsKey("login"));
+ }
+
+ @ParameterizedTest
+ @NullAndEmptySource
+ public void testTextWithoutConfiguredNames(String names) throws Exception {
+
context.getVaultConfiguration().kubernetesConfigmaps().setConfigmaps(names);
+ DevConsole con = startConsole();
+ String out = (String) con.call(DevConsole.MediaType.TEXT);
+ assertTrue(out.contains("Config maps in use:"));
+ assertFalse(out.contains("Master Url:"));
+ assertFalse(out.contains("Login:"));
+ }
+
+ @Test
+ public void testConfiguredNamesJson() throws Exception {
+
context.getVaultConfiguration().kubernetesConfigmaps().setConfigmaps("zulu,alpha");
+ context.getVaultConfiguration().kubernetes().setSecrets("unrelated");
+
context.getVaultConfiguration().kubernetesConfigmaps().setRefreshEnabled(true);
+ context.getVaultConfiguration().kubernetes().setRefreshEnabled(false);
+ DevConsole con = startConsole();
+ Map<?, ?> out = (Map<?, ?>) con.call(DevConsole.MediaType.JSON);
+ assertEquals(true, out.get("refreshEnabled"));
+ assertEquals(List.of(Map.of("name", "alpha"), Map.of("name", "zulu")),
out.get("configmaps"));
+ }
+
+ @Test
+ public void testConfiguredNamesText() throws Exception {
+
context.getVaultConfiguration().kubernetesConfigmaps().setConfigmaps("zulu,alpha");
+ context.getVaultConfiguration().kubernetes().setSecrets("unrelated");
+ DevConsole con = startConsole();
+ String out = (String) con.call(DevConsole.MediaType.TEXT);
+ assertTrue(out.contains("Config maps in use:"));
+ assertTrue(out.contains("alpha"));
+ assertTrue(out.contains("zulu"));
+ assertTrue(out.indexOf("alpha") < out.indexOf("zulu"));
+ assertFalse(out.contains("unrelated"));
+ }
+
+ @Test
+ public void testJsonWithoutVaultConfiguration() throws Exception {
+
context.getVaultConfiguration().setKubernetesConfigMapVaultConfiguration(null);
+ DevConsole con = startConsole();
+ Map<?, ?> out = (Map<?, ?>) con.call(DevConsole.MediaType.JSON);
+ assertEquals(List.of(), out.get("configmaps"));
+ assertFalse(out.containsKey("refreshEnabled"));
+ }
+
+ @Test
+ public void testTextWithoutVaultConfiguration() throws Exception {
+
context.getVaultConfiguration().setKubernetesConfigMapVaultConfiguration(null);
+ DevConsole con = startConsole();
+ String out = (String) con.call(DevConsole.MediaType.TEXT);
+ assertFalse(out.contains("Refresh Enabled:"));
+ }
+
+ private DevConsole startConsole() throws Exception {
+ Properties properties = new Properties();
+ properties.setProperty("camel.kubernetes-config.local-mode", "true");
+ context.getPropertiesComponent().setInitialProperties(properties);
+ context.getPropertiesComponent().addPropertiesFunction(new
ConfigMapPropertiesFunction());
+ DevConsole con =
PluginHelper.getDevConsoleResolver(context).resolveDevConsole("kubernetes-configmaps");
+ ServiceHelper.startService(con);
+ return con;
+ }
}
diff --git
a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/secrets/vault/SecretsDevConsoleTest.java
b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/secrets/vault/SecretsDevConsoleTest.java
index 35688482ee46..4b2b06280145 100644
---
a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/secrets/vault/SecretsDevConsoleTest.java
+++
b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/secrets/vault/SecretsDevConsoleTest.java
@@ -16,19 +16,24 @@
*/
package org.apache.camel.component.kubernetes.secrets.vault;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import
org.apache.camel.component.kubernetes.properties.SecretPropertiesFunction;
import org.apache.camel.console.DevConsole;
import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.test.junit6.CamelTestSupport;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullAndEmptySource;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * SecretsDevConsole unconditionally dereferences the Kubernetes vault
configuration's secrets list (a pre-existing bug,
- * preserved as-is by this migration), so calling it without a real Kubernetes
vault configuration would NPE; this test
- * is limited to verifying the console registers correctly.
- */
public class SecretsDevConsoleTest extends CamelTestSupport {
@Test
@@ -38,4 +43,78 @@ public class SecretsDevConsoleTest extends CamelTestSupport {
assertEquals("camel", con.getGroup());
assertEquals("kubernetes-secrets", con.getId());
}
+
+ @ParameterizedTest
+ @NullAndEmptySource
+ public void testJsonWithoutConfiguredNames(String names) throws Exception {
+ context.getVaultConfiguration().kubernetes().setSecrets(names);
+ DevConsole con = startConsole();
+ Map<?, ?> out = (Map<?, ?>) con.call(DevConsole.MediaType.JSON);
+ assertEquals(List.of(), out.get("secrets"));
+ assertFalse(out.containsKey("masterUrl"));
+ assertFalse(out.containsKey("login"));
+ }
+
+ @ParameterizedTest
+ @NullAndEmptySource
+ public void testTextWithoutConfiguredNames(String names) throws Exception {
+ context.getVaultConfiguration().kubernetes().setSecrets(names);
+ DevConsole con = startConsole();
+ String out = (String) con.call(DevConsole.MediaType.TEXT);
+ assertTrue(out.contains("Secrets in use:"));
+ assertFalse(out.contains("Master Url:"));
+ assertFalse(out.contains("Login:"));
+ }
+
+ @Test
+ public void testConfiguredNamesJson() throws Exception {
+ context.getVaultConfiguration().kubernetes().setSecrets("zulu,alpha");
+
context.getVaultConfiguration().kubernetesConfigmaps().setConfigmaps("unrelated");
+ context.getVaultConfiguration().kubernetes().setRefreshEnabled(true);
+
context.getVaultConfiguration().kubernetesConfigmaps().setRefreshEnabled(false);
+ DevConsole con = startConsole();
+ Map<?, ?> out = (Map<?, ?>) con.call(DevConsole.MediaType.JSON);
+ assertEquals(true, out.get("refreshEnabled"));
+ assertEquals(List.of(Map.of("name", "alpha"), Map.of("name", "zulu")),
out.get("secrets"));
+ }
+
+ @Test
+ public void testConfiguredNamesText() throws Exception {
+ context.getVaultConfiguration().kubernetes().setSecrets("zulu,alpha");
+
context.getVaultConfiguration().kubernetesConfigmaps().setConfigmaps("unrelated");
+ DevConsole con = startConsole();
+ String out = (String) con.call(DevConsole.MediaType.TEXT);
+ assertTrue(out.contains("Secrets in use:"));
+ assertTrue(out.contains("alpha"));
+ assertTrue(out.contains("zulu"));
+ assertTrue(out.indexOf("alpha") < out.indexOf("zulu"));
+ assertFalse(out.contains("unrelated"));
+ }
+
+ @Test
+ public void testJsonWithoutVaultConfiguration() throws Exception {
+ context.getVaultConfiguration().setKubernetesVaultConfiguration(null);
+ DevConsole con = startConsole();
+ Map<?, ?> out = (Map<?, ?>) con.call(DevConsole.MediaType.JSON);
+ assertEquals(List.of(), out.get("secrets"));
+ assertFalse(out.containsKey("refreshEnabled"));
+ }
+
+ @Test
+ public void testTextWithoutVaultConfiguration() throws Exception {
+ context.getVaultConfiguration().setKubernetesVaultConfiguration(null);
+ DevConsole con = startConsole();
+ String out = (String) con.call(DevConsole.MediaType.TEXT);
+ assertFalse(out.contains("Refresh Enabled:"));
+ }
+
+ private DevConsole startConsole() throws Exception {
+ Properties properties = new Properties();
+ properties.setProperty("camel.kubernetes-config.local-mode", "true");
+ context.getPropertiesComponent().setInitialProperties(properties);
+ context.getPropertiesComponent().addPropertiesFunction(new
SecretPropertiesFunction());
+ DevConsole con =
PluginHelper.getDevConsoleResolver(context).resolveDevConsole("kubernetes-secrets");
+ ServiceHelper.startService(con);
+ return con;
+ }
}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 529fd7067b5a..443976a4fa07 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -4775,3 +4775,10 @@ consumes `AiToolResult` and handles its variants
exhaustively — for example an
`AiToolResult.ExecutionError` — must add a branch for `AuthorizationDenied`
and relay its `message()` to the model as
a refusal (do not rethrow). The in-tree adapters (camel-openai,
camel-spring-ai-chat, camel-langchain4j-agent and the
MCP server bridge) already handle it.
+
+=== camel-kubernetes
+
+The `kubernetes-configmaps` developer console now reports the config map names
and refresh settings from
+`camel.vault.kubernetescm` instead of reading the Kubernetes secrets vault
configuration.
+Its text output labels the list `Config maps in use:` instead of `Secrets in
use:`.
+Both Kubernetes vault consoles return empty lists when no resource names are
configured.