diff --git a/spring-cloud-kubernetes-core/pom.xml b/spring-cloud-kubernetes-core/pom.xml
index d8e30162..3bbd4005 100644
--- a/spring-cloud-kubernetes-core/pom.xml
+++ b/spring-cloud-kubernetes-core/pom.xml
@@ -66,31 +66,33 @@
spring-boot-starter-test
test
-
org.springframework.boot
spring-boot-starter-web
test
-
io.fabric8
kubernetes-client
test-jar
test
-
io.fabric8
mockwebserver
test
-
org.spockframework
spock-spring
test
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
\ No newline at end of file
diff --git a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapBootstrapConfiguration.java b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/BootstrapConfiguration.java
similarity index 81%
rename from spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapBootstrapConfiguration.java
rename to spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/BootstrapConfiguration.java
index 64c2d917..74315b30 100644
--- a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapBootstrapConfiguration.java
+++ b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/BootstrapConfiguration.java
@@ -18,6 +18,7 @@
package io.fabric8.spring.cloud.kubernetes.config;
import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.spring.cloud.kubernetes.KubernetesAutoConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,12 +31,12 @@ import org.springframework.context.annotation.Import;
@Configuration
@ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
-@ConditionalOnClass(ConfigMap.class)
-public class ConfigMapBootstrapConfiguration {
+@ConditionalOnClass({ ConfigMap.class, Secret.class })
+public class BootstrapConfiguration {
@Configuration
- @EnableConfigurationProperties(ConfigMapConfigProperties.class)
@Import(KubernetesAutoConfiguration.class)
+ @EnableConfigurationProperties({ ConfigMapConfigProperties.class, SecretsConfigProperties.class })
@ConditionalOnProperty(name = "spring.cloud.kubernetes.config.enabled", matchIfMissing = true)
protected static class KubernetesPropertySourceConfiguration {
@Autowired
@@ -45,5 +46,10 @@ public class ConfigMapBootstrapConfiguration {
public ConfigMapPropertySourceLocator configMapPropertySourceLocator(ConfigMapConfigProperties properties) {
return new ConfigMapPropertySourceLocator(client, properties);
}
+
+ @Bean
+ public SecretsPropertySourceLocator secretsPropertySourceLocator(SecretsConfigProperties properties) {
+ return new SecretsPropertySourceLocator(client, properties);
+ }
}
}
diff --git a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapPropertySource.java b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapPropertySource.java
index 6bbaa05f..42347216 100644
--- a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapPropertySource.java
+++ b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapPropertySource.java
@@ -17,14 +17,6 @@
package io.fabric8.spring.cloud.kubernetes.config;
-import io.fabric8.kubernetes.api.model.ConfigMap;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
-import org.springframework.core.env.MapPropertySource;
-import org.springframework.core.io.ByteArrayResource;
-
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
@@ -33,17 +25,22 @@ import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Collectors;
-public class ConfigMapPropertySource extends MapPropertySource {
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.core.io.ByteArrayResource;
+public class ConfigMapPropertySource extends MapPropertySource {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigMapPropertySource.class);
- private static final String CONFIGMAP_PATH = "/configmaps";
private static final String APPLICATION_YML = "application.yml";
private static final String APPLICATION_YAML = "application.yaml";
private static final String APPLICATION_PROPERTIES = "application.properties";
private static final String PREFIX = "configmap";
- private static final String SEPARATOR = ".";
public ConfigMapPropertySource(KubernetesClient client, String name) {
this(client, name, null);
@@ -54,9 +51,13 @@ public class ConfigMapPropertySource extends MapPropertySource {
}
private static String getName(KubernetesClient client, String name, String namespace) {
- StringBuilder sb = new StringBuilder();
- sb.append(PREFIX).append(SEPARATOR).append(name).append(SEPARATOR).append(namespace == null || namespace.isEmpty() ? client.getNamespace() : namespace);
- return sb.toString();
+ return new StringBuilder()
+ .append(PREFIX)
+ .append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR)
+ .append(name)
+ .append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR)
+ .append(namespace == null || namespace.isEmpty() ? client.getNamespace() : namespace)
+ .toString();
}
private static Map getData(KubernetesClient client, String name, String namespace) {
diff --git a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java
index 6644463f..98e6579e 100644
--- a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java
+++ b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/ConfigMapPropertySourceLocator.java
@@ -26,10 +26,6 @@ import org.springframework.core.env.PropertySource;
@Order(0)
public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
-
- private static final String SPRING_APPLICATION_NAME = "spring.application.name";
- private static final String FALLBACK_APPLICATION_NAME = "application";
-
private final KubernetesClient client;
private final ConfigMapConfigProperties properties;
@@ -42,7 +38,7 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
public PropertySource> locate(Environment environment) {
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
- String appName = env.getProperty(SPRING_APPLICATION_NAME, FALLBACK_APPLICATION_NAME);
+ String appName = env.getProperty(Constants.SPRING_APPLICATION_NAME, Constants.FALLBACK_APPLICATION_NAME);
String name = properties.getName() == null || properties.getName().isEmpty() ? appName : properties.getName();
String namespace = properties.getNamespace();
return new ConfigMapPropertySource(client, name, namespace);
diff --git a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/Constants.java b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/Constants.java
new file mode 100644
index 00000000..b3e46800
--- /dev/null
+++ b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/Constants.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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 io.fabric8.spring.cloud.kubernetes.config;
+
+final class Constants {
+ static final String SPRING_APPLICATION_NAME = "spring.application.name";
+ static final String FALLBACK_APPLICATION_NAME = "application";
+ static final String PROPERTY_SOURCE_NAME_SEPARATOR = ".";
+
+ private Constants() {
+ }
+}
diff --git a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsConfigProperties.java b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsConfigProperties.java
new file mode 100644
index 00000000..5d5f6b55
--- /dev/null
+++ b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsConfigProperties.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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 io.fabric8.spring.cloud.kubernetes.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("spring.cloud.kubernetes.secrets")
+public class SecretsConfigProperties {
+
+ private boolean enabled = true;
+ private String name;
+ private String namespace;
+ private Map labels = new HashMap<>();
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public Map getLabels() {
+ return labels;
+ }
+}
diff --git a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsPropertySource.java b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsPropertySource.java
new file mode 100644
index 00000000..fa37dc51
--- /dev/null
+++ b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsPropertySource.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 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 io.fabric8.spring.cloud.kubernetes.config;
+
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.fabric8.kubernetes.api.model.Secret;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.util.StringUtils;
+
+public class SecretsPropertySource extends MapPropertySource {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SecretsPropertySource.class);
+
+ private static final String PREFIX = "secrets";
+
+ public SecretsPropertySource(KubernetesClient client, Environment env, SecretsConfigProperties config) {
+ super(
+ getSourceName(client, env, config),
+ getSourceData(client, env, config)
+ );
+ }
+
+ private static String getSourceName(KubernetesClient client, Environment env, SecretsConfigProperties config) {
+ return new StringBuilder()
+ .append(PREFIX)
+ .append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR)
+ .append(getApplicationName(env,config))
+ .append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR)
+ .append(getApplicationNamespace(client, config))
+ .toString();
+ }
+
+ private static Map getSourceData(KubernetesClient client, Environment env, SecretsConfigProperties config) {
+ String name = getApplicationName(env, config);
+ String namespace = getApplicationNamespace(client, config);
+
+ Map result = new HashMap<>();
+ try {
+ if (config.getLabels().isEmpty()) {
+ if (StringUtils.isEmpty(namespace)) {
+ putAll(
+ client.secrets()
+ .withName(name)
+ .get(),
+ result);
+ } else {
+ putAll(
+ client.secrets()
+ .inNamespace(namespace)
+ .withName(name)
+ .get(),
+ result);
+ }
+ } else {
+ if (StringUtils.isEmpty(namespace)) {
+ client.secrets()
+ .withLabels(config.getLabels())
+ .list()
+ .getItems()
+ .forEach(s -> putAll(s, result));
+ } else {
+ client.secrets()
+ .inNamespace(namespace)
+ .withLabels(config.getLabels())
+ .list()
+ .getItems()
+ .forEach(s -> putAll(s, result));
+
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.warn("Can't read secret with name: [{}] or labels [{}] in namespace:[{}]. Ignoring",
+ config.getName(),
+ config.getLabels(),
+ namespace,
+ e);
+ }
+ return result;
+ }
+
+ // *****************************
+ // Helpers
+ // *****************************
+
+ private static String getApplicationName(Environment env, SecretsConfigProperties config) {
+ String name = config.getName();
+ if (StringUtils.isEmpty(name)) {
+ name = env.getProperty(
+ Constants.SPRING_APPLICATION_NAME,
+ Constants.FALLBACK_APPLICATION_NAME);
+ }
+
+ return name;
+ }
+
+ private static String getApplicationNamespace(KubernetesClient client, SecretsConfigProperties config) {
+ String namespace = config.getNamespace();
+ if (StringUtils.isEmpty(namespace)) {
+ namespace = client.getNamespace();
+ }
+
+ return namespace;
+ }
+
+ private static void putAll(Secret secret, Map result) {
+ if (secret != null && secret.getData() != null) {
+ secret.getData().forEach((k, v) -> result.put(
+ k,
+ new String(Base64.getDecoder().decode(v)).trim())
+ );
+ }
+ }
+}
diff --git a/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsPropertySourceLocator.java
new file mode 100644
index 00000000..6da9d449
--- /dev/null
+++ b/spring-cloud-kubernetes-core/src/main/java/io/fabric8/spring/cloud/kubernetes/config/SecretsPropertySourceLocator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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 io.fabric8.spring.cloud.kubernetes.config;
+
+import io.fabric8.kubernetes.client.KubernetesClient;
+import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.PropertySource;
+
+@Order(1)
+public class SecretsPropertySourceLocator implements PropertySourceLocator {
+ private final KubernetesClient client;
+ private final SecretsConfigProperties properties;
+
+ public SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties) {
+ this.client = client;
+ this.properties = properties;
+ }
+
+ @Override
+ public PropertySource> locate(Environment environment) {
+ return environment instanceof ConfigurableEnvironment
+ ? new SecretsPropertySource(client, environment, properties)
+ : null;
+ }
+}
diff --git a/spring-cloud-kubernetes-core/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-core/src/main/resources/META-INF/spring.factories
index d055546b..9db489c5 100644
--- a/spring-cloud-kubernetes-core/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-kubernetes-core/src/main/resources/META-INF/spring.factories
@@ -2,7 +2,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.fabric8.spring.cloud.kubernetes.KubernetesAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
-io.fabric8.spring.cloud.kubernetes.config.ConfigMapBootstrapConfiguration
+io.fabric8.spring.cloud.kubernetes.config.BootstrapConfiguration
org.springframework.context.ApplicationContextInitializer=\
io.fabric8.spring.cloud.kubernetes.profile.KubernetesApplicationContextInitializer
diff --git a/spring-cloud-kubernetes-core/src/test/groovy/io/fabric8/spring/cloud/kubernetes/config/test/CoreTest.groovy b/spring-cloud-kubernetes-core/src/test/groovy/io/fabric8/spring/cloud/kubernetes/config/test/CoreTest.groovy
index db06b3dc..97c622f2 100644
--- a/spring-cloud-kubernetes-core/src/test/groovy/io/fabric8/spring/cloud/kubernetes/config/test/CoreTest.groovy
+++ b/spring-cloud-kubernetes-core/src/test/groovy/io/fabric8/spring/cloud/kubernetes/config/test/CoreTest.groovy
@@ -17,6 +17,7 @@
package io.fabric8.spring.cloud.kubernetes.config.test
+import io.fabric8.kubernetes.api.model.SecretBuilder
import io.fabric8.kubernetes.api.model.ConfigMapBuilder
import io.fabric8.kubernetes.client.Config
import io.fabric8.kubernetes.client.KubernetesClient
@@ -27,14 +28,15 @@ import org.springframework.boot.test.IntegrationTest
import org.springframework.boot.test.SpringApplicationConfiguration
import org.springframework.core.env.Environment
import spock.lang.Specification
+import groovy.util.logging.Slf4j
+@Slf4j
@SpringApplicationConfiguration(TestApplication.class)
-@IntegrationTest(
- [
- "spring.application.name=testapp",
- "spring.cloud.kubernetes.client.namespace=testns",
- "spring.cloud.kubernetes.client.trustCerts=true",
- "spring.cloud.kubernetes.config.namespace=testns"
+@IntegrationTest([
+ "spring.application.name=testapp",
+ "spring.cloud.kubernetes.client.namespace=testns",
+ "spring.cloud.kubernetes.client.trustCerts=true",
+ "spring.cloud.kubernetes.config.namespace=testns"
])
@EnableConfigurationProperties
class CoreTest extends Specification {
@@ -55,12 +57,26 @@ class CoreTest extends Specification {
mockServer.init()
mockClient = mockServer.createClient()
- //Setup configmap data
- Map data = new HashMap<>();
- data.put("spring.kubernetes.test.value", "value1")
- mockServer.expect().get().withPath("/api/v1/namespaces/testns/configmaps/testapp").andReturn(200, new ConfigMapBuilder()
- .withData(data)
- .build()).always()
+ mockServer.expect().get()
+ .withPath("/api/v1/namespaces/testns/configmaps/testapp")
+ .andReturn(
+ 200,
+ new ConfigMapBuilder()
+ .withData([
+ 'spring.kubernetes.test.value': 'value1'])
+ .build())
+ .always()
+ mockServer.expect().get()
+ .withPath("/api/v1/namespaces/testns/secrets/testapp")
+ .andReturn(
+ 200,
+ new SecretBuilder()
+ .withData([
+ 'amq.pwd': 'MWYyZDFlMmU2N2Rm',
+ 'amq.usr': 'YWRtaW4K'
+ ])
+ .build())
+ .always()
//Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl())
@@ -80,8 +96,8 @@ class CoreTest extends Specification {
def "Kubernetes client config bean should be configurable via system properties"() {
expect:
- config.getMasterUrl().equals(mockClient.getConfiguration().getMasterUrl());
- config.getNamespace().equals("testns");
+ config.getMasterUrl().equals(mockClient.getConfiguration().getMasterUrl())
+ config.getNamespace().equals("testns")
config.trustCerts
}
@@ -92,12 +108,17 @@ class CoreTest extends Specification {
def "Kubernetes client should be configured from system properties"() {
expect:
- client.getConfiguration().getMasterUrl().equals(mockClient.getConfiguration().getMasterUrl());
+ client.getConfiguration().getMasterUrl().equals(mockClient.getConfiguration().getMasterUrl())
}
-
def "properties should be read from config map"() {
expect:
- environment.getProperty("spring.kubernetes.test.value").equals("value1");
+ environment.getProperty("spring.kubernetes.test.value").equals("value1")
+ }
+
+ def "properties should be read from secrets"() {
+ expect:
+ environment.getProperty("amq.pwd").equals("1f2d1e2e67df")
+ environment.getProperty("amq.usr").equals('admin');
}
}