Merge branch 'main' of github.com:spring-cloud/spring-cloud-kubernetes

This commit is contained in:
Ryan Baxter
2022-10-12 10:28:42 -04:00
39 changed files with 654 additions and 615 deletions

View File

@@ -68,18 +68,19 @@
|spring.cloud.kubernetes.discovery.filter | | SpEL expression to filter services AFTER they have been retrieved from the Kubernetes API server.
|spring.cloud.kubernetes.discovery.include-not-ready-addresses | `+++false+++` | If endpoint addresses not marked 'ready' by the k8s api server should be discovered.
|spring.cloud.kubernetes.discovery.known-secure-ports | | Set the port numbers that are considered secure and use HTTPS.
|spring.cloud.kubernetes.discovery.metadata.add-annotations | `+++true+++` | When set, the Kubernetes annotations of the services will be included as metadata of the returned ServiceInstance.
|spring.cloud.kubernetes.discovery.metadata.add-labels | `+++true+++` | When set, the Kubernetes labels of the services will be included as metadata of the returned ServiceInstance.
|spring.cloud.kubernetes.discovery.metadata.add-ports | `+++true+++` | When set, any named Kubernetes service ports will be included as metadata of the returned ServiceInstance.
|spring.cloud.kubernetes.discovery.metadata.annotations-prefix | | When addAnnotations is set, then this will be used as a prefix to the key names in the metadata map.
|spring.cloud.kubernetes.discovery.metadata.labels-prefix | | When addLabels is set, then this will be used as a prefix to the key names in the metadata map.
|spring.cloud.kubernetes.discovery.metadata.ports-prefix | `+++port.+++` | When addPorts is set, then this will be used as a prefix to the key names in the metadata map.
|spring.cloud.kubernetes.discovery.metadata.add-annotations | `+++true+++` |
|spring.cloud.kubernetes.discovery.metadata.add-labels | `+++true+++` |
|spring.cloud.kubernetes.discovery.metadata.add-ports | `+++true+++` |
|spring.cloud.kubernetes.discovery.metadata.annotations-prefix | |
|spring.cloud.kubernetes.discovery.metadata.labels-prefix | |
|spring.cloud.kubernetes.discovery.metadata.ports-prefix | `+++port.+++` |
|spring.cloud.kubernetes.discovery.order | |
|spring.cloud.kubernetes.discovery.primary-port-name | | If set then the port with a given name is used as primary when multiple ports are defined for a service.
|spring.cloud.kubernetes.discovery.service-labels | | If set, then only the services matching these labels will be fetched from the Kubernetes API server.
|spring.cloud.kubernetes.discovery.wait-cache-ready | `+++true+++` |
|spring.cloud.kubernetes.leader.auto-startup | `+++true+++` | Should leader election be started automatically on startup. Default: true
|spring.cloud.kubernetes.leader.config-map-name | `+++leaders+++` | Kubernetes ConfigMap where leaders information will be stored. Default: leaders
|spring.cloud.kubernetes.leader.create-config-map | `+++true+++` | Enable/disable creating ConfigMap if it does not exist. Default: true
|spring.cloud.kubernetes.leader.enabled | `+++true+++` | Should leader election be enabled. Default: true
|spring.cloud.kubernetes.leader.leader-id-prefix | `+++leader.id.+++` | Leader id property prefix for the ConfigMap. Default: leader.id.
|spring.cloud.kubernetes.leader.namespace | | Kubernetes namespace where the leaders ConfigMap and candidates are located.

View File

