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:51 UTC

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

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);
+        }
+    }
+
+}