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 10:17:48 UTC

[camel] branch cmap created (now 396f644d6e2)

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

davsclaus pushed a change to branch cmap
in repository https://gitbox.apache.org/repos/asf/camel.git


      at 396f644d6e2 CAMEL-18171: camel-kubernetes - Add secret property placeholder function

This branch includes the following new commits:

     new bd53c058d9f CAMEL-18171: camel-kubernetes - Add configmap property placeholder function
     new 889454b6773 CAMEL-18171: camel-kubernetes - Add configmap property placeholder function
     new 396f644d6e2 CAMEL-18171: camel-kubernetes - Add secret property placeholder function

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[camel] 02/03: CAMEL-18171: camel-kubernetes - Add configmap property placeholder function

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 889454b67734df8ad85de8b0b5d1201ed8328727
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Jun 23 11:23:48 2022 +0200

    CAMEL-18171: camel-kubernetes - Add configmap property placeholder function
---
 .../ROOT/pages/using-propertyplaceholder.adoc      | 112 +++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc b/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc
index c5b1f140c64..9475b9747d1 100644
--- a/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc
+++ b/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc
@@ -468,6 +468,118 @@ 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
+----
+
+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.


[camel] 03/03: CAMEL-18171: camel-kubernetes - Add secret property placeholder function

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 396f644d6e2c6113129ef0be574008077d588d36
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Jun 23 12:17:31 2022 +0200

    CAMEL-18171: camel-kubernetes - Add secret property placeholder function
---
 .../org/apache/camel/properties-function/secret    |  2 +
 ...n.java => BaseConfigMapPropertiesFunction.java} | 47 +-----------
 .../properties/ConfigMapPropertiesFunction.java    | 39 +---------
 ...Function.java => SecretPropertiesFunction.java} | 57 +++------------
 .../SecretPropertiesFunctionRouteTest.java         | 85 ++++++++++++++++++++++
 .../properties/SecretPropertiesFunctionTest.java   | 68 +++++++++++++++++
 6 files changed, 170 insertions(+), 128 deletions(-)

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/ConfigMapPropertiesFunction.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/BaseConfigMapPropertiesFunction.java
similarity index 55%
copy from components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunction.java
copy to components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/BaseConfigMapPropertiesFunction.java
index d6bcb117730..8a3c49e813b 100644
--- 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/BaseConfigMapPropertiesFunction.java
@@ -16,9 +16,6 @@
  */
 package org.apache.camel.component.kubernetes.properties;
 
-import java.util.Base64;
-
-import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.client.KubernetesClient;
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
@@ -26,13 +23,11 @@ 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;
 
 /**
- * A {@link PropertiesFunction} that can lookup from Kubernetes configmaps.
+ * Base for kubernetes {@link PropertiesFunction}.
  */