@@ -75,7 +75,7 @@ public class KubernetesClientPodUtils implements PodUtils<V1Pod> {
}
@Override
public Boolean isInsideKubernetes() {
public boolean isInsideKubernetes() {
return currentPod().get() != null;
}

View File

@@ -39,8 +39,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -107,19 +107,19 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient, Initi
Map<String, String> svcMetadata = new HashMap<>();
if (this.properties.getMetadata() != null) {
if (this.properties.getMetadata().isAddLabels()) {
if (this.properties.getMetadata().addLabels()) {
if (service.getMetadata() != null && service.getMetadata().getLabels() != null) {
String labelPrefix = this.properties.getMetadata().getLabelsPrefix() != null
? this.properties.getMetadata().getLabelsPrefix() : "";
String labelPrefix = this.properties.getMetadata().labelsPrefix() != null
? this.properties.getMetadata().labelsPrefix() : "";
service.getMetadata().getLabels().entrySet().stream()
.filter(e -> e.getKey().startsWith(labelPrefix))
.forEach(e -> svcMetadata.put(e.getKey(), e.getValue()));
}
}
if (this.properties.getMetadata().isAddAnnotations()) {
if (this.properties.getMetadata().addAnnotations()) {
if (service.getMetadata() != null && service.getMetadata().getAnnotations() != null) {
String annotationPrefix = this.properties.getMetadata().getAnnotationsPrefix() != null
? this.properties.getMetadata().getAnnotationsPrefix() : "";
String annotationPrefix = this.properties.getMetadata().annotationsPrefix() != null
? this.properties.getMetadata().annotationsPrefix() : "";
service.getMetadata().getAnnotations().entrySet().stream()
.filter(e -> e.getKey().startsWith(annotationPrefix))
.forEach(e -> svcMetadata.put(e.getKey(), e.getValue()));
@@ -145,7 +145,7 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient, Initi
.flatMap(subset -> {
Map<String, String> metadata = new HashMap<>(svcMetadata);
List<V1EndpointPort> endpointPorts = subset.getPorts();
if (this.properties.getMetadata() != null && this.properties.getMetadata().isAddPorts()) {
if (this.properties.getMetadata() != null && this.properties.getMetadata().addPorts()) {
endpointPorts.forEach(p -> metadata.put(p.getName(), Integer.toString(p.getPort())));
}
List<V1EndpointAddress> addresses = subset.getAddresses();
@@ -159,7 +159,7 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient, Initi
final int port = findEndpointPort(endpointPorts, primaryPortName, serviceId);
return addresses.stream()
.map(addr -> new KubernetesServiceInstance(
.map(addr -> new DefaultKubernetesServiceInstance(
addr.getTargetRef() != null ? addr.getTargetRef().getUid() : "", serviceId,
addr.getIp(), port, metadata, false, service.getMetadata().getNamespace(),
service.getMetadata().getClusterName()));

View File

@@ -35,8 +35,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.times;
@@ -151,8 +151,9 @@ public class KubernetesInformerDiscoveryClientTests {
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1").toArray()).isEmpty();
assertThat(discoveryClient.getInstances("test-svc-3").toArray()).containsOnly(new KubernetesServiceInstance("",
"test-svc-3", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null));
assertThat(discoveryClient.getInstances("test-svc-3").toArray())
.containsOnly(new DefaultKubernetesServiceInstance("", "test-svc-3", "2.2.2.2", 8080, new HashMap<>(),
false, "namespace1", null));
}
@Test
@@ -179,7 +180,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(2)).isAllNamespaces();
@@ -196,7 +197,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
@@ -229,7 +230,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
@@ -275,7 +276,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "1.1.1.1", 443, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
@@ -294,7 +295,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "1.1.1.1", 80, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
@@ -311,7 +312,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "1.1.1.1", 443, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
@@ -330,7 +331,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "1.1.1.1", 80, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
@@ -346,7 +347,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "1.1.1.1", 443, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
@@ -362,7 +363,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "1.1.1.1", 80, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();
@@ -379,7 +380,7 @@ public class KubernetesInformerDiscoveryClientTests {
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new KubernetesServiceInstance("",
assertThat(discoveryClient.getInstances("test-svc-1")).containsOnly(new DefaultKubernetesServiceInstance("",
"test-svc-1", "1.1.1.1", 80, new HashMap<>(), false, "namespace1", null));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
verify(kubernetesDiscoveryProperties, times(1)).getPrimaryPortName();

View File

@@ -1,52 +0,0 @@
/*
* 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.client.discovery;
import java.util.Collections;
import org.junit.Test;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import static org.assertj.core.api.Assertions.assertThat;
public class KubernetesServiceInstanceTests {
@Test
public void schemeIsHttp() {
assertServiceInstance(false);
}
private KubernetesServiceInstance assertServiceInstance(boolean secure) {
KubernetesServiceInstance instance = new KubernetesServiceInstance("123", "myservice", "1.2.3.4", 8080,
Collections.emptyMap(), secure);
assertThat(instance.getInstanceId()).isEqualTo("123");
assertThat(instance.getServiceId()).isEqualTo("myservice");
assertThat(instance.getHost()).isEqualTo("1.2.3.4");
assertThat(instance.getPort()).isEqualTo(8080);
assertThat(instance.isSecure()).isEqualTo(secure);
assertThat(instance.getScheme()).isEqualTo(secure ? "https" : "http");
return instance;
}
@Test
public void schemeIsHttps() {
assertServiceInstance(true);
}
}

View File

@@ -36,8 +36,8 @@ import org.mockito.junit.MockitoJUnitRunner;
import reactor.test.StepVerifier;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.mock.env.MockEnvironment;
import static org.mockito.Mockito.mock;
@@ -117,7 +117,7 @@ public class KubernetesInformerReactiveDiscoveryClientTests {
endpointsLister, null, null, kubernetesDiscoveryProperties);
StepVerifier
.create(discoveryClient.getInstances("test-svc-1")).expectNext(new KubernetesServiceInstance("",
.create(discoveryClient.getInstances("test-svc-1")).expectNext(new DefaultKubernetesServiceInstance("",
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null))
.expectComplete().verify();
@@ -137,7 +137,7 @@ public class KubernetesInformerReactiveDiscoveryClientTests {
kubernetesDiscoveryProperties);
StepVerifier
.create(discoveryClient.getInstances("test-svc-1")).expectNext(new KubernetesServiceInstance("",
.create(discoveryClient.getInstances("test-svc-1")).expectNext(new DefaultKubernetesServiceInstance("",
"test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false, "namespace1", null))
.expectComplete().verify();

View File

@@ -25,6 +25,7 @@ import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServicePort;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesLoadBalancerProperties;
@@ -69,21 +70,21 @@ public class KubernetesClientServiceInstanceMapper implements KubernetesServiceI
service.getMetadata().getNamespace(), properties.getClusterDomain());
final boolean secure = KubernetesServiceInstanceMapper.isSecure(service.getMetadata().getLabels(),
service.getMetadata().getAnnotations(), port.getName(), port.getPort());
return new KubernetesServiceInstance(meta.getUid(), meta.getName(), host, port.getPort(),
return new DefaultKubernetesServiceInstance(meta.getUid(), meta.getName(), host, port.getPort(),
getServiceMetadata(service), secure);
}
private Map<String, String> getServiceMetadata(V1Service service) {
final Map<String, String> serviceMetadata = new HashMap<>();
KubernetesDiscoveryProperties.Metadata metadataProps = this.discoveryProperties.getMetadata();
if (metadataProps.isAddLabels()) {
if (metadataProps.addLabels()) {
Map<String, String> labelMetadata = KubernetesServiceInstanceMapper
.getMapWithPrefixedKeys(service.getMetadata().getLabels(), metadataProps.getLabelsPrefix());
.getMapWithPrefixedKeys(service.getMetadata().getLabels(), metadataProps.labelsPrefix());
serviceMetadata.putAll(labelMetadata);
}
if (metadataProps.isAddAnnotations()) {
Map<String, String> annotationMetadata = KubernetesServiceInstanceMapper.getMapWithPrefixedKeys(
service.getMetadata().getAnnotations(), metadataProps.getAnnotationsPrefix());
if (metadataProps.addAnnotations()) {
Map<String, String> annotationMetadata = KubernetesServiceInstanceMapper
.getMapWithPrefixedKeys(service.getMetadata().getAnnotations(), metadataProps.annotationsPrefix());
serviceMetadata.putAll(annotationMetadata);
}

View File

@@ -26,6 +26,7 @@ import io.kubernetes.client.openapi.models.V1ServicePortBuilder;
import io.kubernetes.client.openapi.models.V1ServiceSpecBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesLoadBalancerProperties;
@@ -56,7 +57,7 @@ class KubernetesClientServiceInstanceMapperTests {
Map<String, String> metadata = new HashMap<>();
metadata.put("org.springframework.cloud", "true");
metadata.put("beta", "true");
KubernetesServiceInstance result = new KubernetesServiceInstance("0", "database",
DefaultKubernetesServiceInstance result = new DefaultKubernetesServiceInstance("0", "database",
"database.default.svc.cluster.local", 80, metadata, false);
assertThat(serviceInstance).isEqualTo(result);
}
@@ -79,7 +80,7 @@ class KubernetesClientServiceInstanceMapperTests {
.build();
KubernetesServiceInstance serviceInstance = mapper.map(service);
KubernetesServiceInstance result = new KubernetesServiceInstance("0", "database",
DefaultKubernetesServiceInstance result = new DefaultKubernetesServiceInstance("0", "database",
"database.default.svc.cluster.local", 443, new HashMap(), true);
assertThat(serviceInstance).isEqualTo(result);
}

View File

@@ -42,8 +42,8 @@ import reactor.test.StepVerifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesLoadBalancerProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.mock.env.MockEnvironment;
@@ -131,7 +131,7 @@ class KubernetesClientServicesListSupplierTests {
Map<String, String> metadata = new HashMap<>();
metadata.put("org.springframework.cloud", "true");
metadata.put("beta", "true");
KubernetesServiceInstance service1 = new KubernetesServiceInstance("0", "service1",
DefaultKubernetesServiceInstance service1 = new DefaultKubernetesServiceInstance("0", "service1",
"service1.default.svc.cluster.local", 80, metadata, false);
List<ServiceInstance> services = new ArrayList<>();
services.add(service1);
@@ -161,9 +161,9 @@ class KubernetesClientServicesListSupplierTests {
Map<String, String> metadata = new HashMap<>();
metadata.put("org.springframework.cloud", "true");
metadata.put("beta", "true");
KubernetesServiceInstance service1 = new KubernetesServiceInstance("0", "service1",
DefaultKubernetesServiceInstance service1 = new DefaultKubernetesServiceInstance("0", "service1",
"service1.default.svc.cluster.local", 80, metadata, false);
KubernetesServiceInstance service2 = new KubernetesServiceInstance("1", "service1",
DefaultKubernetesServiceInstance service2 = new DefaultKubernetesServiceInstance("1", "service1",
"service1.test.svc.cluster.local", 80, new HashMap<>(), false);
List<ServiceInstance> services = new ArrayList<>();
services.add(service1);

View File

@@ -26,14 +26,14 @@ import java.util.function.Supplier;
public interface PodUtils<T> {
/**
* @return A supplier of the currentPod {@link Pod}. The supplier will hold the
* currentPod {@link Pod} if inside Kubernetes or false, otherwise.
* @return A supplier of the current Pod. The supplier will hold the current Pod if
* inside Kubernetes or null, otherwise.
*/
Supplier<T> currentPod();
/**
* @return true if called from within Kubernetes, false otherwise.
*/
Boolean isInsideKubernetes();
boolean isInsideKubernetes();
}

View File

@@ -25,7 +25,7 @@ import java.util.Map;
* Container that stores multiple sources, to be exact their names and their flattenned
* data. We force a LinkedHashSet on purpose, to preserve the order of sources.
*/
public final record MultipleSourcesContainer(LinkedHashSet<String> names, Map<String, Object> data) {
public record MultipleSourcesContainer(LinkedHashSet<String> names, Map<String, Object> data) {
private static final MultipleSourcesContainer EMPTY = new MultipleSourcesContainer(new LinkedHashSet<>(0),
Map.of());

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2013-2022 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.commons.discovery;
import java.net.URI;
import java.util.Map;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTP;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTPS;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.NAMESPACE_METADATA_KEY;
/**
* @author wind57
* @param instanceId the id of the instance.
* @param serviceId the id of the service.
* @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 the connection needs to be secure.
* @param namespace the namespace of the service.
* @param cluster the cluster the service resides in.
*/
public record DefaultKubernetesServiceInstance(String instanceId, String serviceId, String host, int port,
Map<String, String> metadata, boolean secure, String namespace,
String cluster) implements KubernetesServiceInstance {
/**
* @param instanceId the id of the instance.
* @param serviceId the id of the service.
* @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 the connection needs to be secure.
*/
public DefaultKubernetesServiceInstance(String instanceId, String serviceId, String host, int port,
Map<String, String> metadata, boolean secure) {
this(instanceId, serviceId, host, port, metadata, secure, null, null);
}
@Override
public String getInstanceId() {
return this.instanceId;
}
@Override
public String getServiceId() {
return serviceId;
}
@Override
public String getHost() {
return host;
}
@Override
public int getPort() {
return port;
}
@Override
public boolean isSecure() {
return secure;
}
@Override
public URI getUri() {
return createUri(secure ? HTTPS : HTTP, host, port);
}
@Override
public Map<String, String> getMetadata() {
return metadata;
}
@Override
public String getScheme() {
return isSecure() ? HTTPS : HTTP;
}
@Override
public String getNamespace() {
return namespace != null ? namespace : this.metadata.get(NAMESPACE_METADATA_KEY);
}
@Override
public String getCluster() {
return this.cluster;
}
private URI createUri(String scheme, String host, int port) {
return URI.create(scheme + "://" + host + ":" + port);
}
}

View File

@@ -16,15 +16,21 @@
package org.springframework.cloud.kubernetes.commons.discovery;
import org.springframework.beans.factory.InitializingBean;
import jakarta.annotation.PostConstruct;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.log.LogAccessor;
/**
* @author Ryan Baxter
*/
public final class KubernetesDiscoveryClientHealthIndicatorInitializer implements InitializingBean {
public final class KubernetesDiscoveryClientHealthIndicatorInitializer {
private static final LogAccessor LOG = new LogAccessor(
LogFactory.getLog(KubernetesDiscoveryClientHealthIndicatorInitializer.class));
private final PodUtils<?> podUtils;
@@ -36,9 +42,21 @@ public final class KubernetesDiscoveryClientHealthIndicatorInitializer implement
this.applicationEventPublisher = applicationEventPublisher;
}
@Override
public void afterPropertiesSet() {
this.applicationEventPublisher.publishEvent(new InstanceRegisteredEvent<>(podUtils.currentPod(), null));
@PostConstruct
private void postConstruct() {
LOG.debug(() -> "publishing InstanceRegisteredEvent");
this.applicationEventPublisher.publishEvent(new InstanceRegisteredEvent<>(
new RegisteredEventSource("kubernetes", podUtils.isInsideKubernetes(), podUtils.currentPod().get()),
null));
}
/**
* @param cloudPlatform "kubernetes" always
* @param inside inside kubernetes or not
* @param pod an actual pod or null, if we are outside kubernetes
*/
public record RegisteredEventSource(String cloudPlatform, boolean inside, Object pod) {
}
}

View File

@@ -24,6 +24,8 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.core.style.ToStringCreator;
import static org.springframework.cloud.client.discovery.DiscoveryClient.DEFAULT_ORDER;
@@ -175,100 +177,25 @@ public class KubernetesDiscoveryProperties {
}
/**
* Metadata properties.
* @param addLabels include labels as metadata
* @param labelsPrefix prefix for the labels
* @param addAnnotations include annotations as metadata
* @param annotationsPrefix prefix for the annotations
* @param addPorts include ports as metadata
* @param portsPrefix prefix for the ports, by default it is "port."
*/
public static class Metadata {
public record Metadata(@DefaultValue("true") boolean addLabels, String labelsPrefix,
@DefaultValue("true") boolean addAnnotations, String annotationsPrefix,
@DefaultValue("true") boolean addPorts, @DefaultValue("port.") String portsPrefix) {
/**
* When set, the Kubernetes labels of the services will be included as metadata of
* the returned ServiceInstance.
*/
private boolean addLabels = true;
@ConstructorBinding
public Metadata {
/**
* When addLabels is set, then this will be used as a prefix to the key names in
* the metadata map.
*/
private String labelsPrefix;
/**
* When set, the Kubernetes annotations of the services will be included as
* metadata of the returned ServiceInstance.
*/
private boolean addAnnotations = true;
/**
* When addAnnotations is set, then this will be used as a prefix to the key names
* in the metadata map.
*/
private String annotationsPrefix;
/**
* When set, any named Kubernetes service ports will be included as metadata of
* the returned ServiceInstance.
*/
private boolean addPorts = true;
/**
* When addPorts is set, then this will be used as a prefix to the key names in
* the metadata map.
*/
private String portsPrefix = "port.";
public boolean isAddLabels() {
return this.addLabels;
}
public void setAddLabels(boolean addLabels) {
this.addLabels = addLabels;
}
public String getLabelsPrefix() {
return this.labelsPrefix;
}
public void setLabelsPrefix(String labelsPrefix) {
this.labelsPrefix = labelsPrefix;
}
public boolean isAddAnnotations() {
return this.addAnnotations;
}
public void setAddAnnotations(boolean addAnnotations) {
this.addAnnotations = addAnnotations;
}
public String getAnnotationsPrefix() {
return this.annotationsPrefix;
}
public void setAnnotationsPrefix(String annotationsPrefix) {
this.annotationsPrefix = annotationsPrefix;
}
public boolean isAddPorts() {
return this.addPorts;
}
public void setAddPorts(boolean addPorts) {
this.addPorts = addPorts;
}
public String getPortsPrefix() {
return this.portsPrefix;
}
public void setPortsPrefix(String portsPrefix) {
this.portsPrefix = portsPrefix;
}
@Override
public String toString() {
return new ToStringCreator(this).append("addLabels", this.addLabels)
.append("labelsPrefix", this.labelsPrefix).append("addAnnotations", this.addAnnotations)
.append("annotationsPrefix", this.annotationsPrefix).append("addPorts", this.addPorts)
.append("portsPrefix", this.portsPrefix).toString();
// needed in order to get the defaults for some fields
public Metadata() {
this(true, null, true, null, true, "port.");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* Copyright 2019-2022 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.
@@ -16,211 +16,17 @@
package org.springframework.cloud.kubernetes.commons.discovery;
import java.net.URI;
import java.util.Map;
import java.util.Objects;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.style.ToStringCreator;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTP;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTPS;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.NAMESPACE_METADATA_KEY;
/**
* @author wind57
*
* {@link ServiceInstance} with additional methods, specific to kubernetes.
*/
sealed public interface KubernetesServiceInstance extends ServiceInstance permits DefaultKubernetesServiceInstance {
public final class KubernetesServiceInstance implements ServiceInstance {
String getNamespace();
private String instanceId;
private String serviceId;
private String host;
private int port;
private URI uri;
private Boolean secure;
private Map<String, String> metadata;
private String namespace;
private String cluster;
/**
* @param instanceId the id of the instance.
* @param serviceId the id of the service.
* @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 the connection needs to be secure.
*/
public KubernetesServiceInstance(String instanceId, String serviceId, String host, int port,
Map<String, String> metadata, Boolean secure) {
this.instanceId = instanceId;
this.serviceId = serviceId;
this.host = host;
this.port = port;
this.metadata = metadata;
this.secure = secure;
this.uri = createUri(secure ? HTTPS : HTTP, host, port);
this.namespace = null;
this.cluster = null;
}
/**
* @param instanceId the id of the instance.
* @param serviceId the id of the service.
* @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 the connection needs to be secure.
* @param namespace the namespace of the service.
* @param cluster the cluster the service resides in.
*/
public KubernetesServiceInstance(String instanceId, String serviceId, String host, int port,
Map<String, String> metadata, Boolean secure, String namespace, String cluster) {
this.instanceId = instanceId;
this.serviceId = serviceId;
this.host = host;
this.port = port;
this.metadata = metadata;
this.secure = secure;
this.uri = createUri(secure ? HTTPS : HTTP, host, port);
this.namespace = namespace;
this.cluster = cluster;
}
// Allows for deserialization
public KubernetesServiceInstance() {
}
@Override
public String getInstanceId() {
return this.instanceId;
}
@Override
public String getServiceId() {
return this.serviceId;
}
@Override
public String getHost() {
return this.host;
}
@Override
public int getPort() {
return this.port;
}
@Override
public boolean isSecure() {
return this.secure;
}
@Override
public URI getUri() {
return uri;
}
public Map<String, String> getMetadata() {
return this.metadata;
}
@Override
public String getScheme() {
return isSecure() ? HTTPS : HTTP;
}
private URI createUri(String scheme, String host, int port) {
return URI.create(scheme + "://" + host + ":" + port);
}
public String getNamespace() {
return namespace != null ? namespace : this.metadata.get(NAMESPACE_METADATA_KEY);
}
public String getCluster() {
return this.cluster;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
public void setUri(URI uri) {
this.uri = uri;
}
public void setSecure(Boolean secure) {
this.secure = secure;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public void setCluster(String cluster) {
this.cluster = cluster;
}
public Boolean getSecure() {
return secure;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
KubernetesServiceInstance that = (KubernetesServiceInstance) o;
return port == that.port && Objects.equals(instanceId, that.instanceId)
&& Objects.equals(serviceId, that.serviceId) && Objects.equals(host, that.host)
&& Objects.equals(uri, that.uri) && Objects.equals(secure, that.secure)
&& Objects.equals(metadata, that.metadata) && Objects.equals(getNamespace(), that.getNamespace())
&& Objects.equals(cluster, that.cluster);
}
@Override
public String toString() {
ToStringCreator creator = new ToStringCreator(this);
creator.append("instanceId", instanceId);
creator.append("serviceId", serviceId);
creator.append("host", host);
creator.append("port", port);
creator.append("uri", uri);
creator.append("secure", secure);
creator.append("namespace", getNamespace());
creator.append("cluster", cluster);
creator.append("metadata", metadata);
return creator.toString();
}
@Override
public int hashCode() {
return Objects.hash(instanceId, serviceId, host, port, uri, secure, getNamespace(), cluster, metadata);
}
String getCluster();
}

View File

@@ -38,6 +38,8 @@ public class LeaderProperties {
private static final boolean DEFAULT_PUBLISH_FAILED_EVENTS = false;
private static final boolean DEFAULT_CREATE_CONFIG_MAP = true;
/**
* Should leader election be enabled. Default: true
*/
@@ -79,6 +81,11 @@ public class LeaderProperties {
*/
private boolean publishFailedEvents = DEFAULT_PUBLISH_FAILED_EVENTS;
/**
* Enable/disable creating ConfigMap if it does not exist. Default: true
*/
private boolean createConfigMap = DEFAULT_CREATE_CONFIG_MAP;
public boolean isEnabled() {
return this.enabled;
}
@@ -151,4 +158,12 @@ public class LeaderProperties {
this.publishFailedEvents = publishFailedEvents;
}
public boolean isCreateConfigMap() {
return this.createConfigMap;
}
public void setCreateConfigMap(boolean createConfigMap) {
this.createConfigMap = createConfigMap;
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2013-2022 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.commons.discovery;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource;
/**
* @author wind57
*
* Tests the
* {@link org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer}
* with the fabric8 client.
*
*/
class KubernetesDiscoveryClientHealthIndicatorInitializerTests {
private static ApplicationEventPublisher publisher;
// we don't really need an actual Pod here (fabric8 or k8s-native), but only
// "something" we can assert for.
private static final Object POD = Mockito.mock(Object.class);
@AfterEach
void afterEach() {
Mockito.reset(publisher, POD);
}
@Test
@SuppressWarnings("unchecked")
void testInstanceRegistrationEventPublishedWhenInsideK8s() {
new ApplicationContextRunner()
.withUserConfiguration(InstanceRegistrationEventPublishedInsideK8sConfiguration.class)
.run(context -> assertThat(context).hasSingleBean(PodUtils.class));
ArgumentCaptor<InstanceRegisteredEvent<RegisteredEventSource>> captor = ArgumentCaptor
.forClass(InstanceRegisteredEvent.class);
Mockito.verify(publisher, Mockito.times(1)).publishEvent(captor.capture());
KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource source = (KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource) captor
.getValue().getSource();
assertThat(source.cloudPlatform()).isEqualTo("kubernetes");
assertThat(source.inside()).isTrue();
assertThat(source.pod()).isSameAs(POD);
}
@Test
@SuppressWarnings("unchecked")
void testInstanceRegistrationEventPublishedWhenOutsideK8s() {
new ApplicationContextRunner()
.withUserConfiguration(InstanceRegistrationEventPublishedOutsideK8sConfiguration.class)
.run(context -> assertThat(context).hasSingleBean(PodUtils.class));
ArgumentCaptor<InstanceRegisteredEvent<RegisteredEventSource>> captor = ArgumentCaptor
.forClass(InstanceRegisteredEvent.class);
Mockito.verify(publisher, Mockito.times(1)).publishEvent(captor.capture());
KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource source = (KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource) captor
.getValue().getSource();
assertThat(source.cloudPlatform()).isEqualTo("kubernetes");
assertThat(source.inside()).isFalse();
assertThat(source.pod()).isNotNull();
}
@Configuration
static class InstanceRegistrationEventPublishedInsideK8sConfiguration {
@Bean
@SuppressWarnings("unchecked")
PodUtils<Object> podUtils() {
PodUtils<Object> podUtils = Mockito.mock(PodUtils.class);
Mockito.when(podUtils.isInsideKubernetes()).thenReturn(true);
Mockito.when(podUtils.currentPod()).thenReturn(() -> POD);
return podUtils;
}
@Bean
@Primary
ApplicationEventPublisher publisher() {
publisher = Mockito.mock(ApplicationEventPublisher.class);
return publisher;
}
@Bean
KubernetesDiscoveryClientHealthIndicatorInitializer indicatorInitializer(PodUtils<Object> podUtils,
ApplicationEventPublisher publisher) {
return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, publisher);
}
}
@Configuration
static class InstanceRegistrationEventPublishedOutsideK8sConfiguration {
@Bean
@SuppressWarnings("unchecked")
PodUtils<Object> podUtils() {
PodUtils<Object> podUtils = Mockito.mock(PodUtils.class);
Mockito.when(podUtils.isInsideKubernetes()).thenReturn(false);
Mockito.when(podUtils.currentPod()).thenReturn(() -> POD);
return podUtils;
}
@Bean
@Primary
ApplicationEventPublisher publisher() {
publisher = Mockito.mock(ApplicationEventPublisher.class);
return publisher;
}
@Bean
KubernetesDiscoveryClientHealthIndicatorInitializer indicatorInitializer(PodUtils<Object> podUtils,
ApplicationEventPublisher publisher) {
return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, publisher);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2013-2022 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.commons.discovery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties.Metadata;
/**
* @author wind57
*/
class KubernetesDiscoveryPropertiesMetadataTests {
@Test
void testDefaultConstructor() {
Metadata m = new Metadata();
assertThat(m.addLabels()).isTrue();
assertThat(m.labelsPrefix()).isNull();
assertThat(m.addAnnotations()).isTrue();
assertThat(m.annotationsPrefix()).isNull();
assertThat(m.addPorts()).isTrue();
assertThat(m.portsPrefix()).isEqualTo("port.");
}
@Test
void testSpringBindingFields() {
new ApplicationContextRunner().withUserConfiguration(Config.class)
.withPropertyValues("spring.cloud.kubernetes.discovery.metadata.labelsPrefix=labelsPrefix")
.run(context -> {
KubernetesDiscoveryProperties props = context.getBean(KubernetesDiscoveryProperties.class);
assertThat(props).isNotNull();
assertThat(props.getMetadata().labelsPrefix()).isEqualTo("labelsPrefix");
assertThat(props.getMetadata().addPorts()).isTrue();
assertThat(props.getMetadata().portsPrefix()).isEqualTo("port.");
});
}
@Configuration
@EnableConfigurationProperties(KubernetesDiscoveryProperties.class)
static class Config {
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2013-2022 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.commons.discovery;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
class KubernetesServiceInstanceTests {
@Test
void testFirstConstructor() {
DefaultKubernetesServiceInstance instance = new DefaultKubernetesServiceInstance("instanceId", "serviceId",
"host", 8080, Map.of("k8s_namespace", "spring-k8s"), true);
assertThat(instance.getInstanceId()).isEqualTo("instanceId");
assertThat(instance.getServiceId()).isEqualTo("serviceId");
assertThat(instance.getHost()).isEqualTo("host");
assertThat(instance.getPort()).isEqualTo(8080);
assertThat(instance.isSecure()).isTrue();
assertThat(instance.getUri()).isEqualTo(URI.create("https://host:8080"));
assertThat(instance.getMetadata()).isEqualTo(Map.of("k8s_namespace", "spring-k8s"));
assertThat(instance.getScheme()).isEqualTo("https");
assertThat(instance.getNamespace()).isEqualTo("spring-k8s");
assertThat(instance.getCluster()).isNull();
}
@Test
void testSecondConstructor() {
DefaultKubernetesServiceInstance instance = new DefaultKubernetesServiceInstance("instanceId", "serviceId",
"host", 8080, Map.of("a", "b"), true, "spring-k8s", "cluster");
assertThat(instance.getInstanceId()).isEqualTo("instanceId");
assertThat(instance.getServiceId()).isEqualTo("serviceId");
assertThat(instance.getHost()).isEqualTo("host");
assertThat(instance.getPort()).isEqualTo(8080);
assertThat(instance.isSecure()).isTrue();
assertThat(instance.getUri()).isEqualTo(URI.create("https://host:8080"));
assertThat(instance.getMetadata()).isEqualTo(Map.of("a", "b"));
assertThat(instance.getScheme()).isEqualTo("https");
assertThat(instance.getNamespace()).isEqualTo("spring-k8s");
assertThat(instance.getCluster()).isEqualTo("cluster");
}
@Test
void schemeIsHttp() {
assertServiceInstance(false);
}
@Test
void schemeIsHttps() {
assertServiceInstance(true);
}
private DefaultKubernetesServiceInstance assertServiceInstance(boolean secure) {
DefaultKubernetesServiceInstance instance = new DefaultKubernetesServiceInstance("123", "myservice", "1.2.3.4",
8080, Collections.emptyMap(), secure);
assertThat(instance.getInstanceId()).isEqualTo("123");
assertThat(instance.getServiceId()).isEqualTo("myservice");
assertThat(instance.getHost()).isEqualTo("1.2.3.4");
assertThat(instance.getPort()).isEqualTo(8080);
assertThat(instance.isSecure()).isEqualTo(secure);
assertThat(instance.getScheme()).isEqualTo(secure ? "https" : "http");
return instance;
}
}

View File

@@ -48,7 +48,7 @@ import org.springframework.cloud.kubernetes.client.discovery.reactive.Kubernetes
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.reactive.function.client.WebClient;
@@ -162,7 +162,7 @@ class HttpBasedConfigMapWatchChangeDetectorTests {
V1EndpointPort fooEndpointPort = new V1EndpointPort();
fooEndpointPort.setPort(port);
List<ServiceInstance> instances = new ArrayList<>();
KubernetesServiceInstance fooServiceInstance = new KubernetesServiceInstance("foo", "foo",
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), metadata, false);
instances.add(fooServiceInstance);
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));
@@ -187,7 +187,7 @@ class HttpBasedConfigMapWatchChangeDetectorTests {
fooEndpointPort.setPort(WIRE_MOCK_SERVER.port());
List<ServiceInstance> instances = new ArrayList<>();
KubernetesServiceInstance fooServiceInstance = new KubernetesServiceInstance("foo", "foo",
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), new HashMap<>(), false);
instances.add(fooServiceInstance);
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));

View File

@@ -48,7 +48,7 @@ import org.springframework.cloud.kubernetes.client.discovery.reactive.Kubernetes
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.reactive.function.client.WebClient;
@@ -156,7 +156,7 @@ class HttpBasedSecretsWatchChangeDetectorTests {
V1EndpointPort fooEndpointPort = new V1EndpointPort();
fooEndpointPort.setPort(WIRE_MOCK_SERVER.port());
List<ServiceInstance> instances = new ArrayList<>();
KubernetesServiceInstance fooServiceInstance = new KubernetesServiceInstance("foo", "foo",
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), metadata, false);
instances.add(fooServiceInstance);
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));
@@ -177,7 +177,7 @@ class HttpBasedSecretsWatchChangeDetectorTests {
V1EndpointPort fooEndpointPort = new V1EndpointPort();
fooEndpointPort.setPort(WIRE_MOCK_SERVER.port());
List<ServiceInstance> instances = new ArrayList<>();
KubernetesServiceInstance fooServiceInstance = new KubernetesServiceInstance("foo", "foo",
DefaultKubernetesServiceInstance fooServiceInstance = new DefaultKubernetesServiceInstance("foo", "foo",
fooEndpointAddress.getIp(), fooEndpointPort.getPort(), new HashMap<>(), false);
instances.add(fooServiceInstance);
when(reactiveDiscoveryClient.getInstances(eq("foo"))).thenReturn(Flux.fromIterable(instances));

View File

@@ -74,7 +74,7 @@ public class Fabric8PodUtils implements PodUtils<Pod> {
}
@Override
public Boolean isInsideKubernetes() {
public boolean isInsideKubernetes() {
return currentPod().get() != null;
}

View File

@@ -154,12 +154,7 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>${groovy.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -34,8 +34,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
@@ -147,11 +147,11 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
// Extend the service metadata map with per-endpoint port information (if
// requested)
Map<String, String> endpointMetadata = new HashMap<>(serviceMetadata);
if (metadataProps.isAddPorts()) {
if (metadataProps.addPorts()) {
Map<String, String> ports = s.getPorts().stream()
.filter(port -> StringUtils.hasText(port.getName()))
.collect(toMap(EndpointPort::getName, port -> Integer.toString(port.getPort())));
Map<String, String> portMetadata = getMapWithPrefixedKeys(ports, metadataProps.getPortsPrefix());
Map<String, String> portMetadata = getMapWithPrefixedKeys(ports, metadataProps.portsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding port metadata: " + portMetadata);
}
@@ -178,7 +178,7 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
if (endpointAddress.getTargetRef() != null) {
instanceId = endpointAddress.getTargetRef().getUid();
}
instances.add(new KubernetesServiceInstance(instanceId, serviceId, endpointAddress.getIp(),
instances.add(new DefaultKubernetesServiceInstance(instanceId, serviceId, endpointAddress.getIp(),
endpointPort, endpointMetadata,
this.servicePortSecureResolver.resolve(new ServicePortSecureResolver.Input(endpointPort,
service.getMetadata().getName(), service.getMetadata().getLabels(),
@@ -193,17 +193,17 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
private Map<String, String> getServiceMetadata(Service service) {
final Map<String, String> serviceMetadata = new HashMap<>();
KubernetesDiscoveryProperties.Metadata metadataProps = this.properties.getMetadata();
if (metadataProps.isAddLabels()) {
if (metadataProps.addLabels()) {
Map<String, String> labelMetadata = getMapWithPrefixedKeys(service.getMetadata().getLabels(),
metadataProps.getLabelsPrefix());
metadataProps.labelsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding label metadata: " + labelMetadata);
}
serviceMetadata.putAll(labelMetadata);
}
if (metadataProps.isAddAnnotations()) {
if (metadataProps.addAnnotations()) {
Map<String, String> annotationMetadata = getMapWithPrefixedKeys(service.getMetadata().getAnnotations(),
metadataProps.getAnnotationsPrefix());
metadataProps.annotationsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding annotation metadata: " + annotationMetadata);
}

View File

@@ -54,6 +54,7 @@ import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties.Metadata;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesDiscoveryClientFilterMetadataTest {
@@ -67,9 +68,6 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
@Mock
private ServicePortSecureResolver isServicePortSecureResolver;
@Mock
private KubernetesDiscoveryProperties.Metadata metadata;
@Mock
private MixedOperation<Service, ServiceList, ServiceResource<Service>> serviceOperation;
@@ -92,10 +90,8 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testAllExtraMetadataDisabled() {
final String serviceId = "s";
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
Metadata metadata = new Metadata(false, null, false, null, false, null);
when(this.properties.getMetadata()).thenReturn(metadata);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns", new HashMap<String, String>() {
{
@@ -121,10 +117,8 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testLabelsEnabled() {
final String serviceId = "s";
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(true);
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
Metadata metadata = new Metadata(true, null, false, null, false, null);
when(this.properties.getMetadata()).thenReturn(metadata);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns", new HashMap<String, String>() {
{
@@ -151,11 +145,8 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testLabelsEnabledWithPrefix() {
final String serviceId = "s";
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(true);
when(this.metadata.getLabelsPrefix()).thenReturn("l_");
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
Metadata metadata = new Metadata(true, "l_", false, null, false, null);
when(this.properties.getMetadata()).thenReturn(metadata);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns", new HashMap<String, String>() {
{
@@ -182,10 +173,8 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testAnnotationsEnabled() {
final String serviceId = "s";
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(true);
when(this.metadata.isAddPorts()).thenReturn(false);
Metadata metadata = new Metadata(false, null, true, null, false, null);
when(this.properties.getMetadata()).thenReturn(metadata);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns", new HashMap<String, String>() {
{
@@ -212,11 +201,8 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testAnnotationsEnabledWithPrefix() {
final String serviceId = "s";
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(true);
when(this.metadata.getAnnotationsPrefix()).thenReturn("a_");
when(this.metadata.isAddPorts()).thenReturn(false);
Metadata metadata = new Metadata(false, null, true, "a_", false, null);
when(this.properties.getMetadata()).thenReturn(metadata);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns", new HashMap<String, String>() {
{
@@ -243,10 +229,8 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testPortsEnabled() {
final String serviceId = "s";
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(true);
Metadata metadata = new Metadata(false, null, false, null, true, null);
when(this.properties.getMetadata()).thenReturn(metadata);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns", new HashMap<String, String>() {
{
@@ -273,11 +257,8 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testPortsEnabledWithPrefix() {
final String serviceId = "s";
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(false);
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(true);
when(this.metadata.getPortsPrefix()).thenReturn("p_");
Metadata metadata = new Metadata(false, null, false, null, true, "p_");
when(this.properties.getMetadata()).thenReturn(metadata);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns", new HashMap<String, String>() {
{
@@ -304,13 +285,8 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
public void testLabelsAndAnnotationsAndPortsEnabledWithPrefix() {
final String serviceId = "s";
when(this.properties.getMetadata()).thenReturn(this.metadata);
when(this.metadata.isAddLabels()).thenReturn(true);
when(this.metadata.getLabelsPrefix()).thenReturn("l_");
when(this.metadata.isAddAnnotations()).thenReturn(true);
when(this.metadata.getAnnotationsPrefix()).thenReturn("a_");
when(this.metadata.isAddPorts()).thenReturn(true);
when(this.metadata.getPortsPrefix()).thenReturn("p_");
Metadata metadata = new Metadata(true, "l_", true, "a_", true, "p_");
when(this.properties.getMetadata()).thenReturn(metadata);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns", new HashMap<String, String>() {
{

View File

@@ -40,6 +40,7 @@ import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceI
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties.Metadata;
@ExtendWith(SpringExtension.class)
@EnableKubernetesMockClient(crud = true, https = false)
@@ -81,9 +82,9 @@ public class KubernetesDiscoveryClientTest {
mockClient.services().inNamespace("test").create(service);
final KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
Metadata metadata = new Metadata(false, null, false, null, true, "port.");
properties.setServiceLabels(labels);
properties.getMetadata().setAddLabels(false);
properties.getMetadata().setAddAnnotations(false);
properties.setMetadata(metadata);
final DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(mockClient, properties,
KubernetesClient::services, new ServicePortSecureResolver(properties));
@@ -167,8 +168,8 @@ public class KubernetesDiscoveryClientTest {
final KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
properties.setServiceLabels(labels);
properties.getMetadata().setAddAnnotations(false);
properties.getMetadata().setAddLabels(false);
Metadata metadata = new Metadata(false, null, false, null, true, "port.");
properties.setMetadata(metadata);
final DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(mockClient, properties,
KubernetesClient::services, new ServicePortSecureResolver(properties));

View File

@@ -1,58 +0,0 @@
/*
* 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.fabric8.discovery;
import java.util.Collections;
import io.fabric8.kubernetes.api.model.EndpointAddress;
import io.fabric8.kubernetes.api.model.EndpointPort;
import org.junit.Test;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import static org.assertj.core.api.Assertions.assertThat;
public class KubernetesServiceInstanceTests {
@Test
public void schemeIsHttp() {
assertServiceInstance(false);
}
private KubernetesServiceInstance assertServiceInstance(boolean secure) {
EndpointAddress address = new EndpointAddress();
address.setIp("1.2.3.4");
EndpointPort port = new EndpointPort();
port.setPort(8080);
KubernetesServiceInstance instance = new KubernetesServiceInstance("123", "myservice", address.getIp(),
port.getPort(), Collections.emptyMap(), secure);
assertThat(instance.getInstanceId()).isEqualTo("123");
assertThat(instance.getServiceId()).isEqualTo("myservice");
assertThat(instance.getHost()).isEqualTo("1.2.3.4");
assertThat(instance.getPort()).isEqualTo(8080);
assertThat(instance.isSecure()).isEqualTo(secure);
assertThat(instance.getScheme()).isEqualTo(secure ? "https" : "http");
return instance;
}
@Test
public void schemeIsHttps() {
assertServiceInstance(true);
}
}

View File

@@ -42,6 +42,7 @@ import org.springframework.cloud.kubernetes.fabric8.discovery.support.Kubernetes
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties.Metadata;
/**
* @author Tim Ysewyn
@@ -180,8 +181,8 @@ class KubernetesReactiveDiscoveryClientTests {
.andReturn(200, services.getItems().get(0)).once();
KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
properties.getMetadata().setAddAnnotations(false);
properties.getMetadata().setAddLabels(false);
Metadata metadata = new Metadata(false, null, false, null, true, "port.");
properties.setMetadata(metadata);
ReactiveDiscoveryClient client = new KubernetesReactiveDiscoveryClient(kubernetesClient, properties,
KubernetesClient::services);
Flux<ServiceInstance> instances = client.getInstances("existing-service");
@@ -224,9 +225,8 @@ class KubernetesReactiveDiscoveryClientTests {
.once();
KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
properties.getMetadata().setAnnotationsPrefix("annotation.");
properties.getMetadata().setLabelsPrefix("label.");
properties.getMetadata().setPortsPrefix("port.");
Metadata metadata = new Metadata(true, "label.", true, "annotation.", true, "port.");
properties.setMetadata(metadata);
ReactiveDiscoveryClient client = new KubernetesReactiveDiscoveryClient(kubernetesClient, properties,
KubernetesClient::services);
Flux<ServiceInstance> instances = client.getInstances("existing-service");

View File

@@ -51,6 +51,12 @@ public class Fabric8LeadershipController extends LeadershipController {
public synchronized void update() {
LOGGER.debug("Checking leader state");
ConfigMap configMap = getConfigMap();
if (configMap == null && !leaderProperties.isCreateConfigMap()) {
LOGGER.warn("ConfigMap '{}' does not exist and leaderProperties.isCreateConfigMap() "
+ "is false, cannot acquire leadership", leaderProperties.getConfigMapName());
notifyOnFailedToAcquire();
return;
}
Leader leader = extractLeader(configMap);
if (leader != null && isPodReady(leader.getId())) {
@@ -98,6 +104,7 @@ public class Fabric8LeadershipController extends LeadershipController {
try {
Map<String, String> data = getLeaderData(this.candidate);
if (configMap == null) {
createConfigMap(data);
}

View File

@@ -17,17 +17,28 @@
package org.springframework.cloud.kubernetes.fabric8.leader;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.cloud.kubernetes.commons.leader.LeaderProperties;
import org.springframework.integration.leader.Candidate;
import org.springframework.integration.leader.event.LeaderEventPublisher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Gytis Trikleris
@@ -44,7 +55,7 @@ public class Fabric8LeadershipControllerTest {
@Mock
private LeaderEventPublisher mockLeaderEventPublisher;
@Mock
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private KubernetesClient mockKubernetesClient;
private Fabric8LeadershipController fabric8LeadershipController;
@@ -60,4 +71,40 @@ public class Fabric8LeadershipControllerTest {
assertThat(this.fabric8LeadershipController.getLocalLeader().isPresent()).isFalse();
}
@ExtendWith(OutputCaptureExtension.class)
@Test
void whenNonExistentConfigmapAndCreationNotAllowedStopLeadershipAcquire(CapturedOutput output) {
// given
String testNamespace = "test-namespace";
String testConfigmap = "test-configmap";
Resource mockResource = Mockito.mock(Resource.class);
NonNamespaceOperation mockNonNamespaceOperation = Mockito.mock(NonNamespaceOperation.class);
Fabric8LeadershipController fabric8LeadershipController = new Fabric8LeadershipController(mockCandidate,
mockLeaderProperties, mockLeaderEventPublisher, mockKubernetesClient);
when(mockLeaderProperties.isCreateConfigMap()).thenReturn(false);
when(mockLeaderProperties.isPublishFailedEvents()).thenReturn(true);
when(mockLeaderProperties.getConfigMapName()).thenReturn(testConfigmap);
when(mockKubernetesClient.getNamespace()).thenReturn(testNamespace);
when(mockLeaderProperties.getNamespace(anyString())).thenReturn(testNamespace);
when(mockKubernetesClient.configMaps().inNamespace(anyString())).thenReturn(mockNonNamespaceOperation);
when(mockNonNamespaceOperation.withName(any())).thenReturn(mockResource);
when(mockResource.get()).thenReturn(null);
// when
fabric8LeadershipController.update();
// then
assertThat(output).contains("ConfigMap '" + testConfigmap + "' does not exist "
+ "and leaderProperties.isCreateConfigMap() is false, cannot acquire leadership");
verify(mockLeaderEventPublisher).publishOnFailedToAcquire(any(), any(), any());
verify(mockKubernetesClient, never()).pods();
verify(mockCandidate, never()).getId();
verify(mockLeaderProperties, never()).getLeaderIdPrefix();
verify(mockLeaderEventPublisher, never()).publishOnGranted(any(), any(), any());
verify(mockLeaderEventPublisher, never()).publishOnRevoked(any(), any(), any());
}
}

View File

@@ -26,6 +26,7 @@ import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServicePort;
import io.fabric8.kubernetes.client.utils.Utils;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesLoadBalancerProperties;
@@ -70,21 +71,21 @@ public class Fabric8ServiceInstanceMapper implements KubernetesServiceInstanceMa
service.getMetadata().getNamespace(), properties.getClusterDomain());
final boolean secure = KubernetesServiceInstanceMapper.isSecure(service.getMetadata().getLabels(),
service.getMetadata().getAnnotations(), port.getName(), port.getPort());
return new KubernetesServiceInstance(meta.getUid(), meta.getName(), host, port.getPort(),
return new DefaultKubernetesServiceInstance(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()) {
if (metadataProps.addLabels()) {
Map<String, String> labelMetadata = KubernetesServiceInstanceMapper
.getMapWithPrefixedKeys(service.getMetadata().getLabels(), metadataProps.getLabelsPrefix());
.getMapWithPrefixedKeys(service.getMetadata().getLabels(), metadataProps.labelsPrefix());
serviceMetadata.putAll(labelMetadata);
}
if (metadataProps.isAddAnnotations()) {
Map<String, String> annotationMetadata = KubernetesServiceInstanceMapper.getMapWithPrefixedKeys(
service.getMetadata().getAnnotations(), metadataProps.getAnnotationsPrefix());
if (metadataProps.addAnnotations()) {
Map<String, String> annotationMetadata = KubernetesServiceInstanceMapper
.getMapWithPrefixedKeys(service.getMetadata().getAnnotations(), metadataProps.annotationsPrefix());
serviceMetadata.putAll(annotationMetadata);
}

View File

@@ -33,8 +33,8 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServicesListSupplier;
import org.springframework.core.env.Environment;
@@ -68,7 +68,8 @@ class KubernetesServiceListSupplierTests {
@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(mapper.map(any(Service.class)))
.thenReturn(new DefaultKubernetesServiceInstance("", "", "", 0, null, false));
when(this.client.getNamespace()).thenReturn("test");
when(this.client.services()).thenReturn(this.serviceOperation);
when(this.serviceOperation.inNamespace("test")).thenReturn(namespaceOperation);
@@ -84,7 +85,8 @@ class KubernetesServiceListSupplierTests {
@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(mapper.map(any(Service.class)))
.thenReturn(new DefaultKubernetesServiceInstance("", "", "", 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);

View File

@@ -64,11 +64,22 @@
</configuration>
<executions>
<execution>
<id>build-image</id>
<configuration>
<skip>${skip.build.image}</skip>
</configuration>
<phase>package</phase>
<goals>
<goal>build-image</goal>
</goals>
</execution>
<execution>
<id>repackage</id>
<phase>package</phase>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>

View File

@@ -10,21 +10,24 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-kubernetes-client-all</artifactId>
<name>Spring Cloud Kubernetes :: Kubernetes Native Starter :: All</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-autoconfig</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-loadbalancer</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -10,16 +10,9 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-kubernetes-client-config</artifactId>
<name>Spring Cloud Kubernetes :: Kubernetes Native Starter :: Config</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-autoconfig</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-config</artifactId>

View File

@@ -10,20 +10,13 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-kubernetes-client-loadbalancer</artifactId>
<name>Spring Cloud Kubernetes :: Kubernetes Native Starter :: LoadBalancer</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-autoconfig</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>

View File

@@ -1,21 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 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.
~
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -27,13 +10,9 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-kubernetes-fabric8-all</artifactId>
<name>Spring Cloud Kubernetes :: Starter :: All</name>
<name>Spring Cloud Kubernetes :: Fabric8 Starter :: All</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-fabric8-autoconfig</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>

View File

@@ -1,21 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 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.
~
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -27,18 +10,9 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId>
<name>Spring Cloud Kubernetes :: Starter :: Config</name>
<name>Spring Cloud Kubernetes :: Fabric8 Starter :: Config</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-fabric8-autoconfig</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-fabric8-config</artifactId>

View File

@@ -10,21 +10,13 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-kubernetes-fabric8-loadbalancer</artifactId>
<name>Spring Cloud Kubernetes :: Starter :: LoadBalancer</name>
<name>Spring Cloud Kubernetes :: Fabric8 Starter :: LoadBalancer</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-fabric8-autoconfig</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-fabric8-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
</project>