Merge branch 'yue9944882-informer-based-discovery-client'

This commit is contained in:
Ryan Baxter
2020-12-01 13:55:55 -05:00
79 changed files with 1290 additions and 127 deletions

View File

@@ -93,7 +93,8 @@
<module>spring-cloud-kubernetes-client-config</module>
<module>spring-cloud-kubernetes-fabric8-autoconfig</module>
<module>spring-cloud-kubernetes-fabric8-config</module>
<module>spring-cloud-kubernetes-discovery</module>
<module>spring-cloud-kubernetes-fabric8-discovery</module>
<module>spring-cloud-kubernetes-client-discovery</module>
<module>spring-cloud-starter-kubernetes</module>
<module>spring-cloud-starter-kubernetes-config</module>
<module>spring-cloud-starter-kubernetes-all</module>

View File

@@ -24,6 +24,10 @@
<groupId>io.kubernetes</groupId>
<artifactId>client-java-extended</artifactId>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java-spring-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>

View File

@@ -46,10 +46,16 @@ public class KubernetesClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CoreV1Api coreApi() throws IOException {
public ApiClient apiClient() throws IOException {
ApiClient apiClient = kubernetesApiClient();
io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient);
return new CoreV1Api();
return apiClient;
}
@Bean
@ConditionalOnMissingBean
public CoreV1Api coreApi(ApiClient apiClient) throws IOException {
return new CoreV1Api(apiClient);
}
@Bean

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.kubernetes.client.profile;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.junit.jupiter.api.Test;
@@ -40,6 +41,9 @@ class KubernetesClientProfileEnvironmentPostProcessorNoProfileTests {
@MockBean
CoreV1Api coreV1Api;
@MockBean
ApiClient apiClient;
@Test
void whenNoKubernetesEnvironmentAndNoApiAccessThenNoProfileEnabled() {

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.kubernetes.client.profile;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.junit.jupiter.api.Test;
@@ -43,6 +44,9 @@ class KubernetesClientProfileEnvironmentPostProcessorTests {
@MockBean
CoreV1Api coreV1Api;
@MockBean
ApiClient apiClient;
@Test
void whenKubernetesEnvironmentAndNoApiAccessThenProfileEnabled() {
assertThat(environment.getActiveProfiles()).contains(KUBERNETES_PROFILE);

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-kubernetes</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-kubernetes-client-discovery</artifactId>
<name>Spring Cloud Kubernetes :: Kubernetes Client Discovery</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-autoconfig</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>${spring-cloud-commons.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<!-- Testing Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2019-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.client.discovery;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ConditionalOnProperty(value = "spring.cloud.kubernetes.discovery.enabled", matchIfMissing = true)
public @interface ConditionalOnKubernetesDiscoveryEnabled {
}

View File

@@ -0,0 +1,101 @@
/*
* 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.client.discovery;
import io.kubernetes.client.informer.SharedInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.informer.cache.Lister;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.models.V1Endpoints;
import io.kubernetes.client.openapi.models.V1EndpointsList;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServiceList;
import io.kubernetes.client.spring.extended.controller.KubernetesInformerFactoryProcessor;
import io.kubernetes.client.spring.extended.controller.annotation.GroupVersionResource;
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesInformer;
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesInformers;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.CommonsClientAutoConfiguration;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.cloud.kubernetes.client.discovery.gson.EndpointsTrimmingStrategy;
import org.springframework.cloud.kubernetes.client.discovery.gson.ServiceTrimmingStrategy;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@ConditionalOnKubernetesDiscoveryEnabled
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class })
@EnableConfigurationProperties(KubernetesDiscoveryProperties.class)
public class KubernetesDiscoveryClientAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnBlockingDiscoveryEnabled
public static class KubernetesInformerDiscoveryConfiguration {
@Bean
@ConditionalOnMissingBean
public KubernetesInformerFactoryProcessor kubernetesInformerFactoryProcessor() {
return new KubernetesInformerFactoryProcessor();
}
@Bean
@ConditionalOnMissingBean
public CatalogSharedInformerFactory catalogSharedInformerFactory(ApiClient apiClient) {
apiClient.getJSON()
.setGson(apiClient.getJSON().getGson().newBuilder()
.addDeserializationExclusionStrategy(new ServiceTrimmingStrategy())
.addDeserializationExclusionStrategy(new EndpointsTrimmingStrategy()).create());
return new CatalogSharedInformerFactory();
}
@Bean
@ConditionalOnMissingBean
public KubernetesInformerDiscoveryClient kubernetesInformerDiscoveryClient(
KubernetesClientProperties kubernetesClientProperties,
CatalogSharedInformerFactory sharedInformerFactory, Lister<V1Service> serviceLister,
Lister<V1Endpoints> endpointsLister, SharedInformer<V1Service> serviceInformer,
SharedInformer<V1Endpoints> endpointsInformer, KubernetesDiscoveryProperties properties) {
return new KubernetesInformerDiscoveryClient(kubernetesClientProperties.getNamespace(),
sharedInformerFactory, serviceLister, endpointsLister, serviceInformer, endpointsInformer,
properties);
}
@KubernetesInformers({
@KubernetesInformer(apiTypeClass = V1Service.class, apiListTypeClass = V1ServiceList.class,
groupVersionResource = @GroupVersionResource(apiGroup = "", apiVersion = "v1",
resourcePlural = "services")),
@KubernetesInformer(apiTypeClass = V1Endpoints.class, apiListTypeClass = V1EndpointsList.class,
groupVersionResource = @GroupVersionResource(apiGroup = "", apiVersion = "v1",
resourcePlural = "endpoints")) })
class CatalogSharedInformerFactory extends SharedInformerFactory {
// TODO: optimization to ease memory pressure from continuous list&watch.
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.client.discovery;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty("spring.cloud.config.discovery.enabled")
@Import({ KubernetesClientAutoConfiguration.class, KubernetesDiscoveryClientAutoConfiguration.class })
public class KubernetesDiscoveryClientConfigClientBootstrapConfiguration {
}

View File

@@ -0,0 +1,163 @@
/*
* 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.client.discovery;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import io.kubernetes.client.extended.wait.Wait;
import io.kubernetes.client.informer.SharedInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.informer.cache.Lister;
import io.kubernetes.client.openapi.models.V1EndpointPort;
import io.kubernetes.client.openapi.models.V1Endpoints;
import io.kubernetes.client.openapi.models.V1Service;
import org.apache.commons.logging.Log;
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.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.util.Assert;
public class KubernetesInformerDiscoveryClient implements DiscoveryClient, InitializingBean {
private static final Log log = LogFactory.getLog(KubernetesInformerDiscoveryClient.class);
private final SharedInformerFactory sharedInformerFactory;
private final Lister<V1Service> serviceLister;
private final Supplier<Boolean> informersReadyFunc;
private final Lister<V1Endpoints> endpointsLister;
private final KubernetesDiscoveryProperties properties;
private final String namespace;
public KubernetesInformerDiscoveryClient(String namespace, SharedInformerFactory sharedInformerFactory,
Lister<V1Service> serviceLister, Lister<V1Endpoints> endpointsLister,
SharedInformer<V1Service> serviceInformer, SharedInformer<V1Endpoints> endpointsInformer,
KubernetesDiscoveryProperties properties) {
this.namespace = namespace;
this.sharedInformerFactory = sharedInformerFactory;
this.serviceLister = serviceLister;
this.endpointsLister = endpointsLister;
this.informersReadyFunc = () -> serviceInformer.hasSynced() && endpointsInformer.hasSynced();
this.properties = properties;
}
@Override
public String description() {
return "Kubernetes Client Discovery";
}
@Override
public List<ServiceInstance> getInstances(String serviceId) {
Assert.notNull(serviceId, "[Assertion failed] - the object argument must not be null");
V1Service service = properties.isAllNamespaces() ? this.serviceLister.list().stream()
.filter(svc -> serviceId.equals(svc.getMetadata().getName())).findFirst().orElse(null)
: this.serviceLister.namespace(this.namespace).get(serviceId);
if (service == null) {
// no such service present in the cluster
return new ArrayList<>();
}
Map<String, String> svcMetadata = new HashMap<>();
if (this.properties.getMetadata() != null) {
if (this.properties.getMetadata().isAddLabels()) {
if (service.getMetadata().getLabels() != null) {
String labelPrefix = this.properties.getMetadata().getLabelsPrefix() != null
? this.properties.getMetadata().getLabelsPrefix() : "";
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 (service.getMetadata().getAnnotations() != null) {
String annotationPrefix = this.properties.getMetadata().getAnnotationsPrefix() != null
? this.properties.getMetadata().getAnnotationsPrefix() : "";
service.getMetadata().getAnnotations().entrySet().stream()
.filter(e -> e.getKey().startsWith(annotationPrefix))
.forEach(e -> svcMetadata.put(e.getKey(), e.getValue()));
}
}
}
V1Endpoints ep = this.endpointsLister.namespace(service.getMetadata().getNamespace())
.get(service.getMetadata().getName());
if (ep == null) {
// no available endpoints in the cluster
return new ArrayList<>();
}
return ep.getSubsets().stream().flatMap(subset -> {
Map<String, String> metadata = new HashMap<>(svcMetadata);
if (this.properties.getMetadata() != null && this.properties.getMetadata().isAddPorts()) {
subset.getPorts().stream().forEach(p -> metadata.put(p.getName(), Integer.toString(p.getPort())));
}
V1EndpointPort port = subset.getPorts() != null && subset.getPorts().size() == 1 ? subset.getPorts().get(0)
: subset.getPorts().stream()
.filter(p -> this.properties.getPrimaryPortName().equalsIgnoreCase(p.getName())).findFirst()
.orElseThrow(IllegalStateException::new);
return subset.getAddresses().stream()
.map(addr -> new KubernetesServiceInstance(
addr.getTargetRef() != null ? addr.getTargetRef().getUid() : "", serviceId, addr.getIp(),
port.getPort(), metadata, false));
}).collect(Collectors.toList());
}
@Override
public List<String> getServices() {
List<V1Service> services = this.properties.isAllNamespaces() ? this.serviceLister.list()
: this.serviceLister.namespace(this.namespace).list();
return services.stream().map(s -> s.getMetadata().getName()).collect(Collectors.toList());
}
@Override
public void afterPropertiesSet() throws Exception {
this.sharedInformerFactory.startAllRegisteredInformers();
if (!Wait.poll(Duration.ofSeconds(1), Duration.ofSeconds(this.properties.getCacheLoadingTimeoutSeconds()),
() -> {
log.info("Waiting for the cache of informers to be fully loaded..");
return this.informersReadyFunc.get();
})) {
if (this.properties.isWaitCacheReady()) {
throw new IllegalStateException(
"Timeout waiting for informers cache to be ready, is the kubernetes service up?");
}
else {
log.warn(
"Timeout waiting for informers cache to be ready, ignoring the failure because waitForInformerCacheReady property is false");
}
}
log.info("Cache fully loaded (total " + serviceLister.list().size()
+ " services) , discovery client is now available");
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.client.discovery.gson;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
public class EndpointsTrimmingStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
// trimming field-managers
if (V1ObjectMeta.class.equals(fieldAttributes.getDeclaringClass())) {
return "managedFields".equals(fieldAttributes.getName());
}
return false;
}
@Override
public boolean shouldSkipClass(Class<?> aClass) {
return false;
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.client.discovery.gson;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1ServiceSpec;
import io.kubernetes.client.openapi.models.V1ServiceStatus;
public class ServiceTrimmingStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
// trimming field-managers
if (V1ObjectMeta.class.equals(fieldAttributes.getDeclaringClass())) {
return "managedFields".equals(fieldAttributes.getName());
}
return false;
}
@Override
public boolean shouldSkipClass(Class<?> aClass) {
if (V1ServiceSpec.class.equals(aClass)) {
return true;
}
return V1ServiceStatus.class.equals(aClass);
}
}

View File

@@ -0,0 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientConfigClientBootstrapConfiguration

View File

@@ -0,0 +1,68 @@
/*
* 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.client.discovery;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.JSON;
import okhttp3.OkHttpClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "spring.cloud.kubernetes.discovery.cacheLoadingTimeoutSeconds=5",
"spring.cloud.kubernetes.discovery.waitCacheReady=false" })
public class KubernetesDiscoveryClientAutoConfigurationTests {
@Autowired(required = false)
private DiscoveryClient discoveryClient;
@Test
public void kubernetesDiscoveryClientCreated() {
assertThat(this.discoveryClient).isNotNull().isInstanceOf(CompositeDiscoveryClient.class);
CompositeDiscoveryClient composite = (CompositeDiscoveryClient) this.discoveryClient;
assertThat(composite.getDiscoveryClients().stream()
.anyMatch(dc -> dc instanceof KubernetesInformerDiscoveryClient)).isTrue();
}
@SpringBootApplication
protected static class TestConfig {
@Bean
public ApiClient apiClient() {
ApiClient apiClient = mock(ApiClient.class);
when(apiClient.getJSON()).thenReturn(new JSON());
when(apiClient.getHttpClient()).thenReturn(new OkHttpClient.Builder().build());
return apiClient;
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.client.discovery;
import java.util.Collections;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.JSON;
import okhttp3.OkHttpClient;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.config.client.ConfigClientProperties;
import org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty("spring.cloud.config.discovery.enabled")
@Import({ KubernetesClientAutoConfiguration.class, KubernetesDiscoveryClientAutoConfiguration.class })
public class KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests {
private AnnotationConfigApplicationContext context;
@After
public void close() {
if (this.context != null) {
if (this.context.getParent() != null) {
((AnnotationConfigApplicationContext) this.context.getParent()).close();
}
this.context.close();
}
}
@Test
public void onWhenRequested() throws Exception {
setup("server.port=7000", "spring.cloud.config.discovery.enabled=true",
"spring.cloud.kubernetes.discovery.enabled:true", "spring.cloud.kubernetes.enabled:true",
"spring.application.name:test", "spring.cloud.config.discovery.service-id:configserver");
assertEquals(1, this.context.getParent().getBeanNamesForType(DiscoveryClient.class).length);
DiscoveryClient client = this.context.getParent().getBean(DiscoveryClient.class);
verify(client, atLeast(2)).getInstances("configserver");
ConfigClientProperties locator = this.context.getBean(ConfigClientProperties.class);
assertEquals("http://fake:8888/", locator.getUri()[0]);
}
private void setup(String... env) {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
TestPropertyValues.of(env).applyTo(parent);
parent.register(UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
EnvironmentKnobbler.class, KubernetesCommonsAutoConfiguration.class,
KubernetesClientAutoConfiguration.class, KubernetesDiscoveryClientAutoConfiguration.class,
DiscoveryClientConfigServiceBootstrapConfiguration.class, ConfigClientProperties.class);
parent.refresh();
this.context = new AnnotationConfigApplicationContext();
this.context.setParent(parent);
this.context.register(PropertyPlaceholderAutoConfiguration.class, KubernetesCommonsAutoConfiguration.class,
KubernetesDiscoveryClientAutoConfiguration.class);
this.context.refresh();
}
@Configuration(proxyBeanMethods = false)
protected static class EnvironmentKnobbler {
@Bean
public ApiClient apiClient() {
ApiClient apiClient = mock(ApiClient.class);
when(apiClient.getJSON()).thenReturn(new JSON());
when(apiClient.getHttpClient()).thenReturn(new OkHttpClient.Builder().build());
return apiClient;
}
@Bean
public KubernetesInformerDiscoveryClient kubernetesInformerDiscoveryClient() {
KubernetesInformerDiscoveryClient client = mock(KubernetesInformerDiscoveryClient.class);
ServiceInstance instance = new DefaultServiceInstance("configserver1", "configserver", "fake", 8888, false);
given(client.getInstances("configserver")).willReturn(Collections.singletonList(instance));
return client;
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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.client.discovery;
import java.util.HashMap;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.informer.cache.Cache;
import io.kubernetes.client.informer.cache.Lister;
import io.kubernetes.client.openapi.models.V1EndpointAddress;
import io.kubernetes.client.openapi.models.V1EndpointPort;
import io.kubernetes.client.openapi.models.V1EndpointSubset;
import io.kubernetes.client.openapi.models.V1Endpoints;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServiceSpec;
import io.kubernetes.client.openapi.models.V1ServiceStatus;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
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;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesInformerDiscoveryClientTests {
@Mock
private SharedInformerFactory sharedInformerFactory;
@Mock
private KubernetesDiscoveryProperties kubernetesDiscoveryProperties;
private static final V1Service testService1 = new V1Service()
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace1"))
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus());
private static final V1Service testService2 = new V1Service()
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace2"))
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus());
private static final V1Endpoints testEndpoints1 = new V1Endpoints()
.metadata(new V1ObjectMeta().name("test-svc-1").namespace("namespace1"))
.addSubsetsItem(new V1EndpointSubset().addPortsItem(new V1EndpointPort().port(8080))
.addAddressesItem(new V1EndpointAddress().ip("2.2.2.2")));
@Test
public void testDiscoveryGetServicesAllNamespaceShouldWork() {
Lister<V1Service> serviceLister = setupServiceLister(testService1, testService2);
when(kubernetesDiscoveryProperties.isAllNamespaces()).thenReturn(true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("",
sharedInformerFactory, serviceLister, null, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getServices().toArray()).containsOnly(testService1.getMetadata().getName(),
testService2.getMetadata().getName());
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
}
@Test
public void testDiscoveryGetServicesOneNamespaceShouldWork() {
Lister<V1Service> serviceLister = setupServiceLister(testService1, testService2);
when(kubernetesDiscoveryProperties.isAllNamespaces()).thenReturn(false);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, null, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getServices().toArray()).containsOnly(testService1.getMetadata().getName());
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
}
@Test
public void testDiscoveryGetInstanceAllNamespaceShouldWork() {
Lister<V1Service> serviceLister = setupServiceLister(testService1, testService2);
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpoints1);
when(kubernetesDiscoveryProperties.isAllNamespaces()).thenReturn(true);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1"))
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
}
@Test
public void testDiscoveryGetInstanceOneNamespaceShouldWork() {
Lister<V1Service> serviceLister = setupServiceLister(testService1, testService2);
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(testEndpoints1);
when(kubernetesDiscoveryProperties.isAllNamespaces()).thenReturn(false);
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient("namespace1",
sharedInformerFactory, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
assertThat(discoveryClient.getInstances("test-svc-1"))
.containsOnly(new KubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080, new HashMap<>(), false));
verify(kubernetesDiscoveryProperties, times(1)).isAllNamespaces();
}
private Lister<V1Service> setupServiceLister(V1Service... services) {
Cache<V1Service> serviceCache = new Cache<>();
Lister<V1Service> serviceLister = new Lister<>(serviceCache);
for (V1Service svc : services) {
serviceCache.add(svc);
}
return serviceLister;
}
private Lister<V1Endpoints> setupEndpointsLister(V1Endpoints... endpoints) {
Cache<V1Endpoints> endpointsCache = new Cache<>();
Lister<V1Endpoints> endpointsLister = new Lister<>(endpointsCache);
for (V1Endpoints ep : endpoints) {
endpointsCache.add(ep);
}
return endpointsLister;
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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

@@ -0,0 +1,45 @@
/*
* 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.client.discovery.gson;
import java.util.Arrays;
import com.google.gson.Gson;
import io.kubernetes.client.openapi.models.V1Endpoints;
import io.kubernetes.client.openapi.models.V1ManagedFieldsEntry;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import org.junit.Assert;
import org.junit.Test;
public class EndpointsTrimmingStrategyTests {
@Test
public void testDeserializingEndpoints() {
Gson gson = new Gson().newBuilder().addDeserializationExclusionStrategy(new EndpointsTrimmingStrategy())
.create();
V1Endpoints input = new V1Endpoints()
.metadata(new V1ObjectMeta().name("foo").managedFields(Arrays.asList(new V1ManagedFieldsEntry())));
String data = gson.toJson(input);
V1Endpoints output = gson.fromJson(data, V1Endpoints.class);
// managed-fields should be excluded
Assert.assertNull(output.getMetadata().getManagedFields());
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.client.discovery.gson;
import java.util.Arrays;
import com.google.gson.Gson;
import io.kubernetes.client.openapi.models.V1LoadBalancerIngress;
import io.kubernetes.client.openapi.models.V1LoadBalancerStatus;
import io.kubernetes.client.openapi.models.V1ManagedFieldsEntry;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServiceSpec;
import io.kubernetes.client.openapi.models.V1ServiceStatus;
import org.junit.Assert;
import org.junit.Test;
public class ServiceTrimmingStrategyTests {
@Test
public void testDeserializingService() {
Gson gson = new Gson().newBuilder().addDeserializationExclusionStrategy(new ServiceTrimmingStrategy()).create();
V1Service input = new V1Service()
.metadata(new V1ObjectMeta().name("foo").managedFields(Arrays.asList(new V1ManagedFieldsEntry())))
.spec(new V1ServiceSpec().loadBalancerIP("1.1.1.1")).status(new V1ServiceStatus().loadBalancer(
new V1LoadBalancerStatus().addIngressItem(new V1LoadBalancerIngress().ip("2.2.2.2"))));
String data = gson.toJson(input);
V1Service output = gson.fromJson(data, V1Service.class);
// spec should be excluded
Assert.assertNull(output.getSpec());
// status should be excluded
Assert.assertNull(output.getStatus());
// managed-fields should be excluded
Assert.assertNull(output.getMetadata().getManagedFields());
}
}

View File

@@ -16,6 +16,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.commons.discovery;
import java.util.HashMap;
import java.util.HashSet;
@@ -23,14 +23,10 @@ import java.util.Set;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.core.style.ToStringCreator;
/**
* Kubernetes discovery properties.
*
* @author Ioannis Canellos
*/
import static org.springframework.cloud.client.discovery.DiscoveryClient.DEFAULT_ORDER;
@ConfigurationProperties("spring.cloud.kubernetes.discovery")
public class KubernetesDiscoveryProperties {
@@ -44,6 +40,17 @@ public class KubernetesDiscoveryProperties {
/** If discovering all namespaces. */
private boolean allNamespaces = false;
/*
* If wait for the discovery cache (service and endpoints) to be fully loaded,
* otherwise aborts the application on starting.
*/
private boolean waitCacheReady = true;
/**
* Timeout for initializing discovery cache, will abort the application if exceeded.
**/
private long cacheLoadingTimeoutSeconds = 60;
/**
* SpEL expression to filter services AFTER they have been retrieved from the
* Kubernetes API server.
@@ -72,7 +79,7 @@ public class KubernetesDiscoveryProperties {
private Metadata metadata = new Metadata();
private int order = DiscoveryClient.DEFAULT_ORDER;
private int order = DEFAULT_ORDER;
public boolean isEnabled() {
return this.enabled;
@@ -146,6 +153,22 @@ public class KubernetesDiscoveryProperties {
this.order = order;
}
public boolean isWaitCacheReady() {
return waitCacheReady;
}
public void setWaitCacheReady(boolean waitCacheReady) {
this.waitCacheReady = waitCacheReady;
}
public long getCacheLoadingTimeoutSeconds() {
return cacheLoadingTimeoutSeconds;
}
public void setCacheLoadingTimeoutSeconds(long cacheLoadingTimeoutSeconds) {
this.cacheLoadingTimeoutSeconds = cacheLoadingTimeoutSeconds;
}
@Override
public String toString() {
return new ToStringCreator(this).append("enabled", this.enabled).append("serviceName", this.serviceName)

View File

@@ -14,18 +14,14 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
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;
/**
* Kubernetes {@link ServiceInstance}.
*
* @author Ioannis Canellos
*/
public class KubernetesServiceInstance implements ServiceInstance {
/**
@@ -114,13 +110,31 @@ public class KubernetesServiceInstance implements ServiceInstance {
}
private URI createUri(String scheme, String host, int port) {
StringBuilder sb = new StringBuilder();
sb.append(scheme).append(COLON).append(DSL).append(host).append(COLON).append(port);
return URI.create(sb.toString());
return URI.create(scheme + COLON + DSL + host + COLON + port);
}
public String getNamespace() {
return this.metadata != null ? this.metadata.get(NAMESPACE_METADATA_KEY) : null;
}
@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);
}
@Override
public int hashCode() {
return Objects.hash(instanceId, serviceId, host, port, uri, secure, metadata);
}
}

View File

@@ -26,9 +26,9 @@ import org.springframework.cloud.bus.BusProperties;
import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

View File

@@ -29,8 +29,8 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

View File

@@ -27,8 +27,8 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

View File

@@ -40,9 +40,9 @@ import reactor.test.StepVerifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.reactive.function.client.WebClient;

View File

@@ -40,9 +40,9 @@ import reactor.test.StepVerifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.reactive.function.client.WebClient;

View File

@@ -61,6 +61,11 @@
<artifactId>client-java-extended</artifactId>
<version>${kubernetes-java-client.version}</version>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java-spring-integration</artifactId>
<version>${kubernetes-java-client.version}</version>
</dependency>
<dependency>
<groupId>me.snowdrop</groupId>
@@ -89,7 +94,13 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
<artifactId>spring-cloud-kubernetes-client-discovery</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-fabric8-discovery</artifactId>
<version>${project.version}</version>
</dependency>

View File

@@ -1,6 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.discovery.KubernetesCatalogWatchAutoConfiguration, \
org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientAutoConfiguration, \
org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClientAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientConfigClientBootstrapConfiguration

View File

@@ -37,7 +37,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
<artifactId>spring-cloud-kubernetes-fabric8-discovery</artifactId>
<version>${project.version}</version>
</dependency>

View File

@@ -26,7 +26,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
<artifactId>spring-cloud-kubernetes-fabric8-discovery</artifactId>
<name>Spring Cloud Kubernetes :: Discovery</name>
<dependencies>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.HashMap;
import java.util.HashSet;
@@ -24,6 +24,8 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
/**
* TODO break up into delegates if the implementation get's more complicated
* <p>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.ArrayList;
import java.util.List;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.Collection;
import java.util.List;
@@ -31,6 +31,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.scheduling.annotation.Scheduled;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -22,6 +22,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.function.Function;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.ArrayList;
import java.util.HashMap;
@@ -34,6 +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.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;
@@ -41,7 +43,7 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static java.util.stream.Collectors.toMap;
import static org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance.NAMESPACE_METADATA_KEY;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance.NAMESPACE_METADATA_KEY;
/**
* Kubeneretes implementation of {@link DiscoveryClient}.

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -26,9 +26,10 @@ import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration;
import org.springframework.cloud.kubernetes.registry.KubernetesRegistration;
import org.springframework.cloud.kubernetes.registry.KubernetesServiceRegistry;
import org.springframework.cloud.kubernetes.fabric8.registry.KubernetesRegistration;
import org.springframework.cloud.kubernetes.fabric8.registry.KubernetesServiceRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery.reactive;
package org.springframework.cloud.kubernetes.fabric8.discovery.reactive;
import io.fabric8.kubernetes.client.KubernetesClient;
import reactor.core.publisher.Flux;
@@ -22,9 +22,9 @@ import reactor.core.scheduler.Schedulers;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.discovery.KubernetesClientServicesFunction;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClient;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesClientServicesFunction;
import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesDiscoveryClient;
import org.springframework.util.Assert;
/**

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery.reactive;
package org.springframework.cloud.kubernetes.fabric8.discovery.reactive;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -31,10 +31,10 @@ import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIn
import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator;
import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled;
import org.springframework.cloud.kubernetes.discovery.ConditionalOnKubernetesDiscoveryEnabled;
import org.springframework.cloud.kubernetes.discovery.KubernetesClientServicesFunction;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.fabric8.discovery.ConditionalOnKubernetesDiscoveryEnabled;
import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesClientServicesFunction;
import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesDiscoveryClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.registry;
package org.springframework.cloud.kubernetes.fabric8.registry;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.registry;
package org.springframework.cloud.kubernetes.fabric8.registry;
import java.io.Closeable;
import java.io.IOException;
@@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
/**
* Kubernetes implementation of a {@link Registration}.

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.registry;
package org.springframework.cloud.kubernetes.fabric8.registry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

View File

@@ -0,0 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration, \
org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesDiscoveryClientAutoConfiguration, \
org.springframework.cloud.kubernetes.fabric8.discovery.reactive.KubernetesReactiveDiscoveryClientAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesDiscoveryClientConfigClientBootstrapConfiguration

View File

@@ -14,12 +14,14 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.HashMap;
import org.junit.Test;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import static org.assertj.core.api.Assertions.assertThat;
public class DefaultIsServicePortSecureResolverTest {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.After;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.Arrays;
import java.util.Collections;
@@ -40,6 +40,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.context.ApplicationEventPublisher;
import static java.util.Arrays.stream;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.After;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.Collections;
import java.util.HashMap;
@@ -49,6 +49,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.ArrayList;
import java.util.Arrays;
@@ -33,6 +33,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.ArrayList;
import java.util.HashMap;
@@ -37,6 +37,8 @@ import org.junit.Test;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery;
package org.springframework.cloud.kubernetes.fabric8.discovery;
import java.util.Collections;
@@ -22,6 +22,8 @@ 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 {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery.reactive;
package org.springframework.cloud.kubernetes.fabric8.discovery.reactive;
import org.junit.jupiter.api.Test;
@@ -26,8 +26,8 @@ import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration;
import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesDiscoveryClientAutoConfiguration;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery.reactive;
package org.springframework.cloud.kubernetes.fabric8.discovery.reactive;
import java.util.ArrayList;
import java.util.HashMap;
@@ -37,10 +37,8 @@ import reactor.test.StepVerifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.discovery.support.KubernetesExtension;
import org.springframework.cloud.kubernetes.discovery.support.KubernetesExtension.Client;
import org.springframework.cloud.kubernetes.discovery.support.KubernetesExtension.Server;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.fabric8.discovery.support.KubernetesExtension;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
@@ -52,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class KubernetesReactiveDiscoveryClientTests {
@BeforeEach
public void setup(@Client KubernetesClient kubernetesClient) {
public void setup(@KubernetesExtension.Client KubernetesClient kubernetesClient) {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY,
kubernetesClient.getConfiguration().getMasterUrl());
@@ -63,7 +61,7 @@ class KubernetesReactiveDiscoveryClientTests {
}
@Test
public void verifyDefaults(@Client KubernetesClient kubernetesClient) {
public void verifyDefaults(@KubernetesExtension.Client KubernetesClient kubernetesClient) {
KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
ReactiveDiscoveryClient client = new KubernetesReactiveDiscoveryClient(kubernetesClient, properties,
KubernetesClient::services);
@@ -72,8 +70,8 @@ class KubernetesReactiveDiscoveryClientTests {
}
@Test
public void shouldReturnFluxOfServices(@Client KubernetesClient kubernetesClient,
@Server KubernetesServer kubernetesServer) {
public void shouldReturnFluxOfServices(@KubernetesExtension.Client KubernetesClient kubernetesClient,
@KubernetesExtension.Server KubernetesServer kubernetesServer) {
kubernetesServer.expect().get().withPath("/api/v1/namespaces/test/services")
.andReturn(200, new ServiceListBuilder().addNewItem().withNewMetadata().withName("s1")
.withLabels(new HashMap<String, String>() {
@@ -98,8 +96,9 @@ class KubernetesReactiveDiscoveryClientTests {
}
@Test
public void shouldReturnEmptyFluxOfServicesWhenNoInstancesFound(@Client KubernetesClient kubernetesClient,
@Server KubernetesServer kubernetesServer) {
public void shouldReturnEmptyFluxOfServicesWhenNoInstancesFound(
@KubernetesExtension.Client KubernetesClient kubernetesClient,
@KubernetesExtension.Server KubernetesServer kubernetesServer) {
kubernetesServer.expect().get().withPath("/api/v1/namespaces/test/services")
.andReturn(200, new ServiceListBuilder().build()).once();
@@ -111,8 +110,9 @@ class KubernetesReactiveDiscoveryClientTests {
}
@Test
public void shouldReturnEmptyFluxForNonExistingService(@Client KubernetesClient kubernetesClient,
@Server KubernetesServer kubernetesServer) {
public void shouldReturnEmptyFluxForNonExistingService(
@KubernetesExtension.Client KubernetesClient kubernetesClient,
@KubernetesExtension.Server KubernetesServer kubernetesServer) {
kubernetesServer.expect().get()
.withPath("/api/v1/namespaces/test/endpoints?fieldSelector=metadata.name%3Dnonexistent-service")
.andReturn(200, new EndpointsBuilder().build()).once();
@@ -125,8 +125,9 @@ class KubernetesReactiveDiscoveryClientTests {
}
@Test
public void shouldReturnEmptyFluxWhenServiceHasNoSubsets(@Client KubernetesClient kubernetesClient,
@Server KubernetesServer kubernetesServer) {
public void shouldReturnEmptyFluxWhenServiceHasNoSubsets(
@KubernetesExtension.Client KubernetesClient kubernetesClient,
@KubernetesExtension.Server KubernetesServer kubernetesServer) {
kubernetesServer.expect().get().withPath("/api/v1/namespaces/test/services")
.andReturn(200, new ServiceListBuilder().addNewItem().withNewMetadata().withName("existing-service")
.withLabels(new HashMap<String, String>() {
@@ -148,7 +149,8 @@ class KubernetesReactiveDiscoveryClientTests {
}
@Test
public void shouldReturnFlux(@Client KubernetesClient kubernetesClient, @Server KubernetesServer kubernetesServer) {
public void shouldReturnFlux(@KubernetesExtension.Client KubernetesClient kubernetesClient,
@KubernetesExtension.Server KubernetesServer kubernetesServer) {
ServiceList services = new ServiceListBuilder().addNewItem().withNewMetadata().withName("existing-service")
.withNamespace("test").withLabels(new HashMap<String, String>() {
{
@@ -187,8 +189,8 @@ class KubernetesReactiveDiscoveryClientTests {
}
@Test
public void shouldReturnFluxWithPrefixedMetadata(@Client KubernetesClient kubernetesClient,
@Server KubernetesServer kubernetesServer) {
public void shouldReturnFluxWithPrefixedMetadata(@KubernetesExtension.Client KubernetesClient kubernetesClient,
@KubernetesExtension.Server KubernetesServer kubernetesServer) {
kubernetesServer.expect().get().withPath("/api/v1/namespaces/test/services")
.andReturn(200, new ServiceListBuilder().addNewItem().withNewMetadata().withName("existing-service")
.withLabels(new HashMap<String, String>() {
@@ -233,7 +235,8 @@ class KubernetesReactiveDiscoveryClientTests {
@Test
public void shouldReturnFluxWhenServiceHasMultiplePortsAndPrimaryPortNameIsSet(
@Client KubernetesClient kubernetesClient, @Server KubernetesServer kubernetesServer) {
@KubernetesExtension.Client KubernetesClient kubernetesClient,
@KubernetesExtension.Server KubernetesServer kubernetesServer) {
kubernetesServer.expect().get().withPath("/api/v1/namespaces/test/services")
.andReturn(200, new ServiceListBuilder().addNewItem().withNewMetadata().withName("existing-service")
.withLabels(new HashMap<String, String>() {
@@ -276,8 +279,9 @@ class KubernetesReactiveDiscoveryClientTests {
}
@Test
public void shouldReturnFluxOfServicesAcrossAllNamespaces(@Client KubernetesClient kubernetesClient,
@Server KubernetesServer kubernetesServer) {
public void shouldReturnFluxOfServicesAcrossAllNamespaces(
@KubernetesExtension.Client KubernetesClient kubernetesClient,
@KubernetesExtension.Server KubernetesServer kubernetesServer) {
kubernetesServer.expect().get().withPath("/api/v1/namespaces/test/services")
.andReturn(200, new ServiceListBuilder().addNewItem().withNewMetadata().withName("existing-service")
.withLabels(new HashMap<String, String>() {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.discovery.support;
package org.springframework.cloud.kubernetes.fabric8.discovery.support;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -24,7 +24,7 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
<artifactId>spring-cloud-kubernetes-fabric8-discovery</artifactId>
</dependency>
</dependencies>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>discovery-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>kubernetes-client-discovery</artifactId>
<name>Spring Cloud Kubernetes :: Integration Tests :: Kubernetes Client Discovery</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-discovery</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,10 @@
# We need this fragment in order for the kubernetes client to talk to
# the Kubernetes API without caring about proper certificates
spec:
template:
spec:
containers:
- env:
- name: KUBERNETES_TRUST_CERTIFICATES
value: true

View File

@@ -0,0 +1,15 @@
# we are using an FMP fragment to ensure that NodePort is used correctly
kind: Service
apiVersion: v1
metadata:
name: ${project.artifactId}
labels:
app: ${project.artifactId}
spec:
selector:
app: ${project.artifactId}
ports:
- protocol: TCP
port: 8080
nodePort: ${nodeport.value}
type: NodePort

View File

@@ -0,0 +1,51 @@
/*
* 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.it;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class DiscoveryClientApplication {
@Autowired
private DiscoveryClient discoveryClient;
public static void main(String[] args) {
SpringApplication.run(DiscoveryClientApplication.class, args);
}
@GetMapping("/services")
public List<String> services() {
return this.discoveryClient.getServices();
}
@GetMapping("/services/{service}/instances")
public List<ServiceInstance> instances(@PathVariable("service") String service) {
return this.discoveryClient.getInstances(service);
}
}

View File

@@ -18,6 +18,7 @@
<module>discovery-service-a</module>
<module>discovery-service-b</module>
<module>discovery-client</module>
<module>kubernetes-client-discovery</module>
<module>tests</module>
</modules>

View File

@@ -9,22 +9,23 @@ items:
app: integration-test
name: spring-cloud-kubernetes-serviceaccount
- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
kind: ClusterRoleBinding
metadata:
labels:
app: spring-cloud-kubernetes-core-k8s-client-it
name: spring-cloud-kubernetes-core-k8s-client-it:view
roleRef:
kind: Role
kind: ClusterRole
apiGroup: rbac.authorization.k8s.io
name: namespace-reader
subjects:
- kind: ServiceAccount
name: spring-cloud-kubernetes-serviceaccount
namespace: default
- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
kind: ClusterRole
metadata:
namespace: default
# namespace: default
name: namespace-reader
rules:
- apiGroups: ["", "extensions", "apps"]

View File

@@ -105,9 +105,9 @@ public class ActuatorEndpointIT {
k8SUtils = new K8SUtils(api, appsApi);
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withRegistryUrl(KIND_REPO_URL).build();
.withRegistryUrl(KIND_REPO_URL).build();
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder().dockerHost(config.getDockerHost())
.sslConfig(config.getSSLConfig()).build();
.sslConfig(config.getSSLConfig()).build();
DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
dockerClient.tagImageCmd(LOCAL_IMAGE, KIND_IMAGE, IMAGE_TAG).exec();
@@ -127,19 +127,19 @@ public class ActuatorEndpointIT {
private static V1Deployment getCoreK8sClientItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) k8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-deployment.yaml");
.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-deployment.yaml");
return deployment;
}
private static V1Service getCoreK8sClientItService() throws Exception {
V1Service service = (V1Service) k8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-service.yaml");
.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-service.yaml");
return service;
}
private static NetworkingV1beta1Ingress getCoreK8sClientItIngress() throws Exception {
NetworkingV1beta1Ingress ingress = (NetworkingV1beta1Ingress) k8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-ingress.yaml");
.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-ingress.yaml");
return ingress;
}
@@ -163,13 +163,14 @@ public class ActuatorEndpointIT {
}
});
//Sometimes the NGINX ingress takes a bit to catch up and realize the service is available and we get a 503, we just need to wait a bit
// Sometimes the NGINX ingress takes a bit to catch up and realize the service is
// available and we get a 503, we just need to wait a bit
await().timeout(Duration.ofSeconds(60))
.until(() -> rest.getForEntity("http://localhost:80/core-k8s-client-it/actuator/health", String.class)
.getStatusCode().is2xxSuccessful());
.until(() -> rest.getForEntity("http://localhost:80/core-k8s-client-it/actuator/health", String.class)
.getStatusCode().is2xxSuccessful());
Map<String, Object> health = rest.getForObject("http://localhost:80/core-k8s-client-it/actuator/health",
Map.class);
Map.class);
Map<String, Object> components = (Map) health.get("components");
assertThat(components.containsKey("kubernetes")).isTrue();
Map<String, Object> kubernetes = (Map) components.get("kubernetes");
@@ -189,7 +190,6 @@ public class ActuatorEndpointIT {
public void testInfo() {
RestTemplate rest = new RestTemplateBuilder().build();
rest.setErrorHandler(new ResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
@@ -206,10 +206,11 @@ public class ActuatorEndpointIT {
}
});
//Sometimes the NGINX ingress takes a bit to catch up and realize the service is available and we get a 503, we just need to wait a bit
// Sometimes the NGINX ingress takes a bit to catch up and realize the service is
// available and we get a 503, we just need to wait a bit
await().timeout(Duration.ofSeconds(60))
.until(() -> rest.getForEntity("http://localhost:80/core-k8s-client-it/actuator/info", String.class)
.getStatusCode().is2xxSuccessful());
.until(() -> rest.getForEntity("http://localhost:80/core-k8s-client-it/actuator/info", String.class)
.getStatusCode().is2xxSuccessful());
Map<String, Object> info = rest.getForObject("http://localhost:80/core-k8s-client-it/actuator/info", Map.class);
Map<String, Object> kubernetes = (Map) info.get("kubernetes");
@@ -225,7 +226,7 @@ public class ActuatorEndpointIT {
@AfterClass
public static void after() throws Exception {
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + K8S_CONFIG_CLIENT_IT_NAME, null, null, null, null, null, null, null, null);
"metadata.name=" + K8S_CONFIG_CLIENT_IT_NAME, null, null, null, null, null, null, null, null);
api.deleteNamespacedService(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, NAMESPACE, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
}

View File

@@ -19,7 +19,7 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
<artifactId>spring-cloud-kubernetes-fabric8-discovery</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>

View File

@@ -19,7 +19,7 @@ package org.springframework.cloud.kubernetes.loadbalancer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -19,7 +19,7 @@ package org.springframework.cloud.kubernetes.loadbalancer;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;

View File

@@ -27,8 +27,8 @@ import io.fabric8.kubernetes.api.model.ServicePort;
import io.fabric8.kubernetes.client.utils.Utils;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
/**
* Class for mapping Kubernetes Service object into {@link KubernetesServiceInstance}.

View File

@@ -25,7 +25,7 @@ import org.apache.commons.lang.StringUtils;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.core.env.Environment;

View File

@@ -20,7 +20,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -29,8 +29,8 @@ import io.fabric8.kubernetes.api.model.ServicePortBuilder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
class KubernetesServiceInstanceMapperTests {

View File

@@ -36,8 +36,8 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
import org.springframework.core.env.Environment;
import static org.mockito.ArgumentMatchers.any;

View File

@@ -43,7 +43,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
<artifactId>spring-cloud-kubernetes-fabric8-discovery</artifactId>
</dependency>
<dependency>

View File

@@ -20,6 +20,10 @@
<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>
</dependencies>

View File

@@ -42,7 +42,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
<artifactId>spring-cloud-kubernetes-fabric8-discovery</artifactId>
</dependency>
</dependencies>