Enhanced Ribbon can support access to workloads through services (#388)
* Enhanced Ribbon can support access to workloads through services while being compatible with Istio traffic policies * update after checkstyle * update * add cluster_domian config * add tests for service mode * update doc for new feature and refactoring code structure
This commit is contained in:
28
README.adoc
28
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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -99,6 +99,11 @@
|
||||
<version>${groovy.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -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<Server> getUpdatedListOfServers() {
|
||||
List<Server> 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Server>
|
||||
implements ServerList<Server> {
|
||||
public abstract class KubernetesServerList extends AbstractServerList<Server>
|
||||
implements ServerList<Server> {
|
||||
|
||||
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<Server>
|
||||
|
||||
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<Server> getInitialListOfServers() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<Server> getUpdatedListOfServers() {
|
||||
Endpoints endpoints = this.namespace != null
|
||||
? this.client.endpoints().inNamespace(this.namespace)
|
||||
.withName(this.serviceId).get()
|
||||
: this.client.endpoints().withName(this.serviceId).get();
|
||||
|
||||
List<Server> result = new ArrayList<Server>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Server> getUpdatedListOfServers() {
|
||||
List<Server> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<Server> 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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user