Add support for selective namespaces (#1274)
This commit is contained in:
@@ -19,6 +19,7 @@ package org.springframework.cloud.kubernetes.client;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1Pod;
|
||||
import io.kubernetes.client.util.Config;
|
||||
@@ -82,10 +83,14 @@ public class KubernetesClientPodUtils implements PodUtils<V1Pod> {
|
||||
private V1Pod internalGetPod() {
|
||||
try {
|
||||
if (isServiceHostEnvVarPresent() && isHostNameEnvVarPresent() && isServiceAccountFound()) {
|
||||
LOG.debug("reading pod in namespace : " + namespace);
|
||||
return client.readNamespacedPod(hostName, namespace, null);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (t instanceof ApiException apiException) {
|
||||
LOG.warn("error reading pod, with error : " + apiException.getResponseBody());
|
||||
}
|
||||
LOG.warn("Failed to get pod with name:[" + hostName + "]. You should look into this if things aren't"
|
||||
+ " working as you expect. Are you missing serviceaccount permissions?", t);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.Set;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.ConfigurationCondition;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* Conditional that checks if our discovery is _not_ based on selective namespaces, i.e.:
|
||||
* 'spring.cloud.kubernetes.discovery.namespaces' is not set.
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
public final class ConditionalOnSelectiveNamespacesMissing implements ConfigurationCondition {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(
|
||||
LogFactory.getLog(ConditionalOnSelectiveNamespacesMissing.class));
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
Set<String> selectiveNamespaces = Binder.get(context.getEnvironment())
|
||||
.bind("spring.cloud.kubernetes.discovery.namespaces", Bindable.setOf(String.class)).orElse(Set.of());
|
||||
boolean selectiveNamespacesMissing = selectiveNamespaces.isEmpty();
|
||||
if (selectiveNamespacesMissing) {
|
||||
LOG.debug(() -> "selective namespaces not present");
|
||||
}
|
||||
else {
|
||||
LOG.debug(() -> "found selective namespaces : " + selectiveNamespaces.stream().sorted().toList());
|
||||
}
|
||||
return selectiveNamespacesMissing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationPhase getConfigurationPhase() {
|
||||
return ConfigurationPhase.REGISTER_BEAN;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.Set;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.ConfigurationCondition;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* Conditional that checks if our discovery is based on selective namespaces, i.e.:
|
||||
* 'spring.cloud.kubernetes.discovery.namespaces' is set.
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
public final class ConditionalOnSelectiveNamespacesPresent implements ConfigurationCondition {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(
|
||||
LogFactory.getLog(ConditionalOnSelectiveNamespacesPresent.class));
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
Set<String> selectiveNamespaces = Binder.get(context.getEnvironment())
|
||||
.bind("spring.cloud.kubernetes.discovery.namespaces", Bindable.setOf(String.class)).orElse(Set.of());
|
||||
boolean selectiveNamespacesPresent = !selectiveNamespaces.isEmpty();
|
||||
if (selectiveNamespacesPresent) {
|
||||
LOG.debug(() -> "found selective namespaces : " + selectiveNamespaces.stream().sorted().toList());
|
||||
}
|
||||
else {
|
||||
LOG.debug(() -> "selective namespaces not present");
|
||||
}
|
||||
return selectiveNamespacesPresent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationCondition.ConfigurationPhase getConfigurationPhase() {
|
||||
return ConfigurationPhase.REGISTER_BEAN;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKuber
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
@@ -57,6 +58,7 @@ import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.
|
||||
@ConditionalOnKubernetesDiscoveryEnabled
|
||||
@ConditionalOnBlockingOrReactiveEnabled
|
||||
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
|
||||
@Conditional(ConditionalOnSelectiveNamespacesMissing.class)
|
||||
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class })
|
||||
public class KubernetesClientInformerAutoConfiguration {
|
||||
@@ -67,6 +69,7 @@ public class KubernetesClientInformerAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SharedInformerFactory sharedInformerFactory(ApiClient client) {
|
||||
LOG.debug(() -> "registering sharedInformerFactory for non-selective namespaces");
|
||||
return new SharedInformerFactory(client);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.kubernetes.client.informer.SharedIndexInformer;
|
||||
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.util.generic.GenericKubernetesApi;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.cloud.client.CommonsClientAutoConfiguration;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesDiscoveryEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
/**
|
||||
* Auto-configuration to be used when "spring.cloud.kubernetes.discovery.namespaces" is
|
||||
* defined.
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnDiscoveryEnabled
|
||||
@ConditionalOnKubernetesDiscoveryEnabled
|
||||
@ConditionalOnBlockingOrReactiveEnabled
|
||||
@Conditional(ConditionalOnSelectiveNamespacesPresent.class)
|
||||
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
|
||||
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class })
|
||||
public class KubernetesClientInformerSelectiveNamespacesAutoConfiguration {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(
|
||||
LogFactory.getLog(KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class));
|
||||
|
||||
// we rely on the order of namespaces to enable listers, as such provide a bean of
|
||||
// namespaces
|
||||
// as a list, instead of the incoming Set.
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public List<String> selectiveNamespaces(KubernetesDiscoveryProperties properties) {
|
||||
List<String> selectiveNamespaces = properties.namespaces().stream().sorted().toList();
|
||||
LOG.debug(() -> "using selective namespaces : " + selectiveNamespaces);
|
||||
return selectiveNamespaces;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = SharedInformerFactory.class, parameterizedContainer = List.class)
|
||||
public List<SharedInformerFactory> sharedInformerFactories(ApiClient apiClient, List<String> selectiveNamespaces) {
|
||||
|
||||
int howManyNamespaces = selectiveNamespaces.size();
|
||||
List<SharedInformerFactory> sharedInformerFactories = new ArrayList<>(howManyNamespaces);
|
||||
for (int i = 0; i < howManyNamespaces; ++i) {
|
||||
sharedInformerFactories.add(new SharedInformerFactory(apiClient));
|
||||
}
|
||||
return sharedInformerFactories;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = V1Service.class,
|
||||
parameterizedContainer = { List.class, SharedIndexInformer.class })
|
||||
public List<SharedIndexInformer<V1Service>> serviceSharedIndexInformers(
|
||||
List<SharedInformerFactory> sharedInformerFactories, List<String> selectiveNamespaces,
|
||||
ApiClient apiClient) {
|
||||
|
||||
int howManyNamespaces = selectiveNamespaces.size();
|
||||
List<SharedIndexInformer<V1Service>> serviceSharedIndexedInformers = new ArrayList<>(howManyNamespaces);
|
||||
for (int i = 0; i < howManyNamespaces; ++i) {
|
||||
GenericKubernetesApi<V1Service, V1ServiceList> servicesApi = new GenericKubernetesApi<>(V1Service.class,
|
||||
V1ServiceList.class, "", "v1", "services", apiClient);
|
||||
SharedIndexInformer<V1Service> sharedIndexInformer = sharedInformerFactories.get(i)
|
||||
.sharedIndexInformerFor(servicesApi, V1Service.class, 0L, selectiveNamespaces.get(i));
|
||||
serviceSharedIndexedInformers.add(sharedIndexInformer);
|
||||
}
|
||||
return serviceSharedIndexedInformers;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = V1Service.class, parameterizedContainer = { List.class, Lister.class })
|
||||
public List<Lister<V1Service>> serviceListers(List<String> selectiveNamespaces,
|
||||
List<SharedIndexInformer<V1Service>> serviceSharedIndexInformers) {
|
||||
|
||||
int howManyNamespaces = selectiveNamespaces.size();
|
||||
List<Lister<V1Service>> serviceListers = new ArrayList<>(howManyNamespaces);
|
||||
|
||||
for (int i = 0; i < howManyNamespaces; ++i) {
|
||||
String namespace = selectiveNamespaces.get(i);
|
||||
Lister<V1Service> lister = new Lister<>(serviceSharedIndexInformers.get(i).getIndexer(), namespace);
|
||||
LOG.debug(() -> "registering lister (for services) in namespace : " + namespace);
|
||||
serviceListers.add(lister);
|
||||
}
|
||||
|
||||
return serviceListers;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = V1Endpoints.class,
|
||||
parameterizedContainer = { List.class, SharedIndexInformer.class })
|
||||
public List<SharedIndexInformer<V1Endpoints>> endpointsSharedIndexInformers(
|
||||
List<SharedInformerFactory> sharedInformerFactories, List<String> selectiveNamespaces,
|
||||
ApiClient apiClient) {
|
||||
|
||||
int howManyNamespaces = selectiveNamespaces.size();
|
||||
List<SharedIndexInformer<V1Endpoints>> endpointsSharedIndexedInformers = new ArrayList<>(howManyNamespaces);
|
||||
for (int i = 0; i < howManyNamespaces; ++i) {
|
||||
GenericKubernetesApi<V1Endpoints, V1EndpointsList> endpointsApi = new GenericKubernetesApi<>(
|
||||
V1Endpoints.class, V1EndpointsList.class, "", "v1", "endpoints", apiClient);
|
||||
SharedIndexInformer<V1Endpoints> sharedIndexInformer = sharedInformerFactories.get(i)
|
||||
.sharedIndexInformerFor(endpointsApi, V1Endpoints.class, 0L, selectiveNamespaces.get(i));
|
||||
endpointsSharedIndexedInformers.add(sharedIndexInformer);
|
||||
}
|
||||
return endpointsSharedIndexedInformers;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = V1Endpoints.class, parameterizedContainer = { List.class, Lister.class })
|
||||
public List<Lister<V1Endpoints>> endpointsListers(List<String> selectiveNamespaces,
|
||||
List<SharedIndexInformer<V1Endpoints>> serviceSharedIndexInformers) {
|
||||
|
||||
int howManyNamespaces = selectiveNamespaces.size();
|
||||
List<Lister<V1Endpoints>> endpointsListers = new ArrayList<>(howManyNamespaces);
|
||||
|
||||
for (int i = 0; i < howManyNamespaces; ++i) {
|
||||
String namespace = selectiveNamespaces.get(i);
|
||||
Lister<V1Endpoints> lister = new Lister<>(serviceSharedIndexInformers.get(i).getIndexer());
|
||||
LOG.debug(() -> "registering lister (for endpoints) in namespace : " + namespace);
|
||||
endpointsListers.add(lister);
|
||||
}
|
||||
|
||||
return endpointsListers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,6 +41,7 @@ import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKuber
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
@@ -59,6 +60,7 @@ import static io.kubernetes.client.util.Namespaces.NAMESPACE_DEFAULT;
|
||||
@ConditionalOnKubernetesDiscoveryEnabled
|
||||
@ConditionalOnBlockingOrReactiveEnabled
|
||||
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
|
||||
@Conditional(ConditionalOnSelectiveNamespacesMissing.class)
|
||||
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class })
|
||||
public class KubernetesInformerAutoConfiguration {
|
||||
|
||||
@@ -63,14 +63,14 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesInformerDiscoveryClient.class));
|
||||
|
||||
private final SharedInformerFactory sharedInformerFactory;
|
||||
private final List<SharedInformerFactory> sharedInformerFactories;
|
||||
|
||||
private final Lister<V1Service> serviceLister;
|
||||
private final List<Lister<V1Service>> serviceListers;
|
||||
|
||||
private final List<Lister<V1Endpoints>> endpointsListers;
|
||||
|
||||
private final Supplier<Boolean> informersReadyFunc;
|
||||
|
||||
private final Lister<V1Endpoints> endpointsLister;
|
||||
|
||||
private final KubernetesDiscoveryProperties properties;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
@@ -78,9 +78,9 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
|
||||
Lister<V1Service> serviceLister, Lister<V1Endpoints> endpointsLister,
|
||||
SharedInformer<V1Service> serviceInformer, SharedInformer<V1Endpoints> endpointsInformer,
|
||||
KubernetesDiscoveryProperties properties) {
|
||||
this.sharedInformerFactory = sharedInformerFactory;
|
||||
this.serviceLister = serviceLister;
|
||||
this.endpointsLister = endpointsLister;
|
||||
this.sharedInformerFactories = List.of(sharedInformerFactory);
|
||||
this.serviceListers = List.of(serviceLister);
|
||||
this.endpointsListers = List.of(endpointsLister);
|
||||
this.informersReadyFunc = () -> serviceInformer.hasSynced() && endpointsInformer.hasSynced();
|
||||
this.properties = properties;
|
||||
}
|
||||
@@ -89,13 +89,32 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
|
||||
Lister<V1Service> serviceLister, Lister<V1Endpoints> endpointsLister,
|
||||
SharedInformer<V1Service> serviceInformer, SharedInformer<V1Endpoints> endpointsInformer,
|
||||
KubernetesDiscoveryProperties properties) {
|
||||
this.sharedInformerFactory = sharedInformerFactory;
|
||||
this.serviceLister = serviceLister;
|
||||
this.endpointsLister = endpointsLister;
|
||||
this.sharedInformerFactories = List.of(sharedInformerFactory);
|
||||
this.serviceListers = List.of(serviceLister);
|
||||
this.endpointsListers = List.of(endpointsLister);
|
||||
this.informersReadyFunc = () -> serviceInformer.hasSynced() && endpointsInformer.hasSynced();
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public KubernetesInformerDiscoveryClient(List<SharedInformerFactory> sharedInformerFactories,
|
||||
List<Lister<V1Service>> serviceListers, List<Lister<V1Endpoints>> endpointsListers,
|
||||
List<SharedInformer<V1Service>> serviceInformers, List<SharedInformer<V1Endpoints>> endpointsInformers,
|
||||
KubernetesDiscoveryProperties properties) {
|
||||
this.sharedInformerFactories = sharedInformerFactories;
|
||||
|
||||
this.serviceListers = serviceListers;
|
||||
this.endpointsListers = endpointsListers;
|
||||
this.informersReadyFunc = () -> {
|
||||
boolean serviceInformersReady = serviceInformers.isEmpty() || serviceInformers.stream()
|
||||
.map(SharedInformer::hasSynced).reduce(Boolean::logicalAnd).orElse(false);
|
||||
boolean endpointsInformersReady = endpointsInformers.isEmpty() || endpointsInformers.stream()
|
||||
.map(SharedInformer::hasSynced).reduce(Boolean::logicalAnd).orElse(false);
|
||||
return serviceInformersReady && endpointsInformersReady;
|
||||
};
|
||||
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Kubernetes Client Discovery";
|
||||
@@ -105,11 +124,11 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
|
||||
public List<ServiceInstance> getInstances(String serviceId) {
|
||||
Objects.requireNonNull(serviceId, "serviceId must be provided");
|
||||
|
||||
List<V1Service> services = serviceLister.list().stream()
|
||||
.filter(svc -> serviceId.equals(svc.getMetadata().getName())).toList();
|
||||
if (services.size() == 0 || !services.stream().anyMatch(service -> matchesServiceLabels(service, properties))) {
|
||||
// no such service present in the cluster
|
||||
return new ArrayList<>();
|
||||
List<V1Service> services = serviceListers.stream().flatMap(x -> x.list().stream())
|
||||
.filter(scv -> scv.getMetadata() != null).filter(svc -> serviceId.equals(svc.getMetadata().getName()))
|
||||
.toList();
|
||||
if (services.size() == 0 || services.stream().noneMatch(service -> matchesServiceLabels(service, properties))) {
|
||||
return List.of();
|
||||
}
|
||||
return services.stream().flatMap(s -> getServiceInstanceDetails(s, serviceId)).toList();
|
||||
}
|
||||
@@ -117,12 +136,10 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
|
||||
private Stream<ServiceInstance> getServiceInstanceDetails(V1Service service, String serviceId) {
|
||||
Map<String, String> serviceMetadata = serviceMetadata(properties, service, serviceId);
|
||||
|
||||
V1Endpoints ep = endpointsLister.namespace(service.getMetadata().getNamespace())
|
||||
.get(service.getMetadata().getName());
|
||||
if (ep == null || ep.getSubsets() == null) {
|
||||
// no available endpoints in the cluster
|
||||
return Stream.empty();
|
||||
}
|
||||
List<V1Endpoints> endpoints = endpointsListers.stream()
|
||||
.map(endpointsLister -> endpointsLister.namespace(service.getMetadata().getNamespace())
|
||||
.get(service.getMetadata().getName()))
|
||||
.filter(Objects::nonNull).filter(ep -> ep.getSubsets() != null).toList();
|
||||
|
||||
Optional<String> discoveredPrimaryPortName = Optional.empty();
|
||||
if (service.getMetadata() != null && service.getMetadata().getLabels() != null) {
|
||||
@@ -133,33 +150,36 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
final boolean secured = isSecured(service);
|
||||
|
||||
return ep.getSubsets().stream().filter(subset -> subset.getPorts() != null && subset.getPorts().size() > 0) // safeguard
|
||||
.flatMap(subset -> {
|
||||
Map<String, String> metadata = new HashMap<>(serviceMetadata);
|
||||
List<CoreV1EndpointPort> endpointPorts = subset.getPorts();
|
||||
if (properties.metadata() != null && properties.metadata().addPorts()) {
|
||||
endpointPorts.forEach(
|
||||
p -> metadata.put(StringUtils.hasText(p.getName()) ? p.getName() : UNSET_PORT_NAME,
|
||||
return endpoints.stream()
|
||||
.flatMap(ep -> ep.getSubsets().stream()
|
||||
.filter(subset -> subset.getPorts() != null && subset.getPorts().size() > 0) // safeguard
|
||||
.flatMap(subset -> {
|
||||
Map<String, String> metadata = new HashMap<>(serviceMetadata);
|
||||
List<CoreV1EndpointPort> endpointPorts = subset.getPorts();
|
||||
if (properties.metadata() != null && properties.metadata().addPorts()) {
|
||||
endpointPorts.forEach(p -> metadata.put(
|
||||
StringUtils.hasText(p.getName()) ? p.getName() : UNSET_PORT_NAME,
|
||||
Integer.toString(p.getPort())));
|
||||
}
|
||||
List<V1EndpointAddress> addresses = subset.getAddresses();
|
||||
if (addresses == null) {
|
||||
addresses = new ArrayList<>();
|
||||
}
|
||||
if (properties.includeNotReadyAddresses()
|
||||
&& !CollectionUtils.isEmpty(subset.getNotReadyAddresses())) {
|
||||
addresses.addAll(subset.getNotReadyAddresses());
|
||||
}
|
||||
}
|
||||
List<V1EndpointAddress> addresses = subset.getAddresses();
|
||||
if (addresses == null) {
|
||||
addresses = new ArrayList<>();
|
||||
}
|
||||
if (properties.includeNotReadyAddresses()
|
||||
&& !CollectionUtils.isEmpty(subset.getNotReadyAddresses())) {
|
||||
addresses.addAll(subset.getNotReadyAddresses());
|
||||
}
|
||||
|
||||
final int port = findEndpointPort(endpointPorts, primaryPortName, serviceId);
|
||||
return addresses.stream()
|
||||
.map(addr -> new DefaultKubernetesServiceInstance(
|
||||
addr.getTargetRef() != null ? addr.getTargetRef().getUid() : "", serviceId,
|
||||
addr.getIp(), port, metadata, secured, service.getMetadata().getNamespace(),
|
||||
// TODO find out how to get cluster name possibly from
|
||||
// KubeConfig
|
||||
null));
|
||||
});
|
||||
final int port = findEndpointPort(endpointPorts, primaryPortName, serviceId);
|
||||
return addresses.stream()
|
||||
.map(addr -> new DefaultKubernetesServiceInstance(
|
||||
addr.getTargetRef() != null ? addr.getTargetRef().getUid() : "", serviceId,
|
||||
addr.getIp(), port, metadata, secured, service.getMetadata().getNamespace(),
|
||||
// TODO find out how to get cluster name
|
||||
// possibly from
|
||||
// KubeConfig
|
||||
null));
|
||||
}));
|
||||
}
|
||||
|
||||
private static boolean isSecured(V1Service service) {
|
||||
@@ -209,17 +229,16 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
@Override
|
||||
public List<String> getServices() {
|
||||
List<String> services = serviceLister.list().stream()
|
||||
List<String> services = serviceListers.stream().flatMap(serviceLister -> serviceLister.list().stream())
|
||||
.filter(service -> matchesServiceLabels(service, properties)).map(s -> s.getMetadata().getName())
|
||||
.distinct().toList();
|
||||
|
||||
LOG.debug(() -> "will return services : " + services);
|
||||
return services;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
sharedInformerFactory.startAllRegisteredInformers();
|
||||
sharedInformerFactories.forEach(SharedInformerFactory::startAllRegisteredInformers);
|
||||
if (!Wait.poll(Duration.ofSeconds(1), Duration.ofSeconds(properties.cacheLoadingTimeoutSeconds()), () -> {
|
||||
LOG.info(() -> "Waiting for the cache of informers to be fully loaded..");
|
||||
return informersReadyFunc.get();
|
||||
@@ -233,8 +252,8 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
|
||||
() -> "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");
|
||||
LOG.info(() -> "Cache fully loaded (total " + serviceListers.stream().mapToLong(x -> x.list().size()).sum()
|
||||
+ " services), discovery client is now available");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.client.discovery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.kubernetes.client.informer.SharedInformer;
|
||||
import io.kubernetes.client.informer.SharedInformerFactory;
|
||||
import io.kubernetes.client.informer.cache.Lister;
|
||||
@@ -44,6 +46,7 @@ import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscover
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
@@ -57,7 +60,8 @@ import org.springframework.core.log.LogAccessor;
|
||||
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
|
||||
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class,
|
||||
KubernetesClientInformerAutoConfiguration.class })
|
||||
KubernetesClientInformerAutoConfiguration.class,
|
||||
KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class })
|
||||
public class KubernetesInformerDiscoveryClientAutoConfiguration {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(
|
||||
@@ -90,7 +94,8 @@ public class KubernetesInformerDiscoveryClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public KubernetesInformerDiscoveryClient kubernetesClientInformerDiscoveryClient(
|
||||
@Conditional(ConditionalOnSelectiveNamespacesMissing.class)
|
||||
KubernetesInformerDiscoveryClient kubernetesClientInformerDiscoveryClient(
|
||||
SharedInformerFactory sharedInformerFactory, Lister<V1Service> serviceLister,
|
||||
Lister<V1Endpoints> endpointsLister, SharedInformer<V1Service> serviceInformer,
|
||||
SharedInformer<V1Endpoints> endpointsInformer, KubernetesDiscoveryProperties properties) {
|
||||
@@ -98,4 +103,15 @@ public class KubernetesInformerDiscoveryClientAutoConfiguration {
|
||||
serviceInformer, endpointsInformer, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Conditional(ConditionalOnSelectiveNamespacesPresent.class)
|
||||
KubernetesInformerDiscoveryClient selectiveNamespacesKubernetesInformerDiscoveryClient(
|
||||
List<SharedInformerFactory> sharedInformerFactories, List<Lister<V1Service>> serviceListers,
|
||||
List<Lister<V1Endpoints>> endpointsListers, List<SharedInformer<V1Service>> serviceInformers,
|
||||
List<SharedInformer<V1Endpoints>> endpointsInformers, KubernetesDiscoveryProperties properties) {
|
||||
return new KubernetesInformerDiscoveryClient(sharedInformerFactories, serviceListers, endpointsListers,
|
||||
serviceInformers, endpointsInformers, properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
* Copyright 2019-2023 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.
|
||||
@@ -49,6 +49,8 @@ public class KubernetesInformerReactiveDiscoveryClient implements ReactiveDiscov
|
||||
serviceInformer, endpointsInformer, properties);
|
||||
}
|
||||
|
||||
// this is either kubernetesClientInformerDiscoveryClient
|
||||
// or selectiveNamespacesKubernetesClientInformerDiscoveryClient
|
||||
KubernetesInformerReactiveDiscoveryClient(KubernetesInformerDiscoveryClient kubernetesDiscoveryClient) {
|
||||
this.kubernetesDiscoveryClient = kubernetesDiscoveryClient;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.client.discovery.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.kubernetes.client.informer.SharedInformer;
|
||||
import io.kubernetes.client.informer.SharedInformerFactory;
|
||||
import io.kubernetes.client.informer.cache.Lister;
|
||||
@@ -39,7 +41,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.client.KubernetesClientPodUtils;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.ConditionalOnSelectiveNamespacesMissing;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.ConditionalOnSelectiveNamespacesPresent;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerSelectiveNamespacesAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.KubernetesInformerDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.PodUtils;
|
||||
@@ -49,6 +54,7 @@ import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscover
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
@@ -66,7 +72,8 @@ import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesD
|
||||
@AutoConfigureBefore({ SimpleReactiveDiscoveryClientAutoConfiguration.class,
|
||||
ReactiveCommonsClientAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ ReactiveCompositeDiscoveryClientAutoConfiguration.class,
|
||||
KubernetesDiscoveryPropertiesAutoConfiguration.class, KubernetesClientInformerAutoConfiguration.class })
|
||||
KubernetesDiscoveryPropertiesAutoConfiguration.class, KubernetesClientInformerAutoConfiguration.class,
|
||||
KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class })
|
||||
public class KubernetesInformerReactiveDiscoveryClientAutoConfiguration {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(
|
||||
@@ -127,6 +134,7 @@ public class KubernetesInformerReactiveDiscoveryClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Conditional(ConditionalOnSelectiveNamespacesMissing.class)
|
||||
KubernetesInformerDiscoveryClient kubernetesClientInformerDiscoveryClient(
|
||||
SharedInformerFactory sharedInformerFactory, Lister<V1Service> serviceLister,
|
||||
Lister<V1Endpoints> endpointsLister, SharedInformer<V1Service> serviceInformer,
|
||||
@@ -135,4 +143,15 @@ public class KubernetesInformerReactiveDiscoveryClientAutoConfiguration {
|
||||
serviceInformer, endpointsInformer, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Conditional(ConditionalOnSelectiveNamespacesPresent.class)
|
||||
KubernetesInformerDiscoveryClient selectiveNamespacesKubernetesClientInformerDiscoveryClient(
|
||||
List<SharedInformerFactory> sharedInformerFactories, List<Lister<V1Service>> serviceListers,
|
||||
List<Lister<V1Endpoints>> endpointsListers, List<SharedInformer<V1Service>> serviceInformers,
|
||||
List<SharedInformer<V1Endpoints>> endpointsInformers, KubernetesDiscoveryProperties properties) {
|
||||
return new KubernetesInformerDiscoveryClient(sharedInformerFactories, serviceListers, endpointsListers,
|
||||
serviceInformers, endpointsInformers, properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ org.springframework.cloud.kubernetes.client.discovery.catalog.KubernetesCatalogW
|
||||
org.springframework.cloud.kubernetes.client.discovery.KubernetesInformerDiscoveryClientAutoConfiguration
|
||||
org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerAutoConfiguration
|
||||
org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClientAutoConfiguration
|
||||
org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerSelectiveNamespacesAutoConfiguration
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
|
||||
public class ConditionalOnSelectiveNamespacesDisabledTests {
|
||||
|
||||
private static final ConditionalOnSelectiveNamespacesMissing TO_TEST = new ConditionalOnSelectiveNamespacesMissing();
|
||||
|
||||
private static final ConditionContext CONDITION_CONTEXT = Mockito.mock(ConditionContext.class);
|
||||
|
||||
@Test
|
||||
void testSelectiveNamespacesNotPresent() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
Mockito.when(CONDITION_CONTEXT.getEnvironment()).thenReturn(environment);
|
||||
boolean result = TO_TEST.matches(CONDITION_CONTEXT, null);
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectiveNamespacesPresentEmpty() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("spring.cloud.kubernetes.discovery.namespaces", "");
|
||||
Mockito.when(CONDITION_CONTEXT.getEnvironment()).thenReturn(environment);
|
||||
boolean result = TO_TEST.matches(CONDITION_CONTEXT, null);
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectiveNamespacesPresentNonEmpty() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("spring.cloud.kubernetes.discovery.namespaces", "default");
|
||||
Mockito.when(CONDITION_CONTEXT.getEnvironment()).thenReturn(environment);
|
||||
boolean result = TO_TEST.matches(CONDITION_CONTEXT, null);
|
||||
Assertions.assertFalse(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
class ConditionalOnSelectiveNamespacesEnabledTests {
|
||||
|
||||
private static final ConditionalOnSelectiveNamespacesPresent TO_TEST = new ConditionalOnSelectiveNamespacesPresent();
|
||||
|
||||
private static final ConditionContext CONDITION_CONTEXT = Mockito.mock(ConditionContext.class);
|
||||
|
||||
@Test
|
||||
void testSelectiveNamespacesNotPresent() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
Mockito.when(CONDITION_CONTEXT.getEnvironment()).thenReturn(environment);
|
||||
boolean result = TO_TEST.matches(CONDITION_CONTEXT, null);
|
||||
Assertions.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectiveNamespacesPresentEmpty() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("spring.cloud.kubernetes.discovery.namespaces", "");
|
||||
Mockito.when(CONDITION_CONTEXT.getEnvironment()).thenReturn(environment);
|
||||
boolean result = TO_TEST.matches(CONDITION_CONTEXT, null);
|
||||
Assertions.assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectiveNamespacesPresentNonEmpty() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("spring.cloud.kubernetes.discovery.namespaces", "default");
|
||||
Mockito.when(CONDITION_CONTEXT.getEnvironment()).thenReturn(environment);
|
||||
boolean result = TO_TEST.matches(CONDITION_CONTEXT, null);
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,9 +18,6 @@ package org.springframework.cloud.kubernetes.client.discovery;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import io.kubernetes.client.informer.SharedIndexInformer;
|
||||
import io.kubernetes.client.informer.SharedInformerFactory;
|
||||
import io.kubernetes.client.informer.cache.Lister;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.util.Config;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
@@ -31,7 +28,9 @@ import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator;
|
||||
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
|
||||
@@ -40,6 +39,10 @@ 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.client.discovery.TestUtils.assertNonSelectiveNamespacesBeansMissing;
|
||||
import static org.springframework.cloud.kubernetes.client.discovery.TestUtils.assertNonSelectiveNamespacesBeansPresent;
|
||||
import static org.springframework.cloud.kubernetes.client.discovery.TestUtils.assertSelectiveNamespacesBeansMissing;
|
||||
import static org.springframework.cloud.kubernetes.client.discovery.TestUtils.assertSelectiveNamespacesBeansPresent;
|
||||
|
||||
/**
|
||||
* Test various conditionals for
|
||||
@@ -62,11 +65,30 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
void discoveryEnabledDefault() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void discoveryEnabledDefaultWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=a,b,c");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 3);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -75,11 +97,30 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.enabled=true");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void discoveryEnabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.enabled=true", "spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,11 +129,30 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(SharedInformerFactory.class);
|
||||
assertThat(context).doesNotHaveBean(SharedIndexInformer.class);
|
||||
assertThat(context).doesNotHaveBean(Lister.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void discoveryDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.enabled=false", "spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -101,11 +161,31 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.enabled=true");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryEnabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.enabled=true",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=a,b,c,d");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 4);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,11 +194,31 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(SharedInformerFactory.class);
|
||||
assertThat(context).doesNotHaveBean(SharedIndexInformer.class);
|
||||
assertThat(context).doesNotHaveBean(Lister.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=a,b,c,d");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,11 +227,30 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.blocking.enabled=true");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryBlockingEnabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.blocking.enabled=true", "spring.cloud.kubernetes.discovery.namespaces=a");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -140,11 +259,30 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.blocking.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryBlockingDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.blocking.enabled=false", "spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -153,11 +291,31 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.client.health-indicator.enabled=true");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryHealthIndicatorEnabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.client.health-indicator.enabled=true",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -166,11 +324,31 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.client.health-indicator.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryHealthIndicatorDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.client.health-indicator.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,11 +357,31 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setupWithFilteredClassLoader(HealthIndicator.class, "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.cloud.config.enabled=false", "spring.cloud.discovery.client.health-indicator.enabled=true");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryHealthIndicatorEnabledHealthIndicatorMissingWithSelectiveNamespaces() {
|
||||
setupWithFilteredClassLoader(HealthIndicator.class, "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.cloud.config.enabled=false", "spring.cloud.discovery.client.health-indicator.enabled=true",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=a,b,c,d,e");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 5);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -195,11 +393,34 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.reactive.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* reactive is disabled and should not impact blocking in any way
|
||||
*/
|
||||
@Test
|
||||
void reactiveDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.reactive.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=a,b,c,d,e");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 5);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -207,7 +428,8 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
applicationContextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(KubernetesInformerDiscoveryClientAutoConfiguration.class,
|
||||
KubernetesClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class,
|
||||
KubernetesClientInformerAutoConfiguration.class))
|
||||
KubernetesClientInformerAutoConfiguration.class,
|
||||
KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class))
|
||||
.withUserConfiguration(ApiClientConfig.class).withPropertyValues(properties);
|
||||
}
|
||||
|
||||
@@ -215,7 +437,8 @@ class KubernetesInformerDiscoveryClientAutoConfigurationApplicationContextTests
|
||||
applicationContextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(KubernetesInformerDiscoveryClientAutoConfiguration.class,
|
||||
KubernetesClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class,
|
||||
KubernetesClientInformerAutoConfiguration.class))
|
||||
KubernetesClientInformerAutoConfiguration.class,
|
||||
KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class))
|
||||
.withClassLoader(new FilteredClassLoader(cls)).withUserConfiguration(ApiClientConfig.class)
|
||||
.withPropertyValues(properties);
|
||||
}
|
||||
|
||||
@@ -95,9 +95,11 @@ class KubernetesInformerDiscoveryClientTests {
|
||||
@Test
|
||||
void testDiscoveryGetServicesAllNamespaceShouldWork() {
|
||||
Lister<V1Service> serviceLister = setupServiceLister(SERVICE_1, SERVICE_2);
|
||||
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(ENDPOINTS_NO_UNSET_PORT_NAME);
|
||||
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(
|
||||
SHARED_INFORMER_FACTORY, serviceLister, null, null, null, KubernetesDiscoveryProperties.DEFAULT);
|
||||
SHARED_INFORMER_FACTORY, serviceLister, endpointsLister, null, null,
|
||||
KubernetesDiscoveryProperties.DEFAULT);
|
||||
|
||||
assertThat(discoveryClient.getServices().toArray()).containsOnly(SERVICE_1.getMetadata().getName(),
|
||||
SERVICE_2.getMetadata().getName());
|
||||
@@ -107,12 +109,13 @@ class KubernetesInformerDiscoveryClientTests {
|
||||
@Test
|
||||
void testDiscoveryWithServiceLabels() {
|
||||
Lister<V1Service> serviceLister = setupServiceLister(SERVICE_1, SERVICE_2, SERVICE_3);
|
||||
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(ENDPOINTS_NO_UNSET_PORT_NAME);
|
||||
|
||||
Map<String, String> labels = Map.of("k8s", "true", "spring", "true");
|
||||
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = properties(true, labels);
|
||||
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(
|
||||
SHARED_INFORMER_FACTORY, serviceLister, null, null, null, kubernetesDiscoveryProperties);
|
||||
SHARED_INFORMER_FACTORY, serviceLister, endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
|
||||
assertThat(discoveryClient.getServices().toArray()).containsOnly(SERVICE_3.getMetadata().getName());
|
||||
|
||||
@@ -164,9 +167,11 @@ class KubernetesInformerDiscoveryClientTests {
|
||||
@Test
|
||||
void testDiscoveryGetServicesOneNamespaceShouldWork() {
|
||||
Lister<V1Service> serviceLister = setupServiceLister(SERVICE_1, SERVICE_2);
|
||||
Lister<V1Endpoints> endpointsLister = setupEndpointsLister(ENDPOINTS_NO_UNSET_PORT_NAME);
|
||||
|
||||
KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(
|
||||
SHARED_INFORMER_FACTORY, serviceLister, null, null, null, KubernetesDiscoveryProperties.DEFAULT);
|
||||
SHARED_INFORMER_FACTORY, serviceLister, endpointsLister, null, null,
|
||||
KubernetesDiscoveryProperties.DEFAULT);
|
||||
|
||||
assertThat(discoveryClient.getServices().toArray()).containsOnly(SERVICE_1.getMetadata().getName());
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.List;
|
||||
|
||||
import io.kubernetes.client.informer.SharedIndexInformer;
|
||||
import io.kubernetes.client.informer.SharedInformerFactory;
|
||||
import io.kubernetes.client.informer.cache.Lister;
|
||||
import io.kubernetes.client.openapi.models.V1Endpoints;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Some common class that test code delegates to.
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
public final class TestUtils {
|
||||
|
||||
private TestUtils() {
|
||||
|
||||
}
|
||||
|
||||
public static void assertSelectiveNamespacesBeansMissing(AssertableApplicationContext context) {
|
||||
String[] sharedInformerFactoriesBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<List<SharedInformerFactory>>() {
|
||||
}));
|
||||
assertThat(sharedInformerFactoriesBeanName).isEmpty();
|
||||
|
||||
String[] serviceSharedIndexInformersBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<List<SharedIndexInformer<V1Service>>>() {
|
||||
}));
|
||||
assertThat(serviceSharedIndexInformersBeanName).isEmpty();
|
||||
|
||||
String[] endpointsSharedIndexInformersBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<List<SharedIndexInformer<V1Endpoints>>>() {
|
||||
}));
|
||||
assertThat(endpointsSharedIndexInformersBeanName).isEmpty();
|
||||
|
||||
String[] serviceListersBeanName = context
|
||||
.getBeanNamesForType(ResolvableType.forType(new ParameterizedTypeReference<List<Lister<V1Service>>>() {
|
||||
}));
|
||||
assertThat(serviceListersBeanName).isEmpty();
|
||||
|
||||
String[] endpointsListersBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<List<Lister<V1Endpoints>>>() {
|
||||
}));
|
||||
assertThat(endpointsListersBeanName).isEmpty();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void assertSelectiveNamespacesBeansPresent(AssertableApplicationContext context, int times) {
|
||||
String sharedInformerFactoriesBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<List<SharedInformerFactory>>() {
|
||||
}))[0];
|
||||
List<SharedInformerFactory> sharedInformerFactories = (List<SharedInformerFactory>) context
|
||||
.getBean(sharedInformerFactoriesBeanName);
|
||||
assertThat(sharedInformerFactories.size()).isEqualTo(times);
|
||||
|
||||
String serviceSharedIndexInformersBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<List<SharedIndexInformer<V1Service>>>() {
|
||||
}))[0];
|
||||
List<SharedIndexInformer<V1Service>> serviceSharedIndexInformers = (List<SharedIndexInformer<V1Service>>) context
|
||||
.getBean(serviceSharedIndexInformersBeanName);
|
||||
assertThat(serviceSharedIndexInformers.size()).isEqualTo(times);
|
||||
|
||||
String endpointsSharedIndexInformersBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<List<SharedIndexInformer<V1Endpoints>>>() {
|
||||
}))[0];
|
||||
List<SharedIndexInformer<V1Endpoints>> endpointsSharedIndexInformers = (List<SharedIndexInformer<V1Endpoints>>) context
|
||||
.getBean(endpointsSharedIndexInformersBeanName);
|
||||
assertThat(endpointsSharedIndexInformers.size()).isEqualTo(times);
|
||||
|
||||
String serviceListersBeanName = context
|
||||
.getBeanNamesForType(ResolvableType.forType(new ParameterizedTypeReference<List<Lister<V1Service>>>() {
|
||||
}))[0];
|
||||
List<Lister<V1Service>> serviceListers = (List<Lister<V1Service>>) context.getBean(serviceListersBeanName);
|
||||
assertThat(serviceListers.size()).isEqualTo(times);
|
||||
|
||||
String endpointsListersBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<List<Lister<V1Endpoints>>>() {
|
||||
}))[0];
|
||||
List<Lister<V1Endpoints>> endpointsListers = (List<Lister<V1Endpoints>>) context
|
||||
.getBean(endpointsListersBeanName);
|
||||
assertThat(endpointsListers.size()).isEqualTo(times);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void assertNonSelectiveNamespacesBeansPresent(AssertableApplicationContext context) {
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
|
||||
String serviceSharedIndexInformerBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<SharedIndexInformer<V1Service>>() {
|
||||
}))[0];
|
||||
SharedIndexInformer<V1Service> serviceSharedIndexInformer = (SharedIndexInformer<V1Service>) context
|
||||
.getBean(serviceSharedIndexInformerBeanName);
|
||||
assertThat(serviceSharedIndexInformer).isNotNull();
|
||||
|
||||
String endpointSharedIndexInformerBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<SharedIndexInformer<V1Endpoints>>() {
|
||||
}))[0];
|
||||
SharedIndexInformer<V1Endpoints> endpointsSharedIndexInformer = (SharedIndexInformer<V1Endpoints>) context
|
||||
.getBean(endpointSharedIndexInformerBeanName);
|
||||
assertThat(endpointsSharedIndexInformer).isNotNull();
|
||||
|
||||
String serviceListerBeanName = context
|
||||
.getBeanNamesForType(ResolvableType.forType(new ParameterizedTypeReference<Lister<V1Service>>() {
|
||||
}))[0];
|
||||
Lister<V1Service> serviceLister = (Lister<V1Service>) context.getBean(serviceListerBeanName);
|
||||
assertThat(serviceLister).isNotNull();
|
||||
|
||||
String endpointsListerBeanName = context
|
||||
.getBeanNamesForType(ResolvableType.forType(new ParameterizedTypeReference<Lister<V1Endpoints>>() {
|
||||
}))[0];
|
||||
Lister<V1Endpoints> endpointsLister = (Lister<V1Endpoints>) context.getBean(endpointsListerBeanName);
|
||||
assertThat(endpointsLister).isNotNull();
|
||||
}
|
||||
|
||||
public static void assertNonSelectiveNamespacesBeansMissing(AssertableApplicationContext context) {
|
||||
String[] serviceSharedIndexInformerBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<SharedIndexInformer<V1Service>>() {
|
||||
}));
|
||||
assertThat(serviceSharedIndexInformerBeanName).isEmpty();
|
||||
|
||||
String[] endpointSharedIndexInformerBeanName = context.getBeanNamesForType(
|
||||
ResolvableType.forType(new ParameterizedTypeReference<SharedIndexInformer<V1Endpoints>>() {
|
||||
}));
|
||||
assertThat(endpointSharedIndexInformerBeanName).isEmpty();
|
||||
|
||||
String[] serviceListerBeanName = context
|
||||
.getBeanNamesForType(ResolvableType.forType(new ParameterizedTypeReference<Lister<V1Service>>() {
|
||||
}));
|
||||
assertThat(serviceListerBeanName).isEmpty();
|
||||
|
||||
String[] endpointsListerBeanName = context
|
||||
.getBeanNamesForType(ResolvableType.forType(new ParameterizedTypeReference<Lister<V1Endpoints>>() {
|
||||
}));
|
||||
assertThat(endpointsListerBeanName).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,9 +18,6 @@ package org.springframework.cloud.kubernetes.client.discovery.reactive;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import io.kubernetes.client.informer.SharedIndexInformer;
|
||||
import io.kubernetes.client.informer.SharedInformerFactory;
|
||||
import io.kubernetes.client.informer.cache.Lister;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.util.Config;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
@@ -35,7 +32,10 @@ import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactive
|
||||
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerSelectiveNamespacesAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.KubernetesInformerDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -43,6 +43,10 @@ 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.client.discovery.TestUtils.assertNonSelectiveNamespacesBeansMissing;
|
||||
import static org.springframework.cloud.kubernetes.client.discovery.TestUtils.assertNonSelectiveNamespacesBeansPresent;
|
||||
import static org.springframework.cloud.kubernetes.client.discovery.TestUtils.assertSelectiveNamespacesBeansMissing;
|
||||
import static org.springframework.cloud.kubernetes.client.discovery.TestUtils.assertSelectiveNamespacesBeansPresent;
|
||||
|
||||
/**
|
||||
* Test various conditionals for
|
||||
@@ -65,12 +69,36 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
void discoveryEnabledDefault() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("kubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void discoveryEnabledDefaultWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=a,b,c");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("selectiveNamespacesKubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 3);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,12 +107,36 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.enabled=true");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("kubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void discoveryEnabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.enabled=true", "spring.cloud.kubernetes.discovery.namespaces=a,b,c");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("selectiveNamespacesKubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 3);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,11 +145,30 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(SharedInformerFactory.class);
|
||||
assertThat(context).doesNotHaveBean(SharedIndexInformer.class);
|
||||
assertThat(context).doesNotHaveBean(Lister.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void discoveryDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.enabled=false", "spring.cloud.kubernetes.discovery.namespaces=a,b,c");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -106,12 +177,36 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.enabled=true");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("kubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryEnabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.enabled=true", "spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("selectiveNamespacesKubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,12 +215,32 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
// only "simple" one from commons, as ours is not picked up
|
||||
assertThat(context).hasSingleBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesDiscoveryDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.enabled=false", "spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(SharedInformerFactory.class);
|
||||
assertThat(context).doesNotHaveBean(SharedIndexInformer.class);
|
||||
assertThat(context).doesNotHaveBean(Lister.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
// only "simple" one from commons, as ours is not picked up
|
||||
assertThat(context).hasSingleBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,12 +249,36 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.reactive.enabled=true");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("kubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesReactiveDiscoveryEnabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.reactive.enabled=true", "spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("selectiveNamespacesKubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -148,11 +287,30 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.reactive.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void kubernetesReactiveDiscoveryDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.reactive.enabled=false", "spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,12 +322,39 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.blocking.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("kubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* blocking is disabled, and it should not impact reactive in any way.
|
||||
*/
|
||||
@Test
|
||||
void blockingDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.blocking.enabled=false", "spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("selectiveNamespacesKubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
|
||||
// simple from commons and ours
|
||||
assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).size().isEqualTo(2);
|
||||
assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
assertThat(context).hasBean("reactiveIndicatorInitializer");
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -178,11 +363,33 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.client.health-indicator.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("kubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void healthDisabledWithSelectiveNamespaces() {
|
||||
setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.client.health-indicator.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("selectiveNamespacesKubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -192,11 +399,34 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
"spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.client.health-indicator.enabled=false");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("kubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
assertThat(context).hasSingleBean(SharedInformerFactory.class);
|
||||
assertThat(context).getBeans(SharedIndexInformer.class).hasSize(2);
|
||||
assertThat(context).getBeans(Lister.class).hasSize(2);
|
||||
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansPresent(context);
|
||||
assertSelectiveNamespacesBeansMissing(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void healthEnabledClassNotPresentWithSelectiveNamespaces() {
|
||||
setupWithFilteredClassLoader("org.springframework.boot.actuate.health.ReactiveHealthIndicator",
|
||||
"spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false",
|
||||
"spring.cloud.discovery.client.health-indicator.enabled=false",
|
||||
"spring.cloud.kubernetes.discovery.namespaces=a,b");
|
||||
applicationContextRunner.run(context -> {
|
||||
assertThat(context).hasSingleBean(KubernetesInformerDiscoveryClient.class);
|
||||
assertThat(context).hasBean("selectiveNamespacesKubernetesClientInformerDiscoveryClient");
|
||||
assertThat(context).hasSingleBean(KubernetesInformerReactiveDiscoveryClient.class);
|
||||
|
||||
assertThat(context).doesNotHaveBean(ReactiveDiscoveryClientHealthIndicator.class);
|
||||
assertThat(context).doesNotHaveBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class);
|
||||
|
||||
assertNonSelectiveNamespacesBeansMissing(context);
|
||||
assertSelectiveNamespacesBeansPresent(context, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -206,6 +436,7 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
KubernetesInformerReactiveDiscoveryClientAutoConfiguration.class,
|
||||
KubernetesClientAutoConfiguration.class, SimpleReactiveDiscoveryClientAutoConfiguration.class,
|
||||
UtilAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class,
|
||||
KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class,
|
||||
KubernetesCommonsAutoConfiguration.class, KubernetesClientInformerAutoConfiguration.class))
|
||||
.withUserConfiguration(ApiClientConfig.class).withPropertyValues(properties);
|
||||
}
|
||||
@@ -216,6 +447,7 @@ class KubernetesInformerReactiveDiscoveryClientAutoConfigurationApplicationConte
|
||||
KubernetesInformerReactiveDiscoveryClientAutoConfiguration.class,
|
||||
KubernetesClientAutoConfiguration.class, SimpleReactiveDiscoveryClientAutoConfiguration.class,
|
||||
UtilAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class,
|
||||
KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class,
|
||||
KubernetesCommonsAutoConfiguration.class, KubernetesClientInformerAutoConfiguration.class))
|
||||
.withUserConfiguration(ApiClientConfig.class).withClassLoader(new FilteredClassLoader(name))
|
||||
.withPropertyValues(properties);
|
||||
|
||||
@@ -37,14 +37,11 @@ import org.mockito.Mockito;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.KubernetesInformerDiscoveryClient;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static io.kubernetes.client.util.Namespaces.NAMESPACE_ALL;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -88,13 +85,14 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
void testDiscoveryGetServicesAllNamespaceShouldWork() {
|
||||
Lister<V1Service> serviceLister = setupServiceLister(NAMESPACE_ALL, TEST_SERVICE_1, TEST_SERVICE_2,
|
||||
TEST_SERVICE_3);
|
||||
Lister<V1Endpoints> endpointsLister = setupEndpointsLister("");
|
||||
|
||||
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true, true,
|
||||
Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, false);
|
||||
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
|
||||
new KubernetesNamespaceProvider(new MockEnvironment()), sharedInformerFactory, serviceLister, null,
|
||||
null, null, kubernetesDiscoveryProperties);
|
||||
new KubernetesInformerDiscoveryClient(sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties));
|
||||
|
||||
StepVerifier.create(discoveryClient.getServices())
|
||||
.expectNext(TEST_SERVICE_1.getMetadata().getName(), TEST_SERVICE_2.getMetadata().getName())
|
||||
@@ -105,12 +103,11 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
@Test
|
||||
void testDiscoveryGetServicesOneNamespaceShouldWork() {
|
||||
Lister<V1Service> serviceLister = setupServiceLister(NAMESPACE_1, TEST_SERVICE_1, TEST_SERVICE_2);
|
||||
Lister<V1Endpoints> endpointsLister = setupEndpointsLister("");
|
||||
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider = mock(KubernetesNamespaceProvider.class);
|
||||
when(kubernetesNamespaceProvider.getNamespace()).thenReturn(NAMESPACE_1);
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
|
||||
kubernetesNamespaceProvider, sharedInformerFactory, serviceLister, null, null, null,
|
||||
KubernetesDiscoveryProperties.DEFAULT);
|
||||
new KubernetesInformerDiscoveryClient(sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
KubernetesDiscoveryProperties.DEFAULT));
|
||||
|
||||
StepVerifier.create(discoveryClient.getServices()).expectNext(TEST_SERVICE_1.getMetadata().getName())
|
||||
.expectComplete().verify();
|
||||
@@ -127,8 +124,8 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, false);
|
||||
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
|
||||
new KubernetesNamespaceProvider(new MockEnvironment()), sharedInformerFactory, serviceLister,
|
||||
endpointsLister, null, null, kubernetesDiscoveryProperties);
|
||||
new KubernetesInformerDiscoveryClient(sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties));
|
||||
|
||||
StepVerifier.create(discoveryClient.getInstances("test-svc-1"))
|
||||
.expectNext(new DefaultKubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080,
|
||||
@@ -147,11 +144,9 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
Set.of(), true, 60, false, null, Set.of(), Map.of(), null,
|
||||
KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, false);
|
||||
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider = mock(KubernetesNamespaceProvider.class);
|
||||
when(kubernetesNamespaceProvider.getNamespace()).thenReturn(NAMESPACE_1);
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
|
||||
kubernetesNamespaceProvider, sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties);
|
||||
new KubernetesInformerDiscoveryClient(sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties));
|
||||
|
||||
StepVerifier.create(discoveryClient.getInstances("test-svc-1"))
|
||||
.expectNext(new DefaultKubernetesServiceInstance("", "test-svc-1", "2.2.2.2", 8080,
|
||||
@@ -172,6 +167,8 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
*/
|
||||
@Test
|
||||
void testAllNamespacesTwoServicesPresent() {
|
||||
Lister<V1Endpoints> endpointsLister = setupEndpointsLister("");
|
||||
|
||||
boolean allNamespaces = true;
|
||||
V1Service serviceA = new V1Service().metadata(new V1ObjectMeta().name("service-a").namespace("namespace-a"));
|
||||
V1Service serviceB = new V1Service().metadata(new V1ObjectMeta().name("service-b").namespace("namespace-b"));
|
||||
@@ -182,12 +179,9 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true,
|
||||
allNamespaces, Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, false);
|
||||
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider = mock(KubernetesNamespaceProvider.class);
|
||||
when(kubernetesNamespaceProvider.getNamespace()).thenReturn("irrelevant");
|
||||
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
|
||||
kubernetesNamespaceProvider, sharedInformerFactory, serviceLister, null, null, null,
|
||||
kubernetesDiscoveryProperties);
|
||||
new KubernetesInformerDiscoveryClient(sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties));
|
||||
|
||||
List<String> result = discoveryClient.getServices().collectList().block();
|
||||
Assertions.assertEquals(result.size(), 2);
|
||||
@@ -207,6 +201,8 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
*/
|
||||
@Test
|
||||
void testSingleNamespaceTwoServicesPresent() {
|
||||
Lister<V1Endpoints> endpointsLister = setupEndpointsLister("");
|
||||
|
||||
boolean allNamespaces = false;
|
||||
V1Service serviceA = new V1Service().metadata(new V1ObjectMeta().name("service-a").namespace("namespace-a"));
|
||||
V1Service serviceB = new V1Service().metadata(new V1ObjectMeta().name("service-b").namespace("namespace-b"));
|
||||
@@ -217,12 +213,9 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true,
|
||||
allNamespaces, Set.of(), true, 60, false, null, Set.of(), Map.of(), null, null, 0, false);
|
||||
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider = mock(KubernetesNamespaceProvider.class);
|
||||
when(kubernetesNamespaceProvider.getNamespace()).thenReturn("irrelevant");
|
||||
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
|
||||
kubernetesNamespaceProvider, sharedInformerFactory, serviceLister, null, null, null,
|
||||
kubernetesDiscoveryProperties);
|
||||
new KubernetesInformerDiscoveryClient(sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties));
|
||||
|
||||
List<String> result = discoveryClient.getServices().collectList().block();
|
||||
Assertions.assertEquals(result.size(), 1);
|
||||
@@ -261,19 +254,16 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
endpointsCache.add(endpointsXNamespaceA);
|
||||
endpointsCache.add(endpointsXNamespaceB);
|
||||
|
||||
Lister<V1Endpoints> endpointsLister = new Lister<>(endpointsCache).namespace(NAMESPACE_ALL);
|
||||
Lister<V1Service> serviceLister = new Lister<>(serviceCache).namespace(NAMESPACE_ALL);
|
||||
Lister<V1Endpoints> endpointsLister = new Lister<>(endpointsCache, NAMESPACE_ALL);
|
||||
Lister<V1Service> serviceLister = new Lister<>(serviceCache, NAMESPACE_ALL);
|
||||
|
||||
KubernetesDiscoveryProperties kubernetesDiscoveryProperties = new KubernetesDiscoveryProperties(true,
|
||||
allNamespaces, Set.of(), true, 60, false, null, Set.of(), Map.of(), null,
|
||||
KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, false);
|
||||
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider = mock(KubernetesNamespaceProvider.class);
|
||||
when(kubernetesNamespaceProvider.getNamespace()).thenReturn("irrelevant");
|
||||
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
|
||||
kubernetesNamespaceProvider, sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties);
|
||||
new KubernetesInformerDiscoveryClient(sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties));
|
||||
|
||||
List<ServiceInstance> result = discoveryClient.getInstances("endpoints-x").collectList().block();
|
||||
Assertions.assertEquals(result.size(), 2);
|
||||
@@ -320,12 +310,9 @@ class KubernetesInformerReactiveDiscoveryClientTests {
|
||||
allNamespaces, Set.of(), true, 60, false, null, Set.of(), Map.of(), null,
|
||||
KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, false);
|
||||
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider = mock(KubernetesNamespaceProvider.class);
|
||||
when(kubernetesNamespaceProvider.getNamespace()).thenReturn("irrelevant");
|
||||
|
||||
KubernetesInformerReactiveDiscoveryClient discoveryClient = new KubernetesInformerReactiveDiscoveryClient(
|
||||
kubernetesNamespaceProvider, sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties);
|
||||
new KubernetesInformerDiscoveryClient(sharedInformerFactory, serviceLister, endpointsLister, null, null,
|
||||
kubernetesDiscoveryProperties));
|
||||
|
||||
List<ServiceInstance> result = discoveryClient.getInstances("endpoints-x").collectList().block();
|
||||
Assertions.assertEquals(result.size(), 1);
|
||||
|
||||
@@ -21,8 +21,10 @@ import java.util.List;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.client.discovery.reactive.KubernetesInformerReactiveDiscoveryClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
@@ -45,4 +47,9 @@ public class ReactiveDiscoveryController {
|
||||
return reactiveDiscoveryClient.getServices().collectList();
|
||||
}
|
||||
|
||||
@GetMapping("reactive/service-instances/{serviceId}")
|
||||
public Mono<List<ServiceInstance>> serviceInstances(@PathVariable("serviceId") String serviceId) {
|
||||
return reactiveDiscoveryClient.getInstances(serviceId).collectList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.client.discovery;
|
||||
package org.springframework.cloud.kubernetes.client.discovery.it;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
@@ -31,7 +31,6 @@ import io.kubernetes.client.openapi.models.V1Service;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
@@ -92,7 +91,7 @@ class KubernetesClientDiscoveryClientIT {
|
||||
manifests(false, null, Phase.CREATE);
|
||||
util.busybox(NAMESPACE, Phase.CREATE);
|
||||
|
||||
assertLogStatement("serviceSharedInformer will use namespace : default");
|
||||
Assertions.assertTrue(logs().contains("serviceSharedInformer will use namespace : default"));
|
||||
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
@@ -107,7 +106,7 @@ class KubernetesClientDiscoveryClientIT {
|
||||
Assertions.assertTrue(servicesResult.contains("busybox-service"));
|
||||
|
||||
WebClient ourServiceClient = builder()
|
||||
.baseUrl("http://localhost//service-instances/spring-cloud-kubernetes-client-discovery-it").build();
|
||||
.baseUrl("http://localhost/service-instances/spring-cloud-kubernetes-client-discovery-it").build();
|
||||
|
||||
List<DefaultKubernetesServiceInstance> ourServiceInstances = ourServiceClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
@@ -126,7 +125,7 @@ class KubernetesClientDiscoveryClientIT {
|
||||
Assertions.assertEquals(serviceInstance.getPort(), 8080);
|
||||
Assertions.assertEquals(serviceInstance.getNamespace(), "default");
|
||||
|
||||
WebClient busyBoxServiceClient = builder().baseUrl("http://localhost//service-instances/busybox-service")
|
||||
WebClient busyBoxServiceClient = builder().baseUrl("http://localhost/service-instances/busybox-service")
|
||||
.build();
|
||||
List<DefaultKubernetesServiceInstance> busyBoxServiceInstances = busyBoxServiceClient.method(HttpMethod.GET)
|
||||
.retrieve().bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
@@ -169,7 +168,7 @@ class KubernetesClientDiscoveryClientIT {
|
||||
util.busybox(NAMESPACE_B, Phase.CREATE);
|
||||
manifests(true, null, Phase.CREATE);
|
||||
|
||||
assertLogStatement("serviceSharedInformer will use all-namespaces");
|
||||
Assertions.assertTrue(logs().contains("serviceSharedInformer will use all-namespaces"));
|
||||
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/services").build();
|
||||
List<String> servicesResult = servicesClient.method(HttpMethod.GET).retrieve()
|
||||
@@ -211,20 +210,21 @@ class KubernetesClientDiscoveryClientIT {
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
@Disabled
|
||||
// TODO will be fixed by this issue :
|
||||
// https://github.com/spring-cloud/spring-cloud-kubernetes/issues/1289
|
||||
void testSpecificNamespace() {
|
||||
util.createNamespace(NAMESPACE_A);
|
||||
util.createNamespace(NAMESPACE_B);
|
||||
util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE_A));
|
||||
util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A));
|
||||
util.wiremock(NAMESPACE_A, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_B, "/wiremock", Phase.CREATE);
|
||||
manifests(false, NAMESPACE_A, Phase.CREATE);
|
||||
|
||||
// first check that wiremock service is present in both namespaces a and b
|
||||
assertServicePresentInNamespaces(List.of("a", "b"), "service-wiremock", "service-wiremock");
|
||||
assertLogStatement("serviceSharedInformer will use namespace : a");
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a]"));
|
||||
Assertions.assertTrue(logs.contains("reading pod in namespace : default"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/services").build();
|
||||
List<String> servicesResult = servicesClient.method(HttpMethod.GET).retrieve()
|
||||
@@ -234,7 +234,7 @@ class KubernetesClientDiscoveryClientIT {
|
||||
Assertions.assertEquals(servicesResult.size(), 1);
|
||||
Assertions.assertTrue(servicesResult.contains("service-wiremock"));
|
||||
|
||||
WebClient wiremockInNamespaceAClient = builder().baseUrl("http://localhost//service-instances/service-wiremock")
|
||||
WebClient wiremockInNamespaceAClient = builder().baseUrl("http://localhost/service-instances/service-wiremock")
|
||||
.build();
|
||||
|
||||
List<DefaultKubernetesServiceInstance> wiremockInNamespaceA = wiremockInNamespaceAClient.method(HttpMethod.GET)
|
||||
@@ -261,7 +261,7 @@ class KubernetesClientDiscoveryClientIT {
|
||||
manifests(false, NAMESPACE_A, Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_A, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_B, "/wiremock", Phase.DELETE);
|
||||
util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE_A));
|
||||
util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A));
|
||||
util.deleteNamespace(NAMESPACE_A);
|
||||
util.deleteNamespace(NAMESPACE_B);
|
||||
}
|
||||
@@ -276,6 +276,10 @@ class KubernetesClientDiscoveryClientIT {
|
||||
.orElse(List.of()));
|
||||
V1EnvVar debugLevel = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY")
|
||||
.value("DEBUG");
|
||||
|
||||
V1EnvVar debugLevelForClient = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT")
|
||||
.value("DEBUG");
|
||||
|
||||
if (allNamespaces) {
|
||||
V1EnvVar allNamespacesVar = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_ALL_NAMESPACES")
|
||||
.value("TRUE");
|
||||
@@ -283,11 +287,12 @@ class KubernetesClientDiscoveryClientIT {
|
||||
}
|
||||
|
||||
if (clientSpecificNamespace != null) {
|
||||
V1EnvVar clientNamespace = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_CLIENT_NAMESPACE")
|
||||
V1EnvVar clientNamespace = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
|
||||
.value(NAMESPACE_A);
|
||||
envVars.add(clientNamespace);
|
||||
}
|
||||
envVars.add(debugLevel);
|
||||
envVars.add(debugLevelForClient);
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
@@ -306,14 +311,13 @@ class KubernetesClientDiscoveryClientIT {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
private void assertLogStatement(String message) {
|
||||
private String logs() {
|
||||
try {
|
||||
String appPodName = K3S.execInContainer("sh", "-c",
|
||||
"kubectl get pods -l app=" + IMAGE_NAME + " -o=name --no-headers | tr -d '\n'").getStdout();
|
||||
|
||||
Container.ExecResult execResult = K3S.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim());
|
||||
String ok = execResult.getStdout();
|
||||
Assertions.assertTrue(ok.contains(message));
|
||||
return execResult.getStdout();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.client.discovery;
|
||||
package org.springframework.cloud.kubernetes.client.discovery.it;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.it;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1EnvVar;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.util.retry.Retry;
|
||||
import reactor.util.retry.RetryBackoffSpec;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
class KubernetesClientDiscoveryMultipleSelectiveNamespacesIT {
|
||||
|
||||
private static final String BLOCKING_PUBLISH = "Will publish InstanceRegisteredEvent from blocking implementation";
|
||||
|
||||
private static final String REACTIVE_PUBLISH = "Will publish InstanceRegisteredEvent from reactive implementation";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final String NAMESPACE_A = "a";
|
||||
|
||||
private static final String NAMESPACE_B = "b";
|
||||
|
||||
private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-discovery-it";
|
||||
|
||||
private static Util util;
|
||||
|
||||
private static final K3sContainer K3S = Commons.container();
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() throws Exception {
|
||||
K3S.start();
|
||||
Commons.validateImage(IMAGE_NAME, K3S);
|
||||
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
|
||||
|
||||
util = new Util(K3S);
|
||||
|
||||
util.createNamespace(NAMESPACE_A);
|
||||
util.createNamespace(NAMESPACE_B);
|
||||
util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B));
|
||||
util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_A, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_B, "/wiremock", Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void afterAll() throws Exception {
|
||||
Commons.cleanUp(IMAGE_NAME, K3S);
|
||||
|
||||
util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_A, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_B, "/wiremock", Phase.DELETE);
|
||||
util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B));
|
||||
util.deleteNamespace(NAMESPACE_A);
|
||||
util.deleteNamespace(NAMESPACE_B);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy wiremock in 3 namespaces: default, a, b. Search in selective namespaces 'a'
|
||||
* and 'b' with blocking enabled and reactive disabled, as such find services and it's
|
||||
* instances.
|
||||
*/
|
||||
@Test
|
||||
void testTwoNamespacesBlockingOnly() {
|
||||
|
||||
manifests(Phase.CREATE, false, true);
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b"));
|
||||
|
||||
// this tiny checks makes sure that blocking is enabled and reactive is disabled.
|
||||
Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertFalse(logs.contains(REACTIVE_PUBLISH));
|
||||
|
||||
blockingCheck();
|
||||
|
||||
manifests(Phase.DELETE, false, true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy wiremock in 3 namespaces: default, a, b. Search in selective namespaces 'a'
|
||||
* and 'b' with blocking disabled and reactive enabled, as such find services and it's
|
||||
* instances.
|
||||
*/
|
||||
@Test
|
||||
void testTwoNamespaceReactiveOnly() {
|
||||
|
||||
manifests(Phase.CREATE, true, false);
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b"));
|
||||
|
||||
// this tiny checks makes sure that blocking is disabled and reactive is enabled.
|
||||
Assertions.assertFalse(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH));
|
||||
|
||||
reactiveCheck();
|
||||
|
||||
manifests(Phase.DELETE, true, false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy wiremock in 3 namespaces: default, a, b. Search in selective namespaces 'a'
|
||||
* and 'b' with blocking enabled and reactive enabled, as such find services and its
|
||||
* service instances.
|
||||
*/
|
||||
@Test
|
||||
void testTwoNamespacesBothBlockingAndReactive() {
|
||||
|
||||
manifests(Phase.CREATE, false, false);
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b"));
|
||||
|
||||
// this tiny checks makes sure that blocking is enabled and reactive is enabled.
|
||||
Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH));
|
||||
|
||||
blockingCheck();
|
||||
reactiveCheck();
|
||||
|
||||
manifests(Phase.DELETE, false, false);
|
||||
|
||||
}
|
||||
|
||||
private static void manifests(Phase phase, boolean disableBlocking, boolean disableReactive) {
|
||||
V1Deployment deployment = (V1Deployment) util.yaml("kubernetes-discovery-deployment.yaml");
|
||||
V1Service service = (V1Service) util.yaml("kubernetes-discovery-service.yaml");
|
||||
V1Ingress ingress = (V1Ingress) util.yaml("kubernetes-discovery-ingress.yaml");
|
||||
|
||||
List<V1EnvVar> envVars = new ArrayList<>(
|
||||
Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv())
|
||||
.orElse(List.of()));
|
||||
V1EnvVar debugLevel = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY")
|
||||
.value("DEBUG");
|
||||
V1EnvVar selectiveNamespaceA = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
|
||||
.value(NAMESPACE_A);
|
||||
V1EnvVar selectiveNamespaceB = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1")
|
||||
.value(NAMESPACE_B);
|
||||
|
||||
if (disableReactive) {
|
||||
V1EnvVar disableReactiveEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED")
|
||||
.value("FALSE");
|
||||
envVars.add(disableReactiveEnvVar);
|
||||
}
|
||||
|
||||
if (disableBlocking) {
|
||||
V1EnvVar disableBlockingEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED")
|
||||
.value("FALSE");
|
||||
envVars.add(disableBlockingEnvVar);
|
||||
}
|
||||
|
||||
envVars.add(debugLevel);
|
||||
envVars.add(selectiveNamespaceA);
|
||||
envVars.add(selectiveNamespaceB);
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
}
|
||||
}
|
||||
|
||||
private String logs() {
|
||||
try {
|
||||
String appPodName = K3S.execInContainer("sh", "-c",
|
||||
"kubectl get pods -l app=" + IMAGE_NAME + " -o=name --no-headers | tr -d '\n'").getStdout();
|
||||
|
||||
Container.ExecResult execResult = K3S.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim());
|
||||
return execResult.getStdout();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void reactiveCheck() {
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/reactive/services").build();
|
||||
|
||||
List<String> servicesResult = servicesClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<String>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
// we get two here, but since there is 'distinct' call, only 1 will be reported
|
||||
// but service instances will report 2 nevertheless
|
||||
Assertions.assertEquals(servicesResult.size(), 1);
|
||||
Assertions.assertTrue(servicesResult.contains("service-wiremock"));
|
||||
|
||||
WebClient ourServiceClient = builder().baseUrl("http://localhost/reactive/service-instances/service-wiremock")
|
||||
.build();
|
||||
|
||||
List<DefaultKubernetesServiceInstance> ourServiceInstances = ourServiceClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(ourServiceInstances.size(), 2);
|
||||
ourServiceInstances = ourServiceInstances.stream()
|
||||
.sorted(Comparator.comparing(DefaultKubernetesServiceInstance::namespace)).toList();
|
||||
|
||||
DefaultKubernetesServiceInstance serviceInstanceA = ourServiceInstances.get(0);
|
||||
// we only care about namespace here, as all other fields are tested in various
|
||||
// other tests.
|
||||
Assertions.assertEquals(serviceInstanceA.getNamespace(), "a");
|
||||
|
||||
DefaultKubernetesServiceInstance serviceInstanceB = ourServiceInstances.get(1);
|
||||
// we only care about namespace here, as all other fields are tested in various
|
||||
// other tests.
|
||||
Assertions.assertEquals(serviceInstanceB.getNamespace(), "b");
|
||||
}
|
||||
|
||||
private void blockingCheck() {
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
List<String> servicesResult = servicesClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<String>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
// we get two here, but since there is 'distinct' call, only 1 will be reported
|
||||
// but service instances will report 2 nevertheless
|
||||
Assertions.assertEquals(servicesResult.size(), 1);
|
||||
Assertions.assertTrue(servicesResult.contains("service-wiremock"));
|
||||
|
||||
WebClient ourServiceClient = builder().baseUrl("http://localhost/service-instances/service-wiremock").build();
|
||||
|
||||
List<DefaultKubernetesServiceInstance> ourServiceInstances = ourServiceClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(ourServiceInstances.size(), 2);
|
||||
ourServiceInstances = ourServiceInstances.stream()
|
||||
.sorted(Comparator.comparing(DefaultKubernetesServiceInstance::namespace)).toList();
|
||||
|
||||
DefaultKubernetesServiceInstance serviceInstanceA = ourServiceInstances.get(0);
|
||||
// we only care about namespace here, as all other fields are tested in various
|
||||
// other tests.
|
||||
Assertions.assertEquals(serviceInstanceA.getNamespace(), "a");
|
||||
|
||||
DefaultKubernetesServiceInstance serviceInstanceB = ourServiceInstances.get(1);
|
||||
// we only care about namespace here, as all other fields are tested in various
|
||||
// other tests.
|
||||
Assertions.assertEquals(serviceInstanceB.getNamespace(), "b");
|
||||
}
|
||||
|
||||
private WebClient.Builder builder() {
|
||||
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
|
||||
}
|
||||
|
||||
private RetryBackoffSpec retrySpec() {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.client.discovery;
|
||||
package org.springframework.cloud.kubernetes.client.discovery.it;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright 2013-2023 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.it;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1EnvVar;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.util.retry.Retry;
|
||||
import reactor.util.retry.RetryBackoffSpec;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
class KubernetesClientDiscoverySingleSelectiveNamespaceIT {
|
||||
|
||||
private static final String BLOCKING_PUBLISH = "Will publish InstanceRegisteredEvent from blocking implementation";
|
||||
|
||||
private static final String REACTIVE_PUBLISH = "Will publish InstanceRegisteredEvent from reactive implementation";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final String NAMESPACE_A = "a";
|
||||
|
||||
private static final String NAMESPACE_B = "b";
|
||||
|
||||
private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-discovery-it";
|
||||
|
||||
private static Util util;
|
||||
|
||||
private static final K3sContainer K3S = Commons.container();
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() throws Exception {
|
||||
K3S.start();
|
||||
Commons.validateImage(IMAGE_NAME, K3S);
|
||||
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
|
||||
|
||||
util = new Util(K3S);
|
||||
|
||||
util.createNamespace(NAMESPACE_A);
|
||||
util.createNamespace(NAMESPACE_B);
|
||||
util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B));
|
||||
util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_A, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_B, "/wiremock", Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void after() throws Exception {
|
||||
Commons.cleanUp(IMAGE_NAME, K3S);
|
||||
|
||||
util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_A, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_B, "/wiremock", Phase.DELETE);
|
||||
util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B));
|
||||
util.deleteNamespace(NAMESPACE_A);
|
||||
util.deleteNamespace(NAMESPACE_B);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy wiremock in 3 namespaces: default, a, b. Search only in selective namespace
|
||||
* 'a' with blocking enabled and reactive disabled, as such find a single service and
|
||||
* its service instance.
|
||||
*/
|
||||
@Test
|
||||
void testOneNamespaceBlockingOnly() {
|
||||
|
||||
manifests(Phase.CREATE, false, true);
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
|
||||
// this tiny checks makes sure that blocking is enabled and reactive is disabled.
|
||||
Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertFalse(logs.contains(REACTIVE_PUBLISH));
|
||||
|
||||
blockingCheck();
|
||||
|
||||
manifests(Phase.DELETE, false, true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy wiremock in 3 namespaces: default, a, b. Search only in selective namespace
|
||||
* 'a' with blocking disabled and reactive enabled, as such find a single service and
|
||||
* its service instance.
|
||||
*/
|
||||
@Test
|
||||
void testOneNamespaceReactiveOnly() {
|
||||
|
||||
manifests(Phase.CREATE, true, false);
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
|
||||
// this tiny checks makes sure that reactive is enabled and blocking is disabled.
|
||||
Assertions.assertFalse(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH));
|
||||
|
||||
reactiveCheck();
|
||||
|
||||
manifests(Phase.DELETE, true, false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy wiremock in 3 namespaces: default, a, b. Search only in selective namespace
|
||||
* 'a' with blocking enabled and reactive enabled, as such find a single service and
|
||||
* its service instance.
|
||||
*/
|
||||
@Test
|
||||
void testOneNamespaceBothBlockingAndReactive() {
|
||||
|
||||
manifests(Phase.CREATE, false, false);
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
|
||||
// this tiny checks makes sure that blocking and reactive is enabled.
|
||||
Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH));
|
||||
|
||||
blockingCheck();
|
||||
reactiveCheck();
|
||||
|
||||
manifests(Phase.DELETE, false, false);
|
||||
|
||||
}
|
||||
|
||||
private static void manifests(Phase phase, boolean disableBlocking, boolean disableReactive) {
|
||||
V1Deployment deployment = (V1Deployment) util.yaml("kubernetes-discovery-deployment.yaml");
|
||||
V1Service service = (V1Service) util.yaml("kubernetes-discovery-service.yaml");
|
||||
V1Ingress ingress = (V1Ingress) util.yaml("kubernetes-discovery-ingress.yaml");
|
||||
|
||||
List<V1EnvVar> envVars = new ArrayList<>(
|
||||
Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv())
|
||||
.orElse(List.of()));
|
||||
V1EnvVar debugLevel = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY")
|
||||
.value("DEBUG");
|
||||
V1EnvVar selectiveNamespaceA = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
|
||||
.value(NAMESPACE_A);
|
||||
if (disableReactive) {
|
||||
V1EnvVar disableReactiveEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED")
|
||||
.value("FALSE");
|
||||
envVars.add(disableReactiveEnvVar);
|
||||
}
|
||||
|
||||
if (disableBlocking) {
|
||||
V1EnvVar disableBlockingEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED")
|
||||
.value("FALSE");
|
||||
envVars.add(disableBlockingEnvVar);
|
||||
}
|
||||
|
||||
envVars.add(debugLevel);
|
||||
envVars.add(selectiveNamespaceA);
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
}
|
||||
}
|
||||
|
||||
private void reactiveCheck() {
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/reactive/services").build();
|
||||
|
||||
List<String> servicesResult = servicesClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<String>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(servicesResult.size(), 1);
|
||||
Assertions.assertTrue(servicesResult.contains("service-wiremock"));
|
||||
|
||||
WebClient ourServiceClient = builder().baseUrl("http://localhost/reactive/service-instances/service-wiremock")
|
||||
.build();
|
||||
|
||||
List<DefaultKubernetesServiceInstance> ourServiceInstances = ourServiceClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(ourServiceInstances.size(), 1);
|
||||
|
||||
DefaultKubernetesServiceInstance serviceInstance = ourServiceInstances.get(0);
|
||||
// we only care about namespace here, as all other fields are tested in various
|
||||
// other tests.
|
||||
Assertions.assertEquals(serviceInstance.getNamespace(), "a");
|
||||
}
|
||||
|
||||
private void blockingCheck() {
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
List<String> servicesResult = servicesClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<String>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(servicesResult.size(), 1);
|
||||
Assertions.assertTrue(servicesResult.contains("service-wiremock"));
|
||||
|
||||
WebClient ourServiceClient = builder().baseUrl("http://localhost/service-instances/service-wiremock").build();
|
||||
|
||||
List<DefaultKubernetesServiceInstance> ourServiceInstances = ourServiceClient.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(ourServiceInstances.size(), 1);
|
||||
|
||||
DefaultKubernetesServiceInstance serviceInstance = ourServiceInstances.get(0);
|
||||
// we only care about namespace here, as all other fields are tested in various
|
||||
// other tests.
|
||||
Assertions.assertEquals(serviceInstance.getNamespace(), "a");
|
||||
}
|
||||
|
||||
private String logs() {
|
||||
try {
|
||||
String appPodName = K3S.execInContainer("sh", "-c",
|
||||
"kubectl get pods -l app=" + IMAGE_NAME + " -o=name --no-headers | tr -d '\n'").getStdout();
|
||||
|
||||
Container.ExecResult execResult = K3S.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim());
|
||||
return execResult.getStdout();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private WebClient.Builder builder() {
|
||||
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
|
||||
}
|
||||
|
||||
private RetryBackoffSpec retrySpec() {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user