diff --git a/README.md b/README.md
index 8eab5891..b916dde9 100644
--- a/README.md
+++ b/README.md
@@ -178,7 +178,9 @@ spec:
| spring.cloud.kubernetes.config.enabled | Boolean | true | Enable Secrets PropertySource
| spring.cloud.kubernetes.config.name | String | ${spring.application.name} | Sets the name of ConfigMap to lookup
| spring.cloud.kubernetes.config.namespace | String | Client namespace | Sets the Kubernetes namespace where to lookup
-
+| spring.cloud.kubernetes.config.paths | List | null | Sets the paths were ConfigMaps are mounted
+| spring.cloud.kubernetes.config.enableApi | Boolean | true | Enable/Disable consuming ConfigMaps via APIs
+
#### Secrets PropertySource
diff --git a/spring-cloud-kubernetes-config/pom.xml b/spring-cloud-kubernetes-config/pom.xml
index 5a619ad3..34ce24a7 100644
--- a/spring-cloud-kubernetes-config/pom.xml
+++ b/spring-cloud-kubernetes-config/pom.xml
@@ -71,6 +71,11 @@
mockwebserver
test
+
+ io.fabric8
+ kubernetes-server-mock
+ test
+
org.spockframework
spock-spring
diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapConfigProperties.java b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapConfigProperties.java
index df0ecc62..3d85e4b3 100644
--- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapConfigProperties.java
+++ b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapConfigProperties.java
@@ -17,6 +17,9 @@
package org.springframework.cloud.kubernetes.config;
+import java.util.LinkedList;
+import java.util.List;
+
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("spring.cloud.kubernetes.config")
@@ -24,7 +27,26 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
private static final String TARGET = "Config Map";
- @Override
+ private boolean enableApi = true;
+ private List paths = new LinkedList<>();
+
+ public boolean isEnableApi() {
+ return enableApi;
+ }
+
+ public void setEnableApi(boolean enableApi) {
+ this.enableApi = enableApi;
+ }
+
+ public void setPaths(List paths) {
+ this.paths = paths;
+ }
+
+ public List getPaths() {
+ return paths;
+ }
+
+ @Override
public String getConfigurationTarget() {
return TARGET;
}
diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySource.java b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySource.java
index e106fcd8..ff8f3138 100644
--- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySource.java
+++ b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySource.java
@@ -30,13 +30,12 @@ import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.config.YamlProcessor;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.yaml.SpringProfileDocumentMatcher;
-import org.springframework.core.env.MapPropertySource;
import org.springframework.core.io.ByteArrayResource;
+import org.springframework.util.StringUtils;
-public class ConfigMapPropertySource extends MapPropertySource {
+public class ConfigMapPropertySource extends KubernetesPropertySource {
private static final Log LOG = LogFactory.getLog(ConfigMapPropertySource.class);
private static final String APPLICATION_YML = "application.yml";
@@ -45,16 +44,16 @@ public class ConfigMapPropertySource extends MapPropertySource {
private static final String PREFIX = "configmap";
- public ConfigMapPropertySource(KubernetesClient client, String name) {
- this(client, name, null);
+ public ConfigMapPropertySource(KubernetesClient client, String name, ConfigMapConfigProperties config) {
+ this(client, name, null, config);
}
- public ConfigMapPropertySource(KubernetesClient client, String name, String[] profiles) {
- this(client, name, null, profiles);
+ public ConfigMapPropertySource(KubernetesClient client, String name, String[] profiles, ConfigMapConfigProperties config) {
+ this(client, name, null, profiles, config);
}
- public ConfigMapPropertySource(KubernetesClient client, String name, String namespace, String[] profiles) {
- super(getName(client, name, namespace), asObjectMap(getData(client, name, namespace, profiles)));
+ public ConfigMapPropertySource(KubernetesClient client, String name, String namespace, String[] profiles, ConfigMapConfigProperties config) {
+ super(getName(client, name, namespace), asObjectMap(getData(client, name, namespace, profiles, config)));
}
private static String getName(KubernetesClient client, String name, String namespace) {
@@ -67,30 +66,37 @@ public class ConfigMapPropertySource extends MapPropertySource {
.toString();
}
- private static Map getData(KubernetesClient client, String name, String namespace, String[] profiles) {
+ private static Map getData(KubernetesClient client, String name, String namespace,
+ String[] profiles, ConfigMapConfigProperties config) {
Map result = new HashMap<>();
- try {
- ConfigMap map = namespace == null || namespace.isEmpty()
- ? client.configMaps().withName(name).get()
- : client.configMaps().inNamespace(namespace).withName(name).get();
+ if (config.isEnableApi()) {
+ try {
+ ConfigMap map = StringUtils.isEmpty(namespace)
+ ? client.configMaps().withName(name).get()
+ : client.configMaps().inNamespace(namespace).withName(name).get();
- if (map != null) {
- for (Map.Entry entry : map.getData().entrySet()) {
- String key = entry.getKey();
- String value = entry.getValue();
- if (key.equals(APPLICATION_YAML) || key.equals(APPLICATION_YML)) {
- result.putAll(yamlParserGenerator(profiles).andThen(PROPERTIES_TO_MAP).apply(value));
- } else if (key.equals(APPLICATION_PROPERTIES)) {
- result.putAll(KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(value));
- } else {
- result.put(key, value);
- }
- }
- }
- } catch (Exception e) {
- LOG.warn("Can't read configMap with name: [" + name + "] in namespace:[" + namespace + "]. Ignoring", e);
- }
- return result;
+ if (map != null) {
+ for (Map.Entry entry : map.getData().entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key.equals(APPLICATION_YAML) || key.equals(APPLICATION_YML)) {
+ result.putAll(yamlParserGenerator(profiles).andThen(PROPERTIES_TO_MAP).apply(value));
+ } else if (key.equals(APPLICATION_PROPERTIES)) {
+ result.putAll(KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(value));
+ } else {
+ result.put(key, value);
+ }
+ }
+ }
+ } catch (Exception e) {
+ LOG.warn("Can't read configMap with name: [" + name + "] in namespace:[" + namespace + "]. Ignoring", e);
+ }
+ }
+
+ // read for secrets mount
+ putPathConfig(result, config.getPaths());
+
+ return result;
}
private static Map asObjectMap(Map source) {
@@ -99,7 +105,7 @@ public class ConfigMapPropertySource extends MapPropertySource {
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
- private static final Function yamlParserGenerator(final String[] profiles) {
+ private static Function yamlParserGenerator(final String[] profiles) {
return s -> {
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
if (profiles == null) {
diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java
index c7cae916..15104c9a 100644
--- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java
+++ b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java
@@ -41,7 +41,7 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
String name = getApplicationName(environment, properties);
String namespace = getApplicationNamespace(client, env, properties);
- return new ConfigMapPropertySource(client, name, namespace, env.getActiveProfiles());
+ return new ConfigMapPropertySource(client, name, namespace, env.getActiveProfiles(), properties);
}
return null;
}
diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/KubernetesPropertySource.java b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/KubernetesPropertySource.java
new file mode 100644
index 00000000..b4695185
--- /dev/null
+++ b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/KubernetesPropertySource.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 to the original authors.
+ *
+ * Licensed 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.springframework.cloud.kubernetes.config;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.env.MapPropertySource;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Secrets and ConfigMaps shared features.
+ */
+public class KubernetesPropertySource extends MapPropertySource {
+ private static final Log LOG = LogFactory.getLog(KubernetesPropertySource.class);
+
+ @SuppressWarnings("unchecked")
+ protected KubernetesPropertySource(String name, Map source) {
+ super(name, source);
+ }
+
+ protected static void putPathConfig(Map result, List paths) {
+ paths
+ .stream()
+ .map(Paths::get)
+ .filter(Files::exists)
+ .forEach(p -> putAll(p, result));
+ }
+
+ private static void putAll(Path path, Map result) {
+ try {
+ Files.walk(path)
+ .filter(Files::isRegularFile)
+ .forEach(p -> readFile(p, result));
+ } catch (IOException e) {
+ LOG.warn("Error walking properties files", e);
+ }
+ }
+
+ private static void readFile(Path path, Map result) {
+ try {
+ result.put(
+ path.getFileName().toString(),
+ new String(Files.readAllBytes(path)).trim());
+ } catch (IOException e) {
+ LOG.warn("Error reading properties file", e);
+ }
+ }
+}
diff --git a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySource.java b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySource.java
index 39d68cd9..9299f136 100644
--- a/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySource.java
+++ b/spring-cloud-kubernetes-config/src/main/java/org/springframework/cloud/kubernetes/config/SecretsPropertySource.java
@@ -16,10 +16,6 @@
*/
package org.springframework.cloud.kubernetes.config;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@@ -30,12 +26,11 @@ import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.Environment;
-import org.springframework.core.env.MapPropertySource;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.*;
-public class SecretsPropertySource extends MapPropertySource {
+public class SecretsPropertySource extends KubernetesPropertySource {
private static final Log LOG = LogFactory.getLog(SecretsPropertySource.class);
private static final String PREFIX = "secrets";
@@ -65,20 +60,18 @@ public class SecretsPropertySource extends MapPropertySource {
if (config.isEnableApi()) {
try {
// Read for secrets api (named)
+ Secret secret;
if (StringUtils.isEmpty(namespace)) {
- putAll(
- client.secrets()
- .withName(name)
- .get(),
- result);
+ secret = client.secrets()
+ .withName(name)
+ .get();
} else {
- putAll(
- client.secrets()
- .inNamespace(namespace)
- .withName(name)
- .get(),
- result);
+ secret = client.secrets()
+ .inNamespace(namespace)
+ .withName(name)
+ .get();
}
+ putAll(secret, result);
// Read for secrets api (label)
if (!config.getLabels().isEmpty()) {
@@ -106,16 +99,12 @@ public class SecretsPropertySource extends MapPropertySource {
}
// read for secrets mount
- config.getPaths()
- .stream()
- .map(Paths::get)
- .filter(Files::exists)
- .forEach(p -> putAll(p, result));
+ putPathConfig(result, config.getPaths());
- return result;
+ return result;
}
- // *****************************
+ // *****************************
// Helpers
// *****************************
private static void putAll(Secret secret, Map result) {
@@ -126,24 +115,4 @@ public class SecretsPropertySource extends MapPropertySource {
);
}
}
-
- private static void putAll(Path path, Map result) {
- try {
- Files.walk(path)
- .filter(Files::isRegularFile)
- .forEach(p -> readFile(p, result));
- } catch (IOException e) {
- LOG.warn("", e);
- }
- }
-
- private static void readFile(Path path, Map result) {
- try {
- result.put(
- path.getFileName().toString(),
- new String(Files.readAllBytes(path)).trim());
- } catch (IOException e) {
- LOG.warn("", e);
- }
- }
}
diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsSpringBootTest.java b/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsSpringBootTest.java
index a50be32f..072e8671 100644
--- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsSpringBootTest.java
+++ b/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsSpringBootTest.java
@@ -23,7 +23,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.server.mock.KubernetesServer;
+import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import io.restassured.RestAssured;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -53,18 +53,18 @@ public class ConfigMapsSpringBootTest {
@ClassRule
public static KubernetesServer server = new KubernetesServer();
- public static KubernetesClient mockClient;
+ private static KubernetesClient mockClient;
@Autowired(required = false)
Config config;
- private static String APPLICATION_NAME = "configmap-example";
+ private static final String APPLICATION_NAME = "configmap-example";
@Value("${local.server.port}")
private int port;
@BeforeClass
- public static void setUpBeforeClass() throws Exception {
+ public static void setUpBeforeClass() {
mockClient = server.getClient();
//Configure the kubernetes master url to point to the mock server
diff --git a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTest.java b/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTest.java
index 653406bb..c14af6c5 100644
--- a/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTest.java
+++ b/spring-cloud-kubernetes-config/src/test/java/org/springframework/cloud/kubernetes/config/ConfigMapsTest.java
@@ -17,21 +17,24 @@
package org.springframework.cloud.kubernetes.config;
-import java.util.HashMap;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
import java.util.Map;
-import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.ConfigMapList;
import io.fabric8.kubernetes.api.model.ConfigMapListBuilder;
-import io.fabric8.kubernetes.api.model.ListMeta;
-import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.server.mock.KubernetesServer;
+import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import org.junit.Rule;
import org.junit.Test;
+import org.springframework.util.FileSystemUtils;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
/**
@@ -65,9 +68,43 @@ public class ConfigMapsTest {
ConfigMapList configMapList = client.configMaps().inNamespace("ns2").list();
assertNotNull(configMapList);
assertEquals(1, configMapList.getAdditionalProperties().size());
- HashMap data = (HashMap) configMapList.getAdditionalProperties().get("data");
+ @SuppressWarnings("unchecked")
+ Map data = (Map) configMapList.getAdditionalProperties().get("data");
assertEquals("123",data.get("KEY"));
+ }
+ @Test
+ public void testConfigMapGetFromVolume() throws IOException {
+ KubernetesClient client = server.getClient();
+ ConfigMapConfigProperties cmConfProperties = new ConfigMapConfigProperties();
+ cmConfProperties.setEnableApi(false);
+
+ // create test data, as if in-container volumes mounted by k8s, see
+ // https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#add-configmap-data-to-a-volume
+ final Path tmp = Files.createTempDirectory("test-k8s-cm-");
+ final Path dbPath = tmp.resolve("cm/db");
+ final Path apiPath = tmp.resolve("cm/api");
+ createConfigMapFile(dbPath, "db.url", "http://localhost/db");
+ createConfigMapFile(apiPath, "api.url", "http://localhost/api");
+ createConfigMapFile(apiPath, "foo.bar", "42");
+
+ // parse ConfigMaps
+ cmConfProperties.setPaths(Arrays.asList(dbPath.toString(), apiPath.toString()));
+ ConfigMapPropertySource cmps = new ConfigMapPropertySource(client, "testapp", cmConfProperties);
+
+ // assert as expected
+ assertEquals("42", cmps.getProperty("foo.bar"));
+ assertEquals("http://localhost/db", cmps.getProperty("db.url"));
+ assertEquals("http://localhost/api", cmps.getProperty("api.url"));
+ assertFalse(cmps.containsProperty("no.such.property"));
+
+ FileSystemUtils.deleteRecursively(tmp.toFile());
+ }
+
+ private void createConfigMapFile(Path basePath, String key, String value) throws IOException {
+ Files.createDirectories(basePath);
+ final Path apiUrlFile = Files.createFile(basePath.resolve(key));
+ Files.write(apiUrlFile, value.getBytes(StandardCharsets.UTF_8));
}
}