merge 1.1.x
This commit is contained in:
@@ -27,8 +27,7 @@ This reference guide covers how to use Spring Cloud Kubernetes.
|
||||
|
||||
== Why do you need Spring Cloud Kubernetes?
|
||||
|
||||
Spring Cloud Kubernetes provide Spring Cloud common interface implementations that consume Kubernetes native services.
|
||||
The main objective of the projects provided in this repository is to facilitate the integration of Spring Cloud and Spring Boot applications running inside Kubernetes.
|
||||
Spring Cloud Kubernetes provides implementations of well known Spring Cloud interfaces allowing developers to build and run Spring Cloud applications on Kubernetes. While this project may be useful to you when building a cloud native application, it is also not a requirement in order to deploy a Spring Boot app on Kubernetes. If you are just getting started in your journey to running your Spring Boot app on Kubernetes you can accomplish a lot with nothing more than a basic Spring Boot app and Kubernetes itself. To learn more, you can get started by reading the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#cloud-deployment-kubernetes[Spring Boot reference documentation for deploying to Kubernetes ] and also working through the workshop material https://hackmd.io/@ryanjbaxter/spring-on-k8s-workshop[Spring and Kubernetes].
|
||||
|
||||
== Starters
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Kubernetes Docs</name>
|
||||
|
||||
7
docs/src/main/asciidoc/info-contributor.adoc
Normal file
7
docs/src/main/asciidoc/info-contributor.adoc
Normal file
@@ -0,0 +1,7 @@
|
||||
== Info Contributor
|
||||
|
||||
Spring Cloud Kubernetes includes an `InfoContributor` which adds Pod information to
|
||||
Spring Boot's `/info` Acturator endpoint.
|
||||
|
||||
You can disable this `InfoContributor` by setting `management.info.kubernetes.enabled`
|
||||
to `false` in `bootstrap.[properties | yaml]`.
|
||||
28
docs/src/main/asciidoc/load-balancer.adoc
Normal file
28
docs/src/main/asciidoc/load-balancer.adoc
Normal file
@@ -0,0 +1,28 @@
|
||||
== LoadBalancer for Kubernetes
|
||||
This project includes Spring Cloud Load Balancer for load balancing based on Kubernetes Endpoints and provides implementation of load balancer based on Kubernetes Service.
|
||||
To include it to your project add the following dependency.
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
====
|
||||
|
||||
To enable load balancing based on Kubernetes Service name use the following property. Then load balancer would try to call application using address, for example `service-a.default.svc.cluster.local`
|
||||
====
|
||||
[source]
|
||||
----
|
||||
spring.cloud.kubernetes.loadbalancer.mode=SERVICE
|
||||
----
|
||||
====
|
||||
|
||||
To enabled load balancing across all namespaces use the following property. Property from `spring-cloud-kubernetes-discovery` module is respected.
|
||||
====
|
||||
[source]
|
||||
----
|
||||
spring.cloud.kubernetes.discovery.all-namespaces=true
|
||||
----
|
||||
====
|
||||
@@ -279,6 +279,10 @@ You can specify multiple (exact) file paths in `spring.cloud.kubernetes.config.p
|
||||
|
||||
NOTE: You have to provide the full exact path to each property file, because directories are not being recursively parsed.
|
||||
|
||||
NOTE: If you use `spring.cloud.kubernetes.config.paths` or `spring.cloud.kubernetes.secrets.path` the automatic reload
|
||||
functionality will not work. You will need to make a `POST` request to the `/actuator/refresh` endpoint or
|
||||
restart/redeploy the application.
|
||||
|
||||
.Properties:
|
||||
[options="header,footer"]
|
||||
|===
|
||||
|
||||
@@ -5,8 +5,7 @@ This reference guide covers how to use Spring Cloud Kubernetes.
|
||||
|
||||
== Why do you need Spring Cloud Kubernetes?
|
||||
|
||||
Spring Cloud Kubernetes provide Spring Cloud common interface implementations that consume Kubernetes native services.
|
||||
The main objective of the projects provided in this repository is to facilitate the integration of Spring Cloud and Spring Boot applications running inside Kubernetes.
|
||||
Spring Cloud Kubernetes provides implementations of well known Spring Cloud interfaces allowing developers to build and run Spring Cloud applications on Kubernetes. While this project may be useful to you when building a cloud native application, it is also not a requirement in order to deploy a Spring Boot app on Kubernetes. If you are just getting started in your journey to running your Spring Boot app on Kubernetes you can accomplish a lot with nothing more than a basic Spring Boot app and Kubernetes itself. To learn more, you can get started by reading the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#cloud-deployment-kubernetes[Spring Boot reference documentation for deploying to Kubernetes ] and also working through the workshop material https://hackmd.io/@ryanjbaxter/spring-on-k8s-workshop[Spring and Kubernetes].
|
||||
|
||||
include::getting-started.adoc[]
|
||||
|
||||
@@ -22,8 +21,12 @@ include::kubernetes-awareness.adoc[]
|
||||
|
||||
include::pod-health-indicator.adoc[]
|
||||
|
||||
include::info-contributor.adoc[]
|
||||
|
||||
include::leader-election.adoc[]
|
||||
|
||||
include::load-balancer.adoc[]
|
||||
|
||||
include::security-service-accounts.adoc[]
|
||||
|
||||
include::service-registry.adoc[]
|
||||
|
||||
12
pom.xml
12
pom.xml
@@ -24,13 +24,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>2.3.1.BUILD-SNAPSHOT</version>
|
||||
<version>2.3.1.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Kubernetes</name>
|
||||
|
||||
@@ -63,9 +63,9 @@
|
||||
|
||||
<properties>
|
||||
<!-- Dependency Versions -->
|
||||
<spring-cloud-commons.version>2.2.4.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.2.4.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-config.version>2.2.4.BUILD-SNAPSHOT</spring-cloud-config.version>
|
||||
<spring-cloud-commons.version>2.2.7.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.2.7.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-config.version>2.2.7.BUILD-SNAPSHOT</spring-cloud-config.version>
|
||||
|
||||
<!-- Maven Plugin Versions -->
|
||||
<maven-compiler-plugin.version>3.5</maven-compiler-plugin.version>
|
||||
@@ -99,6 +99,8 @@
|
||||
<module>spring-cloud-kubernetes-istio</module>
|
||||
<module>spring-cloud-kubernetes-integration-tests</module>
|
||||
<module>docs</module>
|
||||
<module>spring-cloud-kubernetes-loadbalancer</module>
|
||||
<module>spring-cloud-starter-kubernetes-loadbalancer</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
|
||||
}).filter(Files::isRegularFile).forEach(p -> {
|
||||
try {
|
||||
String content = new String(Files.readAllBytes(p)).trim();
|
||||
String filename = p.getFileName().toString().toLowerCase();
|
||||
String filename = p.toAbsolutePath().toString().toLowerCase();
|
||||
if (filename.endsWith(".properties")) {
|
||||
addPropertySourceIfNeeded(
|
||||
c -> PROPERTIES_TO_MAP
|
||||
|
||||
@@ -24,6 +24,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -36,6 +38,7 @@ import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocato
|
||||
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.AbstractEnvironment;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
@@ -72,32 +75,28 @@ public class ConfigReloadAutoConfiguration {
|
||||
@Autowired
|
||||
private KubernetesClient kubernetesClient;
|
||||
|
||||
@Autowired
|
||||
private ConfigMapPropertySourceLocator configMapPropertySourceLocator;
|
||||
|
||||
@Autowired
|
||||
private SecretsPropertySourceLocator secretsPropertySourceLocator;
|
||||
|
||||
/**
|
||||
* @param properties config reload properties
|
||||
* @param strategy configuration update strategy
|
||||
* @return a bean that listen to configuration changes and fire a reload.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Conditional(OnConfigEnabledOrSecretsEnabled.class)
|
||||
public ConfigurationChangeDetector propertyChangeWatcher(
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy) {
|
||||
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
|
||||
@Autowired(
|
||||
required = false) ConfigMapPropertySourceLocator configMapPropertySourceLocator,
|
||||
@Autowired(
|
||||
required = false) SecretsPropertySourceLocator secretsPropertySourceLocator) {
|
||||
switch (properties.getMode()) {
|
||||
case POLLING:
|
||||
return new PollingConfigurationChangeDetector(this.environment,
|
||||
properties, this.kubernetesClient, strategy,
|
||||
this.configMapPropertySourceLocator,
|
||||
this.secretsPropertySourceLocator);
|
||||
configMapPropertySourceLocator, secretsPropertySourceLocator);
|
||||
case EVENT:
|
||||
return new EventBasedConfigurationChangeDetector(this.environment,
|
||||
properties, this.kubernetesClient, strategy,
|
||||
this.configMapPropertySourceLocator,
|
||||
this.secretsPropertySourceLocator);
|
||||
configMapPropertySourceLocator, secretsPropertySourceLocator);
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Unsupported configuration reload mode: " + properties.getMode());
|
||||
@@ -148,6 +147,24 @@ public class ConfigReloadAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private static class OnConfigEnabledOrSecretsEnabled extends AnyNestedCondition {
|
||||
|
||||
OnConfigEnabledOrSecretsEnabled() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
|
||||
static class configEnabled {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(SecretsPropertySourceLocator.class)
|
||||
static class secretsEnabled {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,7 +66,8 @@ public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDe
|
||||
public void watch() {
|
||||
boolean activated = false;
|
||||
|
||||
if (this.properties.isMonitoringConfigMaps()) {
|
||||
if (this.properties.isMonitoringConfigMaps()
|
||||
&& this.configMapPropertySourceLocator != null) {
|
||||
try {
|
||||
String name = "config-maps-watch";
|
||||
this.watches.put(name, this.kubernetesClient.configMaps()
|
||||
@@ -91,7 +92,8 @@ public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDe
|
||||
}
|
||||
}
|
||||
|
||||
if (this.properties.isMonitoringSecrets()) {
|
||||
if (this.properties.isMonitoringSecrets()
|
||||
&& this.secretsPropertySourceLocator != null) {
|
||||
try {
|
||||
activated = false;
|
||||
String name = "secrets-watch";
|
||||
|
||||
@@ -68,7 +68,8 @@ public class PollingConfigurationChangeDetector extends ConfigurationChangeDetec
|
||||
public void executeCycle() {
|
||||
|
||||
boolean changedConfigMap = false;
|
||||
if (this.properties.isMonitoringConfigMaps()) {
|
||||
if (this.properties.isMonitoringConfigMaps()
|
||||
&& this.configMapPropertySourceLocator != null) {
|
||||
List<? extends MapPropertySource> currentConfigMapSources = findPropertySources(
|
||||
ConfigMapPropertySource.class);
|
||||
|
||||
@@ -81,7 +82,8 @@ public class PollingConfigurationChangeDetector extends ConfigurationChangeDetec
|
||||
}
|
||||
|
||||
boolean changedSecrets = false;
|
||||
if (this.properties.isMonitoringSecrets()) {
|
||||
if (this.properties.isMonitoringSecrets()
|
||||
&& this.secretsPropertySourceLocator != null) {
|
||||
List<MapPropertySource> currentSecretSources = locateMapPropertySources(
|
||||
this.secretsPropertySourceLocator, this.environment);
|
||||
if (currentSecretSources != null && !currentSecretSources.isEmpty()) {
|
||||
|
||||
@@ -45,11 +45,14 @@ import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.crea
|
||||
"spring.cloud.kubernetes.config.enableApi=false",
|
||||
"spring.cloud.kubernetes.config.paths="
|
||||
+ ConfigMapsFromFilePathsTests.FIRST_FILE_NAME_FULL_PATH + ","
|
||||
+ ConfigMapsFromFilePathsTests.SECOND_FILE_NAME_FULL_PATH })
|
||||
+ ConfigMapsFromFilePathsTests.SECOND_FILE_NAME_FULL_PATH + ","
|
||||
+ ConfigMapsFromFilePathsTests.FIRST_FILE_NAME_DUPLICATED_FULL_PATH })
|
||||
public class ConfigMapsFromFilePathsTests {
|
||||
|
||||
protected static final String FILES_ROOT_PATH = "/tmp/scktests";
|
||||
|
||||
protected static final String FILES_SUB_PATH = "another-directory";
|
||||
|
||||
protected static final String FIRST_FILE_NAME = "application.properties";
|
||||
|
||||
protected static final String SECOND_FILE_NAME = "extra.properties";
|
||||
@@ -65,6 +68,9 @@ public class ConfigMapsFromFilePathsTests {
|
||||
protected static final String UNUSED_FILE_NAME_FULL_PATH = FILES_ROOT_PATH + "/"
|
||||
+ UNUSED_FILE_NAME;
|
||||
|
||||
protected static final String FIRST_FILE_NAME_DUPLICATED_FULL_PATH = FILES_ROOT_PATH
|
||||
+ "/" + FILES_SUB_PATH + "/" + FIRST_FILE_NAME;
|
||||
|
||||
@ClassRule
|
||||
public static KubernetesServer server = new KubernetesServer();
|
||||
|
||||
@@ -87,12 +93,14 @@ public class ConfigMapsFromFilePathsTests {
|
||||
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
|
||||
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
|
||||
|
||||
Files.createDirectories(Paths.get(FILES_ROOT_PATH));
|
||||
Files.createDirectories(Paths.get(FILES_ROOT_PATH + "/" + FILES_SUB_PATH));
|
||||
createFileWithContent(FIRST_FILE_NAME_FULL_PATH,
|
||||
"bean.greeting=Hello from path!");
|
||||
createFileWithContent(SECOND_FILE_NAME_FULL_PATH, "bean.farewell=Bye from path!");
|
||||
createFileWithContent(UNUSED_FILE_NAME_FULL_PATH,
|
||||
"bean.morning=Morning from path!");
|
||||
createFileWithContent(FIRST_FILE_NAME_DUPLICATED_FULL_PATH,
|
||||
"bean.bonjour=Bonjour from path!");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@@ -125,4 +133,10 @@ public class ConfigMapsFromFilePathsTests {
|
||||
.expectBody().jsonPath("content").isEqualTo("Good morning, World!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bonjourInputShouldReturnPropertyFromDuplicatedFile() {
|
||||
this.webClient.get().uri("/api/bonjour").exchange().expectStatus().isOk()
|
||||
.expectBody().jsonPath("content").isEqualTo("Bonjour from path!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,4 +52,10 @@ public class GreetingController {
|
||||
return new ResponseMessage(String.format(this.properties.getMorning(), name));
|
||||
}
|
||||
|
||||
@RequestMapping("/api/bonjour")
|
||||
public ResponseMessage bonjour(
|
||||
@RequestParam(value = "name", defaultValue = "World") String name) {
|
||||
return new ResponseMessage(String.format(this.properties.getBonjour(), name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ public class GreetingProperties {
|
||||
|
||||
private String morning = "Good morning, %s!";
|
||||
|
||||
private String bonjour = "Bonjour, %s!";
|
||||
|
||||
public String getGreeting() {
|
||||
return this.greeting;
|
||||
}
|
||||
@@ -51,4 +53,12 @@ public class GreetingProperties {
|
||||
this.morning = morning;
|
||||
}
|
||||
|
||||
public String getBonjour() {
|
||||
return this.bonjour;
|
||||
}
|
||||
|
||||
public void setBonjour(String bonjour) {
|
||||
this.bonjour = bonjour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
|
||||
import org.springframework.boot.actuate.autoconfigure.info.ConditionalOnEnabledInfoContributor;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -130,7 +131,7 @@ public class KubernetesAutoConfiguration {
|
||||
base.getHttpsProxy()))
|
||||
.withProxyUsername(or(kubernetesClientProperties.getProxyUsername(),
|
||||
base.getProxyUsername()))
|
||||
.withPassword(or(kubernetesClientProperties.getProxyPassword(),
|
||||
.withProxyPassword(or(kubernetesClientProperties.getProxyPassword(),
|
||||
base.getProxyPassword()))
|
||||
.withNoProxy(
|
||||
or(kubernetesClientProperties.getNoProxy(), base.getNoProxy()))
|
||||
@@ -166,6 +167,7 @@ public class KubernetesAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledInfoContributor("kubernetes")
|
||||
public KubernetesInfoContributor kubernetesInfoContributor(PodUtils podUtils) {
|
||||
return new KubernetesInfoContributor(podUtils);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or 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
|
||||
*
|
||||
* https://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;
|
||||
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.server.mock.KubernetesServer;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
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.cloud.kubernetes.example.App;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = App.class,
|
||||
properties = { "spring.cloud.kubernetes.client.password=mypassword",
|
||||
"spring.cloud.kubernetes.client.proxy-password=myproxypassword" })
|
||||
public class KubernetesAutoConfigurationTests {
|
||||
|
||||
@ClassRule
|
||||
public static KubernetesServer server = new KubernetesServer();
|
||||
|
||||
@Autowired
|
||||
ConfigurableApplicationContext context;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpBeforeClass() {
|
||||
KubernetesClient mockClient = server.getClient();
|
||||
|
||||
// Configure the kubernetes master url to point to the mock server
|
||||
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
|
||||
mockClient.getConfiguration().getMasterUrl());
|
||||
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
|
||||
"false");
|
||||
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
|
||||
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beansAreCreated() {
|
||||
assertThat(context.getBeanNamesForType(Config.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesClient.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(StandardPodUtils.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesHealthIndicator.class))
|
||||
.hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesInfoContributor.class))
|
||||
.hasSize(1);
|
||||
|
||||
Config config = context.getBean(Config.class);
|
||||
assertThat(config.getPassword()).isEqualTo("mypassword");
|
||||
assertThat(config.getProxyPassword()).isEqualTo("myproxypassword");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,21 +23,22 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.3.1.BUILD-SNAPSHOT</version>
|
||||
<version>2.3.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-kubernetes-dependencies</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Kubernetes :: Dependencies</name>
|
||||
<description>Spring Cloud Kubernetes Dependencies</description>
|
||||
<properties>
|
||||
<arquillian.version>1.4.0.Final</arquillian.version>
|
||||
<arquillian-cube.version>1.15.2</arquillian-cube.version>
|
||||
<hoverfly.version>0.13.0</hoverfly.version>
|
||||
<kubernetes-client.version>4.10.3</kubernetes-client.version>
|
||||
<istio-client.version>1.0.0</istio-client.version>
|
||||
<mockwebserver.version>0.1.2</mockwebserver.version>
|
||||
<okhttp.version>3.12.0</okhttp.version>
|
||||
<okhttp.version>3.14.4</okhttp.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -98,6 +99,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-loadbalancer</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Own dependencies - Starters -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -117,6 +124,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-loadbalancer</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-all</artifactId>
|
||||
@@ -148,6 +161,18 @@
|
||||
<version>${arquillian-cube.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.specto</groupId>
|
||||
<artifactId>hoverfly-java-junit5</artifactId>
|
||||
<version>${hoverfly.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.specto</groupId>
|
||||
<artifactId>hoverfly-java</artifactId>
|
||||
<version>${hoverfly.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import static org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance.NAMESPACE_METADATA_KEY;
|
||||
|
||||
/**
|
||||
* Kubeneretes implementation of {@link DiscoveryClient}.
|
||||
@@ -153,6 +154,10 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
|
||||
endpointMetadata.putAll(portMetadata);
|
||||
}
|
||||
|
||||
if (this.properties.isAllNamespaces()) {
|
||||
endpointMetadata.put(NAMESPACE_METADATA_KEY, namespace);
|
||||
}
|
||||
|
||||
List<EndpointAddress> addresses = s.getAddresses();
|
||||
for (EndpointAddress endpointAddress : addresses) {
|
||||
String instanceId = null;
|
||||
@@ -162,7 +167,8 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
EndpointPort endpointPort = findEndpointPort(s);
|
||||
instances.add(new KubernetesServiceInstance(instanceId, serviceId,
|
||||
endpointAddress, endpointPort, endpointMetadata,
|
||||
endpointAddress.getIp(), endpointPort.getPort(),
|
||||
endpointMetadata,
|
||||
this.isServicePortSecureResolver
|
||||
.resolve(new DefaultIsServicePortSecureResolver.Input(
|
||||
endpointPort.getPort(),
|
||||
|
||||
@@ -19,9 +19,6 @@ package org.springframework.cloud.kubernetes.discovery;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.EndpointAddress;
|
||||
import io.fabric8.kubernetes.api.model.EndpointPort;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
|
||||
/**
|
||||
@@ -31,49 +28,50 @@ import org.springframework.cloud.client.ServiceInstance;
|
||||
*/
|
||||
public class KubernetesServiceInstance implements ServiceInstance {
|
||||
|
||||
/**
|
||||
* Key of the namespace metadata.
|
||||
*/
|
||||
public static final String NAMESPACE_METADATA_KEY = "k8s_namespace";
|
||||
|
||||
private static final String HTTP_PREFIX = "http";
|
||||
|
||||
private static final String HTTPS_PREFIX = "https";
|
||||
|
||||
private static final String DSL = "//";
|
||||
|
||||
private static final String COLN = ":";
|
||||
private static final String COLON = ":";
|
||||
|
||||
private final String instanceId;
|
||||
|
||||
private final String serviceId;
|
||||
|
||||
private final EndpointAddress endpointAddress;
|
||||
private final String host;
|
||||
|
||||
private final EndpointPort endpointPort;
|
||||
private final int port;
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final Boolean secure;
|
||||
|
||||
private final Map<String, String> metadata;
|
||||
|
||||
/**
|
||||
* @param instanceId the id of the instance.
|
||||
* @param serviceId the id of the service.
|
||||
* @param endpointAddress the address where the service instance can be found.
|
||||
* @param endpointPort the port on which the service is running.
|
||||
* @param host the address where the service instance can be found.
|
||||
* @param port the port on which the service is running.
|
||||
* @param metadata a map containing metadata.
|
||||
* @param secure indicates whether or not the connection needs to be secure.
|
||||
* @deprecated - use other constructor
|
||||
*/
|
||||
@Deprecated
|
||||
public KubernetesServiceInstance(String serviceId, EndpointAddress endpointAddress,
|
||||
EndpointPort endpointPort, Map<String, String> metadata, Boolean secure) {
|
||||
this(null, serviceId, endpointAddress, endpointPort, metadata, secure);
|
||||
}
|
||||
|
||||
public KubernetesServiceInstance(String instanceId, String serviceId,
|
||||
EndpointAddress endpointAddress, EndpointPort endpointPort,
|
||||
Map<String, String> metadata, Boolean secure) {
|
||||
public KubernetesServiceInstance(String instanceId, String serviceId, String host,
|
||||
int port, Map<String, String> metadata, Boolean secure) {
|
||||
this.instanceId = instanceId;
|
||||
this.serviceId = serviceId;
|
||||
this.endpointAddress = endpointAddress;
|
||||
this.endpointPort = endpointPort;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.metadata = metadata;
|
||||
this.secure = secure;
|
||||
this.uri = createUri(secure ? HTTPS_PREFIX : HTTP_PREFIX, host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,12 +86,12 @@ public class KubernetesServiceInstance implements ServiceInstance {
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return this.endpointAddress.getIp();
|
||||
return this.host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return this.endpointPort.getPort();
|
||||
return this.port;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,10 +101,7 @@ public class KubernetesServiceInstance implements ServiceInstance {
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getScheme()).append(COLN).append(DSL).append(getHost()).append(COLN)
|
||||
.append(getPort());
|
||||
return URI.create(sb.toString());
|
||||
return uri;
|
||||
}
|
||||
|
||||
public Map<String, String> getMetadata() {
|
||||
@@ -118,4 +113,15 @@ public class KubernetesServiceInstance implements ServiceInstance {
|
||||
return isSecure() ? HTTPS_PREFIX : HTTP_PREFIX;
|
||||
}
|
||||
|
||||
private URI createUri(String scheme, String host, int port) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(scheme).append(COLON).append(DSL).append(host).append(COLON)
|
||||
.append(port);
|
||||
return URI.create(sb.toString());
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return this.metadata != null ? this.metadata.get(NAMESPACE_METADATA_KEY) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -377,6 +377,14 @@ public class KubernetesDiscoveryClientTest {
|
||||
.hasSize(1);
|
||||
assertThat(instances).filteredOn(s -> s.getHost().equals("ip2") && !s.isSecure())
|
||||
.hasSize(1);
|
||||
assertThat(instances)
|
||||
.filteredOn(s -> s.getServiceId().contains("endpoint")
|
||||
&& ((KubernetesServiceInstance) s).getNamespace().equals("test"))
|
||||
.hasSize(1);
|
||||
assertThat(instances)
|
||||
.filteredOn(s -> s.getServiceId().contains("endpoint")
|
||||
&& ((KubernetesServiceInstance) s).getNamespace().equals("test2"))
|
||||
.hasSize(1);
|
||||
assertThat(instances).filteredOn(s -> s.getInstanceId().equals("60")).hasSize(1);
|
||||
assertThat(instances).filteredOn(s -> s.getInstanceId().equals("70")).hasSize(1);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ public class KubernetesServiceInstanceTests {
|
||||
EndpointPort port = new EndpointPort();
|
||||
port.setPort(8080);
|
||||
KubernetesServiceInstance instance = new KubernetesServiceInstance("123",
|
||||
"myservice", address, port, Collections.emptyMap(), secure);
|
||||
"myservice", address.getIp(), port.getPort(), Collections.emptyMap(),
|
||||
secure);
|
||||
|
||||
assertThat(instance.getInstanceId()).isEqualTo("123");
|
||||
assertThat(instance.getServiceId()).isEqualTo("myservice");
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>kubernetes-circuitbreaker-ribbon-example</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>greeting-service</artifactId>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>kubernetes-circuitbreaker-ribbon-example</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>name-service</artifactId>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-examples</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>kubernetes-leader-election-example</artifactId>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
## Kubernetes Spring Cloud LoadBalancer Example
|
||||
|
||||
This example demonstrates how to use [Spring Cloud LoadBalancer](https://github.com/spring-cloud-incubator/spring-cloud-loadbalancer). In our example the REST `greeting service` calls the `name service`.
|
||||
As the Spring Cloud LoadBalancer for Kubernetes is configured within this example, it will fetch from the Kubernetes API Server the list of the endpoints available for the name service and load-balance the request between the IP addresses available.
|
||||
|
||||
### Running the example
|
||||
|
||||
This project example runs on ALL the Kubernetes or OpenShift environments, but for development purposes you can use [Minikube - Kubernetes](https://kubernetes.io/docs/getting-started-guides/minikube/) tool
|
||||
to install the platform locally within a virtual machine managed by VirtualBox, Xhyve or KVM, with no fuss.
|
||||
|
||||
IMPORTANT: In order for this setup to work, you need to grant permissions to retrieve "pods", "services" and "enpoints" to the serviceaccont that will be used with the greeting-service.
|
||||
|
||||
### Build/Deploy using Minikube
|
||||
|
||||
First, create a new virtual machine provisioned with Kubernetes on your laptop using the command `minikube start`.
|
||||
|
||||
Next, you can compile your project and generate the Kubernetes resources (yaml files containing the definition of the pod, deployment, build, service and route to be created)
|
||||
like also to deploy the application on Kubernetes in one maven line by running:
|
||||
|
||||
```
|
||||
mvn clean install fabric8:deploy -Dfabric8.generator.from=fabric8/java-jboss-openjdk8-jdk -Pkubernetes
|
||||
```
|
||||
|
||||
### Call the Greeting service
|
||||
|
||||
When maven has finished to compile the code but also to call the platform in order to deploy the yaml files generated and tell to the platform to start the process
|
||||
to build/deploy the docker image and create the containers where the Spring Boot application will run 'greeting-service" and "name-service", you will be able to
|
||||
check if the pods have been created using this command :
|
||||
|
||||
```
|
||||
kc get pods
|
||||
```
|
||||
|
||||
If the status of the Spring Boot pod application is `running` and ready state `1`, then you can
|
||||
get the external address IP/Hostname to be used to call the service from your laptop
|
||||
|
||||
```
|
||||
minikube service --url greeting-service
|
||||
```
|
||||
|
||||
and then call the service using the curl client
|
||||
|
||||
```
|
||||
curl https://IP_OR_HOSTNAME/greeting
|
||||
```
|
||||
|
||||
to get a response as such
|
||||
|
||||
```
|
||||
Hello from name-service-1-0dzb4!d
|
||||
```
|
||||
|
||||
### Verify the load balancing
|
||||
|
||||
First, scale the number of pods of the `name service` to 2
|
||||
|
||||
```
|
||||
kc scale --replicas=2 deployment name-service
|
||||
```
|
||||
|
||||
Wait a few minutes before to issue the curl request to call the Greeting Service to let the platform to create the new pod.
|
||||
|
||||
```
|
||||
kc get pods --selector=project=name-service
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
name-service-1652024859-fsnfw 1/1 Running 0 33s
|
||||
name-service-1652024859-wrzjs 1/1 Running 0 6m
|
||||
```
|
||||
|
||||
If you issue the curl request to access the greeting service, you should see that the message response
|
||||
contains a different id end of the message which corresponds to the name of the pod.
|
||||
|
||||
```
|
||||
Hello from name-service-1-0ss0r!
|
||||
```
|
||||
|
||||
As Spring Cloud LoadBalancer will question the Kubernetes API to get, base on the `name-service` name, the list of IP Addresses assigned to the service as endpoints,
|
||||
you should see that you will get a response from one of the 2 pods running
|
||||
|
||||
```
|
||||
kc get endpoints/name-service
|
||||
NAME ENDPOINTS AGE
|
||||
name-service 172.17.0.5:8080,172.17.0.6:8080 40m
|
||||
```
|
||||
|
||||
Here is an example about what you will get
|
||||
|
||||
```
|
||||
curl https://IP_OR_HOSTNAME/greeting
|
||||
Hello from name-service-1652024859-hf3xv!
|
||||
curl https://IP_OR_HOSTNAME/greeting
|
||||
Hello from name-service-1652024859-426kv!
|
||||
...
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
<?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>kubernetes-loadbalancer-example</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>greeting-service</artifactId>
|
||||
<name>Spring Cloud LoadBalancer :: Greeting Service</name>
|
||||
<description>Spring Cloud LoadBalancer :: Greeting Service</description>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!--skip deploy -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<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>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>fabric8-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>fmp</id>
|
||||
<goals>
|
||||
<goal>resource</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<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>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-loadbalancer</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: greeting-service
|
||||
spec:
|
||||
port:
|
||||
targetPort: 8080
|
||||
to:
|
||||
kind: Service
|
||||
name: greeting-service
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or 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
|
||||
*
|
||||
* https://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.examples;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Greeting service controller.
|
||||
*
|
||||
* @author Gytis Trikleris
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@RestController
|
||||
public class GreetingController {
|
||||
|
||||
private final NameService nameService;
|
||||
|
||||
public GreetingController(NameService nameService) {
|
||||
this.nameService = nameService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint to get a greeting. This endpoint uses a name server to get a name for the
|
||||
* greeting.
|
||||
*
|
||||
* Request to the name service is guarded with a circuit breaker. Therefore if a name
|
||||
* service is not available or is too slow to response fallback name is used.
|
||||
*
|
||||
* Delay parameter can me used to make name service response slower.
|
||||
* @param delay Milliseconds for how long the response from name service should be
|
||||
* delayed.
|
||||
* @return Greeting string.
|
||||
*/
|
||||
@GetMapping("/greeting")
|
||||
public Mono<String> getGreeting(
|
||||
@RequestParam(value = "delay", defaultValue = "0") int delay) {
|
||||
return nameService.getName(delay)
|
||||
.map(name -> String.format("Hello from %s!", name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or 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
|
||||
*
|
||||
* https://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.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Entry point to the application.
|
||||
*
|
||||
* @author Gytis Trikleris
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class GreetingServiceApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GreetingServiceApplication.class, args);
|
||||
}
|
||||
|
||||
@LoadBalanced
|
||||
@Bean
|
||||
WebClient.Builder loadBalancedWebClientBuilder() {
|
||||
return WebClient.builder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or 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
|
||||
*
|
||||
* https://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.examples;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Service invoking name-service via REST and guarded by Hystrix.
|
||||
*
|
||||
* @author Gytis Trikleris
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Service
|
||||
public class NameService {
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
public NameService(WebClient.Builder webClientBuilder) {
|
||||
webClient = webClientBuilder.build();
|
||||
}
|
||||
|
||||
public Mono<String> getName(int delay) {
|
||||
return webClient.get()
|
||||
.uri(String.format("http://name-service/name?delay=%d", delay)).retrieve()
|
||||
.bodyToMono(String.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
spring:
|
||||
application:
|
||||
name: greeting-service
|
||||
cloud:
|
||||
loadbalancer:
|
||||
ribbon:
|
||||
enabled: false
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
@@ -0,0 +1,67 @@
|
||||
<?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>kubernetes-loadbalancer-example</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>name-service</artifactId>
|
||||
|
||||
<name>Spring Cloud LoadBalancer :: Name Service</name>
|
||||
<description>Spring Cloud LoadBalancer :: Name Service</description>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!--skip deploy -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<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>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>fabric8-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>fmp</id>
|
||||
<goals>
|
||||
<goal>resource</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: name-service
|
||||
spec:
|
||||
port:
|
||||
targetPort: 8080
|
||||
to:
|
||||
kind: Service
|
||||
name: name-service
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or 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
|
||||
*
|
||||
* https://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.examples;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Name service controller.
|
||||
*
|
||||
* @author Gytis Trikleris
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@RestController
|
||||
public class NameController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NameController.class);
|
||||
|
||||
private final String hostName = System.getenv("HOSTNAME");
|
||||
|
||||
@GetMapping("/")
|
||||
public String ribbonPing() {
|
||||
LOG.info("Ribbon ping");
|
||||
return hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint to get a name with a capability to delay a response for some number of
|
||||
* milliseconds.
|
||||
* @param delayValue Milliseconds for how long the response should be delayed.
|
||||
* @return Host name.
|
||||
*/
|
||||
@GetMapping("/name")
|
||||
public Mono<String> getName(
|
||||
@RequestParam(value = "delay", defaultValue = "0") int delayValue) {
|
||||
LOG.info(String.format("Returning a name '%s' with a delay '%d'", hostName,
|
||||
delayValue));
|
||||
delay(delayValue);
|
||||
return Mono.just(hostName);
|
||||
}
|
||||
|
||||
private void delay(int delayValue) {
|
||||
try {
|
||||
Thread.sleep(delayValue);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or 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
|
||||
*
|
||||
* https://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.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Entry point to the application.
|
||||
*
|
||||
* @author Gytis Trikleris
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class NameServiceApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(NameServiceApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
spring:
|
||||
application:
|
||||
name: name-service
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
@@ -0,0 +1,115 @@
|
||||
<?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>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>kubernetes-loadbalancer-example</artifactId>
|
||||
<name>Spring Cloud LoadBalancer Example</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>kubernetes</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<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>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<enricher>
|
||||
<config>
|
||||
<fmp-service>
|
||||
<type>NodePort</type>
|
||||
</fmp-service>
|
||||
</config>
|
||||
</enricher>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<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>
|
||||
<goal>helm</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>integration</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<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>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${maven-failsafe-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>run-integration-tests</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<skipTests>false</skipTests>
|
||||
<skipITs>false</skipITs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<modules>
|
||||
<module>name-service</module>
|
||||
<module>greeting-service</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-examples</artifactId>
|
||||
@@ -37,6 +37,7 @@
|
||||
<module>kubernetes-reload-example</module>
|
||||
<module>kubernetes-hello-world-example</module>
|
||||
<module>kubernetes-leader-election-example</module>
|
||||
<module>kubernetes-loadbalancer-example</module>
|
||||
<!-- <module>kubernetes-circuitbreaker-ribbon-example</module>
|
||||
<module>kubernetes-zipkin-example</module> -->
|
||||
</modules>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>discovery-parent</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>discovery-client</artifactId>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>discovery-parent</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>discovery-service-a</artifactId>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>discovery-parent</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>discovery-service-b</artifactId>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Cloud Kubernetes :: Integration Tests :: Discovery Parent</name>
|
||||
|
||||
@@ -7,13 +7,21 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>discovery-parent</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>tests</artifactId>
|
||||
<name>Spring Cloud Kubernetes :: Integration Tests :: Discovery Tests</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Cloud Kubernetes :: Integration Tests :: Istio</name>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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-integration-tests</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<name>Spring Cloud Kubernetes :: Integration Tests :: Load Balancer</name>
|
||||
<artifactId>load-balancer</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-kubernetes-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-server-mock</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.specto</groupId>
|
||||
<artifactId>hoverfly-java-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.specto</groupId>
|
||||
<artifactId>hoverfly-java</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class SimpleLoadBalancerApp {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SimpleLoadBalancerApp.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
RestTemplate restTemplate() {
|
||||
return new RestTemplateBuilder().build();
|
||||
}
|
||||
|
||||
@GetMapping("/greeting")
|
||||
public String greeting() {
|
||||
return "greeting";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.ServicePortBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ServiceSpecBuilder;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@TestPropertySource(properties = "spring.cloud.kubernetes.discovery.all-namespaces=true")
|
||||
@EnableKubernetesMockClient(crud = true)
|
||||
class LoadBalancerAllNamespacesTests {
|
||||
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
@LocalServerPort
|
||||
int randomServerPort;
|
||||
|
||||
static KubernetesClient client;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() {
|
||||
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
|
||||
client.getConfiguration().getMasterUrl());
|
||||
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
|
||||
"false");
|
||||
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
|
||||
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadBalancerDifferentNamespace() {
|
||||
createTestData("service-b", "b");
|
||||
String response = restTemplate.getForObject("http://service-b/greeting",
|
||||
String.class);
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertEquals("greeting", response);
|
||||
}
|
||||
|
||||
private void createTestData(String name, String namespace) {
|
||||
client.services().inNamespace(namespace).createNew().withNewMetadata()
|
||||
.withName(name).withNamespace(namespace).endMetadata()
|
||||
.withSpec(new ServiceSpecBuilder().withPorts(new ServicePortBuilder()
|
||||
.withProtocol("TCP").withPort(randomServerPort).build()).build())
|
||||
.done();
|
||||
client.endpoints().inNamespace(namespace).createNew().withNewMetadata()
|
||||
.withName("service-a").withNamespace(namespace).endMetadata()
|
||||
.addNewSubset().addNewAddress().withIp("localhost").endAddress()
|
||||
.addNewPort().withName("http").withPort(randomServerPort).endPort()
|
||||
.endSubset().done();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.ServicePortBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ServiceSpecBuilder;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@EnableKubernetesMockClient(crud = true)
|
||||
class LoadBalancerTests {
|
||||
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
@LocalServerPort
|
||||
int randomServerPort;
|
||||
|
||||
static KubernetesClient client;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() {
|
||||
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
|
||||
client.getConfiguration().getMasterUrl());
|
||||
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
|
||||
"false");
|
||||
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
|
||||
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadBalancerSameNamespace() {
|
||||
createTestData("service-a", "test");
|
||||
String response = restTemplate.getForObject("http://service-a/greeting",
|
||||
String.class);
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertEquals("greeting", response);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadBalancerDifferentNamespace() {
|
||||
createTestData("service-b", "b");
|
||||
Assertions.assertThrows(IllegalStateException.class, () -> restTemplate
|
||||
.getForObject("http://service-b/greeting", String.class));
|
||||
}
|
||||
|
||||
private void createTestData(String name, String namespace) {
|
||||
client.services().inNamespace(namespace).createNew().withNewMetadata()
|
||||
.withName(name).endMetadata()
|
||||
.withSpec(new ServiceSpecBuilder().withPorts(new ServicePortBuilder()
|
||||
.withProtocol("TCP").withPort(randomServerPort).build()).build())
|
||||
.done();
|
||||
client.endpoints().inNamespace(namespace).createNew().withNewMetadata()
|
||||
.withName("service-a").endMetadata().addNewSubset().addNewAddress()
|
||||
.withIp("localhost").endAddress().addNewPort().withName("http")
|
||||
.withPort(randomServerPort).endPort().endSubset().done();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServiceBuilder;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.specto.hoverfly.junit.core.Hoverfly;
|
||||
import io.specto.hoverfly.junit5.HoverflyExtension;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static io.specto.hoverfly.junit.core.SimulationSource.dsl;
|
||||
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.service;
|
||||
import static io.specto.hoverfly.junit.dsl.HttpBodyConverter.json;
|
||||
import static io.specto.hoverfly.junit.dsl.ResponseCreators.success;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@TestPropertySource(properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE",
|
||||
"spring.cloud.kubernetes.loadbalancer.enabled=true" })
|
||||
@ExtendWith(HoverflyExtension.class)
|
||||
class LoadBalancerWithServiceTests {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory
|
||||
.getLogger(LoadBalancerWithServiceTests.class);
|
||||
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
KubernetesClient client;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() {
|
||||
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY,
|
||||
"false");
|
||||
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
|
||||
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadBalancerInServiceMode(Hoverfly hoverfly) {
|
||||
LOGGER.info("Master URL: {}", client.getConfiguration().getMasterUrl());
|
||||
hoverfly.simulate(
|
||||
dsl(service("http://service-a.test.svc.cluster.local:8080")
|
||||
.get("/greeting").willReturn(success().body("greeting"))),
|
||||
dsl(service(client.getConfiguration().getMasterUrl().replace("/", "")
|
||||
.replace("https:", ""))
|
||||
.get("/api/v1/namespaces/test/services/service-a")
|
||||
.willReturn(success().body(
|
||||
json(buildService("service-a", 8080, "test"))))));
|
||||
String response = restTemplate.getForObject("http://service-a/greeting",
|
||||
String.class);
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertEquals("greeting", response);
|
||||
}
|
||||
|
||||
private Service buildService(String name, int port, String namespace) {
|
||||
return new ServiceBuilder().withNewMetadata().withName(name)
|
||||
.withNamespace(namespace).withLabels(new HashMap<>())
|
||||
.withAnnotations(new HashMap<>()).endMetadata().withNewSpec().addNewPort()
|
||||
.withPort(port).endPort().endSpec().build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
|
||||
@@ -23,6 +23,7 @@
|
||||
<arquillian-cube.version>1.18.2</arquillian-cube.version>
|
||||
<arquillian.version>1.4.0.Final</arquillian.version>
|
||||
<rest-assured.version>3.2.0</rest-assured.version>
|
||||
<okhttptests.version>3.12.12</okhttptests.version>
|
||||
|
||||
<!--
|
||||
The port on localhost where the application will listen to from outside the cluster
|
||||
@@ -125,6 +126,16 @@
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>${rest-assured.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>${okhttptests.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
<version>${okhttptests.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -133,7 +144,6 @@
|
||||
<module>simple-configmap</module>
|
||||
<!-- <module>istio</module>-->
|
||||
<module>discovery</module>
|
||||
</modules>
|
||||
|
||||
|
||||
<module>load-balancer</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Cloud Kubernetes :: Integration Tests :: Simple Configmap</name>
|
||||
@@ -31,6 +31,14 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
@@ -58,6 +66,7 @@
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-integration-tests</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Cloud Kubernetes :: Integration Tests :: Simple Core</name>
|
||||
@@ -31,6 +31,14 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-leader</artifactId>
|
||||
|
||||
50
spring-cloud-kubernetes-loadbalancer/pom.xml
Normal file
50
spring-cloud-kubernetes-loadbalancer/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-kubernetes-loadbalancer</artifactId>
|
||||
<name>Spring Cloud Kubernetes :: Load Balancer</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-server-mock</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Kubernetes load balancer auto-configuration.
|
||||
*
|
||||
* @author Piotr Minkowski
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(KubernetesLoadBalancerProperties.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.loadbalancer.enabled",
|
||||
matchIfMissing = true)
|
||||
@LoadBalancerClients(
|
||||
defaultConfiguration = KubernetesLoadBalancerClientConfiguration.class)
|
||||
public class KubernetesLoadBalancerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
KubernetesServiceInstanceMapper mapper(KubernetesLoadBalancerProperties properties,
|
||||
KubernetesDiscoveryProperties discoveryProperties) {
|
||||
return new KubernetesServiceInstanceMapper(properties, discoveryProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Kubernetes load balancer client configuration.
|
||||
*
|
||||
* @author Piotr Minkowski
|
||||
*/
|
||||
public class KubernetesLoadBalancerClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.kubernetes.loadbalancer.mode",
|
||||
havingValue = "SERVICE")
|
||||
KubernetesServicesListSupplier kubernetesServicesListSupplier(Environment environment,
|
||||
KubernetesClient kubernetesClient, KubernetesServiceInstanceMapper mapper,
|
||||
KubernetesDiscoveryProperties discoveryProperties) {
|
||||
return new KubernetesServicesListSupplier(environment, kubernetesClient, mapper,
|
||||
discoveryProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
/**
|
||||
* Kubernetes load balancer mode enum.
|
||||
*
|
||||
* @author Piotr Minkowski
|
||||
*/
|
||||
public enum KubernetesLoadBalancerMode {
|
||||
|
||||
/**
|
||||
* using pod ip and port.
|
||||
*/
|
||||
POD,
|
||||
/**
|
||||
* using kubernetes service name and port.
|
||||
*/
|
||||
SERVICE
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Kubernetes load balancer client properties.
|
||||
*
|
||||
* @author Piotr Minkowski
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.cloud.kubernetes.loadbalancer")
|
||||
public class KubernetesLoadBalancerProperties {
|
||||
|
||||
/**
|
||||
* Load balancer enabled,default true.
|
||||
*/
|
||||
private Boolean enabled = true;
|
||||
|
||||
/**
|
||||
* {@link KubernetesLoadBalancerMode} setting load balancer server list with ip of pod
|
||||
* or service name. default value is POD.
|
||||
*/
|
||||
private KubernetesLoadBalancerMode mode = KubernetesLoadBalancerMode.POD;
|
||||
|
||||
/**
|
||||
* cluster domain.
|
||||
*/
|
||||
private String clusterDomain = "cluster.local";
|
||||
|
||||
/**
|
||||
* service port name.
|
||||
*/
|
||||
private String portName = "http";
|
||||
|
||||
/**
|
||||
* Get cluster domain.
|
||||
* @return the cluster domain
|
||||
*/
|
||||
public String getClusterDomain() {
|
||||
return clusterDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cluster domain.
|
||||
* @param clusterDomain the cluster domain
|
||||
*/
|
||||
public void setClusterDomain(String clusterDomain) {
|
||||
this.clusterDomain = clusterDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets mode.
|
||||
* @return the mode
|
||||
*/
|
||||
public KubernetesLoadBalancerMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets mode.
|
||||
* @param mode the mode
|
||||
*/
|
||||
public void setMode(KubernetesLoadBalancerMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets enabled.
|
||||
* @return the enabled
|
||||
*/
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets enabled.
|
||||
* @param enabled the enabled
|
||||
*/
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets portName.
|
||||
* @return portName port name
|
||||
*/
|
||||
public String getPortName() {
|
||||
return portName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets portName.
|
||||
* @param portName port name
|
||||
*/
|
||||
public void setPortName(String portName) {
|
||||
this.portName = portName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.ObjectMeta;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServicePort;
|
||||
import io.fabric8.kubernetes.client.utils.Utils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
|
||||
|
||||
/**
|
||||
* Class for mapping Kubernetes Service object into {@link KubernetesServiceInstance}.
|
||||
*
|
||||
* @author Piotr Minkowski
|
||||
*/
|
||||
public class KubernetesServiceInstanceMapper {
|
||||
|
||||
private final KubernetesLoadBalancerProperties properties;
|
||||
|
||||
private final KubernetesDiscoveryProperties discoveryProperties;
|
||||
|
||||
KubernetesServiceInstanceMapper(KubernetesLoadBalancerProperties properties,
|
||||
KubernetesDiscoveryProperties discoveryProperties) {
|
||||
this.properties = properties;
|
||||
this.discoveryProperties = discoveryProperties;
|
||||
}
|
||||
|
||||
public KubernetesServiceInstance map(Service service) {
|
||||
final ObjectMeta meta = service.getMetadata();
|
||||
final List<ServicePort> ports = service.getSpec().getPorts();
|
||||
ServicePort port = null;
|
||||
if (ports.size() == 1) {
|
||||
port = ports.get(0);
|
||||
}
|
||||
else if (ports.size() > 1
|
||||
&& Utils.isNotNullOrEmpty(this.properties.getPortName())) {
|
||||
Optional<ServicePort> optPort = ports.stream()
|
||||
.filter(it -> properties.getPortName().endsWith(it.getName()))
|
||||
.findAny();
|
||||
if (optPort.isPresent()) {
|
||||
port = optPort.get();
|
||||
}
|
||||
}
|
||||
if (port == null) {
|
||||
return null;
|
||||
}
|
||||
final String host = createHost(service);
|
||||
final boolean secure = isSecure(service, port);
|
||||
return new KubernetesServiceInstance(meta.getUid(), meta.getName(), host,
|
||||
port.getPort(), getServiceMetadata(service), secure);
|
||||
}
|
||||
|
||||
private Map<String, String> getServiceMetadata(Service service) {
|
||||
final Map<String, String> serviceMetadata = new HashMap<>();
|
||||
KubernetesDiscoveryProperties.Metadata metadataProps = this.discoveryProperties
|
||||
.getMetadata();
|
||||
if (metadataProps.isAddLabels()) {
|
||||
Map<String, String> labelMetadata = getMapWithPrefixedKeys(
|
||||
service.getMetadata().getLabels(), metadataProps.getLabelsPrefix());
|
||||
serviceMetadata.putAll(labelMetadata);
|
||||
}
|
||||
if (metadataProps.isAddAnnotations()) {
|
||||
Map<String, String> annotationMetadata = getMapWithPrefixedKeys(
|
||||
service.getMetadata().getAnnotations(),
|
||||
metadataProps.getAnnotationsPrefix());
|
||||
serviceMetadata.putAll(annotationMetadata);
|
||||
}
|
||||
|
||||
return serviceMetadata;
|
||||
}
|
||||
|
||||
private Map<String, String> getMapWithPrefixedKeys(Map<String, String> map,
|
||||
String prefix) {
|
||||
if (map == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
if (!org.springframework.util.StringUtils.hasText(prefix)) {
|
||||
return map;
|
||||
}
|
||||
final Map<String, String> result = new HashMap<>();
|
||||
map.forEach((k, v) -> result.put(prefix + k, v));
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isSecure(Service service, ServicePort port) {
|
||||
if (service.getMetadata().getLabels() != null) {
|
||||
final String securedLabelValue = service.getMetadata().getLabels()
|
||||
.getOrDefault("secured", "false");
|
||||
if (securedLabelValue.equals("true")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (service.getMetadata().getAnnotations() != null) {
|
||||
final String securedAnnotationValue = service.getMetadata().getAnnotations()
|
||||
.getOrDefault("secured", "false");
|
||||
if (securedAnnotationValue.equals("true")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return (port.getName() != null && port.getName().endsWith("https"))
|
||||
|| port.getPort().toString().endsWith("443");
|
||||
}
|
||||
|
||||
private String createHost(Service service) {
|
||||
return String.format("%s.%s.svc.%s", service.getMetadata().getName(),
|
||||
StringUtils.isNotBlank(service.getMetadata().getNamespace())
|
||||
? service.getMetadata().getNamespace() : "default",
|
||||
properties.getClusterDomain());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ServiceInstanceListSupplier} for load balancer in SERVICE
|
||||
* mode.
|
||||
*
|
||||
* @author Piotr Minkowski
|
||||
*/
|
||||
public class KubernetesServicesListSupplier implements ServiceInstanceListSupplier {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final KubernetesClient kubernetesClient;
|
||||
|
||||
private final KubernetesDiscoveryProperties discoveryProperties;
|
||||
|
||||
private final KubernetesServiceInstanceMapper mapper;
|
||||
|
||||
KubernetesServicesListSupplier(Environment environment,
|
||||
KubernetesClient kubernetesClient, KubernetesServiceInstanceMapper mapper,
|
||||
KubernetesDiscoveryProperties discoveryProperties) {
|
||||
this.environment = environment;
|
||||
this.kubernetesClient = kubernetesClient;
|
||||
this.discoveryProperties = discoveryProperties;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<List<ServiceInstance>> get() {
|
||||
List<ServiceInstance> result = new ArrayList<>();
|
||||
if (discoveryProperties.isAllNamespaces()) {
|
||||
List<Service> services = this.kubernetesClient.services().inAnyNamespace()
|
||||
.withField("metadata.name", this.getServiceId()).list().getItems();
|
||||
services.forEach(service -> result.add(mapper.map(service)));
|
||||
}
|
||||
else {
|
||||
Service service = StringUtils.isNotBlank(this.kubernetesClient.getNamespace())
|
||||
? this.kubernetesClient.services()
|
||||
.inNamespace(this.kubernetesClient.getNamespace())
|
||||
.withName(this.getServiceId()).get()
|
||||
: this.kubernetesClient.services().withName(this.getServiceId())
|
||||
.get();
|
||||
if (service != null) {
|
||||
result.add(mapper.map(service));
|
||||
}
|
||||
}
|
||||
return Flux.defer(() -> Flux.just(result));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.kubernetes.loadbalancer.KubernetesLoadBalancerAutoConfiguration
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServiceBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ServicePort;
|
||||
import io.fabric8.kubernetes.api.model.ServicePortBuilder;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
|
||||
|
||||
class KubernetesServiceInstanceMapperTests {
|
||||
|
||||
@Test
|
||||
public void testMapperSimple() {
|
||||
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
|
||||
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
|
||||
Service service = buildService("test", "abc", 8080, null, new HashMap<>());
|
||||
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
|
||||
properties, discoveryProperties).map(service);
|
||||
Assertions.assertNotNull(instance);
|
||||
Assertions.assertEquals("test", instance.getServiceId());
|
||||
Assertions.assertEquals("abc", instance.getInstanceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMapperMultiplePorts() {
|
||||
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
|
||||
properties.setPortName("http");
|
||||
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
|
||||
List<ServicePort> ports = new ArrayList<>();
|
||||
ports.add(new ServicePortBuilder().withPort(8080).withName("web").build());
|
||||
ports.add(new ServicePortBuilder().withPort(9000).withName("http").build());
|
||||
Service service = buildService("test", "abc", ports, new HashMap<>());
|
||||
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
|
||||
properties, discoveryProperties).map(service);
|
||||
Assertions.assertNotNull(instance);
|
||||
Assertions.assertEquals("test", instance.getServiceId());
|
||||
Assertions.assertEquals("abc", instance.getInstanceId());
|
||||
Assertions.assertEquals(9000, instance.getPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMapperSecure() {
|
||||
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
|
||||
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
|
||||
Service service = buildService("test", "abc", 443, null, new HashMap<>());
|
||||
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
|
||||
properties, discoveryProperties).map(service);
|
||||
Assertions.assertNotNull(instance);
|
||||
Assertions.assertEquals("test", instance.getServiceId());
|
||||
Assertions.assertEquals("abc", instance.getInstanceId());
|
||||
Assertions.assertTrue(instance.isSecure());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMapperSecureNullLabelsAndAnnotations() {
|
||||
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
|
||||
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
|
||||
List<ServicePort> ports = new ArrayList<>();
|
||||
ports.add(new ServicePortBuilder().withPort(443).build());
|
||||
Service service = buildService("test", "abc", ports, null, null);
|
||||
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
|
||||
properties, discoveryProperties).map(service);
|
||||
Assertions.assertNotNull(instance);
|
||||
Assertions.assertEquals("test", instance.getServiceId());
|
||||
Assertions.assertEquals("abc", instance.getInstanceId());
|
||||
Assertions.assertTrue(instance.isSecure());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMapperSecureWithLabels() {
|
||||
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
|
||||
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
|
||||
HashMap<String, String> labels = new HashMap<>();
|
||||
labels.put("secured", "true");
|
||||
labels.put("label1", "123");
|
||||
Service service = buildService("test", "abc", 8080, null, labels);
|
||||
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
|
||||
properties, discoveryProperties).map(service);
|
||||
Assertions.assertNotNull(instance);
|
||||
Assertions.assertEquals("test", instance.getServiceId());
|
||||
Assertions.assertEquals("abc", instance.getInstanceId());
|
||||
Assertions.assertTrue(instance.isSecure());
|
||||
Assertions.assertEquals(2, instance.getMetadata().keySet().size());
|
||||
}
|
||||
|
||||
private Service buildService(String name, String uid, int port, String portName,
|
||||
Map<String, String> labels) {
|
||||
ServicePort servicePort = new ServicePortBuilder().withPort(port)
|
||||
.withName(portName).build();
|
||||
return buildService(name, uid, Collections.singletonList(servicePort), labels);
|
||||
}
|
||||
|
||||
private Service buildService(String name, String uid, List<ServicePort> ports,
|
||||
Map<String, String> labels, Map<String, String> annotations) {
|
||||
return new ServiceBuilder().withNewMetadata().withName(name).withNewUid(uid)
|
||||
.addToLabels(labels).withAnnotations(annotations).endMetadata()
|
||||
.withNewSpec().addAllToPorts(ports).endSpec().build();
|
||||
}
|
||||
|
||||
private Service buildService(String name, String uid, List<ServicePort> ports,
|
||||
Map<String, String> labels) {
|
||||
return buildService(name, uid, ports, labels, new HashMap<>(0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or 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
|
||||
*
|
||||
* https://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.loadbalancer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.DoneableService;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServiceBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ServiceList;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.Watch;
|
||||
import io.fabric8.kubernetes.client.Watcher;
|
||||
import io.fabric8.kubernetes.client.dsl.FilterWatchListMultiDeletable;
|
||||
import io.fabric8.kubernetes.client.dsl.MixedOperation;
|
||||
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
|
||||
import io.fabric8.kubernetes.client.dsl.ServiceResource;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class KubernetesServiceListSupplierTests {
|
||||
|
||||
@Mock
|
||||
Environment environment;
|
||||
|
||||
@Mock
|
||||
KubernetesServiceInstanceMapper mapper;
|
||||
|
||||
@Mock
|
||||
KubernetesClient client;
|
||||
|
||||
@Mock
|
||||
MixedOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> serviceOperation;
|
||||
|
||||
@Mock
|
||||
NonNamespaceOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> namespaceOperation;
|
||||
|
||||
@Mock
|
||||
ServiceResource<Service, DoneableService> serviceResource;
|
||||
|
||||
@Mock
|
||||
FilterWatchListMultiDeletable<Service, ServiceList, Boolean, Watch, Watcher<Service>> multiDeletable;
|
||||
|
||||
@Test
|
||||
void testPositiveMatch() {
|
||||
when(environment.getProperty("loadbalancer.client.name"))
|
||||
.thenReturn("test-service");
|
||||
when(mapper.map(any(Service.class)))
|
||||
.thenReturn(new KubernetesServiceInstance("", "", "", 0, null, false));
|
||||
when(this.client.getNamespace()).thenReturn("test");
|
||||
when(this.client.services()).thenReturn(this.serviceOperation);
|
||||
when(this.serviceOperation.inNamespace("test")).thenReturn(namespaceOperation);
|
||||
when(this.namespaceOperation.withName("test-service"))
|
||||
.thenReturn(this.serviceResource);
|
||||
when(this.serviceResource.get()).thenReturn(buildService("test-service", 8080));
|
||||
KubernetesServicesListSupplier supplier = new KubernetesServicesListSupplier(
|
||||
environment, client, mapper, new KubernetesDiscoveryProperties());
|
||||
List<ServiceInstance> instances = supplier.get().blockFirst();
|
||||
assert instances != null;
|
||||
Assertions.assertEquals(1, instances.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPositiveMatchAllNamespaces() {
|
||||
when(environment.getProperty("loadbalancer.client.name"))
|
||||
.thenReturn("test-service");
|
||||
when(mapper.map(any(Service.class)))
|
||||
.thenReturn(new KubernetesServiceInstance("", "", "", 0, null, false));
|
||||
when(this.client.services()).thenReturn(this.serviceOperation);
|
||||
when(this.serviceOperation.inAnyNamespace()).thenReturn(this.multiDeletable);
|
||||
when(this.multiDeletable.withField("metadata.name", "test-service"))
|
||||
.thenReturn(this.multiDeletable);
|
||||
ServiceList serviceList = new ServiceList();
|
||||
serviceList.getItems().add(buildService("test-service", 8080));
|
||||
when(this.multiDeletable.list()).thenReturn(serviceList);
|
||||
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
|
||||
discoveryProperties.setAllNamespaces(true);
|
||||
KubernetesServicesListSupplier supplier = new KubernetesServicesListSupplier(
|
||||
environment, client, mapper, discoveryProperties);
|
||||
List<ServiceInstance> instances = supplier.get().blockFirst();
|
||||
assert instances != null;
|
||||
Assertions.assertEquals(1, instances.size());
|
||||
}
|
||||
|
||||
private Service buildService(String name, int port) {
|
||||
return new ServiceBuilder().withNewMetadata().withName(name).endMetadata()
|
||||
.withNewSpec().addNewPort().withPort(port).endPort().endSpec().build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -51,6 +51,11 @@
|
||||
<artifactId>spring-cloud-kubernetes-ribbon</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
30
spring-cloud-starter-kubernetes-loadbalancer/pom.xml
Normal file
30
spring-cloud-starter-kubernetes-loadbalancer/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-starter-kubernetes-loadbalancer</artifactId>
|
||||
<name>Spring Cloud Kubernetes :: Starter :: LoadBalancer</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-kubernetes-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-kubernetes</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.8.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user