You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/06/23 13:55:57 UTC
[camel] branch main updated: camel-kubernetes - configmap and secret property placeholder function (#7865)
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 4a053652f3b camel-kubernetes - configmap and secret property placeholder function (#7865)
4a053652f3b is described below
commit 4a053652f3bf50bcf6243884deb62d7d3fb3ee5b
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Jun 23 15:55:50 2022 +0200
camel-kubernetes - configmap and secret property placeholder function (#7865)
CAMEL-18171: camel-kubernetes - Add secret/configmap property placeholder function
---
components/camel-kubernetes/README.md | 38 ++++++
.../org/apache/camel/properties-function/configmap | 2 +
.../org/apache/camel/properties-function/secret | 2 +
.../properties/BasePropertiesFunction.java | 149 +++++++++++++++++++++
.../properties/ConfigMapPropertiesFunction.java | 69 ++++++++++
.../properties/SecretPropertiesFunction.java | 69 ++++++++++
.../ConfigMapMountPropertiesFunctionTest.java | 52 +++++++
.../ConfigMapPropertiesFunctionRouteTest.java | 97 ++++++++++++++
.../ConfigMapPropertiesFunctionTest.java | 80 +++++++++++
.../SecretMountPropertiesFunctionTest.java | 52 +++++++
.../SecretPropertiesFunctionRouteTest.java | 101 ++++++++++++++
.../properties/SecretPropertiesFunctionTest.java | 84 ++++++++++++
.../src/test/resources/myconfig/bar | 1 +
.../src/test/resources/myconfig/foo | 1 +
.../src/test/resources/mysecret/mypass | 1 +
.../src/test/resources/mysecret/myuser | 1 +
.../ROOT/pages/using-propertyplaceholder.adoc | 136 +++++++++++++++++++
17 files changed, 935 insertions(+)
diff --git a/components/camel-kubernetes/README.md b/components/camel-kubernetes/README.md
index bc14b34380a..59670dd2689 100644
--- a/components/camel-kubernetes/README.md
+++ b/components/camel-kubernetes/README.md
@@ -4,6 +4,8 @@
This component contains unit and integration tests. Some of them - like the consumer ones - require a Kubernetes environment.
+## Running using Kind
+
It is possible to run the integration tests using Kind. To do so, follow these steps:
1. Create a cluster:
@@ -30,3 +32,39 @@ export KUBE_HOST=https://localhost:$KIND_PORT
mvn -Dkubernetes.test.auth="$KUBE_TOKEN" -Dkubernetes.test.host=$KUBE_HOST -Dkubernetes.test.host.k8s=true clean verify
```
+
+## Running using Minikube
+
+It is possible to run the integration tests using Minikube. To do so, follow these steps:
+
+1. Create a cluster:
+
+```
+minikube start
+```
+
+2. Get the auth token:
+
+```
+export KUBE_TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='default')].data.token}"|base64 --decode)
+```
+
+4. Get the host:
+
+Find out the URL where the control plane is running:
+
+````
+kubectl cluster-info
+````
+
+And then set that as export, for example:
+
+```
+export KUBE_HOST=https://127.0.0.1:50179
+```
+
+5. Run the test:
+```
+mvn -Dkubernetes.test.auth="$KUBE_TOKEN" -Dkubernetes.test.host=$KUBE_HOST -Dkubernetes.test.host.k8s=true clean verify
+```
+
diff --git a/components/camel-kubernetes/src/generated/resources/META-INF/services/org/apache/camel/properties-function/configmap b/components/camel-kubernetes/src/generated/resources/META-INF/services/org/apache/camel/properties-function/configmap
new file mode 100644
index 00000000000..fd8e5d4f6d4
--- /dev/null
+++ b/components/camel-kubernetes/src/generated/resources/META-INF/services/org/apache/camel/properties-function/configmap
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.kubernetes.properties.ConfigMapPropertiesFunction
diff --git a/components/camel-kubernetes/src/generated/resources/META-INF/services/org/apache/camel/properties-function/secret b/components/camel-kubernetes/src/generated/resources/META-INF/services/org/apache/camel/properties-function/secret
new file mode 100644
index 00000000000..ff86576daa6
--- /dev/null
+++ b/components/camel-kubernetes/src/generated/resources/META-INF/services/org/apache/camel/properties-function/secret
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.kubernetes.properties.SecretPropertiesFunction
diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/BasePropertiesFunction.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/BasePropertiesFunction.java
new file mode 100644
index 00000000000..b8ff0740bb5
--- /dev/null
+++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/BasePropertiesFunction.java
@@ -0,0 +1,149 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Locale;
+
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.spi.PropertiesFunction;
+import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.service.ServiceSupport;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+
+/**
+ * Base for kubernetes {@link PropertiesFunction}.
+ */
+abstract class BasePropertiesFunction extends ServiceSupport implements PropertiesFunction, CamelContextAware {
+
+ // keys in application.properties for mount paths
+ public static final String MOUNT_PATH_CONFIGMAPS = "org.apache.camel.component.kubernetes.properties.mount-path-configmaps";
+ public static final String MOUNT_PATH_SECRETS = "org.apache.camel.component.kubernetes.properties.mount-path-secrets";
+
+ // use camel-k ENV for mount paths
+ public static final String ENV_MOUNT_PATH_CONFIGMAPS = "camel.k.mount-path.configmaps";
+ public static final String ENV_MOUNT_PATH_SECRETS = "camel.k.mount-path.secrets";
+
+ private CamelContext camelContext;
+ private KubernetesClient client;
+ private String mountPathConfigMaps;
+ private String mountPathSecrets;
+
+ @Override
+ protected void doInit() throws Exception {
+ ObjectHelper.notNull(camelContext, "CamelContext");
+ if (mountPathConfigMaps == null) {
+ mountPathConfigMaps = camelContext.getPropertiesComponent().resolveProperty(MOUNT_PATH_CONFIGMAPS)
+ .orElseGet(() -> System.getProperty(ENV_MOUNT_PATH_CONFIGMAPS, System.getenv(ENV_MOUNT_PATH_CONFIGMAPS)));
+ }
+ if (mountPathSecrets == null) {
+ mountPathSecrets = camelContext.getPropertiesComponent().resolveProperty(MOUNT_PATH_SECRETS)
+ .orElseGet(() -> System.getProperty(ENV_MOUNT_PATH_SECRETS, System.getenv(ENV_MOUNT_PATH_SECRETS)));
+ }
+ if (client == null) {
+ client = CamelContextHelper.findSingleByType(camelContext, KubernetesClient.class);
+ }
+ if (client == null && getMountPath() == null) {
+ throw new IllegalArgumentException("Either a mount path or the Kubernetes Client must be configured");
+ }
+ }
+
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ public KubernetesClient getClient() {
+ return client;
+ }
+
+ /**
+ * To use an existing kubernetes client to use
+ */
+ public void setClient(KubernetesClient client) {
+ this.client = client;
+ }
+
+ public String getMountPathConfigMaps() {
+ return mountPathConfigMaps;
+ }
+
+ /**
+ * To use a volume mount to load configmaps, instead of using the Kubernetes API server
+ */
+ public void setMountPathConfigMaps(String mountPathConfigMaps) {
+ this.mountPathConfigMaps = mountPathConfigMaps;
+ }
+
+ public String getMountPathSecrets() {
+ return mountPathSecrets;
+ }
+
+ /**
+ * To use a volume mount to load secrets, instead of using the Kubernetes API server.
+ */
+ public void setMountPathSecrets(String mountPathSecrets) {
+ this.mountPathSecrets = mountPathSecrets;
+ }
+
+ @Override
+ public String apply(String remainder) {
+ String defaultValue = StringHelper.after(remainder, ":");
+ String name = StringHelper.before(remainder, "/");
+ String key = StringHelper.after(remainder, "/");
+ if (name == null || key == null) {
+ return defaultValue;
+ }
+
+ String answer = null;
+ Path root = getMountPath();
+ if (root != null) {
+ Path file = root.resolve(name.toLowerCase(Locale.US)).resolve(key);
+ if (Files.exists(file) && !Files.isDirectory(file)) {
+ try {
+ answer = Files.readString(file, StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ if (answer == null && client != null) {
+ answer = lookup(name, key, defaultValue);
+ }
+ if (answer == null) {
+ answer = defaultValue;
+ }
+
+ return answer;
+ }
+
+ abstract Path getMountPath();
+
+ abstract String lookup(String name, String key, String defaultValue);
+}
diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunction.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunction.java
new file mode 100644
index 00000000000..45c557c2333
--- /dev/null
+++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunction.java
@@ -0,0 +1,69 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Base64;
+
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import org.apache.camel.spi.PropertiesFunction;
+
+/**
+ * A {@link PropertiesFunction} that can lookup from Kubernetes configmaps.
+ */
+@org.apache.camel.spi.annotations.PropertiesFunction("configmap")
+public class ConfigMapPropertiesFunction extends BasePropertiesFunction {
+
+ @Override
+ public String getName() {
+ return "configmap";
+ }
+
+ @Override
+ Path getMountPath() {
+ if (getMountPathConfigMaps() != null) {
+ return Paths.get(getMountPathConfigMaps());
+ }
+ return null;
+ }
+
+ @Override
+ String lookup(String name, String key, String defaultValue) {
+ String answer = null;
+ ConfigMap cm = getClient().configMaps().withName(name).get();
+ if (cm != null) {
+ answer = cm.getData() != null ? cm.getData().get(key) : null;
+ if (answer == null) {
+ // maybe a binary data
+ answer = cm.getBinaryData() != null ? cm.getBinaryData().get(key) : null;
+ if (answer != null) {
+ // need to decode base64
+ byte[] data = Base64.getDecoder().decode(answer);
+ if (data != null) {
+ answer = new String(data);
+ }
+ }
+ }
+ }
+ if (answer == null) {
+ return defaultValue;
+ }
+
+ return answer;
+ }
+}
diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunction.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunction.java
new file mode 100644
index 00000000000..1dc32727705
--- /dev/null
+++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunction.java
@@ -0,0 +1,69 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Base64;
+
+import io.fabric8.kubernetes.api.model.Secret;
+import org.apache.camel.spi.PropertiesFunction;
+
+/**
+ * A {@link PropertiesFunction} that can lookup from Kubernetes secret.
+ */
+@org.apache.camel.spi.annotations.PropertiesFunction("secret")
+public class SecretPropertiesFunction extends BasePropertiesFunction {
+
+ @Override
+ public String getName() {
+ return "secret";
+ }
+
+ @Override
+ Path getMountPath() {
+ if (getMountPathSecrets() != null) {
+ return Paths.get(getMountPathSecrets());
+ }
+ return null;
+ }
+
+ @Override
+ String lookup(String name, String key, String defaultValue) {
+ String answer = null;
+ Secret sec = getClient().secrets().withName(name).get();
+ if (sec != null) {
+ // string data can be used as-is
+ answer = sec.getStringData() != null ? sec.getStringData().get(key) : null;
+ if (answer == null) {
+ // need to base64 decode from data
+ answer = sec.getData() != null ? sec.getData().get(key) : null;
+ if (answer != null) {
+ byte[] data = Base64.getDecoder().decode(answer);
+ if (data != null) {
+ answer = new String(data);
+ }
+ }
+ }
+ }
+ if (answer == null) {
+ return defaultValue;
+ }
+
+ return answer;
+ }
+}
diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapMountPropertiesFunctionTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapMountPropertiesFunctionTest.java
new file mode 100644
index 00000000000..64eee16ca01
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapMountPropertiesFunctionTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import org.apache.camel.component.kubernetes.KubernetesTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class ConfigMapMountPropertiesFunctionTest extends KubernetesTestSupport {
+
+ @Test
+ @Order(1)
+ public void configMapMountPropertiesFunction() throws Exception {
+ ConfigMapPropertiesFunction cmf = new ConfigMapPropertiesFunction();
+ cmf.setMountPathConfigMaps("src/test/resources/");
+ cmf.setCamelContext(context);
+ cmf.start();
+
+ String out = cmf.apply("myconfig/foo");
+ Assertions.assertEquals("456", out);
+
+ out = cmf.apply("myconfig/unknown");
+ Assertions.assertNull(out);
+
+ out = cmf.apply("myconfig/unknown:444");
+ Assertions.assertEquals("444", out);
+
+ out = cmf.apply("myconfig/bar");
+ Assertions.assertEquals("Jacks Bar", out);
+
+ cmf.stop();
+ }
+
+}
diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunctionRouteTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunctionRouteTest.java
new file mode 100644
index 00000000000..3f21d598dc8
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunctionRouteTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import java.util.Map;
+
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.kubernetes.KubernetesTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperties;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+
+@EnabledIfSystemProperties({
+ @EnabledIfSystemProperty(named = "kubernetes.test.auth", matches = ".*", disabledReason = "Requires kubernetes"),
+ @EnabledIfSystemProperty(named = "kubernetes.test.host", matches = ".*", disabledReason = "Requires kubernetes"),
+ @EnabledIfSystemProperty(named = "kubernetes.test.host.k8s", matches = "true", disabledReason = "Requires kubernetes"),
+})
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class ConfigMapPropertiesFunctionRouteTest extends KubernetesTestSupport {
+
+ private KubernetesClient client;
+ private ConfigMap cm;
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .transform().simple("Hello ${body} we are at {{configmap:myconfig/bar}}");
+ }
+ };
+ }
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ CamelContext context = super.createCamelContext();
+
+ ConfigBuilder builder = new ConfigBuilder();
+ builder.withOauthToken(authToken);
+ builder.withMasterUrl(host);
+ client = new DefaultKubernetesClient(builder.build());
+ context.getRegistry().bind("KubernetesClient", client);
+
+ Map<String, String> data = Map.of("foo", "123", "bar", "Moes Bar");
+ ConfigMap cm = new ConfigMapBuilder().editOrNewMetadata().withName("myconfig").endMetadata().withData(data).build();
+ this.cm = client.configMaps().createOrReplace(cm);
+
+ return context;
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (client != null && cm != null) {
+ try {
+ client.configMaps().delete(cm);
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ super.tearDown();
+ }
+
+ @Test
+ @Order(1)
+ public void configMapPropertiesFunction() throws Exception {
+ String out = template.requestBody("direct:start", "Jack", String.class);
+ Assertions.assertEquals("Hello Jack we are at Moes Bar", out);
+ }
+
+}
diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunctionTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunctionTest.java
new file mode 100644
index 00000000000..efa18410218
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunctionTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import java.util.Map;
+
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.apache.camel.component.kubernetes.KubernetesTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperties;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+
+@EnabledIfSystemProperties({
+ @EnabledIfSystemProperty(named = "kubernetes.test.auth", matches = ".*", disabledReason = "Requires kubernetes"),
+ @EnabledIfSystemProperty(named = "kubernetes.test.host", matches = ".*", disabledReason = "Requires kubernetes"),
+ @EnabledIfSystemProperty(named = "kubernetes.test.host.k8s", matches = "true", disabledReason = "Requires kubernetes"),
+})
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class ConfigMapPropertiesFunctionTest extends KubernetesTestSupport {
+
+ @Test
+ @Order(1)
+ public void configMapPropertiesFunction() throws Exception {
+ ConfigBuilder builder = new ConfigBuilder();
+ builder.withOauthToken(authToken);
+ builder.withMasterUrl(host);
+
+ KubernetesClient client = new DefaultKubernetesClient(builder.build());
+
+ Map<String, String> data = Map.of("foo", "123", "bar", "Moes Bar");
+ ConfigMap cm = new ConfigMapBuilder().editOrNewMetadata().withName("myconfig").endMetadata().withData(data).build();
+ client.configMaps().createOrReplace(cm);
+
+ try {
+ ConfigMapPropertiesFunction cmf = new ConfigMapPropertiesFunction();
+ cmf.setClient(client);
+ cmf.setCamelContext(context);
+ cmf.start();
+
+ String out = cmf.apply("myconfig/foo");
+ Assertions.assertEquals("123", out);
+
+ out = cmf.apply("myconfig/unknown");
+ Assertions.assertNull(out);
+
+ out = cmf.apply("myconfig/unknown:444");
+ Assertions.assertEquals("444", out);
+
+ out = cmf.apply("myconfig/bar");
+ Assertions.assertEquals("Moes Bar", out);
+
+ cmf.stop();
+ } finally {
+ client.configMaps().delete(cm);
+ }
+ }
+
+}
diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretMountPropertiesFunctionTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretMountPropertiesFunctionTest.java
new file mode 100644
index 00000000000..d45a1bb8356
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretMountPropertiesFunctionTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import org.apache.camel.component.kubernetes.KubernetesTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class SecretMountPropertiesFunctionTest extends KubernetesTestSupport {
+
+ @Test
+ @Order(1)
+ public void secretMountPropertiesFunction() throws Exception {
+ SecretPropertiesFunction cmf = new SecretPropertiesFunction();
+ cmf.setMountPathSecrets("src/test/resources/");
+ cmf.setCamelContext(context);
+ cmf.start();
+
+ String out = cmf.apply("mysecret/myuser");
+ Assertions.assertEquals("donald", out);
+
+ out = cmf.apply("mysecret/unknown");
+ Assertions.assertNull(out);
+
+ out = cmf.apply("mysecret/unknown:444");
+ Assertions.assertEquals("444", out);
+
+ out = cmf.apply("mysecret/mypass");
+ Assertions.assertEquals("seCre!t", out);
+
+ cmf.stop();
+ }
+
+}
diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunctionRouteTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunctionRouteTest.java
new file mode 100644
index 00000000000..f26adb9349c
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunctionRouteTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Map;
+
+import io.fabric8.kubernetes.api.model.Secret;
+import io.fabric8.kubernetes.api.model.SecretBuilder;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.kubernetes.KubernetesTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperties;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+
+@EnabledIfSystemProperties({
+ @EnabledIfSystemProperty(named = "kubernetes.test.auth", matches = ".*", disabledReason = "Requires kubernetes"),
+ @EnabledIfSystemProperty(named = "kubernetes.test.host", matches = ".*", disabledReason = "Requires kubernetes"),
+ @EnabledIfSystemProperty(named = "kubernetes.test.host.k8s", matches = "true", disabledReason = "Requires kubernetes"),
+})
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class SecretPropertiesFunctionRouteTest extends KubernetesTestSupport {
+
+ private KubernetesClient client;
+ private Secret sec;
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .transform().simple("Connect with {{secret:mysecret/myuser}}:{{secret:mysecret/mypass}}");
+ }
+ };
+ }
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ CamelContext context = super.createCamelContext();
+
+ ConfigBuilder builder = new ConfigBuilder();
+ builder.withOauthToken(authToken);
+ builder.withMasterUrl(host);
+ client = new DefaultKubernetesClient(builder.build());
+ context.getRegistry().bind("KubernetesClient", client);
+
+ Map<String, String> data
+ = Map.of("myuser", Base64.getEncoder().encodeToString("scott".getBytes(StandardCharsets.UTF_8)),
+ "mypass", Base64.getEncoder().encodeToString("tiger".getBytes(StandardCharsets.UTF_8)));
+ Secret sec = new SecretBuilder().editOrNewMetadata().withName("mysecret").endMetadata().withData(data).build();
+ this.sec = client.secrets().createOrReplace(sec);
+
+ return context;
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (client != null && sec != null) {
+ try {
+ client.secrets().delete(sec);
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ super.tearDown();
+ }
+
+ @Test
+ @Order(1)
+ public void secretPropertiesFunction() throws Exception {
+ String out = template.requestBody("direct:start", null, String.class);
+ Assertions.assertEquals("Connect with scott:tiger", out);
+ }
+
+}
diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunctionTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunctionTest.java
new file mode 100644
index 00000000000..c925ae677f7
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunctionTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.component.kubernetes.properties;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Map;
+
+import io.fabric8.kubernetes.api.model.Secret;
+import io.fabric8.kubernetes.api.model.SecretBuilder;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.apache.camel.component.kubernetes.KubernetesTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperties;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+
+@EnabledIfSystemProperties({
+ @EnabledIfSystemProperty(named = "kubernetes.test.auth", matches = ".*", disabledReason = "Requires kubernetes"),
+ @EnabledIfSystemProperty(named = "kubernetes.test.host", matches = ".*", disabledReason = "Requires kubernetes"),
+ @EnabledIfSystemProperty(named = "kubernetes.test.host.k8s", matches = "true", disabledReason = "Requires kubernetes"),
+})
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class SecretPropertiesFunctionTest extends KubernetesTestSupport {
+
+ @Test
+ @Order(1)
+ public void secretPropertiesFunction() throws Exception {
+ ConfigBuilder builder = new ConfigBuilder();
+ builder.withOauthToken(authToken);
+ builder.withMasterUrl(host);
+
+ KubernetesClient client = new DefaultKubernetesClient(builder.build());
+
+ Map<String, String> data
+ = Map.of("myuser", Base64.getEncoder().encodeToString("scott".getBytes(StandardCharsets.UTF_8)),
+ "mypass", Base64.getEncoder().encodeToString("tiger".getBytes(StandardCharsets.UTF_8)));
+ Secret sec = new SecretBuilder().editOrNewMetadata().withName("mysecret").endMetadata().withData(data).build();
+ client.secrets().createOrReplace(sec);
+
+ try {
+ SecretPropertiesFunction cmf = new SecretPropertiesFunction();
+ cmf.setClient(client);
+ cmf.setCamelContext(context);
+ cmf.start();
+
+ String out = cmf.apply("mysecret/myuser");
+ Assertions.assertEquals("scott", out);
+
+ out = cmf.apply("mysecret/unknown");
+ Assertions.assertNull(out);
+
+ out = cmf.apply("mysecret/unknown:444");
+ Assertions.assertEquals("444", out);
+
+ out = cmf.apply("mysecret/mypass");
+ Assertions.assertEquals("tiger", out);
+
+ cmf.stop();
+ } finally {
+ client.secrets().delete(sec);
+ }
+ }
+
+}
diff --git a/components/camel-kubernetes/src/test/resources/myconfig/bar b/components/camel-kubernetes/src/test/resources/myconfig/bar
new file mode 100644
index 00000000000..5d3a415ad68
--- /dev/null
+++ b/components/camel-kubernetes/src/test/resources/myconfig/bar
@@ -0,0 +1 @@
+Jacks Bar
\ No newline at end of file
diff --git a/components/camel-kubernetes/src/test/resources/myconfig/foo b/components/camel-kubernetes/src/test/resources/myconfig/foo
new file mode 100644
index 00000000000..ee2b8364542
--- /dev/null
+++ b/components/camel-kubernetes/src/test/resources/myconfig/foo
@@ -0,0 +1 @@
+456
\ No newline at end of file
diff --git a/components/camel-kubernetes/src/test/resources/mysecret/mypass b/components/camel-kubernetes/src/test/resources/mysecret/mypass
new file mode 100644
index 00000000000..45efababf6a
--- /dev/null
+++ b/components/camel-kubernetes/src/test/resources/mysecret/mypass
@@ -0,0 +1 @@
+seCre!t
\ No newline at end of file
diff --git a/components/camel-kubernetes/src/test/resources/mysecret/myuser b/components/camel-kubernetes/src/test/resources/mysecret/myuser
new file mode 100644
index 00000000000..21115598500
--- /dev/null
+++ b/components/camel-kubernetes/src/test/resources/mysecret/myuser
@@ -0,0 +1 @@
+donald
\ No newline at end of file
diff --git a/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc b/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc
index c5b1f140c64..9ea2a0b1450 100644
--- a/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc
+++ b/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc
@@ -468,6 +468,142 @@ And we can use default values if the service has not been defined, for example t
</camelContext>
----
+=== Using Kubernetes property placeholder functions
+
+The `camel-kubernetes` component include the following functions:
+
+* `configmap` - A function to lookup the property from Kubernetes ConfigMaps.
+* `secert` - A function to lookup the property from Kubernetes Secrets.
+
+The syntax for both functions are:
+
+[source]
+----
+configmap:name/key[:defaultValue]
+----
+
+Where the default value is optional, for example
+
+[source]
+----
+configmap:mymap/mykey
+configmap:mymap/mykey:123
+----
+
+Before the Kubernetes property placeholder functions can be used they need to be configured with either (or both)
+
+- path - A _mount path_ that must be mounted to the running pod, to load the configmaps or secrets from local disk.
+- kubernetes client - *Autowired* An `io.fabric8.kubernetes.client.KubernetesClient` instance to use for connecting to the Kubernetes API server.
+
+Camel will autowire the `KubernetesClient` if a single instance of the client exists in the running application (lookup via the xref:registry.adoc[Registry]).
+
+The configuration of the _month path_ are used by the given order:
+
+1. Reading configuration property with keys `org.apache.camel.component.kubernetes.properties.mount-path-configmaps`
+and `org.apache.camel.component.kubernetes.properties.mount-path-secrets`.
+2. Use JVM system property with key `camel.k.mount-path.configmaps` and `camel.k.mount-path.secrets` (Camel K compatible).
+3. Use OS ENV variable with key `camel.k.mount-path.configmaps` and `camel.k.mount-path.secrets` (Camel K compatible).
+
+For example to use `/etc/camel/resources/` as mount path, you can configure this in the `application.properties`:
+
+[source,properties]
+----
+org.apache.camel.component.kubernetes.properties.mount-path-configmaps = /etc/camel/myconfig/
+org.apache.camel.component.kubernetes.properties.mount-path-secrets = /etc/camel/mysecrets/
+----
+
+==== Using configmap with Kubernetes
+
+Given a configmap named `myconfig` in Kubernetes that has two entries:
+
+[source,properties]
+----
+drink = beer
+first = Carlsberg
+----
+
+Then these values can be used in your Camel routes such as:
+
+[source,xml]
+----
+<camelContext>
+ <route>
+ <from uri="direct:start"/>
+ <log message="What {{configmap:myconfig/drink}} do you want?"/>
+ <log message="I want {{configmap:myconfig/first}}"/>
+ </route>
+</camelContext>
+----
+
+You can also provide a default value in case a key does not exists:
+
+[source,xml]
+----
+ <log message="I want {{configmap:myconfig/second:Heineken}}"/>
+----
+
+==== Using secrets with Kubernetes
+
+Camel reads ConfigMaps from the Kubernetes API Server. And when RBAC is enabled on the cluster,
+the ServiceAccount that is used to run the application needs to have the proper permissions for such access.
+
+A secret named `mydb` could contain username and passwords to connect to a database such as:
+
+[source,properties]
+----
+myhost = killroy
+myport = 5555
+myuser = scott
+mypass = tiger
+----
+
+This can be used in Camel with for example the Postrgres Sink Kamelet:
+
+[source,xml]
+----
+<camelContext>
+ <route>
+ <from uri="direct:rome"/>
+ <setBody>
+ <constant>{ "username":"oscerd", "city":"Rome"}</constant>
+ </setBody>
+ <to uri="kamelet:postgresql-sink?serverName={{secret:mydb/myhost}}
+ &serverPort={{secret:mydb/myport}}
+ &username={{secret:mydb/myuser}}
+ &password={{secret:mydb/mypass}}
+ &databaseName=cities
+ &query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"/>
+ </route>
+</camelContext>
+----
+
+The postgres-sink Kamelet can also be configured in `application.properties` which reduces the configuration
+in the route above:
+
+[source,properties]
+----
+camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
+camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
+camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
+camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
+----
+
+Which reduces the route to:
+
+[source,xml]
+----
+<camelContext>
+ <route>
+ <from uri="direct:rome"/>
+ <setBody>
+ <constant>{ "username":"oscerd", "city":"Rome"}</constant>
+ </setBody>
+ <to uri="kamelet:postgresql-sink?databaseName=cities
+ &query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"/>
+ </route>
+</camelContext>
+----
+
=== Using custom property placeholder functions
The xref:components::properties-component.adoc[Properties] component allow to plugin 3rd party functions which can be used during parsing of the property placeholders.