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}}
+             &amp;serverPort={{secret:mydb/myport}}
+             &amp;username={{secret:mydb/myuser}}
+             &amp;password={{secret:mydb/mypass}}
+             &amp;databaseName=cities
+             &amp;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
+             &amp;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.