Merge branch 'master' into heartbeat-event
This commit is contained in:
128
.settings.xml
128
.settings.xml
@@ -1,66 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<settings>
|
||||
<servers>
|
||||
<server>
|
||||
<id>repo.spring.io</id>
|
||||
<username>${env.CI_DEPLOY_USERNAME}</username>
|
||||
<password>${env.CI_DEPLOY_PASSWORD}</password>
|
||||
</server>
|
||||
</servers>
|
||||
<profiles>
|
||||
<profile>
|
||||
<!--
|
||||
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
|
||||
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
|
||||
a native Maven with the right version. Eclipse users should points their Maven tooling to
|
||||
this settings file, or copy the profile into their ~/.m2/settings.xml.
|
||||
-->
|
||||
<id>spring</id>
|
||||
<activation><activeByDefault>true</activeByDefault></activation>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>http://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
<servers>
|
||||
<server>
|
||||
<id>repo.spring.io</id>
|
||||
<username>${env.CI_DEPLOY_USERNAME}</username>
|
||||
<password>${env.CI_DEPLOY_PASSWORD}</password>
|
||||
</server>
|
||||
</servers>
|
||||
<profiles>
|
||||
<profile>
|
||||
<!--
|
||||
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
|
||||
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
|
||||
a native Maven with the right version. Eclipse users should points their Maven tooling to
|
||||
this settings file, or copy the profile into their ~/.m2/settings.xml.
|
||||
-->
|
||||
<id>spring</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>http://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
</settings>
|
||||
|
||||
68
pom.xml
68
pom.xml
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.3.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<name>Spring Cloud Kubernetes</name>
|
||||
|
||||
<url>http://cloud.spring.io</url>
|
||||
<inceptionYear>2016</inceptionYear>
|
||||
<inceptionYear>2017</inceptionYear>
|
||||
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
@@ -59,17 +59,20 @@
|
||||
|
||||
<properties>
|
||||
<!-- Dependency Versions -->
|
||||
<spring-cloud-commons.version>1.2.3.RELEASE</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>1.3.2.RELEASE</spring-cloud-netflix.version>
|
||||
<spring-cloud-sleuth.version>1.2.2.RELEASE</spring-cloud-sleuth.version>
|
||||
<!-- <spring-cloud-netflix.version>1.3.2.RELEASE</spring-cloud-netflix.version>
|
||||
<spring-cloud-sleuth.version>1.2.2.RELEASE</spring-cloud-sleuth.version> -->
|
||||
<spring-cloud-commons.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
|
||||
<!-- Maven Plugin Versions -->
|
||||
<maven-compiler-plugin.version>3.5</maven-compiler-plugin.version>
|
||||
<maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
|
||||
<maven-failsafe-plugin.version>2.19.1</maven-failsafe-plugin.version>
|
||||
<maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>
|
||||
<fabric8.maven.plugin.version>3.2.28</fabric8.maven.plugin.version>
|
||||
<gmavenplus-plugin.version>1.2</gmavenplus-plugin.version>
|
||||
<maven-failsafe-plugin.version>2.18.1</maven-failsafe-plugin.version>
|
||||
<maven-surefire-plugin.version>2.21.0</maven-surefire-plugin.version>
|
||||
<fabric8.maven.plugin.version>3.5.37</fabric8.maven.plugin.version>
|
||||
<gmavenplus-plugin.version>1.6</gmavenplus-plugin.version>
|
||||
<groovy.version>2.4.12</groovy.version>
|
||||
<restassured.version>3.0.2</restassured.version>
|
||||
<spock-spring.version>1.1-groovy-2.4</spock-spring.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
@@ -77,18 +80,25 @@
|
||||
<module>spring-cloud-kubernetes-core</module>
|
||||
<module>spring-cloud-kubernetes-config</module>
|
||||
<module>spring-cloud-kubernetes-discovery</module>
|
||||
<module>spring-cloud-starter-kubernetes</module>
|
||||
<module>spring-cloud-starter-kubernetes-config</module>
|
||||
<module>spring-cloud-kubernetes-examples</module>
|
||||
|
||||
<!-- <module>spring-cloud-starter-kubernetes-netflix</module>
|
||||
<module>spring-cloud-starter-kubernetes-zipkin</module>
|
||||
<module>spring-cloud-starter-kubernetes-all</module>
|
||||
<module>spring-cloud-kubernetes-ribbon</module>
|
||||
<module>spring-cloud-kubernetes-zipkin</module>
|
||||
<module>spring-cloud-kubernetes-hystrix</module>
|
||||
<module>spring-cloud-kubernetes-archaius</module>
|
||||
<module>spring-cloud-starter-kubernetes</module>
|
||||
<module>spring-cloud-starter-kubernetes-config</module>
|
||||
<module>spring-cloud-starter-kubernetes-netflix</module>
|
||||
<module>spring-cloud-starter-kubernetes-zipkin</module>
|
||||
<module>spring-cloud-starter-kubernetes-all</module>
|
||||
<module>spring-cloud-kubernetes-examples</module>
|
||||
<module>spring-cloud-kubernetes-archaius</module> -->
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>${groovy.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -108,21 +118,23 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>${groovy.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-dependencies</artifactId>
|
||||
<version>${spring-cloud-sleuth.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>${restassured.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<version>${spock-spring.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -147,7 +159,7 @@
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>testCompile</goal>
|
||||
<goal>compileTests</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
@@ -82,7 +82,7 @@ public class ArchaiusConfigMapSourceConfiguration implements InitializingBean, D
|
||||
}
|
||||
watch = StringUtils.isEmpty(namespace)
|
||||
? client.configMaps().withName(name).watch(watcher)
|
||||
: client.configMaps().inNamespace(namespace).withName(namespace).watch(watcher);
|
||||
: client.configMaps().inNamespace(namespace).withName(name).watch(watcher);
|
||||
started.set(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,16 @@
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-client</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -32,6 +42,11 @@
|
||||
<artifactId>spring-boot-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
@@ -42,6 +57,11 @@
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-rsa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
@@ -50,6 +70,24 @@
|
||||
</dependency>
|
||||
|
||||
<!-- Testing Dependencies -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.9.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
@@ -59,22 +97,62 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-client</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-server-mock</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
|
||||
@@ -31,7 +31,6 @@ import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||
import org.springframework.boot.yaml.SpringProfileDocumentMatcher;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -76,29 +75,41 @@ public class ConfigMapPropertySource extends KubernetesPropertySource {
|
||||
: client.configMaps().inNamespace(namespace).withName(name).get();
|
||||
|
||||
if (map != null) {
|
||||
for (Map.Entry<String, String> 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);
|
||||
}
|
||||
}
|
||||
result.putAll(processAllEntries(map.getData(), profiles));
|
||||
}
|
||||
} 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());
|
||||
|
||||
Map<String, String> configsFromPaths = new HashMap<>();
|
||||
putPathConfig(configsFromPaths, config.getPaths());
|
||||
result.putAll(processAllEntries(configsFromPaths, profiles));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String, String> processAllEntries(Map<String, String> input,
|
||||
String[] profiles) {
|
||||
return input.entrySet().stream()
|
||||
.map(e -> extractProperties(e.getKey(), e.getValue(), profiles))
|
||||
.filter(m -> !m.isEmpty())
|
||||
.flatMap(m -> m.entrySet().stream())
|
||||
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
|
||||
}
|
||||
|
||||
private static Map<String, String> extractProperties(String resourceName, String content, String[] profiles) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
|
||||
if (resourceName.equals(APPLICATION_YAML) || resourceName.equals(APPLICATION_YML)) {
|
||||
result.putAll(yamlParserGenerator(profiles).andThen(PROPERTIES_TO_MAP).apply(content));
|
||||
} else if (resourceName.equals(APPLICATION_PROPERTIES)) {
|
||||
result.putAll(KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(content));
|
||||
} else {
|
||||
result.put(resourceName, content);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String, Object> asObjectMap(Map<String, String> source) {
|
||||
return source.entrySet()
|
||||
.stream()
|
||||
@@ -108,11 +119,6 @@ public class ConfigMapPropertySource extends KubernetesPropertySource {
|
||||
private static Function<String, Properties> yamlParserGenerator(final String[] profiles) {
|
||||
return s -> {
|
||||
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
|
||||
if (profiles == null) {
|
||||
yamlFactory.setDocumentMatchers(new SpringProfileDocumentMatcher());
|
||||
} else {
|
||||
yamlFactory.setDocumentMatchers(new SpringProfileDocumentMatcher(profiles));
|
||||
}
|
||||
yamlFactory.setResources(new ByteArrayResource(s.getBytes()));
|
||||
return yamlFactory.getObject();
|
||||
};
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
package org.springframework.cloud.kubernetes.config.reload;
|
||||
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.info.InfoEndpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
|
||||
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
|
||||
|
||||
@@ -39,7 +44,9 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
|
||||
@AutoConfigureAfter({InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class, RefreshAutoConfiguration.class})
|
||||
@EnableConfigurationProperties(ConfigReloadProperties.class)
|
||||
|
||||
public class ConfigReloadAutoConfiguration {
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ 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
|
||||
import io.fabric8.kubernetes.server.mock.KubernetesMockServer
|
||||
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
|
||||
@@ -47,7 +47,8 @@ import static org.junit.Assert.assertEquals;
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = App.class,
|
||||
properties = { "spring.application.name=configmap-example",
|
||||
"spring.cloud.kubernetes.reload.enabled=false"})
|
||||
"spring.cloud.kubernetes.reload.enabled=false"}
|
||||
)
|
||||
public class ConfigMapsSpringBootTest {
|
||||
|
||||
@ClassRule
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.config;
|
||||
|
||||
import io.fabric8.kubernetes.client.utils.IOHelpers;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@@ -88,8 +89,13 @@ public class ConfigMapsTest {
|
||||
createConfigMapFile(apiPath, "api.url", "http://localhost/api");
|
||||
createConfigMapFile(apiPath, "foo.bar", "42");
|
||||
|
||||
final Path filesPath = tmp.resolve("cm/files");
|
||||
|
||||
createConfigMapFile(filesPath, "application.yaml", readResourceFile("application.yaml"));
|
||||
createConfigMapFile(filesPath, "application.properties", readResourceFile("application.properties"));
|
||||
|
||||
// parse ConfigMaps
|
||||
cmConfProperties.setPaths(Arrays.asList(dbPath.toString(), apiPath.toString()));
|
||||
cmConfProperties.setPaths(Arrays.asList(dbPath.toString(), apiPath.toString(), filesPath.toString()));
|
||||
ConfigMapPropertySource cmps = new ConfigMapPropertySource(client, "testapp", cmConfProperties);
|
||||
|
||||
// assert as expected
|
||||
@@ -98,7 +104,26 @@ public class ConfigMapsTest {
|
||||
assertEquals("http://localhost/api", cmps.getProperty("api.url"));
|
||||
assertFalse(cmps.containsProperty("no.such.property"));
|
||||
|
||||
FileSystemUtils.deleteRecursively(tmp.toFile());
|
||||
assertEquals("a", cmps.getProperty("dummy.property.string1"));
|
||||
assertEquals("1", cmps.getProperty("dummy.property.int1"));
|
||||
assertEquals("true", cmps.getProperty("dummy.property.bool1"));
|
||||
|
||||
assertEquals("a", cmps.getProperty("dummy.property.string2"));
|
||||
assertEquals("1", cmps.getProperty("dummy.property.int2"));
|
||||
assertEquals("true", cmps.getProperty("dummy.property.bool2"));
|
||||
|
||||
FileSystemUtils.deleteRecursively(tmp.toFile());
|
||||
}
|
||||
|
||||
private String readResourceFile(String file) {
|
||||
String resource;
|
||||
try {
|
||||
resource = IOHelpers.readFully(getClass().getClassLoader().getResourceAsStream(file));
|
||||
}
|
||||
catch (IOException e) {
|
||||
resource = "";
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
private void createConfigMapFile(Path basePath, String key, String value) throws IOException {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
dummy.property.string1=a
|
||||
dummy.property.int1=1
|
||||
dummy.property.bool1=true
|
||||
@@ -0,0 +1,5 @@
|
||||
dummy:
|
||||
property:
|
||||
string2: "a"
|
||||
int2: 1
|
||||
bool2: true
|
||||
@@ -32,6 +32,16 @@
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-client</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -81,6 +91,16 @@
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
|
||||
@@ -41,7 +41,7 @@ public class KubernetesAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Config.class)
|
||||
public Config kubernetesClientConfig(KubernetesClientProperties kubernetesClientProperties) {
|
||||
Config base = new Config();
|
||||
Config base = Config.autoConfigure(null);
|
||||
Config properties = new ConfigBuilder(base)
|
||||
//Only set values that have been explicitly specified
|
||||
.withMasterUrl(or(kubernetesClientProperties.getMasterUrl(), base.getMasterUrl()))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.3.3.BUILD-SNAPSHOT</version>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-kubernetes-dependencies</artifactId>
|
||||
@@ -14,12 +14,10 @@
|
||||
<name>Spring Cloud Kubernetes :: Dependencies</name>
|
||||
<description>Spring Cloud Kubernetes Dependencies</description>
|
||||
<properties>
|
||||
<arquillian.version>1.1.13.Final</arquillian.version>
|
||||
<arquillian-cube.version>1.3.2</arquillian-cube.version>
|
||||
<kubernetes-client.version>2.4.1</kubernetes-client.version>
|
||||
<mockwebserver.version>0.0.13</mockwebserver.version>
|
||||
<restassured.version>3.0.2</restassured.version>
|
||||
<spock-spring.version>1.0-groovy-2.3</spock-spring.version>
|
||||
<arquillian.version>1.4.0.Final</arquillian.version>
|
||||
<arquillian-cube.version>1.15.2</arquillian-cube.version>
|
||||
<kubernetes-client.version>3.1.8</kubernetes-client.version>
|
||||
<mockwebserver.version>0.1.0</mockwebserver.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -46,13 +44,13 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-archaius</artifactId>
|
||||
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!--<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
|
||||
<artifactId>spring-cloud-kubernetes-archaius</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
@@ -74,20 +72,6 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Own dependencies - Starters -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-netflix</artifactId>
|
||||
@@ -104,6 +88,19 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-all</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>-->
|
||||
|
||||
<!-- Own dependencies - Starters -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing Dependencies -->
|
||||
@@ -137,6 +134,16 @@
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<version>${mockwebserver.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -145,20 +152,19 @@
|
||||
<version>${kubernetes-client.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>${restassured.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<version>${spock-spring.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<profiles>
|
||||
|
||||
@@ -44,12 +44,10 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing Dependencies -->
|
||||
@@ -70,12 +68,32 @@
|
||||
<artifactId>kubernetes-client</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -16,83 +16,129 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.EndpointAddress;
|
||||
import io.fabric8.kubernetes.api.model.EndpointSubset;
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.utils.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class KubernetesDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
private static final String HOSTNAME = "HOSTNAME";
|
||||
private static final Log log = LogFactory.getLog(KubernetesDiscoveryClient.class);
|
||||
private static final String HOSTNAME = "HOSTNAME";
|
||||
|
||||
private KubernetesClient client;
|
||||
private KubernetesDiscoveryProperties properties;
|
||||
private KubernetesClient client;
|
||||
private KubernetesDiscoveryProperties properties;
|
||||
|
||||
public KubernetesDiscoveryClient(KubernetesClient client, KubernetesDiscoveryProperties properties) {
|
||||
this.client = client;
|
||||
this.properties = properties;
|
||||
}
|
||||
public KubernetesDiscoveryClient(KubernetesClient client,
|
||||
KubernetesDiscoveryProperties kubernetesDiscoveryProperties) {
|
||||
this.client = client;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public KubernetesClient getClient() {
|
||||
return client;
|
||||
}
|
||||
public KubernetesClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(KubernetesClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
public void setClient(KubernetesClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Kubernetes Discovery Client";
|
||||
}
|
||||
@Override
|
||||
public String description() {
|
||||
return "Kubernetes Discovery Client";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceInstance getLocalServiceInstance() {
|
||||
String serviceName = properties.getServiceName();
|
||||
String podName = System.getenv(HOSTNAME);
|
||||
ServiceInstance defaultInstance = new DefaultServiceInstance(serviceName, "localhost", 8080, false);
|
||||
public ServiceInstance getLocalServiceInstance() {
|
||||
String serviceName = properties.getServiceName();
|
||||
String podName = System.getenv(HOSTNAME);
|
||||
ServiceInstance defaultInstance = new DefaultServiceInstance(serviceName,
|
||||
"localhost",
|
||||
8080,
|
||||
false);
|
||||
|
||||
Endpoints endpoints = client.endpoints().withName(serviceName).get();
|
||||
if (Utils.isNullOrEmpty(podName) || endpoints == null) {
|
||||
return defaultInstance;
|
||||
}
|
||||
try {
|
||||
return endpoints.getSubsets()
|
||||
.stream()
|
||||
.filter(s -> s.getAddresses().get(0).getTargetRef().getName().equals(podName))
|
||||
.map(s -> (ServiceInstance) new KubernetesServiceInstance(serviceName,
|
||||
s.getAddresses().stream().findFirst().orElseThrow(IllegalStateException::new),
|
||||
s.getPorts().stream().findFirst().orElseThrow(IllegalStateException::new),
|
||||
false))
|
||||
.findFirst().orElse(defaultInstance);
|
||||
} catch (Throwable t) {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
Endpoints endpoints = client.endpoints().withName(serviceName).get();
|
||||
Optional<Service> service = Optional.ofNullable(client.services().withName(serviceName).get());
|
||||
final Map<String, String> labels;
|
||||
if (service.isPresent()) {
|
||||
labels = service.get().getMetadata().getLabels();
|
||||
} else {
|
||||
labels = null;
|
||||
}
|
||||
if (Utils.isNullOrEmpty(podName) || endpoints == null) {
|
||||
return defaultInstance;
|
||||
}
|
||||
try {
|
||||
List<EndpointSubset> subsets = endpoints.getSubsets();
|
||||
|
||||
@Override
|
||||
public List<ServiceInstance> getInstances(String serviceId) {
|
||||
Assert.notNull(serviceId, "[Assertion failed] - the object argument must be null");
|
||||
return Optional.ofNullable(client.endpoints().withName(serviceId).get()).orElse(new Endpoints())
|
||||
.getSubsets()
|
||||
.stream()
|
||||
.flatMap(s -> s.getAddresses().stream().map(a -> (ServiceInstance) new KubernetesServiceInstance(serviceId, a ,s.getPorts().stream().findFirst().orElseThrow(IllegalStateException::new), false)))
|
||||
.collect(Collectors.toList());
|
||||
if (subsets != null) {
|
||||
for (EndpointSubset s : subsets) {
|
||||
List<EndpointAddress> addresses = s.getAddresses();
|
||||
for (EndpointAddress a : addresses) {
|
||||
return new KubernetesServiceInstance(serviceName,
|
||||
a,
|
||||
s.getPorts().stream().findFirst().orElseThrow(IllegalStateException::new),
|
||||
labels,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultInstance;
|
||||
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getServices() {
|
||||
return client.services().list()
|
||||
.getItems()
|
||||
.stream().map(s -> s.getMetadata().getName())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@Override
|
||||
public List<ServiceInstance> getInstances(String serviceId) {
|
||||
Assert.notNull(serviceId,
|
||||
"[Assertion failed] - the object argument must be null");
|
||||
Optional<Service> service = Optional.ofNullable(client.services().withName(serviceId).get());
|
||||
final Map<String, String> labels;
|
||||
if (service.isPresent()) {
|
||||
labels = service.get().getMetadata().getLabels();
|
||||
} else {
|
||||
labels = null;
|
||||
}
|
||||
|
||||
Optional<Endpoints> endpoints = Optional.ofNullable(client.endpoints().withName(serviceId).get());
|
||||
List<EndpointSubset> subsets = endpoints.get().getSubsets();
|
||||
List<ServiceInstance> instances = new ArrayList<>();
|
||||
if (subsets != null) {
|
||||
for (EndpointSubset s : subsets) {
|
||||
List<EndpointAddress> addresses = s.getAddresses();
|
||||
for (EndpointAddress a : addresses) {
|
||||
instances.add(new KubernetesServiceInstance(serviceId,
|
||||
a,
|
||||
s.getPorts().stream().findFirst().orElseThrow(IllegalStateException::new),
|
||||
labels,
|
||||
false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getServices() {
|
||||
return client.services().list()
|
||||
.getItems()
|
||||
.stream().map(s -> s.getMetadata().getName())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.registry.KubernetesRegistration;
|
||||
import org.springframework.cloud.kubernetes.registry.KubernetesServiceRegistry;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
@Configuration
|
||||
public class KubernetesDiscoveryClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public DiscoveryClient discoveryClient(KubernetesClient client,
|
||||
KubernetesDiscoveryProperties properties) {
|
||||
return new KubernetesDiscoveryClient(client, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KubernetesServiceRegistry getServiceRegistry() {
|
||||
return new KubernetesServiceRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KubernetesRegistration getRegistration(KubernetesClient client,
|
||||
KubernetesDiscoveryProperties properties) {
|
||||
return new KubernetesRegistration(client, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public KubernetesDiscoveryProperties getKubernetesDiscoveryProperties() {
|
||||
return new KubernetesDiscoveryProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "spring.cloud.kubernetes.discovery.catalog-services-watch.enabled", matchIfMissing = true)
|
||||
public KubernetesCatalogWatch kubernetesCatalogWatch(KubernetesDiscoveryClient client) {
|
||||
return new KubernetesCatalogWatch(client);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(KubernetesDiscoveryProperties.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.discovery.enabled", matchIfMissing = true)
|
||||
public class KubernetesDiscoveryClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public KubernetesDiscoveryClient kubernetesDiscoveryClient(KubernetesClient client, KubernetesDiscoveryProperties properties) {
|
||||
return new KubernetesDiscoveryClient(client, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KubernetesDiscoveryLifecycle kubernetesDiscoveryLifecycle(KubernetesClient client, KubernetesDiscoveryProperties properties) {
|
||||
return new KubernetesDiscoveryLifecycle(client, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "spring.cloud.kubernetes.discovery.catalog-services-watch.enabled", matchIfMissing = true)
|
||||
public KubernetesCatalogWatch kubernetesCatalogWatch(KubernetesDiscoveryClient client) {
|
||||
return new KubernetesCatalogWatch(client);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;
|
||||
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class KubernetesDiscoveryLifecycle extends AbstractDiscoveryLifecycle {
|
||||
|
||||
private KubernetesClient client;
|
||||
private KubernetesDiscoveryProperties properties;
|
||||
|
||||
private AtomicBoolean running = new AtomicBoolean(false);
|
||||
|
||||
public KubernetesDiscoveryLifecycle(KubernetesClient client, KubernetesDiscoveryProperties properties) {
|
||||
this.client = client;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (running.compareAndSet(false, true)) {
|
||||
register();
|
||||
getContext().publishEvent(new InstanceRegisteredEvent<>(this,
|
||||
getConfiguration()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running.get();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected int getConfiguredPort() {
|
||||
return client.getMasterUrl().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setConfiguredPort(int port) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getConfiguration() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void register() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deregister() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled() {
|
||||
return properties.isEnabled();
|
||||
}
|
||||
}
|
||||
@@ -18,34 +18,33 @@ package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
|
||||
|
||||
@ConfigurationProperties("spring.cloud.kubernetes.discovery")
|
||||
public class KubernetesDiscoveryProperties {
|
||||
public class KubernetesDiscoveryProperties extends AutoServiceRegistrationProperties {
|
||||
|
||||
private boolean enabled = true;
|
||||
private boolean enabled = true;
|
||||
|
||||
@Value("${spring.application.name:unknown}")
|
||||
private String serviceName = "unknown";
|
||||
@Value("${spring.application.name:unknown}")
|
||||
private String serviceName = "unknown";
|
||||
|
||||
private int catalogServicesWatchDelay = 30000;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public int getCatalogServicesWatchDelay() {
|
||||
return catalogServicesWatchDelay;
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setCatalogServicesWatchDelay(int catalogServicesWatchDelay) {
|
||||
this.catalogServicesWatchDelay = catalogServicesWatchDelay;
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KubernetesDiscoveryProperties{" +
|
||||
"enabled=" + enabled +
|
||||
", serviceName='" + serviceName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,75 +16,77 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.discovery;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.EndpointAddress;
|
||||
import io.fabric8.kubernetes.api.model.EndpointPort;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.fabric8.kubernetes.client.utils.Utils.isNotNullOrEmpty;
|
||||
import static io.fabric8.kubernetes.client.utils.Utils.isNullOrEmpty;
|
||||
|
||||
public class KubernetesServiceInstance implements ServiceInstance {
|
||||
|
||||
private static final String HTTP_PREFIX = "http://";
|
||||
private static final String HTTPS_PREFIX = "https://";
|
||||
private static final String COLN = ":";
|
||||
private static final String HTTP_PREFIX = "http://";
|
||||
private static final String HTTPS_PREFIX = "https://";
|
||||
private static final String COLN = ":";
|
||||
|
||||
private final String serviceId;
|
||||
private final EndpointAddress endpointAddress;
|
||||
private final EndpointPort endpointPort;
|
||||
private final Boolean secure;
|
||||
private final String serviceId;
|
||||
private final EndpointAddress endpointAddress;
|
||||
private final EndpointPort endpointPort;
|
||||
private final Boolean secure;
|
||||
private final Map<String, String> metadata;
|
||||
|
||||
public KubernetesServiceInstance(String serviceId, EndpointAddress endpointAddress, EndpointPort endpointPort, Boolean secure) {
|
||||
this.serviceId = serviceId;
|
||||
this.endpointAddress = endpointAddress;
|
||||
this.endpointPort = endpointPort;
|
||||
this.secure = secure;
|
||||
}
|
||||
public KubernetesServiceInstance(String serviceId,
|
||||
EndpointAddress endpointAddress,
|
||||
EndpointPort endpointPort,
|
||||
Map<String, String> metadata,
|
||||
Boolean secure) {
|
||||
this.serviceId = serviceId;
|
||||
this.endpointAddress = endpointAddress;
|
||||
this.endpointPort = endpointPort;
|
||||
this.metadata = metadata;
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return endpointAddress.getIp();
|
||||
}
|
||||
@Override
|
||||
public String getHost() {
|
||||
return endpointAddress.getIp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return endpointPort.getPort();
|
||||
}
|
||||
@Override
|
||||
public int getPort() {
|
||||
return endpointPort.getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return secure;
|
||||
}
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@Override
|
||||
public URI getUri() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (isSecure()) {
|
||||
sb.append(HTTPS_PREFIX);
|
||||
} else {
|
||||
sb.append(HTTP_PREFIX);
|
||||
}
|
||||
if (isSecure()) {
|
||||
sb.append(HTTPS_PREFIX);
|
||||
} else {
|
||||
sb.append(HTTP_PREFIX);
|
||||
}
|
||||
|
||||
sb.append(getHost()).append(COLN).append(getPort());
|
||||
try {
|
||||
return new URI(sb.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
sb.append(getHost()).append(COLN).append(getPort());
|
||||
try {
|
||||
return new URI(sb.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getMetadata() {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
public Map<String, String> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.springframework.cloud.kubernetes.registry;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent;
|
||||
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
|
||||
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
public class KubernetesAutoServiceRegistration implements AutoServiceRegistration,
|
||||
SmartLifecycle,
|
||||
Ordered {
|
||||
|
||||
private static final Log log = LogFactory.getLog(KubernetesAutoServiceRegistration.class);
|
||||
|
||||
private AtomicBoolean running = new AtomicBoolean(false);
|
||||
|
||||
private int order = 0;
|
||||
|
||||
private AtomicInteger port = new AtomicInteger(0);
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
private KubernetesServiceRegistry serviceRegistry;
|
||||
|
||||
private KubernetesRegistration registration;
|
||||
|
||||
public KubernetesAutoServiceRegistration(ApplicationContext context,
|
||||
KubernetesServiceRegistry serviceRegistry,
|
||||
KubernetesRegistration registration) {
|
||||
this.context = context;
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
this.registration = registration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.serviceRegistry.register(this.registration);
|
||||
|
||||
this.context.publishEvent(
|
||||
new InstanceRegisteredEvent<>(this,
|
||||
this.registration.getProperties()));
|
||||
this.running.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.serviceRegistry.deregister(this.registration);
|
||||
this.running.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@EventListener(ServletWebServerInitializedEvent.class)
|
||||
public void onApplicationEvent(ServletWebServerInitializedEvent event) {
|
||||
// TODO: take SSL into account
|
||||
int localPort = event.getWebServer().getPort();
|
||||
if (this.port.get() == 0) {
|
||||
log.info("Updating port to " + localPort);
|
||||
this.port.compareAndSet(0,
|
||||
localPort);
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener(ContextClosedEvent.class)
|
||||
public void onApplicationEvent(ContextClosedEvent event) {
|
||||
if (event.getApplicationContext() == context) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.springframework.cloud.kubernetes.registry;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
|
||||
|
||||
public class KubernetesRegistration implements Registration, Closeable {
|
||||
|
||||
|
||||
private final KubernetesClient client;
|
||||
private KubernetesDiscoveryProperties properties;
|
||||
private AtomicBoolean running = new AtomicBoolean(false);
|
||||
|
||||
public KubernetesRegistration(KubernetesClient client,
|
||||
KubernetesDiscoveryProperties properties) {
|
||||
this.client = client;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.client.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return properties.getServiceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return client.getMasterUrl().getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
try {
|
||||
return client.getMasterUrl().toURI();
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public KubernetesDiscoveryProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KubernetesRegistration{" +
|
||||
"client=" + client +
|
||||
", properties=" + properties +
|
||||
", running=" + running +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.springframework.cloud.kubernetes.registry;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
|
||||
|
||||
public class KubernetesServiceRegistry implements ServiceRegistry<KubernetesRegistration> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(KubernetesServiceRegistry.class);
|
||||
|
||||
public KubernetesServiceRegistry() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(KubernetesRegistration registration) {
|
||||
log.info("Registering : " + registration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregister(KubernetesRegistration registration) {
|
||||
log.info("DeRegistering : " + registration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(KubernetesRegistration registration,
|
||||
String status) {
|
||||
log.info("Set Status for : " + registration + " Status: " + status);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getStatus(KubernetesRegistration registration) {
|
||||
log.info("Get Status for : " + registration );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientAutoConfiguration
|
||||
# Discovery Client Configuration
|
||||
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
|
||||
org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientConfiguration
|
||||
org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClient
|
||||
|
||||
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Oleg Vyukov
|
||||
*/
|
||||
public class KubernetesCatalogServicesWatchtConfigurationTest {
|
||||
public class KubernetesCatalogServicesWatchConfigurationTest {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@@ -51,7 +51,7 @@ public class KubernetesCatalogServicesWatchtConfigurationTest {
|
||||
this.context = new SpringApplicationBuilder(
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
KubernetesClientTestConfiguration.class,
|
||||
KubernetesDiscoveryClientConfiguration.class).web(false)
|
||||
KubernetesDiscoveryClientAutoConfiguration.class).web(false)
|
||||
.properties(env).run();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
# Setting up the Environment
|
||||
|
||||
To play with these examples, you can install locally Kubernetes & Docker using `[Minikube](https://kubernetes.io/docs/getting-started-guides/minikube/)` within a Virtual Machine
|
||||
managed by a hypervisor (Xhyve, Virtualbox or KVM) if your machine is not a native Unix operating system.
|
||||
|
||||
|
||||
When the minikube is installed on your machine, you can start kubernetes using this command:
|
||||
```
|
||||
minikube start
|
||||
```
|
||||
|
||||
You also probably want to configure your docker client to point the minikube docker deamon with:
|
||||
```
|
||||
eval $(minikube docker-env)
|
||||
```
|
||||
|
||||
This will make sure that the docker images that you build are available to the minikube environment.
|
||||
|
||||
# Hello World Example
|
||||
|
||||
This Spring Boot application exposes an endpoint that we can call to receive a `Hello World` message as response. The application is configured using the
|
||||
@@ -6,25 +24,18 @@ This Spring Boot application exposes an endpoint that we can call to receive a `
|
||||
The uberjar of the Spring Boot application is packaged within a Docker image using the [Fabric8 Maven plugin](maven.fabric8.io) and next deployed top of the Kubernetes management platform as a pod
|
||||
using the replication controller created by the plugin.
|
||||
|
||||
To play with the example, it is required to have access to a Kubernetes Management Platform which is available on GCE. If you don't have an account on GCE,
|
||||
you can install locally Kubernetes & Docker using `[Minikube](https://kubernetes.io/docs/getting-started-guides/minikube/)` within a Virtual Machine
|
||||
managed by a hypervisor (Xhyve, Virtualbox or KVM) if your machine is not a native Unix operating system.
|
||||
|
||||
The script to install `minikube` is
|
||||
```
|
||||
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.17.1/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
|
||||
```
|
||||
|
||||
When the client is installed on your machine, you can start kubernetes using this command:
|
||||
```
|
||||
minikube start
|
||||
```
|
||||
Once you have the environment set up (minikube or kubectl configured against a kubernetes cluster)
|
||||
|
||||
Next, you can play with this Spring Boot application in the cloud using the following maven command to deploy it:
|
||||
You can play with this Spring Boot application in the cloud using the following maven command to deploy it:
|
||||
```
|
||||
mvn clean package fabric8:deploy -Pkubernetes
|
||||
```
|
||||
|
||||
**Note**: Unfortuntaly, when you deploy using the fabric8 plugin, the readyness and liveness probes fail to point to the right actuator URL due a lack of support for spring boot.
|
||||
This push you to edit the generated deployment inside kubernetes and change these probes which points to "path": "/health" to "path": "/actuator/health".
|
||||
This will make your deployment go green. This issue is already reported into the fabric8 community: https://github.com/fabric8io/fabric8-maven-plugin/issues/1178
|
||||
|
||||
When the application has been deployed, you can access its service or endpoint url using this command:
|
||||
```
|
||||
minikube service kubernetes-hello-world --url
|
||||
@@ -35,19 +46,15 @@ And next you can curl the endpoint using the url returned by the previous comman
|
||||
```
|
||||
curl http://IP_OR_HOSTNAME/
|
||||
```
|
||||
|
||||
then
|
||||
|
||||
```
|
||||
curl http://IP_OR_HOSTNAME/services
|
||||
```
|
||||
|
||||
Should return you the list of available services discovered by the DiscoveryClient
|
||||
|
||||
## Integration test
|
||||
|
||||
To deploy the resources required on Kubernetes/OpenShift and to test if the service deployed can answer to requests, we will use the [Arquillian Kubernetes Cube](https://github.com/arquillian/arquillian-cube/blob/master/docs/kubernetes.adoc) framework.
|
||||
This testing framework uses the Kubernetes Java API to communicate with the platform, deploy the resources (pod, service, deployment, ...) generated by the Fabric8 maven plugin.
|
||||
When the service becomes available, the method defines within thr Junit test will call the endpoint to verify that it receives as response the string
|
||||
`Hello World`
|
||||
|
||||
Remark : If you run the integration test against OpenShift, create first the namespace/project `it` that arquillian will use to deploy the resources using the oc client
|
||||
`oc new-project it`.
|
||||
|
||||
When you are logged to the OpenShift platform, execute the following maven command to run the Integration Test against OpenShift
|
||||
|
||||
```
|
||||
mvn clean install -Pintegration
|
||||
```
|
||||
|
||||
@@ -35,6 +35,17 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
@@ -57,23 +68,23 @@
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>org.jboss.arquillian.junit</groupId>
|
||||
<artifactId>arquillian-junit-standalone</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.arquillian.cube</groupId>
|
||||
<artifactId>arquillian-cube-requirement</artifactId>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.arquillian.cube</groupId>
|
||||
<artifactId>arquillian-cube-kubernetes</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.arquillian.cube</groupId>
|
||||
<artifactId>arquillian-cube-openshift</artifactId>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -18,16 +18,17 @@ package org.springframework.cloud.kubernetes.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(App.class, args);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(App.class,
|
||||
args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
package org.springframework.cloud.kubernetes.examples;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class HelloController {
|
||||
|
||||
private static final Log log = LogFactory.getLog(HelloController.class);
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
@RequestMapping("/")
|
||||
public String hello() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
@RequestMapping("/services")
|
||||
public List<String> services() {
|
||||
return this.discoveryClient.getServices();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.springframework.cloud.kubernetes.examples;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = App.class)
|
||||
public class ApplicationTestIT {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
/*
|
||||
* This test proves that the application can be loaded successful and that all @configurations and dependencies are there
|
||||
*/
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
assertThat(context).isNotNull();
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.springframework.cloud.kubernetes.examples;
|
||||
|
||||
|
||||
import org.arquillian.cube.kubernetes.annotations.PortForward;
|
||||
import org.arquillian.cube.kubernetes.impl.requirement.RequiresKubernetes;
|
||||
import org.arquillian.cube.requirement.ArquillianConditionalRunner;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
@RunWith(ArquillianConditionalRunner.class)
|
||||
@RequiresKubernetes
|
||||
public class HelloWorldIT {
|
||||
|
||||
@ArquillianResource
|
||||
@Named("kubernetes-hello-world") //The service name is "${project.artifactId}".substring(0,23)
|
||||
@PortForward
|
||||
URL url;
|
||||
|
||||
@Test
|
||||
public void service_should_be_accessible() throws IOException {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Request request = new Request.Builder().get().url(url).build();
|
||||
Response response = client.newCall(request).execute();
|
||||
Assert.assertTrue(response.isSuccessful());
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://jboss.org/schema/arquillian"
|
||||
xsi:schemaLocation="http://jboss.org/schema/arquillian
|
||||
http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
|
||||
|
||||
<extension qualifier="openshift">
|
||||
<property name="namespace.use.existing">it</property>
|
||||
<!-- <property name="wait.timeout">600000</property>-->
|
||||
<property name="env.config.resource.name">openshift.yml</property>
|
||||
<property name="namespace.cleanup.enabled">true</property>
|
||||
</extension>
|
||||
</arquillian>
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
# Setting up the Environment
|
||||
|
||||
To play with these examples, you can install locally Kubernetes & Docker using `[Minikube](https://kubernetes.io/docs/getting-started-guides/minikube/)` within a Virtual Machine
|
||||
managed by a hypervisor (Xhyve, Virtualbox or KVM) if your machine is not a native Unix operating system.
|
||||
|
||||
|
||||
When the minikube is installed on your machine, you can start kubernetes using this command:
|
||||
```
|
||||
minikube start
|
||||
```
|
||||
|
||||
You also probably want to configure your docker client to point the minikube docker deamon with:
|
||||
```
|
||||
eval $(minikube docker-env)
|
||||
```
|
||||
|
||||
This will make sure that the docker images that you build are available to the minikube environment.
|
||||
|
||||
## Kubernetes Reload Example
|
||||
|
||||
This example demonstrate how to use the reload feature to change the configuration of a spring-boot application at runtime.
|
||||
|
||||
The application consists of a timed bean that periodically prints a message to the console.
|
||||
The message can be changed using a config map.
|
||||
|
||||
### Running the example
|
||||
Once you have your environment set up, you can deploy the application using the fabric8 maven plugin:
|
||||
|
||||
```
|
||||
mvn clean install fabric8:build fabric8:deploy -Pintegration
|
||||
```
|
||||
|
||||
**Note**: Unfortuntaly, when you deploy using the fabric8 plugin, the readyness and liveness probes fail to point to the right actuator URL due a lack of support for spring boot.
|
||||
This push you to edit the generated deployment inside kubernetes and change these probes which points to "path": "/health" to "path": "/actuator/health".
|
||||
This will make your deployment go green. This issue is already reported into the fabric8 community: https://github.com/fabric8io/fabric8-maven-plugin/issues/1178
|
||||
|
||||
### Changing the configuration
|
||||
|
||||
Create a yaml file with the following contents:
|
||||
|
||||
```yml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: reload-example
|
||||
data:
|
||||
application.properties: |-
|
||||
bean.message=Hello World!
|
||||
another.property=value
|
||||
```
|
||||
|
||||
A sample config map is provided with this example in the *config-map.yml* file.
|
||||
|
||||
To deploy the config map, just run the following command on Kubernetes:
|
||||
|
||||
```
|
||||
kubectl create -f config-map.yml
|
||||
```
|
||||
|
||||
As soon as the config map is deployed, the output of the application changes accordingly.
|
||||
The config map can be now edited with the following command:
|
||||
|
||||
```
|
||||
kubectl edit configmap reload-example
|
||||
```
|
||||
|
||||
Changes are applied immediately when using the *event* reload mode.
|
||||
|
||||
The name of the config map (*"reload-example"*) matches the name of the application as declared in the *application.properties* file.
|
||||
@@ -1,109 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>0.2.1.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>0.2.1.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-example-reload</artifactId>
|
||||
<artifactId>spring-cloud-kubernetes-example-reload</artifactId>
|
||||
|
||||
<name>Spring Cloud Kubernetes :: Examples :: Reload ConfigMap</name>
|
||||
<description>Example demonstrating how to use the configuration reload feature.</description>
|
||||
<name>Spring Cloud Kubernetes :: Examples :: Reload ConfigMap</name>
|
||||
<description>Example demonstrating how to use the configuration reload feature.</description>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<properties>
|
||||
<hibernate-validator.version>5.2.4.Final</hibernate-validator.version>
|
||||
</properties>
|
||||
<properties>
|
||||
<hibernate-validator.version>5.2.4.Final</hibernate-validator.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!--skip deploy -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>${maven-deploy-plugin.version}</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<!--skip deploy -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>${maven-deploy-plugin.version}</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>fabric8-maven-plugin</artifactId>
|
||||
<version>${fabric8.maven.plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>fmp</id>
|
||||
<goals>
|
||||
<goal>resource</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>fabric8-maven-plugin</artifactId>
|
||||
<version>${fabric8.maven.plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>fmp</id>
|
||||
<goals>
|
||||
<goal>resource</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<enricher>
|
||||
<config>
|
||||
@@ -116,9 +109,9 @@
|
||||
</config>
|
||||
</enricher>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
@@ -143,7 +136,7 @@
|
||||
<config>
|
||||
<fmp-controller>
|
||||
<name>spring-cloud-reload</name>
|
||||
</fmp-controller>
|
||||
</fmp-controller>
|
||||
<fmp-service>
|
||||
<name>spring-cloud-reload</name>
|
||||
<type>NodePort</type>
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
## Kubernetes Reload Example
|
||||
|
||||
This example demonstrate how to use the reload feature to change the configuration of a spring-boot application at runtime.
|
||||
|
||||
The application consists of a timed bean that periodically prints a message to the console.
|
||||
The message can be changed using a config map.
|
||||
|
||||
### Running the example
|
||||
|
||||
When using Openshift, you must assign the `view` role to the *default* service account in the current project:
|
||||
|
||||
```
|
||||
oc policy add-role-to-user view --serviceaccount=default
|
||||
```
|
||||
|
||||
You can deploy the application using the fabric8 maven plugin:
|
||||
|
||||
```
|
||||
mvn clean install fabric8:build fabric8:deploy -Pintegration
|
||||
```
|
||||
|
||||
### Changing the configuration
|
||||
|
||||
Create a yaml file with the following contents:
|
||||
|
||||
```yml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: reload-example
|
||||
data:
|
||||
application.properties: |-
|
||||
bean.message=Hello World!
|
||||
another.property=value
|
||||
```
|
||||
|
||||
A sample config map is provided with this example in the *config-map.yml* file.
|
||||
|
||||
To deploy the config map, just run the following command on Openshift (just replace `oc` with `kubectl` if you are using plain Kubernetes):
|
||||
|
||||
```
|
||||
oc create -f config-map.yml
|
||||
```
|
||||
|
||||
As soon as the config map is deployed, the output of the application changes accordingly.
|
||||
The config map can be now edited with the following command:
|
||||
|
||||
```
|
||||
oc edit configmap reload-example
|
||||
```
|
||||
|
||||
Changes are applied immediately when using the *event* reload mode.
|
||||
|
||||
The name of the config map (*"reload-example"*) matches the name of the application as declared in the *application.properties* file.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.kubernetes.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
spring.application.name=reload-example
|
||||
|
||||
management.endpoint.health.enabled=true
|
||||
management.endpoint.info.enabled=true
|
||||
management.endpoint.restart.enabled=true
|
||||
spring.cloud.kubernetes.reload.enabled=true
|
||||
|
||||
#spring.cloud.kubernetes.reload.strategy=restart_context
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<modules>
|
||||
<module>kubernetes-reload-example</module>
|
||||
<module>kubernetes-hello-world-example</module>
|
||||
<module>kubernetes-circuitbreaker-ribbon-example</module>
|
||||
<module>kubernetes-zipkin-example</module>
|
||||
<!-- <module>kubernetes-circuitbreaker-ribbon-example</module>
|
||||
<module>kubernetes-zipkin-example</module> -->
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
||||
Reference in New Issue
Block a user