Merge branch '3.0.x'
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.fabric8.discovery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
|
||||
import org.springframework.boot.test.json.BasicJsonTester;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.builder;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.retrySpec;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.waitForLogStatement;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
final class Fabric8DiscoveryClientHealthDelegate {
|
||||
|
||||
private Fabric8DiscoveryClientHealthDelegate() {
|
||||
|
||||
}
|
||||
|
||||
private static final String REACTIVE_STATUS = "$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].status";
|
||||
|
||||
private static final String BLOCKING_STATUS = "$.components.discoveryComposite.components.discoveryClient.status";
|
||||
|
||||
private static final BasicJsonTester BASIC_JSON_TESTER = new BasicJsonTester(
|
||||
Fabric8DiscoveryClientHealthDelegate.class);
|
||||
|
||||
/**
|
||||
* Reactive is disabled, only blocking is active. As such,
|
||||
* KubernetesInformerDiscoveryClientAutoConfiguration::indicatorInitializer will post
|
||||
* an InstanceRegisteredEvent.
|
||||
*
|
||||
* We assert for logs and call '/health' endpoint to see that blocking discovery
|
||||
* client was initialized.
|
||||
*/
|
||||
static void testBlockingConfiguration(K3sContainer k3sContainer, String imageName) {
|
||||
|
||||
waitForLogStatement("Will publish InstanceRegisteredEvent from blocking implementation", k3sContainer,
|
||||
imageName);
|
||||
waitForLogStatement("publishing InstanceRegisteredEvent", k3sContainer, imageName);
|
||||
waitForLogStatement("Discovery Client has been initialized", k3sContainer, imageName);
|
||||
waitForLogStatement(
|
||||
"received InstanceRegisteredEvent from pod with 'app' label value : spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
k3sContainer, imageName);
|
||||
|
||||
WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build();
|
||||
|
||||
String healthResult = healthClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.discoveryComposite.status").isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue(BLOCKING_STATUS)
|
||||
.isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathArrayValue(
|
||||
"$.components.discoveryComposite.components.discoveryClient.details.services")
|
||||
.containsExactlyInAnyOrder("spring-cloud-kubernetes-fabric8-client-discovery", "kubernetes",
|
||||
"busybox-service", "external-name-service", "service-wiremock");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).doesNotHaveJsonPath(REACTIVE_STATUS);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Both blocking and reactive are enabled.
|
||||
*/
|
||||
static void testDefaultConfiguration(K3sContainer k3sContainer, String imageName) {
|
||||
|
||||
waitForLogStatement("Will publish InstanceRegisteredEvent from blocking implementation", k3sContainer,
|
||||
imageName);
|
||||
waitForLogStatement("publishing InstanceRegisteredEvent", k3sContainer, imageName);
|
||||
waitForLogStatement("Discovery Client has been initialized", k3sContainer, imageName);
|
||||
waitForLogStatement("received InstanceRegisteredEvent from pod with 'app' label value : "
|
||||
+ "spring-cloud-kubernetes-fabric8-client-discovery", k3sContainer, imageName);
|
||||
|
||||
WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build();
|
||||
|
||||
String healthResult = healthClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.discoveryComposite.status").isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.discoveryComposite.components.discoveryClient.status")
|
||||
.isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathArrayValue(
|
||||
"$.components.discoveryComposite.components.discoveryClient.details.services")
|
||||
.containsExactlyInAnyOrder("spring-cloud-kubernetes-fabric8-client-discovery", "kubernetes",
|
||||
"external-name-service", "service-wiremock", "busybox-service");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status").isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue(
|
||||
"$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].status")
|
||||
.isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathArrayValue(
|
||||
"$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].details.services")
|
||||
.containsExactlyInAnyOrder("spring-cloud-kubernetes-fabric8-client-discovery", "kubernetes",
|
||||
"external-name-service", "service-wiremock", "busybox-service");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactive is enabled, blocking is disabled. As such,
|
||||
* KubernetesInformerDiscoveryClientAutoConfiguration::indicatorInitializer will post
|
||||
* an InstanceRegisteredEvent.
|
||||
*
|
||||
* We assert for logs and call '/health' endpoint to see that blocking discovery
|
||||
* client was initialized.
|
||||
*/
|
||||
static void testReactiveConfiguration(K3sContainer k3sContainer, String imageName) {
|
||||
|
||||
waitForLogStatement("Will publish InstanceRegisteredEvent from reactive implementation", k3sContainer,
|
||||
imageName);
|
||||
waitForLogStatement("publishing InstanceRegisteredEvent", k3sContainer, imageName);
|
||||
waitForLogStatement("Discovery Client has been initialized", k3sContainer, imageName);
|
||||
waitForLogStatement(
|
||||
"received InstanceRegisteredEvent from pod with 'app' label value : spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
k3sContainer, imageName);
|
||||
|
||||
WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build();
|
||||
|
||||
String healthResult = healthClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status").isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue(REACTIVE_STATUS)
|
||||
.isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathArrayValue(
|
||||
"$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].details.services")
|
||||
.containsExactlyInAnyOrder("spring-cloud-kubernetes-fabric8-client-discovery", "kubernetes",
|
||||
"external-name-service", "service-wiremock", "busybox-service");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).doesNotHaveJsonPath(BLOCKING_STATUS);
|
||||
|
||||
// test for services also:
|
||||
|
||||
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.assertThat(servicesResult).contains("spring-cloud-kubernetes-fabric8-client-discovery");
|
||||
Assertions.assertThat(servicesResult).contains("kubernetes");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* 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.fabric8.discovery;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.EnvVar;
|
||||
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.apps.Deployment;
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
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.boot.test.json.BasicJsonTester;
|
||||
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.fabric8_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 Fabric8DiscoveryClientHealthIT {
|
||||
|
||||
private static final String REACTIVE_STATUS = "$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].status";
|
||||
|
||||
private static final String BLOCKING_STATUS = "$.components.discoveryComposite.components.discoveryClient.status";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-discovery";
|
||||
|
||||
private static KubernetesClient client;
|
||||
|
||||
private static final BasicJsonTester BASIC_JSON_TESTER = new BasicJsonTester(Fabric8DiscoveryClientHealthIT.class);
|
||||
|
||||
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);
|
||||
client = util.client();
|
||||
util.setUp(NAMESPACE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void after() throws Exception {
|
||||
Commons.cleanUp(IMAGE_NAME, K3S);
|
||||
Commons.systemPrune();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactive is disabled, only blocking is active. As such,
|
||||
* KubernetesInformerDiscoveryClientAutoConfiguration::indicatorInitializer will post
|
||||
* an InstanceRegisteredEvent.
|
||||
*
|
||||
* We assert for logs and call '/health' endpoint to see that blocking discovery
|
||||
* client was initialized.
|
||||
*/
|
||||
@Test
|
||||
void testBlockingConfiguration() {
|
||||
|
||||
manifests(true, false, Phase.CREATE);
|
||||
|
||||
assertLogStatement("Will publish InstanceRegisteredEvent from blocking implementation");
|
||||
assertLogStatement("publishing InstanceRegisteredEvent");
|
||||
assertLogStatement("Discovery Client has been initialized");
|
||||
assertLogStatement(
|
||||
"received InstanceRegisteredEvent from pod with 'app' label value : spring-cloud-kubernetes-fabric8-client-discovery");
|
||||
|
||||
WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build();
|
||||
|
||||
String healthResult = healthClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.discoveryComposite.status").isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue(BLOCKING_STATUS)
|
||||
.isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathArrayValue(
|
||||
"$.components.discoveryComposite.components.discoveryClient.details.services")
|
||||
.containsExactlyInAnyOrder("spring-cloud-kubernetes-fabric8-client-discovery", "kubernetes");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).doesNotHaveJsonPath(REACTIVE_STATUS);
|
||||
|
||||
manifests(true, false, Phase.DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Both blocking and reactive are enabled.
|
||||
*/
|
||||
@Test
|
||||
void testDefaultConfiguration() {
|
||||
|
||||
manifests(false, false, Phase.CREATE);
|
||||
|
||||
assertLogStatement("Will publish InstanceRegisteredEvent from blocking implementation");
|
||||
assertLogStatement("publishing InstanceRegisteredEvent");
|
||||
assertLogStatement("Discovery Client has been initialized");
|
||||
assertLogStatement("received InstanceRegisteredEvent from pod with 'app' label value : "
|
||||
+ "spring-cloud-kubernetes-fabric8-client-discovery");
|
||||
|
||||
WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build();
|
||||
|
||||
String healthResult = healthClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.discoveryComposite.status").isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.discoveryComposite.components.discoveryClient.status")
|
||||
.isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathArrayValue(
|
||||
"$.components.discoveryComposite.components.discoveryClient.details.services")
|
||||
.containsExactlyInAnyOrder("spring-cloud-kubernetes-fabric8-client-discovery", "kubernetes");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status").isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue(
|
||||
"$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].status")
|
||||
.isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathArrayValue(
|
||||
"$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].details.services")
|
||||
.containsExactlyInAnyOrder("spring-cloud-kubernetes-fabric8-client-discovery", "kubernetes");
|
||||
|
||||
manifests(false, false, Phase.DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactive is enabled, blocking is disabled. As such,
|
||||
* KubernetesInformerDiscoveryClientAutoConfiguration::indicatorInitializer will post
|
||||
* an InstanceRegisteredEvent.
|
||||
*
|
||||
* We assert for logs and call '/health' endpoint to see that blocking discovery
|
||||
* client was initialized.
|
||||
*/
|
||||
@Test
|
||||
void testReactiveConfiguration() {
|
||||
|
||||
manifests(false, true, Phase.CREATE);
|
||||
|
||||
assertLogStatement("Will publish InstanceRegisteredEvent from reactive implementation");
|
||||
assertLogStatement("publishing InstanceRegisteredEvent");
|
||||
assertLogStatement("Discovery Client has been initialized");
|
||||
assertLogStatement(
|
||||
"received InstanceRegisteredEvent from pod with 'app' label value : spring-cloud-kubernetes-fabric8-client-discovery");
|
||||
|
||||
WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build();
|
||||
|
||||
String healthResult = healthClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult))
|
||||
.extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status").isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue(REACTIVE_STATUS)
|
||||
.isEqualTo("UP");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathArrayValue(
|
||||
"$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].details.services")
|
||||
.containsExactlyInAnyOrder("spring-cloud-kubernetes-fabric8-client-discovery", "kubernetes");
|
||||
|
||||
Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).doesNotHaveJsonPath(BLOCKING_STATUS);
|
||||
|
||||
// test for services also:
|
||||
|
||||
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.assertThat(servicesResult).contains("spring-cloud-kubernetes-fabric8-client-discovery");
|
||||
Assertions.assertThat(servicesResult).contains("kubernetes");
|
||||
|
||||
manifests(false, true, Phase.DELETE);
|
||||
}
|
||||
|
||||
private static void manifests(boolean disableReactive, boolean disableBlocking, Phase phase) {
|
||||
|
||||
InputStream deploymentStream = util.inputStream("fabric8-discovery-deployment.yaml");
|
||||
InputStream serviceStream = util.inputStream("fabric8-discovery-service.yaml");
|
||||
InputStream ingressStream = util.inputStream("fabric8-discovery-ingress.yaml");
|
||||
|
||||
Deployment deployment = client.apps().deployments().load(deploymentStream).item();
|
||||
List<EnvVar> envVars = new ArrayList<>(
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
|
||||
|
||||
EnvVar debugLevelForCommons = new EnvVarBuilder()
|
||||
.withName("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_DISCOVERY").withValue("DEBUG")
|
||||
.build();
|
||||
|
||||
if (!disableBlocking) {
|
||||
EnvVar debugBlockingEnvVar = new EnvVarBuilder()
|
||||
.withName("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH").withValue("DEBUG")
|
||||
.build();
|
||||
|
||||
EnvVar debugLevelForBlocking = new EnvVarBuilder()
|
||||
.withName("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY").withValue("DEBUG")
|
||||
.build();
|
||||
|
||||
envVars.add(debugBlockingEnvVar);
|
||||
envVars.add(debugLevelForBlocking);
|
||||
}
|
||||
|
||||
if (!disableReactive) {
|
||||
EnvVar debugReactiveEnvVar = new EnvVarBuilder()
|
||||
.withName("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH_REACTIVE")
|
||||
.withValue("DEBUG").build();
|
||||
|
||||
EnvVar debugLevelForReactive = new EnvVarBuilder()
|
||||
.withName("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY_REACTIVE")
|
||||
.withValue("DEBUG").build();
|
||||
|
||||
EnvVar debugLevelForBlocking = new EnvVarBuilder()
|
||||
.withName("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY").withValue("DEBUG")
|
||||
.build();
|
||||
|
||||
envVars.add(debugReactiveEnvVar);
|
||||
envVars.add(debugLevelForBlocking);
|
||||
envVars.add(debugLevelForReactive);
|
||||
}
|
||||
|
||||
Service service = client.services().load(serviceStream).item();
|
||||
Ingress ingress = client.network().v1().ingresses().load(ingressStream).item();
|
||||
|
||||
if (disableBlocking) {
|
||||
EnvVar disableBlockingEnvVar = new EnvVarBuilder().withName("SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED")
|
||||
.withValue("FALSE").build();
|
||||
envVars.add(disableBlockingEnvVar);
|
||||
}
|
||||
|
||||
if (disableReactive) {
|
||||
EnvVar disableReactiveEnvVar = new EnvVarBuilder().withName("SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED")
|
||||
.withValue("FALSE").build();
|
||||
envVars.add(disableReactiveEnvVar);
|
||||
}
|
||||
|
||||
envVars.add(debugLevelForCommons);
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
}
|
||||
else {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void assertLogStatement(String message) {
|
||||
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.assertThat(ok).contains(message);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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.fabric8.discovery;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.util.retry.Retry;
|
||||
import reactor.util.retry.RetryBackoffSpec;
|
||||
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
final class Fabric8DiscoveryClientUtil {
|
||||
|
||||
private Fabric8DiscoveryClientUtil() {
|
||||
|
||||
}
|
||||
|
||||
static final String BODY_ONE = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_INCLUDEEXTERNALNAMESERVICES",
|
||||
"value": "TRUE"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
static final String BODY_TWO = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED",
|
||||
"value": "FALSE"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
static final String BODY_THREE = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH_REACTIVE",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY_REACTIVE",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
static final String BODY_FOUR = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH_REACTIVE",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY_REACTIVE",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED",
|
||||
"value": "FALSE"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
static final String BODY_FIVE = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0",
|
||||
"value": "a-uat"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1",
|
||||
"value": "b-uat"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_FILTER",
|
||||
"value": "#root.metadata.namespace matches '^.*uat$'"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
static final String BODY_SIX = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0",
|
||||
"value": "a-uat"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1",
|
||||
"value": "b-uat"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_FILTER",
|
||||
"value": "#root.metadata.namespace matches 'a-uat$'"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
|
||||
"value": "DEBUG"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
static final String BODY_SEVEN = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-fabric8-client-discovery",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0",
|
||||
"value": "namespace-left"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
static WebClient.Builder builder() {
|
||||
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
|
||||
}
|
||||
|
||||
static RetryBackoffSpec retrySpec() {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
static void waitForLogStatement(String message, K3sContainer k3sContainer, String imageName) {
|
||||
try {
|
||||
String appPodName = k3sContainer.execInContainer("sh", "-c",
|
||||
"kubectl get pods -l app=" + imageName + " -o=name --no-headers | tr -d '\n'").getStdout();
|
||||
|
||||
await().atMost(Duration.ofMinutes(2)).pollInterval(Duration.ofSeconds(2)).until(() -> {
|
||||
String execResult = k3sContainer.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim())
|
||||
.getStdout();
|
||||
return execResult.contains(message);
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.discovery;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.builder;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.retrySpec;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
final class Fabric8DiscoveryDelegate {
|
||||
|
||||
private Fabric8DiscoveryDelegate() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* KubernetesDiscoveryClient::getServices call must include the external-name-service
|
||||
* also.
|
||||
*/
|
||||
static void testAllServices() {
|
||||
WebClient client = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
List<String> result = client.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<String>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(result.size(), 5);
|
||||
Assertions.assertTrue(result.contains("kubernetes"));
|
||||
Assertions.assertTrue(result.contains("spring-cloud-kubernetes-fabric8-client-discovery"));
|
||||
Assertions.assertTrue(result.contains("service-wiremock"));
|
||||
Assertions.assertTrue(result.contains("busybox-service"));
|
||||
Assertions.assertTrue(result.contains("external-name-service"));
|
||||
}
|
||||
|
||||
static void testExternalNameServiceInstance() {
|
||||
|
||||
WebClient client = builder().baseUrl("http://localhost/service-instances/external-name-service").build();
|
||||
List<DefaultKubernetesServiceInstance> serviceInstances = client.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
DefaultKubernetesServiceInstance result = serviceInstances.get(0);
|
||||
|
||||
Assertions.assertEquals(serviceInstances.size(), 1);
|
||||
Assertions.assertEquals(result.getServiceId(), "external-name-service");
|
||||
Assertions.assertNotNull(result.getInstanceId());
|
||||
Assertions.assertEquals(result.getHost(), "spring.io");
|
||||
Assertions.assertEquals(result.getPort(), -1);
|
||||
Assertions.assertEquals(result.getMetadata(), Map.of("k8s_namespace", "default", "type", "ExternalName"));
|
||||
Assertions.assertFalse(result.isSecure());
|
||||
Assertions.assertEquals(result.getUri().toASCIIString(), "spring.io");
|
||||
Assertions.assertEquals(result.getScheme(), "http");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.fabric8.discovery;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.builder;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.retrySpec;
|
||||
|
||||
final class Fabric8DiscoveryFilterDelegate {
|
||||
|
||||
private Fabric8DiscoveryFilterDelegate() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - service "wiremock" is present in namespace "a-uat"
|
||||
* - service "wiremock" is present in namespace "b-uat"
|
||||
*
|
||||
* - we search with a predicate : "#root.metadata.namespace matches '^uat.*$'"
|
||||
*
|
||||
* As such, both services are found via 'getInstances' call.
|
||||
* </pre>
|
||||
*/
|
||||
static void filterMatchesBothNamespacesViaThePredicate() {
|
||||
|
||||
WebClient clientServices = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> services = (List<String>) clientServices.method(HttpMethod.GET).retrieve().bodyToMono(List.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(services.size(), 1);
|
||||
Assertions.assertTrue(services.contains("service-wiremock"));
|
||||
|
||||
WebClient client = builder().baseUrl("http://localhost/service-instances/service-wiremock").build();
|
||||
List<DefaultKubernetesServiceInstance> serviceInstances = client.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(serviceInstances.size(), 2);
|
||||
List<DefaultKubernetesServiceInstance> sorted = serviceInstances.stream()
|
||||
.sorted(Comparator.comparing(DefaultKubernetesServiceInstance::getNamespace)).toList();
|
||||
|
||||
DefaultKubernetesServiceInstance first = sorted.get(0);
|
||||
Assertions.assertEquals(first.getServiceId(), "service-wiremock");
|
||||
Assertions.assertNotNull(first.getInstanceId());
|
||||
Assertions.assertEquals(first.getPort(), 8080);
|
||||
Assertions.assertEquals(first.getNamespace(), "a-uat");
|
||||
Assertions.assertEquals(first.getMetadata(),
|
||||
Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP"));
|
||||
|
||||
DefaultKubernetesServiceInstance second = sorted.get(1);
|
||||
Assertions.assertEquals(second.getServiceId(), "service-wiremock");
|
||||
Assertions.assertNotNull(second.getInstanceId());
|
||||
Assertions.assertEquals(second.getPort(), 8080);
|
||||
Assertions.assertEquals(second.getNamespace(), "b-uat");
|
||||
Assertions.assertEquals(second.getMetadata(),
|
||||
Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "b-uat", "type", "ClusterIP"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - service "wiremock" is present in namespace "a-uat"
|
||||
* - service "wiremock" is present in namespace "b-uat"
|
||||
*
|
||||
* - we search with a predicate : "#root.metadata.namespace matches 'a-uat$'"
|
||||
*
|
||||
* As such, only service from 'a-uat' namespace matches.
|
||||
* </pre>
|
||||
*/
|
||||
static void filterMatchesOneNamespaceViaThePredicate() {
|
||||
|
||||
WebClient clientServices = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> services = (List<String>) clientServices.method(HttpMethod.GET).retrieve().bodyToMono(List.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(services.size(), 1);
|
||||
Assertions.assertTrue(services.contains("service-wiremock"));
|
||||
|
||||
WebClient client = builder().baseUrl("http://localhost/service-instances/service-wiremock").build();
|
||||
List<DefaultKubernetesServiceInstance> serviceInstances = client.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(serviceInstances.size(), 1);
|
||||
|
||||
DefaultKubernetesServiceInstance first = serviceInstances.get(0);
|
||||
Assertions.assertEquals(first.getServiceId(), "service-wiremock");
|
||||
Assertions.assertNotNull(first.getInstanceId());
|
||||
Assertions.assertEquals(first.getPort(), 8080);
|
||||
Assertions.assertEquals(first.getNamespace(), "a-uat");
|
||||
Assertions.assertEquals(first.getMetadata(),
|
||||
Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* 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.fabric8.discovery;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.EnvVar;
|
||||
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.apps.Deployment;
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
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.fabric8_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;
|
||||
|
||||
class Fabric8DiscoveryFilterIT {
|
||||
|
||||
private static final String FILTER_BOTH_NAMESPACES = "#root.metadata.namespace matches '^.*uat$'";
|
||||
|
||||
private static final String FILTER_SINGLE_NAMESPACE = "#root.metadata.namespace matches 'a-uat$'";
|
||||
|
||||
private static final String NAMESPACE_A_UAT = "a-uat";
|
||||
|
||||
private static final String NAMESPACE_B_UAT = "b-uat";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-discovery";
|
||||
|
||||
private static KubernetesClient client;
|
||||
|
||||
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);
|
||||
client = util.client();
|
||||
util.setUp(NAMESPACE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void afterAll() {
|
||||
Commons.systemPrune();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
util.createNamespace(NAMESPACE_A_UAT);
|
||||
util.createNamespace(NAMESPACE_B_UAT);
|
||||
util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.DELETE);
|
||||
util.deleteNamespace(NAMESPACE_A_UAT);
|
||||
util.deleteNamespace(NAMESPACE_B_UAT);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void after() throws Exception {
|
||||
Commons.cleanUp(IMAGE_NAME, K3S);
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - service "wiremock" is present in namespace "a-uat"
|
||||
* - service "wiremock" is present in namespace "b-uat"
|
||||
*
|
||||
* - we search with a predicate : "#root.metadata.namespace matches '^uat.*$'"
|
||||
*
|
||||
* As such, both services are found via 'getInstances' call.
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void filterMatchesBothNamespacesViaThePredicate() {
|
||||
|
||||
manifests(Phase.CREATE, FILTER_BOTH_NAMESPACES);
|
||||
|
||||
WebClient clientServices = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> services = (List<String>) clientServices.method(HttpMethod.GET).retrieve().bodyToMono(List.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(services.size(), 1);
|
||||
Assertions.assertTrue(services.contains("service-wiremock"));
|
||||
|
||||
WebClient client = builder().baseUrl("http://localhost/service-instances/service-wiremock").build();
|
||||
List<DefaultKubernetesServiceInstance> serviceInstances = client.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(serviceInstances.size(), 2);
|
||||
List<DefaultKubernetesServiceInstance> sorted = serviceInstances.stream()
|
||||
.sorted(Comparator.comparing(DefaultKubernetesServiceInstance::getNamespace)).toList();
|
||||
|
||||
DefaultKubernetesServiceInstance first = sorted.get(0);
|
||||
Assertions.assertEquals(first.getServiceId(), "service-wiremock");
|
||||
Assertions.assertNotNull(first.getInstanceId());
|
||||
Assertions.assertEquals(first.getPort(), 8080);
|
||||
Assertions.assertEquals(first.getNamespace(), "a-uat");
|
||||
Assertions.assertEquals(first.getMetadata(),
|
||||
Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP"));
|
||||
|
||||
DefaultKubernetesServiceInstance second = sorted.get(1);
|
||||
Assertions.assertEquals(second.getServiceId(), "service-wiremock");
|
||||
Assertions.assertNotNull(second.getInstanceId());
|
||||
Assertions.assertEquals(second.getPort(), 8080);
|
||||
Assertions.assertEquals(second.getNamespace(), "b-uat");
|
||||
Assertions.assertEquals(second.getMetadata(),
|
||||
Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "b-uat", "type", "ClusterIP"));
|
||||
|
||||
manifests(Phase.DELETE, FILTER_BOTH_NAMESPACES);
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - service "wiremock" is present in namespace "a-uat"
|
||||
* - service "wiremock" is present in namespace "b-uat"
|
||||
*
|
||||
* - we search with a predicate : "#root.metadata.namespace matches 'a-uat$'"
|
||||
*
|
||||
* As such, only service from 'a-uat' namespace matches.
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void filterMatchesOneNamespaceViaThePredicate() {
|
||||
manifests(Phase.CREATE, FILTER_SINGLE_NAMESPACE);
|
||||
|
||||
WebClient clientServices = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> services = (List<String>) clientServices.method(HttpMethod.GET).retrieve().bodyToMono(List.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(services.size(), 1);
|
||||
Assertions.assertTrue(services.contains("service-wiremock"));
|
||||
|
||||
WebClient client = builder().baseUrl("http://localhost/service-instances/service-wiremock").build();
|
||||
List<DefaultKubernetesServiceInstance> serviceInstances = client.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(serviceInstances.size(), 1);
|
||||
|
||||
DefaultKubernetesServiceInstance first = serviceInstances.get(0);
|
||||
Assertions.assertEquals(first.getServiceId(), "service-wiremock");
|
||||
Assertions.assertNotNull(first.getInstanceId());
|
||||
Assertions.assertEquals(first.getPort(), 8080);
|
||||
Assertions.assertEquals(first.getNamespace(), "a-uat");
|
||||
Assertions.assertEquals(first.getMetadata(),
|
||||
Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP"));
|
||||
|
||||
manifests(Phase.DELETE, FILTER_SINGLE_NAMESPACE);
|
||||
}
|
||||
|
||||
private static void manifests(Phase phase, String serviceFilter) {
|
||||
|
||||
InputStream deploymentStream = util.inputStream("fabric8-discovery-deployment.yaml");
|
||||
InputStream serviceStream = util.inputStream("fabric8-discovery-service.yaml");
|
||||
InputStream ingressStream = util.inputStream("fabric8-discovery-ingress.yaml");
|
||||
|
||||
Deployment deployment = client.apps().deployments().load(deploymentStream).item();
|
||||
List<EnvVar> envVars = new ArrayList<>(
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
|
||||
EnvVar namespaceAUat = new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
|
||||
.withValue(NAMESPACE_A_UAT).build();
|
||||
EnvVar namespaceBUat = new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1")
|
||||
.withValue(NAMESPACE_B_UAT).build();
|
||||
EnvVar filter = new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_FILTER")
|
||||
.withValue(serviceFilter).build();
|
||||
EnvVar debug = new EnvVarBuilder()
|
||||
.withName("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY").withValue("DEBUG")
|
||||
.build();
|
||||
envVars.add(namespaceAUat);
|
||||
envVars.add(namespaceBUat);
|
||||
envVars.add(filter);
|
||||
envVars.add(debug);
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
|
||||
|
||||
Service service = client.services().load(serviceStream).item();
|
||||
Ingress ingress = client.network().v1().ingresses().load(ingressStream).item();
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
client.rbac().clusterRoleBindings()
|
||||
.resource(client.rbac().clusterRoleBindings().load(getAdminRole()).item()).create();
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
}
|
||||
else {
|
||||
client.rbac().clusterRoleBindings()
|
||||
.resource(client.rbac().clusterRoleBindings().load(getAdminRole()).item()).delete();
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static InputStream getAdminRole() {
|
||||
return util.inputStream("namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml");
|
||||
}
|
||||
|
||||
private WebClient.Builder builder() {
|
||||
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
|
||||
}
|
||||
|
||||
private RetryBackoffSpec retrySpec() {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(2)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.discovery;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.EnvVar;
|
||||
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.apps.Deployment;
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
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.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.fabric8_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 Fabric8DiscoveryIT {
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-discovery";
|
||||
|
||||
private static KubernetesClient client;
|
||||
|
||||
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);
|
||||
client = util.client();
|
||||
|
||||
util.setUp(NAMESPACE);
|
||||
|
||||
manifests(Phase.CREATE);
|
||||
util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void after() throws Exception {
|
||||
util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE);
|
||||
manifests(Phase.DELETE);
|
||||
Commons.cleanUp(IMAGE_NAME, K3S);
|
||||
Commons.systemPrune();
|
||||
}
|
||||
|
||||
/**
|
||||
* KubernetesDiscoveryClient::getServices call must include the external-name-service
|
||||
* also.
|
||||
*/
|
||||
@Test
|
||||
void testAllServices() {
|
||||
WebClient client = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
List<String> result = client.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<String>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(result.size(), 4);
|
||||
Assertions.assertTrue(result.contains("kubernetes"));
|
||||
Assertions.assertTrue(result.contains("spring-cloud-kubernetes-fabric8-client-discovery"));
|
||||
Assertions.assertTrue(result.contains("service-wiremock"));
|
||||
Assertions.assertTrue(result.contains("external-name-service"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExternalNameServiceInstance() {
|
||||
|
||||
WebClient client = builder().baseUrl("http://localhost/service-instances/external-name-service").build();
|
||||
List<DefaultKubernetesServiceInstance> serviceInstances = client.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<DefaultKubernetesServiceInstance>>() {
|
||||
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
DefaultKubernetesServiceInstance result = serviceInstances.get(0);
|
||||
|
||||
Assertions.assertEquals(serviceInstances.size(), 1);
|
||||
Assertions.assertEquals(result.getServiceId(), "external-name-service");
|
||||
Assertions.assertNotNull(result.getInstanceId());
|
||||
Assertions.assertEquals(result.getHost(), "spring.io");
|
||||
Assertions.assertEquals(result.getPort(), -1);
|
||||
Assertions.assertEquals(result.getMetadata(), Map.of("k8s_namespace", "default", "type", "ExternalName"));
|
||||
Assertions.assertFalse(result.isSecure());
|
||||
Assertions.assertEquals(result.getUri().toASCIIString(), "spring.io");
|
||||
Assertions.assertEquals(result.getScheme(), "http");
|
||||
}
|
||||
|
||||
private static void manifests(Phase phase) {
|
||||
|
||||
InputStream deploymentStream = util.inputStream("fabric8-discovery-deployment.yaml");
|
||||
InputStream serviceStream = util.inputStream("fabric8-discovery-service.yaml");
|
||||
InputStream ingressStream = util.inputStream("fabric8-discovery-ingress.yaml");
|
||||
InputStream externalNameServiceInputStream = util.inputStream("external-name-service.yaml");
|
||||
|
||||
Deployment deployment = client.apps().deployments().load(deploymentStream).item();
|
||||
|
||||
List<EnvVar> existing = new ArrayList<>(
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
|
||||
existing.add(new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_INCLUDEEXTERNALNAMESERVICES")
|
||||
.withValue("true").build());
|
||||
existing.add(
|
||||
new EnvVarBuilder().withName("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY")
|
||||
.withValue("DEBUG").build());
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(existing);
|
||||
|
||||
Service service = client.services().load(serviceStream).item();
|
||||
Service externalNameService = client.services().load(externalNameServiceInputStream).item();
|
||||
Ingress ingress = client.network().v1().ingresses().load(ingressStream).item();
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
util.createAndWait(NAMESPACE, null, null, externalNameService, null, false);
|
||||
}
|
||||
else {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
util.deleteAndWait(NAMESPACE, null, externalNameService, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.discovery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.builder;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.retrySpec;
|
||||
|
||||
/**
|
||||
* @author mbialkowski1
|
||||
*/
|
||||
final class Fabric8DiscoveryNamespaceDelegate {
|
||||
|
||||
private Fabric8DiscoveryNamespaceDelegate() {
|
||||
|
||||
}
|
||||
|
||||
static void namespaceFilter() {
|
||||
WebClient clientServices = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> services = (List<String>) clientServices.method(HttpMethod.GET).retrieve().bodyToMono(List.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(services.size(), 1);
|
||||
Assertions.assertTrue(services.contains("service-wiremock"));
|
||||
|
||||
WebClient clientEndpoints = builder().baseUrl("http://localhost/endpoints/service-wiremock").build();
|
||||
|
||||
List<Endpoints> endpoints = clientEndpoints.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<Endpoints>>() {
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(endpoints.size(), 1);
|
||||
Assertions.assertEquals(endpoints.get(0).getMetadata().getNamespace(), "namespace-left");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.discovery;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import io.fabric8.kubernetes.api.model.EnvVar;
|
||||
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.apps.Deployment;
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
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.k3s.K3sContainer;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.util.retry.Retry;
|
||||
import reactor.util.retry.RetryBackoffSpec;
|
||||
|
||||
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.fabric8_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 mbialkowski1
|
||||
*/
|
||||
class Fabric8DiscoveryNamespaceFilterIT {
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final String NAMESPACE_LEFT = "namespace-left";
|
||||
|
||||
private static final String NAMESPACE_RIGHT = "namespace-right";
|
||||
|
||||
private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-discovery";
|
||||
|
||||
private static Util util;
|
||||
|
||||
private static KubernetesClient client;
|
||||
|
||||
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);
|
||||
client = util.client();
|
||||
util.setUp(NAMESPACE);
|
||||
|
||||
manifests(Phase.CREATE);
|
||||
util.createNamespace(NAMESPACE_LEFT);
|
||||
util.createNamespace(NAMESPACE_RIGHT);
|
||||
util.wiremock(NAMESPACE_LEFT, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_RIGHT, "/wiremock", Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void after() throws Exception {
|
||||
manifests(Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_LEFT, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_RIGHT, "/wiremock", Phase.DELETE);
|
||||
util.deleteNamespace(NAMESPACE_LEFT);
|
||||
util.deleteNamespace(NAMESPACE_RIGHT);
|
||||
Commons.cleanUp(IMAGE_NAME, K3S);
|
||||
Commons.systemPrune();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
WebClient clientServices = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> services = (List<String>) clientServices.method(HttpMethod.GET).retrieve().bodyToMono(List.class)
|
||||
.retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(services.size(), 1);
|
||||
Assertions.assertTrue(services.contains("service-wiremock"));
|
||||
|
||||
WebClient clientEndpoints = builder().baseUrl("http://localhost/endpoints/service-wiremock").build();
|
||||
|
||||
List<Endpoints> endpoints = clientEndpoints.method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<List<Endpoints>>() {
|
||||
}).retryWhen(retrySpec()).block();
|
||||
|
||||
Assertions.assertEquals(endpoints.size(), 1);
|
||||
Assertions.assertEquals(endpoints.get(0).getMetadata().getNamespace(), NAMESPACE_LEFT);
|
||||
|
||||
}
|
||||
|
||||
private static void manifests(Phase phase) {
|
||||
|
||||
InputStream deploymentStream = util.inputStream("fabric8-discovery-deployment.yaml");
|
||||
InputStream serviceStream = util.inputStream("fabric8-discovery-service.yaml");
|
||||
InputStream ingressStream = util.inputStream("fabric8-discovery-ingress.yaml");
|
||||
|
||||
Deployment deployment = client.apps().deployments().load(deploymentStream).item();
|
||||
List<EnvVar> envVars = new ArrayList<>(
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
|
||||
EnvVar activeProfileProperty = new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
|
||||
.withValue(NAMESPACE_LEFT).build();
|
||||
envVars.add(activeProfileProperty);
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
|
||||
|
||||
Service service = client.services().load(serviceStream).item();
|
||||
Ingress ingress = client.network().v1().ingresses().load(ingressStream).item();
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
client.rbac().clusterRoleBindings()
|
||||
.resource(client.rbac().clusterRoleBindings().load(getAdminRole()).item()).create();
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
}
|
||||
else {
|
||||
client.rbac().clusterRoleBindings()
|
||||
.resource(client.rbac().clusterRoleBindings().load(getAdminRole()).item()).delete();
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static InputStream getAdminRole() {
|
||||
return util.inputStream("namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml");
|
||||
}
|
||||
|
||||
private WebClient.Builder builder() {
|
||||
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
|
||||
}
|
||||
|
||||
private RetryBackoffSpec retrySpec() {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(2)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,15 +47,36 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.BODY_FIVE;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.BODY_FOUR;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.BODY_ONE;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.BODY_SEVEN;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.BODY_SIX;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.BODY_THREE;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.BODY_TWO;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
class Fabric8DiscoveryPodMetadataIT {
|
||||
|
||||
private static final String DEPLOYMENT_NAME = "spring-cloud-kubernetes-fabric8-client-discovery-deployment";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final String NAMESPACE_A_UAT = "a-uat";
|
||||
|
||||
private static final String NAMESPACE_B_UAT = "b-uat";
|
||||
|
||||
private static final String NAMESPACE_LEFT = "namespace-left";
|
||||
|
||||
private static final String NAMESPACE_RIGHT = "namespace-right";
|
||||
|
||||
private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-discovery";
|
||||
|
||||
private static final String DOCKER_IMAGE = "docker.io/springcloud/" + IMAGE_NAME + ":" + pomVersion();
|
||||
|
||||
private static KubernetesClient client;
|
||||
|
||||
private static Util util;
|
||||
@@ -74,12 +95,35 @@ class Fabric8DiscoveryPodMetadataIT {
|
||||
util.setUp(NAMESPACE);
|
||||
|
||||
manifests(Phase.CREATE);
|
||||
util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE);
|
||||
util.busybox(NAMESPACE, Phase.CREATE);
|
||||
|
||||
util.createNamespace(NAMESPACE_A_UAT);
|
||||
util.createNamespace(NAMESPACE_B_UAT);
|
||||
util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.CREATE);
|
||||
|
||||
util.createNamespace(NAMESPACE_LEFT);
|
||||
util.createNamespace(NAMESPACE_RIGHT);
|
||||
util.wiremock(NAMESPACE_LEFT, "/wiremock", Phase.CREATE);
|
||||
util.wiremock(NAMESPACE_RIGHT, "/wiremock", Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void after() throws Exception {
|
||||
util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE);
|
||||
util.busybox(NAMESPACE, Phase.DELETE);
|
||||
|
||||
util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.DELETE);
|
||||
util.deleteNamespace(NAMESPACE_A_UAT);
|
||||
util.deleteNamespace(NAMESPACE_B_UAT);
|
||||
|
||||
util.wiremock(NAMESPACE_LEFT, "/wiremock", Phase.DELETE);
|
||||
util.wiremock(NAMESPACE_RIGHT, "/wiremock", Phase.DELETE);
|
||||
util.deleteNamespace(NAMESPACE_LEFT);
|
||||
util.deleteNamespace(NAMESPACE_RIGHT);
|
||||
|
||||
manifests(Phase.DELETE);
|
||||
Commons.cleanUp(IMAGE_NAME, K3S);
|
||||
Commons.systemPrune();
|
||||
@@ -123,15 +167,68 @@ class Fabric8DiscoveryPodMetadataIT {
|
||||
Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80"));
|
||||
Assertions.assertTrue(withCustomAnnotation.podMetadata().get("annotations").entrySet().stream().anyMatch(
|
||||
x -> x.getKey().equals("custom-annotation") && x.getValue().equals("custom-annotation-value")));
|
||||
|
||||
testAllOther();
|
||||
}
|
||||
|
||||
private void testAllOther() {
|
||||
testAllServices();
|
||||
testExternalNameServiceInstance();
|
||||
testBlockingConfiguration();
|
||||
testDefaultConfiguration();
|
||||
testReactiveConfiguration();
|
||||
filterMatchesBothNamespacesViaThePredicate();
|
||||
filterMatchesOneNamespaceViaThePredicate();
|
||||
namespaceFilter();
|
||||
}
|
||||
|
||||
private void testAllServices() {
|
||||
util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_ONE, Map.of("app", IMAGE_NAME));
|
||||
Fabric8DiscoveryDelegate.testAllServices();
|
||||
}
|
||||
|
||||
private void testExternalNameServiceInstance() {
|
||||
Fabric8DiscoveryDelegate.testExternalNameServiceInstance();
|
||||
}
|
||||
|
||||
private void testBlockingConfiguration() {
|
||||
util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_TWO, Map.of("app", IMAGE_NAME));
|
||||
Fabric8DiscoveryClientHealthDelegate.testBlockingConfiguration(K3S, IMAGE_NAME);
|
||||
}
|
||||
|
||||
private void testDefaultConfiguration() {
|
||||
util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_THREE, Map.of("app", IMAGE_NAME));
|
||||
Fabric8DiscoveryClientHealthDelegate.testDefaultConfiguration(K3S, IMAGE_NAME);
|
||||
}
|
||||
|
||||
private void testReactiveConfiguration() {
|
||||
util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_FOUR, Map.of("app", IMAGE_NAME));
|
||||
Fabric8DiscoveryClientHealthDelegate.testReactiveConfiguration(K3S, IMAGE_NAME);
|
||||
}
|
||||
|
||||
private void filterMatchesBothNamespacesViaThePredicate() {
|
||||
util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_FIVE, Map.of("app", IMAGE_NAME));
|
||||
Fabric8DiscoveryFilterDelegate.filterMatchesBothNamespacesViaThePredicate();
|
||||
}
|
||||
|
||||
private void filterMatchesOneNamespaceViaThePredicate() {
|
||||
util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_SIX, Map.of("app", IMAGE_NAME));
|
||||
Fabric8DiscoveryFilterDelegate.filterMatchesOneNamespaceViaThePredicate();
|
||||
}
|
||||
|
||||
private void namespaceFilter() {
|
||||
util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_SEVEN, Map.of("app", IMAGE_NAME));
|
||||
Fabric8DiscoveryNamespaceDelegate.namespaceFilter();
|
||||
}
|
||||
|
||||
private static void manifests(Phase phase) {
|
||||
|
||||
InputStream deploymentStream = util.inputStream("fabric8-discovery-deployment.yaml");
|
||||
InputStream serviceStream = util.inputStream("fabric8-discovery-service.yaml");
|
||||
InputStream externalNameServiceStream = util.inputStream("external-name-service.yaml");
|
||||
InputStream discoveryServiceStream = util.inputStream("fabric8-discovery-service.yaml");
|
||||
InputStream ingressStream = util.inputStream("fabric8-discovery-ingress.yaml");
|
||||
|
||||
Deployment deployment = client.apps().deployments().load(deploymentStream).item();
|
||||
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
|
||||
|
||||
List<EnvVar> existing = new ArrayList<>(
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
|
||||
@@ -144,18 +241,29 @@ class Fabric8DiscoveryPodMetadataIT {
|
||||
.withValue("DEBUG").build());
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(existing);
|
||||
|
||||
Service service = client.services().load(serviceStream).item();
|
||||
Ingress ingress = client.network().v1().ingresses().load(ingressStream).item();
|
||||
Service externalServiceName = client.services().load(externalNameServiceStream).get();
|
||||
Service discoveryService = client.services().load(discoveryServiceStream).get();
|
||||
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
|
||||
client.rbac().clusterRoleBindings().resource(client.rbac().clusterRoleBindings().load(getAdminRole()).get())
|
||||
.create();
|
||||
util.createAndWait(NAMESPACE, IMAGE_NAME, deployment, discoveryService, ingress, true);
|
||||
util.createAndWait(NAMESPACE, null, null, externalServiceName, null, true);
|
||||
}
|
||||
else {
|
||||
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
|
||||
client.rbac().clusterRoleBindings().resource(client.rbac().clusterRoleBindings().load(getAdminRole()).get())
|
||||
.delete();
|
||||
util.deleteAndWait(NAMESPACE, deployment, discoveryService, ingress);
|
||||
util.deleteAndWait(NAMESPACE, null, externalServiceName, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static InputStream getAdminRole() {
|
||||
return util.inputStream("namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml");
|
||||
}
|
||||
|
||||
private WebClient.Builder builder() {
|
||||
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -30,6 +31,7 @@ import io.fabric8.kubernetes.api.model.Secret;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServiceAccount;
|
||||
import io.fabric8.kubernetes.api.model.apps.Deployment;
|
||||
import io.fabric8.kubernetes.api.model.apps.DeploymentList;
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.IngressLoadBalancerIngress;
|
||||
import io.fabric8.kubernetes.api.model.rbac.ClusterRole;
|
||||
@@ -38,6 +40,8 @@ import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
|
||||
import io.fabric8.kubernetes.client.dsl.base.PatchContext;
|
||||
import io.fabric8.kubernetes.client.dsl.base.PatchType;
|
||||
import jakarta.annotation.Nullable;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -46,6 +50,7 @@ import org.testcontainers.k3s.K3sContainer;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.loadImage;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pullImage;
|
||||
@@ -366,6 +371,50 @@ public final class Util {
|
||||
|
||||
}
|
||||
|
||||
public void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody,
|
||||
Map<String, String> labels) {
|
||||
String body = patchBody.replace("image_name_here", imageName);
|
||||
|
||||
client.apps().deployments().inNamespace(namespace).withName(deploymentName)
|
||||
.patch(PatchContext.of(PatchType.JSON_MERGE), body);
|
||||
|
||||
waitForDeploymentAfterPatch(deploymentName, namespace, labels);
|
||||
}
|
||||
|
||||
private void waitForDeploymentAfterPatch(String deploymentName, String namespace, Map<String, String> labels) {
|
||||
try {
|
||||
await().pollDelay(Duration.ofSeconds(4)).pollInterval(Duration.ofSeconds(3)).atMost(60, TimeUnit.SECONDS)
|
||||
.until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace, labels));
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace, Map<String, String> labels) {
|
||||
|
||||
DeploymentList deployments = client.apps().deployments().inNamespace(namespace).list();
|
||||
|
||||
if (deployments.getItems().isEmpty()) {
|
||||
fail("No deployment with name " + deploymentName);
|
||||
}
|
||||
|
||||
Deployment deployment = deployments.getItems().get(0);
|
||||
// if no replicas are defined, it means only 1 is needed
|
||||
int replicas = Optional.ofNullable(deployment.getSpec().getReplicas()).orElse(1);
|
||||
|
||||
int numberOfPods = client.pods().inNamespace(namespace).withLabels(labels).list().getItems().size();
|
||||
|
||||
if (numberOfPods != replicas) {
|
||||
LOG.info("number of pods not yet stabilized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return replicas == Optional.ofNullable(deployment.getStatus().getReadyReplicas()).orElse(0);
|
||||
|
||||
}
|
||||
|
||||
private void innerSetup(String namespace, InputStream serviceAccountAsStream, InputStream roleBindingAsStream,
|
||||
InputStream roleAsStream) {
|
||||
ServiceAccount serviceAccountFromStream = client.serviceAccounts().inNamespace(namespace)
|
||||
|
||||
@@ -610,7 +610,7 @@ public final class Util {
|
||||
|
||||
V1DeploymentList deployments = appsV1Api.listNamespacedDeployment(namespace, null, null, null,
|
||||
"metadata.name=" + deploymentName, null, null, null, null, null, null);
|
||||
if (deployments.getItems().size() < 1) {
|
||||
if (deployments.getItems().isEmpty()) {
|
||||
fail("No deployment with name " + deploymentName);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user