diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/functions/FunctionConfig.java b/pulsar-common/src/main/java/org/apache/pulsar/common/functions/FunctionConfig.java index 2163df1db96ce..28a15af4d1a1e 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/functions/FunctionConfig.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/functions/FunctionConfig.java @@ -82,7 +82,8 @@ public enum Runtime { // This is a map of secretName(aka how the secret is going to be // accessed in the function via context) to an object that // encapsulates how the secret is fetched by the underlying - // secrets provider + // secrets provider. The type of an value here can be found by the + // SecretProviderConfigurator.getSecretObjectType() method. private Map secrets; private Runtime runtime; private boolean autoAck; diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/io/SinkConfig.java b/pulsar-common/src/main/java/org/apache/pulsar/common/io/SinkConfig.java index 355c6962e5287..3eacfbcaef5b8 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/io/SinkConfig.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/io/SinkConfig.java @@ -58,7 +58,8 @@ public class SinkConfig { // This is a map of secretName(aka how the secret is going to be // accessed in the function via context) to an object that // encapsulates how the secret is fetched by the underlying - // secrets provider + // secrets provider. The type of an value here can be found by the + // SecretProviderConfigurator.getSecretObjectType() method. private Map secrets; private int parallelism = 1; private FunctionConfig.ProcessingGuarantees processingGuarantees; diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/io/SourceConfig.java b/pulsar-common/src/main/java/org/apache/pulsar/common/io/SourceConfig.java index 9dbe97c3cdfdc..c1b2efea9828a 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/io/SourceConfig.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/io/SourceConfig.java @@ -49,7 +49,8 @@ public class SourceConfig { // This is a map of secretName(aka how the secret is going to be // accessed in the function via context) to an object that // encapsulates how the secret is fetched by the underlying - // secrets provider + // secrets provider. The type of an value here can be found by the + // SecretProviderConfigurator.getSecretObjectType() method. private Map secrets; private int parallelism = 1; private FunctionConfig.ProcessingGuarantees processingGuarantees; diff --git a/pulsar-functions/pom.xml b/pulsar-functions/pom.xml index f54effdb1368c..a27c21b3875cc 100644 --- a/pulsar-functions/pom.xml +++ b/pulsar-functions/pom.xml @@ -43,6 +43,7 @@ runtime-shaded runtime-all worker + secrets diff --git a/pulsar-functions/secrets/pom.xml b/pulsar-functions/secrets/pom.xml new file mode 100644 index 0000000000000..fa411d7d1c2ae --- /dev/null +++ b/pulsar-functions/secrets/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + org.apache.pulsar + pulsar-functions + 2.3.0-SNAPSHOT + + + pulsar-functions-secrets + Pulsar Functions :: Secrets + + + + io.kubernetes + client-java + 2.0.0 + compile + + + ch.qos.logback + logback-classic + + + + + + org.apache.pulsar + pulsar-functions-proto + ${project.version} + + + + diff --git a/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/ClearTextSecretsProvider.java b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/ClearTextSecretsProvider.java new file mode 100644 index 0000000000000..adb285287a075 --- /dev/null +++ b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/ClearTextSecretsProvider.java @@ -0,0 +1,38 @@ +/** + * 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.pulsar.functions.secretsprovider; + +/** + * This file defines a very basic clear text secrets provider which treats + * the secrets as being passed in cleartext. + */ +public class ClearTextSecretsProvider implements SecretsProvider { + /** + * Fetches a secret + * @return The actual secret + */ + @Override + public String provideSecret(String secretName, Object pathToSecret) { + if (pathToSecret != null) { + return pathToSecret.toString(); + } else { + return null; + } + } +} \ No newline at end of file diff --git a/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/EnvironmentBasedSecretsProvider.java b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/EnvironmentBasedSecretsProvider.java new file mode 100644 index 0000000000000..b058709741884 --- /dev/null +++ b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/EnvironmentBasedSecretsProvider.java @@ -0,0 +1,35 @@ +/** + * 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.pulsar.functions.secretsprovider; + +/** + * This defines a very simple Secrets Provider that looks up environment variable + * thats named the same as secretName and fetches it. + */ +public class EnvironmentBasedSecretsProvider implements SecretsProvider { + + /** + * Fetches a secret + * @return The actual secret + */ + @Override + public String provideSecret(String secretName, Object pathToSecret) { + return System.getenv(secretName); + } +} \ No newline at end of file diff --git a/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/SecretsProvider.java b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/SecretsProvider.java new file mode 100644 index 0000000000000..7d5330dfd0124 --- /dev/null +++ b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsprovider/SecretsProvider.java @@ -0,0 +1,40 @@ +/** + * 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.pulsar.functions.secretsprovider; + +import java.util.Map; + +/** + * This file defines the SecretsProvider interface. This interface is used by the function + * instances/containers to actually fetch the secrets. What SecretsProvider to use is + * decided by the SecretsProviderConfigurator + */ +public interface SecretsProvider { + /** + * Initialize the SecretsProvider + * @return + */ + default void init(Map config) {} + + /** + * Fetches a secret + * @return The actual secret + */ + String provideSecret(String secretName, Object pathToSecret); +} \ No newline at end of file diff --git a/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/DefaultSecretsProviderConfigurator.java b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/DefaultSecretsProviderConfigurator.java new file mode 100644 index 0000000000000..10f9d546e4e7f --- /dev/null +++ b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/DefaultSecretsProviderConfigurator.java @@ -0,0 +1,67 @@ +/** + * 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.pulsar.functions.secretsproviderconfigurator; + +import com.google.gson.reflect.TypeToken; +import io.kubernetes.client.models.V1Container; +import org.apache.pulsar.functions.proto.Function; +import org.apache.pulsar.functions.secretsprovider.ClearTextSecretsProvider; + +import java.lang.reflect.Type; +import java.util.Map; + +/** + * This is a barebones version of a secrets provider which wires in ClearTextSecretsProvider + * to the function instances/containers. + * While this is the default configurator, it is highly recommended that for real-security + * you use some alternate provider. + */ +public class DefaultSecretsProviderConfigurator implements SecretsProviderConfigurator { + @Override + public String getSecretsProviderClassName(Function.FunctionDetails functionDetails) { + switch (functionDetails.getRuntime()) { + case JAVA: + return ClearTextSecretsProvider.class.getName(); + case PYTHON: + return "secretsprovider.ClearTextSecretsProvider"; + default: + throw new RuntimeException("Unknwon runtime " + functionDetails.getRuntime()); + } + } + + @Override + public Map getSecretsProviderConfig(Function.FunctionDetails functionDetails) { + return null; + } + + @Override + public void configureKubernetesRuntimeSecretsProvider(V1Container container, Function.FunctionDetails functionDetails) { + // noop + } + + @Override + public void configureProcessRuntimeSecretsProvider(ProcessBuilder processBuilder, Function.FunctionDetails functionDetails) { + // noop + } + + @Override + public Type getSecretObjectType() { + return new TypeToken() {}.getType(); + } +} \ No newline at end of file diff --git a/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfigurator.java b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfigurator.java new file mode 100644 index 0000000000000..0321734a382d4 --- /dev/null +++ b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfigurator.java @@ -0,0 +1,113 @@ +/** + * 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.pulsar.functions.secretsproviderconfigurator; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import io.kubernetes.client.models.V1Container; +import io.kubernetes.client.models.V1EnvVar; +import io.kubernetes.client.models.V1EnvVarSource; +import io.kubernetes.client.models.V1SecretKeySelector; +import org.apache.commons.lang3.StringUtils; +import org.apache.pulsar.functions.proto.Function; +import org.apache.pulsar.functions.secretsprovider.EnvironmentBasedSecretsProvider; + +import java.lang.reflect.Type; +import java.util.Map; + +/** + * This file defines the SecretsProviderConfigurator that will be used by default for running in Kubernetes. + * As such this implementation is strictly when workers are configured to use kubernetes runtime. + * We use kubernetes in built secrets and bind them as environment variables within the function container + * to ensure that the secrets are availble to the function at runtime. Then we plug in the + * EnvironmentBasedSecretsConfig as the secrets provider who knows how to read these environment variables + */ +public class KubernetesSecretsProviderConfigurator implements SecretsProviderConfigurator { + private static String ID_KEY = "id"; + private static String KEY_KEY = "key"; + @Override + public String getSecretsProviderClassName(Function.FunctionDetails functionDetails) { + switch (functionDetails.getRuntime()) { + case JAVA: + return EnvironmentBasedSecretsProvider.class.getName(); + case PYTHON: + return "secretsprovider.EnvironmentBasedSecretsProvider"; + default: + throw new RuntimeException("Unknown function runtime " + functionDetails.getRuntime()); + } + } + + @Override + public Map getSecretsProviderConfig(Function.FunctionDetails functionDetails) { + return null; + } + + // Kubernetes secrets can be exposed as volume mounts or as environment variables in the pods. We are currently using the + // environment variables way. Essentially the secretName/secretPath is attached as secretRef to the environment variables + // of a pod and kubernetes magically makes the secret pointed to by this combination available as a env variable. + @Override + public void configureKubernetesRuntimeSecretsProvider(V1Container container, Function.FunctionDetails functionDetails) { + if (!StringUtils.isEmpty(functionDetails.getSecretsMap())) { + Type type = new TypeToken>() { + }.getType(); + Map secretsMap = new Gson().fromJson(functionDetails.getSecretsMap(), type); + for (Map.Entry entry : secretsMap.entrySet()) { + final V1EnvVar secretEnv = new V1EnvVar(); + Map kv = (Map) entry.getValue(); + secretEnv.name(entry.getKey()) + .valueFrom(new V1EnvVarSource() + .secretKeyRef(new V1SecretKeySelector() + .name(kv.get(ID_KEY)) + .key(kv.get(KEY_KEY)))); + container.addEnvItem(secretEnv); + } + } + } + + @Override + public void configureProcessRuntimeSecretsProvider(ProcessBuilder processBuilder, Function.FunctionDetails functionDetails) { + throw new RuntimeException("KubernetesSecretsProviderConfigurator should only be setup for Kubernetes Runtime"); + } + + @Override + public Type getSecretObjectType() { + return new TypeToken>() {}.getType(); + } + + // The secret object should be of type Map and it should contain "id" and "key" + @Override + public void validateSecretMap(Map secretMap) { + for (Object object : secretMap.values()) { + if (object instanceof Map) { + Map kubernetesSecret = (Map) object; + if (kubernetesSecret.size() < 2) { + throw new IllegalArgumentException("Kubernetes Secret should contain id and key"); + } + if (!kubernetesSecret.containsKey(ID_KEY)) { + throw new IllegalArgumentException("Kubernetes Secret should contain id information"); + } + if (!kubernetesSecret.containsKey(KEY_KEY)) { + throw new IllegalArgumentException("Kubernetes Secret should contain key information"); + } + } else { + throw new IllegalArgumentException("Kubernetes Secret should be a Map containing id/key pairs"); + } + } + } +} \ No newline at end of file diff --git a/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/SecretsProviderConfigurator.java b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/SecretsProviderConfigurator.java new file mode 100644 index 0000000000000..a1792f1e3afc4 --- /dev/null +++ b/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/SecretsProviderConfigurator.java @@ -0,0 +1,72 @@ +/** + * 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.pulsar.functions.secretsproviderconfigurator; + +import io.kubernetes.client.models.V1Container; +import org.apache.pulsar.functions.proto.Function; + +import java.lang.reflect.Type; +import java.util.Map; + +/** + * This file defines the SecretsProviderConfigurator interface. This interface is used by the function_workers + * to choose the SecretProvider class name(if any) and its associated config at the time of starting + * the function instances. + */ +public interface SecretsProviderConfigurator { + /** + * Initialize the SecretsProviderConfigurator + * @return + */ + default void init(Map config) {} + + /** + * Return the Secrets Provider Classname. This will be passed to the cmdline + * of the instance and should contain the logic of connecting with the secrets + * provider and obtaining secrets + */ + String getSecretsProviderClassName(Function.FunctionDetails functionDetails); + + /** + * Return the secrets provider config + */ + Map getSecretsProviderConfig(Function.FunctionDetails functionDetails); + + /** + * Attaches any secrets specific stuff to the k8 container for kubernetes runtime + */ + void configureKubernetesRuntimeSecretsProvider(V1Container container, Function.FunctionDetails functionDetails); + + /** + * Attaches any secrets specific stuff to the ProcessBuilder for process runtime + */ + void configureProcessRuntimeSecretsProvider(ProcessBuilder processBuilder, Function.FunctionDetails functionDetails); + + /** + * What is the type of the object that should be in the user secret config + * @return + */ + Type getSecretObjectType(); + + /** + * Do config checks to see whether the secrets provided are conforming + */ + default void validateSecretMap(Map secretMap) {} + +} \ No newline at end of file diff --git a/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsprovider/ClearTextSecretsProviderTest.java b/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsprovider/ClearTextSecretsProviderTest.java new file mode 100644 index 0000000000000..d6f01395a3166 --- /dev/null +++ b/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsprovider/ClearTextSecretsProviderTest.java @@ -0,0 +1,37 @@ +/** + * 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.pulsar.functions.secretsprovider; + +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * Unit test of {@link Exceptions}. + */ +public class ClearTextSecretsProviderTest { + + @Test + public void testConfigValidation() throws Exception { + ClearTextSecretsProvider provider = new ClearTextSecretsProvider(); + Assert.assertEquals(provider.provideSecret("SecretName", "SecretValue"), "SecretValue"); + Assert.assertEquals(provider.provideSecret("SecretName", ""), ""); + Assert.assertEquals(provider.provideSecret("SecretName", null), null); + } +} diff --git a/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsprovider/EnvironmentBasedSecretsProviderTest.java b/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsprovider/EnvironmentBasedSecretsProviderTest.java new file mode 100644 index 0000000000000..fa80d70486632 --- /dev/null +++ b/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsprovider/EnvironmentBasedSecretsProviderTest.java @@ -0,0 +1,75 @@ +/** + * 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.pulsar.functions.secretsprovider; + +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.lang.reflect.Field; +import java.util.Map; + +import static org.mockito.Matchers.anyString; + +/** + * Unit test of {@link Exceptions}. + */ +public class EnvironmentBasedSecretsProviderTest { + @Test + public void testConfigValidation() throws Exception { + EnvironmentBasedSecretsProvider provider = new EnvironmentBasedSecretsProvider(); + Assert.assertEquals(provider.provideSecret("mySecretName", "Ignored"), null); + injectEnvironmentVariable("mySecretName", "SecretValue"); + Assert.assertEquals(provider.provideSecret("mySecretName", "Ignored"), "SecretValue"); + } + + private static void injectEnvironmentVariable(String key, String value) + throws Exception { + + Class processEnvironment = Class.forName("java.lang.ProcessEnvironment"); + + Field unmodifiableMapField = getAccessibleField(processEnvironment, "theUnmodifiableEnvironment"); + Object unmodifiableMap = unmodifiableMapField.get(null); + injectIntoUnmodifiableMap(key, value, unmodifiableMap); + + Field mapField = getAccessibleField(processEnvironment, "theEnvironment"); + Map map = (Map) mapField.get(null); + map.put(key, value); + } + + private static Field getAccessibleField(Class clazz, String fieldName) + throws NoSuchFieldException { + + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } + + private static void injectIntoUnmodifiableMap(String key, String value, Object map) + throws ReflectiveOperationException { + + Class unmodifiableMap = Class.forName("java.util.Collections$UnmodifiableMap"); + Field field = getAccessibleField(unmodifiableMap, "m"); + Object obj = field.get(map); + ((Map) obj).put(key, value); + } +} diff --git a/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfiguratorTest.java b/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfiguratorTest.java new file mode 100644 index 0000000000000..6d64d5e913003 --- /dev/null +++ b/pulsar-functions/secrets/src/test/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfiguratorTest.java @@ -0,0 +1,62 @@ +/** + * 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.pulsar.functions.secretsproviderconfigurator; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.HashMap; + +/** + * Unit test of {@link Exceptions}. + */ +public class KubernetesSecretsProviderConfiguratorTest { + + @Test + public void testConfigValidation() throws Exception { + KubernetesSecretsProviderConfigurator provider = new KubernetesSecretsProviderConfigurator(); + try { + HashMap map = new HashMap(); + map.put("secretname", "randomsecret"); + provider.validateSecretMap(map); + Assert.fail("Non conforming secret object should not validate"); + } catch (Exception e) { + } + try { + HashMap map = new HashMap(); + HashMap map1 = new HashMap(); + map1.put("secretname", "secretvalue"); + map.put("secretname", map1); + provider.validateSecretMap(map); + Assert.fail("Non conforming secret object should not validate"); + } catch (Exception e) { + } + try { + HashMap map = new HashMap(); + HashMap map1 = new HashMap(); + map1.put("id", "secretvalue"); + map1.put("key", "secretvalue"); + map.put("secretname", map1); + provider.validateSecretMap(map); + } catch (Exception e) { + Assert.fail("Conforming secret object should validate"); + } + } +}