diff --git a/README.adoc b/README.adoc
index aa34f217..b2301858 100644
--- a/README.adoc
+++ b/README.adoc
@@ -652,6 +652,34 @@ the `PortName` key. If you want to specify in which Kubernetes namespace the tar
the `KubernetesNamespace` key, remembering in both instances to prefix these keys with your service name and
`ribbon` prefix, as specified earlier.
+
+.Spring Cloud Kubernetes Ribbon Configuration
+|===
+|Property Key |Type |Default Value
+
+|spring.cloud.kubernetes.ribbon.enabled
+|boolean
+|true
+
+|spring.cloud.kubernetes.ribbon.mode
+|`KubernetesRibbonMode`
+|POD
+
+|spring.cloud.kubernetes.ribbon.cluster-domain
+|string
+|cluster.local
+|===
+
+* `spring.cloud.kubernetes.ribbon.mode` supports `POD` and `SERVICE` modes.
+** The POD mode is to achieve load balancing by obtaining the Pod IP address of Kubernetes and using Ribbon.
+ POD mode uses the load balancing of the Ribbon Does not support Kubernetes load balancing, The traffic policy of `Istio` is not supported.
+** the `SERVICE` mode is directly based on the `service name` of the Ribbon. Get
+The Kubernetes service is concatenated into `service-name.{namespace}.svc.{cluster.domain}:{port}` such as: `demo1.default.svc.cluster.local:8080`.
+the `SERVICE` mode uses load balancing of the Kubernetes service to support Istio's traffic policy.
+
+* `spring.cloud.kubernetes.ribbon.cluster-domain` Set the custom Kubernetes cluster domain suffix. The default value is: 'cluster.local'
+
+
The following examples use this module for ribbon discovery:
* link:./spring-cloud-kubernetes-examples/kubernetes-circuitbreaker-ribbon-example[Spring Cloud Circuitbreaker and Ribbon]
diff --git a/docs/src/main/asciidoc/ribbon-integration.adoc b/docs/src/main/asciidoc/ribbon-integration.adoc
index a9ee311e..967ef343 100644
--- a/docs/src/main/asciidoc/ribbon-integration.adoc
+++ b/docs/src/main/asciidoc/ribbon-integration.adoc
@@ -45,6 +45,32 @@ the `PortName` key. If you want to specify in which Kubernetes namespace the tar
the `KubernetesNamespace` key, remembering in both instances to prefix these keys with your service name and
`ribbon` prefix, as specified earlier.
+.Spring Cloud Kubernetes Ribbon Configuration
+|===
+|Property Key |Type |Default Value
+
+|spring.cloud.kubernetes.ribbon.enabled
+|boolean
+|true
+
+|spring.cloud.kubernetes.ribbon.mode
+|`KubernetesRibbonMode`
+|POD
+
+|spring.cloud.kubernetes.ribbon.cluster-domain
+|string
+|cluster.local
+|===
+
+* `spring.cloud.kubernetes.ribbon.mode` supports `POD` and `SERVICE` modes.
+** The POD mode is to achieve load balancing by obtaining the Pod IP address of Kubernetes and using Ribbon.
+ POD mode uses the load balancing of the Ribbon Does not support Kubernetes load balancing, The traffic policy of `Istio` is not supported.
+** the `SERVICE` mode is directly based on the `service name` of the Ribbon. Get
+The Kubernetes service is concatenated into `service-name.{namespace}.svc.{cluster.domain}:{port}` such as: `demo1.default.svc.cluster.local:8080`.
+the `SERVICE` mode uses load balancing of the Kubernetes service to support Istio's traffic policy.
+
+* `spring.cloud.kubernetes.ribbon.cluster-domain` Set the custom Kubernetes cluster domain suffix. The default value is: 'cluster.local'
+
The following examples use this module for ribbon discovery:
* link:./spring-cloud-kubernetes-examples/kubernetes-circuitbreaker-ribbon-example[Spring Cloud Circuitbreaker and Ribbon]
diff --git a/spring-cloud-kubernetes-ribbon/pom.xml b/spring-cloud-kubernetes-ribbon/pom.xml
index 582f0081..06275909 100644
--- a/spring-cloud-kubernetes-ribbon/pom.xml
+++ b/spring-cloud-kubernetes-ribbon/pom.xml
@@ -99,6 +99,11 @@
${groovy.version}
test
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
diff --git a/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesEndpointsServerList.java b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesEndpointsServerList.java
new file mode 100644
index 00000000..7166564c
--- /dev/null
+++ b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesEndpointsServerList.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ribbon;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.netflix.loadbalancer.Server;
+import io.fabric8.kubernetes.api.model.EndpointAddress;
+import io.fabric8.kubernetes.api.model.EndpointPort;
+import io.fabric8.kubernetes.api.model.EndpointSubset;
+import io.fabric8.kubernetes.api.model.Endpoints;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.utils.Utils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * the KubernetesEndpointsServerList description.
+ * @author wuzishu
+ */
+public class KubernetesEndpointsServerList extends KubernetesServerList {
+
+ private static final Log LOG = LogFactory.getLog(KubernetesEndpointsServerList.class);
+
+ /**
+ * Instantiates a new Kubernetes endpoints server list.
+ *
+ * @param client the client
+ * @param properties the properties
+ */
+ KubernetesEndpointsServerList(KubernetesClient client, KubernetesRibbonProperties properties) {
+ super(client, properties);
+ }
+
+ @Override
+ public List getUpdatedListOfServers() {
+ List result = new ArrayList<>();
+ Endpoints endpoints = StringUtils.isNotBlank(this.getNamespace())
+ ? this.getClient().endpoints().inNamespace(this.getNamespace())
+ .withName(this.getServiceId()).get()
+ : this.getClient().endpoints().withName(this.getServiceId()).get();
+ if (endpoints != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format(
+ "Found [%d] endpoints in l [%s] for name [%s] and portName [%s]",
+ endpoints.getSubsets().size(),
+ endpoints.getMetadata().getNamespace(), this.getServiceId(),
+ this.getPortName()));
+ }
+ for (EndpointSubset subset : endpoints.getSubsets()) {
+
+ if (subset.getPorts().size() == 1) {
+ EndpointPort port = subset.getPorts().get(getFIRST());
+ for (EndpointAddress address : subset.getAddresses()) {
+ result.add(new Server(address.getIp(), port.getPort()));
+ }
+ }
+ else {
+ for (EndpointPort port : subset.getPorts()) {
+ if (Utils.isNullOrEmpty(this.getPortName())
+ || this.getPortName().endsWith(port.getName())) {
+ for (EndpointAddress address : subset.getAddresses()) {
+ result.add(
+ new Server(address.getIp(), port.getPort()));
+ }
+ }
+ }
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ LOG.warn(String.format(
+ "Did not find any endpoints in ribbon in namespace [%s] for name [%s] and portName [%s]",
+ this.getNamespace(), this.getServiceId(), this.getPortName()));
+ }
+
+ return result;
+ }
+}
diff --git a/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonClientConfiguration.java b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonClientConfiguration.java
index 7bb5fc5e..aa1018ad 100644
--- a/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonClientConfiguration.java
+++ b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonClientConfiguration.java
@@ -21,6 +21,7 @@ import com.netflix.loadbalancer.ServerList;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -30,6 +31,7 @@ import org.springframework.context.annotation.Configuration;
* @author Ioannis Canellos
*/
@Configuration
+@EnableConfigurationProperties(KubernetesRibbonProperties.class)
public class KubernetesRibbonClientConfiguration {
public KubernetesRibbonClientConfiguration() {
@@ -37,8 +39,15 @@ public class KubernetesRibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
- public ServerList> ribbonServerList(KubernetesClient client, IClientConfig config) {
- KubernetesServerList serverList = new KubernetesServerList(client);
+ public ServerList> ribbonServerList(KubernetesClient client, IClientConfig config,
+ KubernetesRibbonProperties properties) {
+ KubernetesServerList serverList;
+ if (properties.getMode() == KubernetesRibbonMode.SERVICE) {
+ serverList = new KubernetesServicesServerList(client, properties);
+ }
+ else {
+ serverList = new KubernetesEndpointsServerList(client, properties);
+ }
serverList.initWithNiwsConfig(config);
return serverList;
}
diff --git a/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonMode.java b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonMode.java
new file mode 100644
index 00000000..f3a07e3e
--- /dev/null
+++ b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonMode.java
@@ -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.ribbon;
+
+/**
+ * the KubernetesRibbonMode description.
+ *
+ * @author wuzishu
+ */
+public enum KubernetesRibbonMode {
+
+ /**
+ * using pod ip and port.
+ */
+ POD,
+ /**
+ * using kubernetes service name and port.
+ */
+ SERVICE
+
+}
diff --git a/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonProperties.java b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonProperties.java
new file mode 100644
index 00000000..3b0f694f
--- /dev/null
+++ b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesRibbonProperties.java
@@ -0,0 +1,91 @@
+/*
+ * 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.ribbon;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * The type Kubernetes ribbon properties.
+ */
+@ConfigurationProperties(prefix = "spring.cloud.kubernetes.ribbon")
+public class KubernetesRibbonProperties {
+
+ /**
+ * Ribbon enabled,default true.
+ */
+ private Boolean enabled = true;
+
+ /**
+ * {@link KubernetesRibbonMode} setting ribbon server list with ip of pod or service
+ * name. default value is POD.
+ */
+ private KubernetesRibbonMode mode = KubernetesRibbonMode.POD;
+
+ /**
+ * cluster domain.
+ */
+ private String clusterDomain = "cluster.local";
+
+ /**
+ * 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 KubernetesRibbonMode getMode() {
+ return mode;
+ }
+
+ /**
+ * Sets mode.
+ * @param mode the mode
+ */
+ public void setMode(KubernetesRibbonMode 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;
+ }
+
+}
diff --git a/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesServerList.java b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesServerList.java
index 44d413bf..7c6abc2a 100644
--- a/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesServerList.java
+++ b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesServerList.java
@@ -16,7 +16,6 @@
package org.springframework.cloud.kubernetes.ribbon;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -24,27 +23,19 @@ import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
-import io.fabric8.kubernetes.api.model.EndpointAddress;
-import io.fabric8.kubernetes.api.model.EndpointPort;
-import io.fabric8.kubernetes.api.model.EndpointSubset;
-import io.fabric8.kubernetes.api.model.Endpoints;
import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.client.utils.Utils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
/**
* Kubernetes {@link ServerList}.
*
* @author Ioannis Canellos
+ * @author wuzishu
*/
-public class KubernetesServerList extends AbstractServerList
- implements ServerList {
+public abstract class KubernetesServerList extends AbstractServerList
+ implements ServerList {
private static final int FIRST = 0;
- private static final Log LOG = LogFactory.getLog(KubernetesServerList.class);
-
private final KubernetesClient client;
private String serviceId;
@@ -53,62 +44,84 @@ public class KubernetesServerList extends AbstractServerList
private String portName;
- public KubernetesServerList(KubernetesClient client) {
+ private KubernetesRibbonProperties properties;
+
+ /**
+ * Instantiates a new Kubernetes server list.
+ *
+ * @param client the client
+ * @param properties the properties
+ */
+ public KubernetesServerList(KubernetesClient client,
+ KubernetesRibbonProperties properties) {
this.client = client;
+ this.properties = properties;
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
this.serviceId = clientConfig.getClientName();
this.namespace = clientConfig.getPropertyAsString(KubernetesConfigKey.Namespace,
- this.client.getNamespace());
+ this.client.getNamespace());
this.portName = clientConfig.getPropertyAsString(KubernetesConfigKey.PortName,
- null);
+ null);
}
public List getInitialListOfServers() {
return Collections.emptyList();
}
- public List getUpdatedListOfServers() {
- Endpoints endpoints = this.namespace != null
- ? this.client.endpoints().inNamespace(this.namespace)
- .withName(this.serviceId).get()
- : this.client.endpoints().withName(this.serviceId).get();
- List result = new ArrayList();
- if (endpoints != null) {
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("Found [" + endpoints.getSubsets().size()
- + "] endpoints in namespace [" + this.namespace + "] for name ["
- + this.serviceId + "] and portName [" + this.portName + "]");
- }
- for (EndpointSubset subset : endpoints.getSubsets()) {
-
- if (subset.getPorts().size() == 1) {
- EndpointPort port = subset.getPorts().get(FIRST);
- for (EndpointAddress address : subset.getAddresses()) {
- result.add(new Server(address.getIp(), port.getPort()));
- }
- }
- else {
- for (EndpointPort port : subset.getPorts()) {
- if (Utils.isNullOrEmpty(this.portName)
- || this.portName.endsWith(port.getName())) {
- for (EndpointAddress address : subset.getAddresses()) {
- result.add(new Server(address.getIp(), port.getPort()));
- }
- }
- }
- }
- }
- }
- else {
- LOG.warn("Did not find any endpoints in ribbon in namespace ["
- + this.namespace + "] for name [" + this.serviceId
- + "] and portName [" + this.portName + "]");
- }
- return result;
+ /**
+ * Gets first.
+ *
+ * @return the first
+ */
+ static int getFIRST() {
+ return FIRST;
}
+ /**
+ * Gets client.
+ *
+ * @return the client
+ */
+ KubernetesClient getClient() {
+ return client;
+ }
+
+ /**
+ * Gets service id.
+ *
+ * @return the service id
+ */
+ String getServiceId() {
+ return serviceId;
+ }
+
+ /**
+ * Gets namespace.
+ *
+ * @return the namespace
+ */
+ String getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * Gets port name.
+ *
+ * @return the port name
+ */
+ String getPortName() {
+ return portName;
+ }
+
+ /**
+ * Gets properties.
+ *
+ * @return the properties
+ */
+ KubernetesRibbonProperties getProperties() {
+ return properties;
+ }
}
diff --git a/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesServicesServerList.java b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesServicesServerList.java
new file mode 100644
index 00000000..2c445126
--- /dev/null
+++ b/spring-cloud-kubernetes-ribbon/src/main/java/org/springframework/cloud/kubernetes/ribbon/KubernetesServicesServerList.java
@@ -0,0 +1,92 @@
+/*
+ * 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.ribbon;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.netflix.loadbalancer.Server;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.ServicePort;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.utils.Utils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * the KubernetesServicesServerList description.
+ * @author wuzishu
+ */
+public class KubernetesServicesServerList extends KubernetesServerList {
+ private static final Log LOG = LogFactory.getLog(KubernetesServicesServerList.class);
+
+ /**
+ * Instantiates a new Kubernetes services server list.
+ *
+ * @param client the client
+ * @param properties the properties
+ */
+ KubernetesServicesServerList(KubernetesClient client, KubernetesRibbonProperties properties) {
+ super(client, properties);
+ }
+
+ /**
+ * Concat service fully qualified domain name.
+ * @param service Service model
+ * @return service FQDN
+ */
+ private String concatServiceFQDN(Service service) {
+ return String.format("%s.%s.svc.%s", service.getMetadata().getName(),
+ StringUtils.isNotBlank(service.getMetadata().getNamespace()) ? service.getMetadata()
+ .getNamespace() : "default", this.getProperties().getClusterDomain());
+ }
+
+ @Override
+ public List getUpdatedListOfServers() {
+ List result = new ArrayList<>();
+ Service service = StringUtils.isNotBlank(this.getNamespace())
+ ? this.getClient().services().inNamespace(this.getNamespace())
+ .withName(this.getServiceId()).get()
+ : this.getClient().services().withName(this.getServiceId()).get();
+ if (service != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Found Service[" + service.getMetadata().getName() + "]");
+ }
+ if (service.getSpec().getPorts().size() == 1) {
+ result.add(new Server(this.concatServiceFQDN(service),
+ service.getSpec().getPorts().get(0).getPort()));
+ }
+ else {
+ for (ServicePort servicePort : service.getSpec().getPorts()) {
+ if (Utils.isNotNullOrEmpty(this.getPortName())
+ || this.getPortName().endsWith(servicePort.getName())) {
+ result.add(new Server(concatServiceFQDN(service),
+ servicePort.getPort()));
+ }
+ }
+
+ }
+ }
+ if (result.isEmpty()) {
+ LOG.warn(String.format(
+ "Did not find any service in ribbon in namespace [%s] for name [%s] and portName [%s]",
+ this.getNamespace(), this.getServiceId(), this.getPortName()));
+ }
+ return result;
+ }
+}
diff --git a/spring-cloud-kubernetes-ribbon/src/test/java/org/springframework/cloud/kubernetes/ribbon/RibbonWithServiceModeTest.java b/spring-cloud-kubernetes-ribbon/src/test/java/org/springframework/cloud/kubernetes/ribbon/RibbonWithServiceModeTest.java
new file mode 100644
index 00000000..8d75bcc2
--- /dev/null
+++ b/spring-cloud-kubernetes-ribbon/src/test/java/org/springframework/cloud/kubernetes/ribbon/RibbonWithServiceModeTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.ribbon;
+
+import java.util.List;
+
+import com.netflix.loadbalancer.ILoadBalancer;
+import com.netflix.loadbalancer.Server;
+import io.fabric8.kubernetes.api.model.IntOrString;
+import io.fabric8.kubernetes.api.model.ServiceBuilder;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.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.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * the RibbonWithServiceModeTest description.
+ * @author wuzishu
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class, properties = {
+ "spring.application.name=testapp",
+ "spring.cloud.kubernetes.client.namespace=testns",
+ "spring.cloud.kubernetes.client.trustCerts=true",
+ "spring.cloud.kubernetes.config.namespace=testns",
+ "spring.cloud.kubernetes.enabled=true",
+ "spring.cloud.kubernetes.discovery.enabled=true",
+ "spring.cloud.kubernetes.ribbon.enabled=true",
+ "spring.cloud.kubernetes.ribbon.mode=SERVICE",
+ "spring.cloud.kubernetes.ribbon.clusterDomain=test.com"})
+@EnableAutoConfiguration
+@EnableDiscoveryClient
+public class RibbonWithServiceModeTest {
+ @ClassRule
+ public static KubernetesServer server = new KubernetesServer();
+
+ @ClassRule
+ public static KubernetesServer mockEndpointA = new KubernetesServer(false);
+
+ private static KubernetesClient mockClient;
+
+ @Autowired
+ private RestTemplate restTemplate;
+
+ @BeforeClass
+ public static void setUpBefore() {
+ 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");
+
+ // Configured
+ server.expect().get().withPath("/api/v1/namespaces/testns/services/testapp").andReturn(200,
+ new ServiceBuilder().withNewMetadata().withName("testapp").withNamespace("testns")
+ .endMetadata().withNewSpec().addToSelector("app", "testapp-a")
+ .addNewPort().withName("http")
+ .withPort(mockEndpointA.getMockServer().getPort())
+ .withTargetPort(new IntOrString(mockEndpointA.getMockServer().getPort()))
+ .withProtocol("TCP").endPort().endSpec().build())
+ .always();
+
+ }
+
+ @Autowired
+ private ApplicationContext context;
+
+ @Test
+ public void testGreetingWithServiceMode() {
+ SpringClientFactory springClientFactory = context.getBean(SpringClientFactory.class);
+ ILoadBalancer testapp = springClientFactory.getLoadBalancer("testapp");
+ List allServers = testapp.getAllServers();
+ assertThat(allServers.stream().map(c -> String.format("%s:%s", c.getHost(), c.getPort())))
+ .containsOnly("testapp.testns.svc.test.com:" + mockEndpointA.getMockServer().getPort());
+ }
+}