-@org.apache.camel.spi.annotations.PropertiesFunction("configmap")
-public class ConfigMapPropertiesFunction extends ServiceSupport implements PropertiesFunction, CamelContextAware {
+abstract class BaseConfigMapPropertiesFunction extends ServiceSupport implements PropertiesFunction, CamelContextAware {
 
     private CamelContext camelContext;
     private KubernetesClient client;
@@ -45,11 +40,6 @@ public class ConfigMapPropertiesFunction extends ServiceSupport implements Prope
         ObjectHelper.notNull(client, "KubernetesClient must be configured");
     }
 
-    @Override
-    public String getName() {
-        return "configmap";
-    }
-
     @Override
     public CamelContext getCamelContext() {
         return camelContext;
@@ -68,37 +58,4 @@ public class ConfigMapPropertiesFunction extends ServiceSupport implements Prope
         this.client = client;
     }
 
-    @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;
-        ConfigMap cm = client.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/ConfigMapPropertiesFunction.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunction.java
index d6bcb117730..b888d01c147 100644
--- 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
@@ -19,55 +19,20 @@ package org.apache.camel.component.kubernetes.properties;
 import java.util.Base64;
 
 import io.fabric8.kubernetes.api.model.ConfigMap;
-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;
 
 /**
  * A {@link PropertiesFunction} that can lookup from Kubernetes configmaps.
  */
 @org.apache.camel.spi.annotations.PropertiesFunction("configmap")
-public class ConfigMapPropertiesFunction extends ServiceSupport implements PropertiesFunction, CamelContextAware {
-
-    private CamelContext camelContext;
-    private KubernetesClient client;
-
-    @Override
-    protected void doInit() throws Exception {
-        if (client == null) {
-            client = CamelContextHelper.findSingleByType(camelContext, KubernetesClient.class);
-        }
-        ObjectHelper.notNull(client, "KubernetesClient must be configured");
-    }
+public class ConfigMapPropertiesFunction extends BaseConfigMapPropertiesFunction {
 
     @Override
     public String getName() {
         return "configmap";
     }
 
-    @Override
-    public CamelContext getCamelContext() {
-        return camelContext;
-    }
-
-    @Override
-    public void setCamelContext(CamelContext camelContext) {
-        this.camelContext = camelContext;
-    }
-
-    public KubernetesClient getClient() {
-        return client;
-    }
-
-    public void setClient(KubernetesClient client) {
-        this.client = client;
-    }
-
     @Override
     public String apply(String remainder) {
         String defaultValue = StringHelper.after(remainder, ":");
@@ -80,7 +45,7 @@ public class ConfigMapPropertiesFunction extends ServiceSupport implements Prope
         }
 
         String answer = null;
-        ConfigMap cm = client.configMaps().withName(name).get();
+        ConfigMap cm = getClient().configMaps().withName(name).get();
         if (cm != null) {
             answer = cm.getData() != null ? cm.getData().get(key) : null;
             if (answer == null) {
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/SecretPropertiesFunction.java
similarity index 53%
copy from components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunction.java
copy to components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunction.java
index d6bcb117730..70c49df0b55 100644
--- 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/SecretPropertiesFunction.java
@@ -18,54 +18,19 @@ package org.apache.camel.component.kubernetes.properties;
 
 import java.util.Base64;
 
-import io.fabric8.kubernetes.api.model.ConfigMap;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import org.apache.camel.CamelContext;
-import org.apache.camel.CamelContextAware;
+import io.fabric8.kubernetes.api.model.Secret;
 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;
 
 /**
- * A {@link PropertiesFunction} that can lookup from Kubernetes configmaps.
+ * A {@link PropertiesFunction} that can lookup from Kubernetes secret.
  */
-@org.apache.camel.spi.annotations.PropertiesFunction("configmap")
-public class ConfigMapPropertiesFunction extends ServiceSupport implements PropertiesFunction, CamelContextAware {
-
-    private CamelContext camelContext;
-    private KubernetesClient client;
-
-    @Override
-    protected void doInit() throws Exception {
-        if (client == null) {
-            client = CamelContextHelper.findSingleByType(camelContext, KubernetesClient.class);
-        }
-        ObjectHelper.notNull(client, "KubernetesClient must be configured");
-    }
+@org.apache.camel.spi.annotations.PropertiesFunction("secret")
+public class SecretPropertiesFunction extends BaseConfigMapPropertiesFunction {
 
     @Override
     public String getName() {
-        return "configmap";
-    }
-
-    @Override
-    public CamelContext getCamelContext() {
-        return camelContext;
-    }
-
-    @Override
-    public void setCamelContext(CamelContext camelContext) {
-        this.camelContext = camelContext;
-    }
-
-    public KubernetesClient getClient() {
-        return client;
-    }
-
-    public void setClient(KubernetesClient client) {
-        this.client = client;
+        return "secret";
     }
 
     @Override
@@ -80,14 +45,14 @@ public class ConfigMapPropertiesFunction extends ServiceSupport implements Prope
         }
 
         String answer = null;
-        ConfigMap cm = client.configMaps().withName(name).get();
-        if (cm != null) {
-            answer = cm.getData() != null ? cm.getData().get(key) : 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) {
-                // maybe a binary data
-                answer = cm.getBinaryData() != null ? cm.getBinaryData().get(key) : null;
+                // need to base64 decode from data
+                answer = sec.getData() != null ? sec.getData().get(key) : null;
                 if (answer != null) {
-                    // need to decode base64
                     byte[] data = Base64.getDecoder().decode(answer);
                     if (data != null) {
                         answer = new String(data);
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..5dfef3fbc29
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunctionRouteTest.java
@@ -0,0 +1,85 @@
+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();
+        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..5d7339f7c85
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/SecretPropertiesFunctionTest.java
@@ -0,0 +1,68 @@
+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);
+        }
+    }
+
+}


[camel] 01/03: CAMEL-18171: camel-kubernetes - Add configmap property placeholder function

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit bd53c058d9f9d911ed6ddce88a6ebac3292af356
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Jun 23 10:52:38 2022 +0200

    CAMEL-18171: camel-kubernetes - Add configmap property placeholder function
---
 components/camel-kubernetes/README.md              |  38 ++++++++
 .../org/apache/camel/properties-function/configmap |   2 +
 .../properties/ConfigMapPropertiesFunction.java    | 104 +++++++++++++++++++++
 .../ConfigMapPropertiesFunctionRouteTest.java      |  81 ++++++++++++++++
 .../ConfigMapPropertiesFunctionTest.java           |  64 +++++++++++++
 5 files changed, 289 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/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..d6bcb117730
--- /dev/null
+++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunction.java
@@ -0,0 +1,104 @@
+/*
+ * 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.Base64;
+
+import io.fabric8.kubernetes.api.model.ConfigMap;
+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;
+
+/**
+ * A {@link PropertiesFunction} that can lookup from Kubernetes configmaps.
+ */
+@org.apache.camel.spi.annotations.PropertiesFunction("configmap")
+public class ConfigMapPropertiesFunction extends ServiceSupport implements PropertiesFunction, CamelContextAware {
+
+    private CamelContext camelContext;
+    private KubernetesClient client;
+
+    @Override
+    protected void doInit() throws Exception {
+        if (client == null) {
+            client = CamelContextHelper.findSingleByType(camelContext, KubernetesClient.class);
+        }
+        ObjectHelper.notNull(client, "KubernetesClient must be configured");
+    }
+
+    @Override
+    public String getName() {
+        return "configmap";
+    }
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    public KubernetesClient getClient() {
+        return client;
+    }
+
+    public void setClient(KubernetesClient client) {
+        this.client = client;
+    }
+
+    @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;
+        ConfigMap cm = client.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/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..e4e65b806c8
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunctionRouteTest.java
@@ -0,0 +1,81 @@
+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..80cef7af3b2
--- /dev/null
+++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/properties/ConfigMapPropertiesFunctionTest.java
@@ -0,0 +1,64 @@
+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);
+        }
+    }
+
+}