initial Spring Boot 2.0.x refactoring (#152)

* updating discovery and config modules, removing netflix modules to start simple

* hacking some jackson dependencies as they were not working

* update Discovery AutoConfiguration, still messy, more work needed here

* getting configuration working with the service registry

* removing profiles check document matcher that doesn't exist anymore

* fixing configMaps example

* improving readmes and removing arquillian due it requires undertow

* re adding all netflix modules

* re adding commented example modules

* fixing minor naming issues and adding commented out modules
This commit is contained in:
Salaboy
2018-03-16 19:58:24 +00:00
committed by Spencer Gibb
parent 7507e75b43
commit 1e71319f9f
34 changed files with 1007 additions and 647 deletions

View File

@@ -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
View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
@@ -108,11 +107,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();
};

View File

@@ -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 {
/**

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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()))

View File

@@ -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>
@@ -44,36 +42,53 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-archaius</artifactId>
<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-kubernetes-hystrix</artifactId>
<version>${project.version}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-kubernetes-archaius</artifactId>-->
<!--<version>${project.version}</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-ribbon</artifactId>
<version>${project.version}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-kubernetes-hystrix</artifactId>-->
<!--<version>${project.version}</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-zipkin</artifactId>
<version>${project.version}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-kubernetes-ribbon</artifactId>-->
<!--<version>${project.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-kubernetes-zipkin</artifactId>-->
<!--<version>${project.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-kubernetes-netflix</artifactId>-->
<!--<version>${project.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-kubernetes-zipkin</artifactId>-->
<!--<version>${project.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-kubernetes-all</artifactId>-->
<!--<version>${project.version}</version>-->
<!--</dependency>-->
<!-- Own dependencies - Starters -->
<dependency>
@@ -88,24 +103,6 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-netflix</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-zipkin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-all</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Testing Dependencies -->
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
@@ -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>

View File

@@ -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>

View File

@@ -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());
}
}

View File

@@ -0,0 +1,42 @@
package org.springframework.cloud.kubernetes.discovery;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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 {
private static final Log log = LogFactory.getLog(KubernetesDiscoveryClientAutoConfiguration.class);
@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();
}
}

View File

@@ -1,42 +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);
}
}

View File

@@ -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();
}
}

View File

@@ -18,24 +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";
public boolean isEnabled() {
return enabled;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getServiceName() {
return serviceName;
}
public String getServiceName() {
return serviceName;
}
@Override
public String toString() {
return "KubernetesDiscoveryProperties{" +
"enabled=" + enabled +
", serviceName='" + serviceName + '\'' +
'}';
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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 +
'}';
}
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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
```

View File

@@ -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>

View File

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

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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>

View File

@@ -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.

View 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>

View File

@@ -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.

View 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;
/**

View File

@@ -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

View File

@@ -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>