From 40252b6e30083db452d7e68fa91ea8256496ba13 Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 4 Oct 2024 19:31:46 +0300 Subject: [PATCH 01/35] started work --- pom.xml | 13 ++ .../pom.xml | 17 +++ .../catalog/watch/Fabric8CatalogWatchIT.java | 104 +-------------- .../watch/Fabric8CatalogWatchUtil.java | 9 +- .../watch/it/Fabric8CatalogWatchIT.java | 116 ++++++++++++++++ .../catalog/watch/it/SuiteSandbox.java | 33 +++++ .../fabric8/catalog/watch/it/TestUtil.java | 124 ++++++++++++++++++ 7 files changed, 311 insertions(+), 105 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Fabric8CatalogWatchIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/TestUtil.java diff --git a/pom.xml b/pom.xml index c12ab9d73e..4b337d1605 100644 --- a/pom.xml +++ b/pom.xml @@ -180,6 +180,19 @@ ${spring-cloud-commons.version} test + + + org.junit.platform + junit-platform-suite-api + 1.11.2 + + + + org.junit.platform + junit-platform-suite-engine + 1.11.2 + + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml index 0c1e1e3eb7..d8a062a51c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml @@ -30,6 +30,16 @@ spring-cloud-kubernetes-test-support + + org.junit.platform + junit-platform-suite-api + + + + org.junit.platform + junit-platform-suite-engine + + @@ -43,5 +53,12 @@ + + + maven_central + Maven Central + https://repo.maven.apache.org/maven2/ + + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java index 652699eaad..0f65b94556 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java @@ -108,19 +108,18 @@ static void afterAll() { */ @Test void testCatalogWatchWithEndpoints() throws Exception { - assertLogStatement(); - test(); + //test(); - testCatalogWatchWithEndpointSlices(); - testCatalogWatchWithNamespaceFilterAndEndpoints(); - testCatalogWatchWithNamespaceFilterAndEndpointSlices(); +// testCatalogWatchWithEndpointSlices(); +// testCatalogWatchWithNamespaceFilterAndEndpoints(); +// testCatalogWatchWithNamespaceFilterAndEndpointSlices(); } void testCatalogWatchWithEndpointSlices() { util.busybox(NAMESPACE, Phase.CREATE); patchForEndpointSlices(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); Commons.waitForLogStatement("stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch", K3S, IMAGE_NAME); - test(); + //test(); } void testCatalogWatchWithNamespaceFilterAndEndpoints() { @@ -139,99 +138,6 @@ void testCatalogWatchWithNamespaceFilterAndEndpointSlices() { util); } - /** - * we log in debug mode the type of the StateGenerator we use, be that Endpoints or - * EndpointSlices. Here we make sure that in the test we actually use the correct - * type. - */ - private void assertLogStatement() throws Exception { - String appPodName = K3S - .execInContainer("kubectl", "get", "pods", "-l", - "app=spring-cloud-kubernetes-fabric8-client-catalog-watcher", "-o=name", "--no-headers") - .getStdout(); - String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout(); - Assertions.assertTrue(allLogs.contains("stateGenerator is of type: Fabric8EndpointsCatalogWatch")); - } - - /** - * the test is the same for both endpoints and endpoint slices, the set-up for them is - * different. - */ - @SuppressWarnings("unchecked") - private void test() { - - WebClient client = builder().baseUrl("http://localhost/result").build(); - EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[2]; - ResolvableType resolvableType = ResolvableType.forClassWithGenerics(List.class, EndpointNameAndNamespace.class); - - await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(240)).until(() -> { - List result = (List) client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType())) - .retryWhen(retrySpec()) - .block(); - - // we get 3 pods as input, but because they are sorted by name in the catalog - // watcher implementation - // we will get the first busybox instances here. - if (result != null) { - if (result.size() != 3) { - return false; - } - holder[0] = result.get(0); - holder[1] = result.get(1); - return true; - } - - return false; - }); - - EndpointNameAndNamespace resultOne = holder[0]; - EndpointNameAndNamespace resultTwo = holder[1]; - - Assertions.assertNotNull(resultOne); - Assertions.assertNotNull(resultTwo); - - Assertions.assertTrue(resultOne.endpointName().contains("busybox")); - Assertions.assertTrue(resultTwo.endpointName().contains("busybox")); - Assertions.assertEquals("default", resultOne.namespace()); - Assertions.assertEquals("default", resultTwo.namespace()); - - util.busybox(NAMESPACE, Phase.DELETE); - - // what we get after delete - EndpointNameAndNamespace[] afterDelete = new EndpointNameAndNamespace[1]; - - await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(240)).until(() -> { - List result = (List) client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType())) - .retryWhen(retrySpec()) - .block(); - - // we need to get the event from KubernetesCatalogWatch, but that happens - // on periodic bases. So in order to be sure we got the event we care about - // we wait until the result has a single entry, which means busybox was - // deleted - // + KubernetesCatalogWatch received the new update. - if (result != null && result.size() != 1) { - return false; - } - - // we will only receive one pod here, our own - if (result != null) { - afterDelete[0] = result.get(0); - return true; - } - - return false; - }); - - Assertions.assertTrue(afterDelete[0].endpointName().contains(IMAGE_NAME)); - Assertions.assertEquals("default", afterDelete[0].namespace()); - - } - private static void app(Phase phase) { InputStream endpointsDeploymentStream = util.inputStream("app/watcher-endpoints-deployment.yaml"); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchUtil.java index 16b9f818e2..0ee2313e1e 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchUtil.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchUtil.java @@ -62,8 +62,7 @@ private Fabric8CatalogWatchUtil() { } } } - } - """; + }"""; static final String BODY_TWO = """ { @@ -95,8 +94,7 @@ private Fabric8CatalogWatchUtil() { } } } - } - """; + }"""; static final String BODY_THREE = """ { @@ -128,8 +126,7 @@ private Fabric8CatalogWatchUtil() { } } } - } - """; + }"""; static void patchForEndpointSlices(Util util, String dockerImage, String deploymentName, String namespace) { util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_ONE, POD_LABELS); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Fabric8CatalogWatchIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Fabric8CatalogWatchIT.java new file mode 100644 index 0000000000..52c37f6e01 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Fabric8CatalogWatchIT.java @@ -0,0 +1,116 @@ +/* + * Copyright 2012-2024 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.catalog.watch.it; + +import java.util.Map; +import java.util.Set; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.platform.suite.api.BeforeSuite; +import org.junit.platform.suite.api.Suite; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.fabric8.catalog.watch.Application; +import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.testcontainers.k3s.K3sContainer; + +import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; + +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.it.Fabric8CatalogWatchIT.TestConfig; + +/** + * @author wind57 + */ +@Suite +@SpringBootTest( + classes = { + KubernetesCatalogWatchAutoConfiguration.class, + TestConfig.class, + Application.class + }, + properties = { + "spring.main.cloud-platform=kubernetes", + "spring.cloud.config.import-check.enabled=false", + "spring.cloud.kubernetes.discovery.catalogServicesWatchDelay=2000", + "spring.cloud.kubernetes.client.namespace=default", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" + }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT +) +@ExtendWith(OutputCaptureExtension.class) +class Fabric8CatalogWatchIT { + + private static final String NAMESPACE = "default"; + + private static final K3sContainer K3S = Commons.container(); + + private static Util util; + + @LocalServerPort + private int port; + + @BeforeSuite + static void beforeAll() { + K3S.start(); + Images.loadBusybox(K3S); + util = new Util(K3S); + util.busybox(NAMESPACE, Phase.CREATE); + } + + @Test + void test(CapturedOutput output) { + TestUtil.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointsCatalogWatch"); + TestUtil.test(util, NAMESPACE, port); + } + + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + String kubeConfigYaml = K3S.getKubeConfigYaml(); + Config config = Config.fromKubeconfig(kubeConfigYaml); + return new KubernetesClientBuilder().withConfig(config).build(); + } + + @Bean + @Primary + KubernetesDiscoveryProperties kubernetesDiscoveryProperties() { + return new KubernetesDiscoveryProperties(true, false, Set.of(), + true, 60, false, null, Set.of(443, 8443), Map.of(), null, + KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, false, false, null); + } + + } + +} + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java new file mode 100644 index 0000000000..9f366eaae9 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java @@ -0,0 +1,33 @@ +package org.springframework.cloud.kubernetes.fabric8.catalog.watch.it; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.suite.api.AfterSuite; +import org.junit.platform.suite.api.BeforeSuite; +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; + +@Suite +@SelectClasses(SuiteSandbox.InnerTest.class) +public class SuiteSandbox { + + public static class InnerTest { + + @BeforeSuite + public static void beforeSuite() { + System.out.println("go"); + } + + @AfterSuite + static void afterSuite() { + + } + + @Test + void test() { + + } + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/TestUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/TestUtil.java new file mode 100644 index 0000000000..041313c67d --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/TestUtil.java @@ -0,0 +1,124 @@ +/* + * Copyright 2012-2024 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.catalog.watch.it; + +import java.time.Duration; +import java.util.List; +import java.util.Objects; + +import org.assertj.core.api.Assertions; +import org.awaitility.Awaitility; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; + +import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.ResolvableType; +import org.springframework.http.HttpMethod; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.netty.http.client.HttpClient; +import reactor.util.retry.Retry; +import reactor.util.retry.RetryBackoffSpec; + +import static org.awaitility.Awaitility.await; + +/** + * @author wind57 + */ +class TestUtil { + + private TestUtil() { + + } + + static void assertLogStatement(CapturedOutput output, String textToAssert) { + Awaitility.await().during(Duration.ofSeconds(5)) + .pollInterval(Duration.ofMillis(200)) + .untilAsserted(() -> Assertions.assertThat(output.getOut()).contains(textToAssert)); + } + + /** + * the checks are the same for both endpoints and endpoint slices, + * while the set-up for them is different. + */ + @SuppressWarnings("unchecked") + static void test(Util util, String namespace, int port) { + + WebClient client = builder().baseUrl("http://localhost:" + port + "/result").build(); + EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[2]; + ResolvableType resolvableType = ResolvableType.forClassWithGenerics(List.class, EndpointNameAndNamespace.class); + + await().pollInterval(Duration.ofMillis(200)).atMost(Duration.ofSeconds(30)).until(() -> { + List result = (List) client.method(HttpMethod.GET) + .retrieve() + .bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType())) + .retryWhen(retrySpec()) + .block(); + + if (result != null) { + if (result.size() != 2) { + return false; + } + holder[0] = result.get(0); + holder[1] = result.get(1); + return true; + } + + return false; + }); + + EndpointNameAndNamespace resultOne = holder[0]; + EndpointNameAndNamespace resultTwo = holder[1]; + + Assertions.assertThat(resultOne).isNotNull(); + Assertions.assertThat(resultTwo).isNotNull(); + + Assertions.assertThat(resultOne.endpointName()).contains("busybox"); + Assertions.assertThat(resultTwo.endpointName()).contains("busybox"); + + Assertions.assertThat(resultOne.namespace()).isEqualTo("default"); + Assertions.assertThat(resultTwo.namespace()).isEqualTo("default"); + + util.busybox(namespace, Phase.DELETE); + + await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(240)).until(() -> { + List result = (List) client.method(HttpMethod.GET) + .retrieve() + .bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType())) + .retryWhen(retrySpec()) + .block(); + + // we need to get the event from KubernetesCatalogWatch, but that happens + // on periodic bases. So in order to be sure we got the event we care about + // we wait until there is no entry, which means busybox was deleted + // and KubernetesCatalogWatch received that update. + return result.isEmpty(); + }); + + } + + private static WebClient.Builder builder() { + return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); + } + + private static RetryBackoffSpec retrySpec() { + return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull); + } + +} From 86d2e8a190434144538458237152409c6a00b2e8 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sat, 5 Oct 2024 09:49:18 +0300 Subject: [PATCH 02/35] dirty --- pom.xml | 12 --------- .../pom.xml | 16 ++++++++++++ .../catalog/watch/Fabric8CatalogWatchIT.java | 2 +- .../fabric8/catalog/watch/it/Sandbox.java | 12 +++++++++ .../catalog/watch/it/SuiteSandbox.java | 25 +++---------------- 5 files changed, 33 insertions(+), 34 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Sandbox.java diff --git a/pom.xml b/pom.xml index 4b337d1605..9f4d75f76c 100644 --- a/pom.xml +++ b/pom.xml @@ -181,18 +181,6 @@ test - - org.junit.platform - junit-platform-suite-api - 1.11.2 - - - - org.junit.platform - junit-platform-suite-engine - 1.11.2 - - diff --git a/spring-cloud-kubernetes-integration-tests/pom.xml b/spring-cloud-kubernetes-integration-tests/pom.xml index 261c2e1e5a..6b7c4fc15d 100644 --- a/spring-cloud-kubernetes-integration-tests/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/pom.xml @@ -87,6 +87,22 @@ + + + + org.junit.platform + junit-platform-suite-api + 1.11.2 + + + + org.junit.platform + junit-platform-suite-engine + 1.11.2 + + + + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java index 0f65b94556..ef609d6d7d 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java @@ -106,7 +106,7 @@ static void afterAll() { * - assert that we receive only spring-cloud-kubernetes-fabric8-client-catalog-watcher pod * */ - @Test + //@Test void testCatalogWatchWithEndpoints() throws Exception { //test(); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Sandbox.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Sandbox.java new file mode 100644 index 0000000000..5160aafd2e --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Sandbox.java @@ -0,0 +1,12 @@ +package org.springframework.cloud.kubernetes.fabric8.catalog.watch.it; + +import org.junit.jupiter.api.Test; + +public class Sandbox { + + @Test + void test() { + System.out.println("running a test method"); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java index 9f366eaae9..860ce27d1a 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java @@ -1,33 +1,16 @@ package org.springframework.cloud.kubernetes.fabric8.catalog.watch.it; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Test; -import org.junit.platform.suite.api.AfterSuite; import org.junit.platform.suite.api.BeforeSuite; import org.junit.platform.suite.api.SelectClasses; -import org.junit.platform.suite.api.SelectPackages; import org.junit.platform.suite.api.Suite; @Suite -@SelectClasses(SuiteSandbox.InnerTest.class) +@SelectClasses(Sandbox.class) public class SuiteSandbox { - public static class InnerTest { - - @BeforeSuite - public static void beforeSuite() { - System.out.println("go"); - } - - @AfterSuite - static void afterSuite() { - - } - - @Test - void test() { - - } + @BeforeSuite + void beforeSuite() { + System.out.println("beforeSuite"); } } From 058916743bd51424186380b10da7558785feb3a1 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sat, 5 Oct 2024 17:52:32 +0300 Subject: [PATCH 03/35] dirty --- docs/modules/ROOT/pages/discovery-client.adoc | 2 +- .../pom.xml | 16 -- .../pom.xml | 26 +-- .../watch/Fabric8CatalogWatchBase.java | 75 ++++++++ .../catalog/watch/Fabric8CatalogWatchIT.java | 160 ------------------ .../watch/Fabric8CatalogWatchUtil.java | 153 ----------------- ...ithEndpointSlicesAndNamespaceFilterIT.java | 98 +++++++++++ ...bric8CatalogWatchWithEndpointSlicesIT.java | 84 +++++++++ ...atchWithEndpointsAndNamespaceFilterIT.java | 98 +++++++++++ .../Fabric8CatalogWatchWithEndpointsIT.java | 84 +++++++++ ...ic8CatalogWatchWithNamespacesDelegate.java | 150 ---------------- .../{it/TestUtil.java => TestAssertions.java} | 36 ++-- .../watch/it/Fabric8CatalogWatchIT.java | 116 ------------- .../fabric8/catalog/watch/it/Sandbox.java | 12 -- .../catalog/watch/it/SuiteSandbox.java | 16 -- .../tests/commons/fabric8_client/Util.java | 2 + 16 files changed, 477 insertions(+), 651 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchUtil.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithNamespacesDelegate.java rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/{it/TestUtil.java => TestAssertions.java} (87%) delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Fabric8CatalogWatchIT.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Sandbox.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java diff --git a/docs/modules/ROOT/pages/discovery-client.adoc b/docs/modules/ROOT/pages/discovery-client.adoc index 8383ff59cd..880650e8a3 100644 --- a/docs/modules/ROOT/pages/discovery-client.adoc +++ b/docs/modules/ROOT/pages/discovery-client.adoc @@ -126,7 +126,7 @@ Once namespaces have been configured, the next question to answer is what servic spring.cloud.kubernetes.discovery.filter='#root.metadata.namespace matches "^.+A$"' ---- -which tells discovery client to only get services that have the `metadata.namespace` that ends in upper case `A`. +which tells discovery client to only get services that have the `metadata.namespace` that ends in upper case `MySuite`. If your discovery client is based on k8s-native client, then the SpEL expression must be based on `io.kubernetes.client.openapi.models.V1Service` class. The same filter showed above would work here. diff --git a/spring-cloud-kubernetes-integration-tests/pom.xml b/spring-cloud-kubernetes-integration-tests/pom.xml index 6b7c4fc15d..261c2e1e5a 100644 --- a/spring-cloud-kubernetes-integration-tests/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/pom.xml @@ -87,22 +87,6 @@ - - - - org.junit.platform - junit-platform-suite-api - 1.11.2 - - - - org.junit.platform - junit-platform-suite-engine - 1.11.2 - - - - diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml index d8a062a51c..d39102b11b 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml @@ -30,16 +30,6 @@ spring-cloud-kubernetes-test-support - - org.junit.platform - junit-platform-suite-api - - - - org.junit.platform - junit-platform-suite-engine - - @@ -52,6 +42,22 @@ true + + + + org.springframework.boot + spring-boot-maven-plugin + + + build-image + + true + + + + + + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java new file mode 100644 index 0000000000..f8a35dbd31 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java @@ -0,0 +1,75 @@ +/* + * Copyright 2012-2024 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.catalog.watch; + +import java.util.Map; +import java.util.Set; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.k3s.K3sContainer; + +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; +import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; + +import org.springframework.test.context.TestPropertySource; + +/** + * @author wind57 + */ + +@TestPropertySource(properties = {"spring.main.cloud-platform=kubernetes", "spring.cloud.config.import-check.enabled=false", + "spring.cloud.kubernetes.discovery.catalogServicesWatchDelay=2000", + "spring.cloud.kubernetes.client.namespace=default", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG"}) +@ExtendWith(OutputCaptureExtension.class) +abstract class Fabric8CatalogWatchBase { + + protected static final String NAMESPACE = "default"; + + protected static final String NAMESPACE_A = "a"; + + protected static final String NAMESPACE_B = "b"; + + protected static final K3sContainer K3S = Commons.container(); + + protected static Util util; + + @BeforeAll + protected static void beforeAll() { + K3S.start(); + util = new Util(K3S); + } + + protected static KubernetesDiscoveryProperties discoveryProperties(boolean useEndpointSlices) { + return new KubernetesDiscoveryProperties(true, false, Set.of(NAMESPACE, NAMESPACE_A), + true, 60, false, null, Set.of(443, 8443), Map.of(), null, + KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, useEndpointSlices , false, null); + } + + protected static KubernetesClient client() { + String kubeConfigYaml = K3S.getKubeConfigYaml(); + Config config = Config.fromKubeconfig(kubeConfigYaml); + return new KubernetesClientBuilder().withConfig(config).build(); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java deleted file mode 100644 index ef609d6d7d..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchIT.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2013-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.kubernetes.fabric8.catalog.watch; - -import java.io.InputStream; -import java.time.Duration; -import java.util.List; -import java.util.Set; - -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.utils.Serialization; -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 org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace; -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; -import org.springframework.cloud.kubernetes.integration.tests.commons.Images; -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.core.ResolvableType; -import org.springframework.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.patchForEndpointSlices; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.patchForNamespaceFilterAndEndpointSlices; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.patchForNamespaceFilterAndEndpoints; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.retrySpec; -import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion; - -/** - * @author wind57 - */ -class Fabric8CatalogWatchIT { - - private static final String NAMESPACE = "default"; - - public static final String NAMESPACE_A = "namespacea"; - - public static final String NAMESPACE_B = "namespaceb"; - - private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-catalog-watcher"; - - private static final String DOCKER_IMAGE = "docker.io/springcloud/" + IMAGE_NAME + ":" + pomVersion(); - - private static final K3sContainer K3S = Commons.container(); - - private static Util util; - - @BeforeAll - static void beforeAll() throws Exception { - K3S.start(); - Commons.validateImage(IMAGE_NAME, K3S); - Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); - - Images.loadBusybox(K3S); - - util = new Util(K3S); - - util.createNamespace(NAMESPACE_A); - util.createNamespace(NAMESPACE_B); - - util.setUp(NAMESPACE); - util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B)); - util.busybox(NAMESPACE, Phase.CREATE); - - app(Phase.CREATE); - } - - @AfterAll - static void afterAll() { - - util.deleteNamespace(NAMESPACE_A); - util.deleteNamespace(NAMESPACE_B); - - app(Phase.DELETE); - Commons.systemPrune(); - } - - /** - *
-	 *     - we deploy a busybox service with 2 replica pods
-	 *     - we receive an event from KubernetesCatalogWatcher, assert what is inside it
-	 *     - delete the busybox service
-	 *     - assert that we receive only spring-cloud-kubernetes-fabric8-client-catalog-watcher pod
-	 * 
- */ - //@Test - void testCatalogWatchWithEndpoints() throws Exception { - //test(); - -// testCatalogWatchWithEndpointSlices(); -// testCatalogWatchWithNamespaceFilterAndEndpoints(); -// testCatalogWatchWithNamespaceFilterAndEndpointSlices(); - } - - void testCatalogWatchWithEndpointSlices() { - util.busybox(NAMESPACE, Phase.CREATE); - patchForEndpointSlices(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - Commons.waitForLogStatement("stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch", K3S, IMAGE_NAME); - //test(); - } - - void testCatalogWatchWithNamespaceFilterAndEndpoints() { - util.busybox(NAMESPACE_A, Phase.CREATE); - util.busybox(NAMESPACE_B, Phase.CREATE); - patchForNamespaceFilterAndEndpoints(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - Fabric8CatalogWatchWithNamespacesDelegate.testCatalogWatchWithNamespaceFilterAndEndpoints(K3S, IMAGE_NAME, - util); - } - - void testCatalogWatchWithNamespaceFilterAndEndpointSlices() { - util.busybox(NAMESPACE_A, Phase.CREATE); - util.busybox(NAMESPACE_B, Phase.CREATE); - patchForNamespaceFilterAndEndpointSlices(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - Fabric8CatalogWatchWithNamespacesDelegate.testCatalogWatchWithNamespaceFilterAndEndpointSlices(K3S, IMAGE_NAME, - util); - } - - private static void app(Phase phase) { - - InputStream endpointsDeploymentStream = util.inputStream("app/watcher-endpoints-deployment.yaml"); - InputStream serviceStream = util.inputStream("app/watcher-service.yaml"); - InputStream ingressStream = util.inputStream("app/watcher-ingress.yaml"); - - Deployment deployment = Serialization.unmarshal(endpointsDeploymentStream, Deployment.class); - Service service = Serialization.unmarshal(serviceStream, Service.class); - Ingress ingress = Serialization.unmarshal(ingressStream, Ingress.class); - - if (phase.equals(Phase.CREATE)) { - util.createAndWait(Fabric8CatalogWatchIT.NAMESPACE, null, deployment, service, ingress, true); - } - else { - util.deleteAndWait(Fabric8CatalogWatchIT.NAMESPACE, deployment, service, ingress); - } - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchUtil.java deleted file mode 100644 index 0ee2313e1e..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchUtil.java +++ /dev/null @@ -1,153 +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.catalog.watch; - -import java.time.Duration; -import java.util.Map; -import java.util.Objects; - -import reactor.netty.http.client.HttpClient; -import reactor.util.retry.Retry; -import reactor.util.retry.RetryBackoffSpec; - -import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; -import org.springframework.web.reactive.function.client.WebClient; - -/** - * @author wind57 - */ -final class Fabric8CatalogWatchUtil { - - private static final Map POD_LABELS = Map.of("app", - "spring-cloud-kubernetes-fabric8-client-catalog-watcher"); - - private Fabric8CatalogWatchUtil() { - - } - - static final String BODY_ONE = """ - { - "spec": { - "template": { - "spec": { - "containers": [{ - "name": "spring-cloud-kubernetes-fabric8-client-catalog-watcher", - "image": "image_name_here", - "env": [ - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY", - "value": "DEBUG" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES", - "value": "TRUE" - } - ] - }] - } - } - } - }"""; - - static final String BODY_TWO = """ - { - "spec": { - "template": { - "spec": { - "containers": [{ - "name": "spring-cloud-kubernetes-fabric8-client-catalog-watcher", - "image": "image_name_here", - "env": [ - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY", - "value": "DEBUG" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES", - "value": "FALSE" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0", - "value": "namespacea" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1", - "value": "default" - } - ] - }] - } - } - } - }"""; - - static final String BODY_THREE = """ - { - "spec": { - "template": { - "spec": { - "containers": [{ - "name": "spring-cloud-kubernetes-fabric8-client-catalog-watcher", - "image": "image_name_here", - "env": [ - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY", - "value": "DEBUG" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES", - "value": "TRUE" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0", - "value": "namespacea" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1", - "value": "default" - } - ] - }] - } - } - } - }"""; - - static void patchForEndpointSlices(Util util, String dockerImage, String deploymentName, String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_ONE, POD_LABELS); - } - - static void patchForNamespaceFilterAndEndpoints(Util util, String dockerImage, String deploymentName, - String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_TWO, POD_LABELS); - } - - static void patchForNamespaceFilterAndEndpointSlices(Util util, String dockerImage, String deploymentName, - String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_THREE, POD_LABELS); - } - - 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); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java new file mode 100644 index 0000000000..1ed96e1895 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java @@ -0,0 +1,98 @@ +/* + * Copyright 2012-2024 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.catalog.watch; + +import java.util.Set; + +import io.fabric8.kubernetes.client.KubernetesClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.TestConfig; + +/** + * @author wind57 + */ +@SpringBootTest(classes = { KubernetesCatalogWatchAutoConfiguration.class, TestConfig.class, Application.class }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT extends Fabric8CatalogWatchBase { + + @LocalServerPort + private int port; + + @BeforeEach + void beforeEach() { + + util.createNamespace(NAMESPACE_A); + util.createNamespace(NAMESPACE_B); + + util.busybox(NAMESPACE_A, Phase.CREATE); + util.busybox(NAMESPACE_B, Phase.CREATE); + + } + + @AfterEach + void afterEach() { + util.deleteNamespace(NAMESPACE_A); + util.deleteNamespace(NAMESPACE_B); + } + + /** + *
+	 *     - we deploy a busybox service with 2 replica pods in two namespaces : a, b
+	 *     - we use endpoint slices
+	 *     - we enable namespace filtering for 'default' and 'a'
+	 *     - we receive an event from KubernetesCatalogWatcher, assert what is inside it
+	 *     - delete the busybox service in 'a' and 'b'
+	 *     - assert that we receive an empty response
+	 * 
+ */ + @Test + void test(CapturedOutput output) { + TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch"); + TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE_A, NAMESPACE_B), port, NAMESPACE_A); + } + + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + return client(); + } + + @Bean + @Primary + KubernetesDiscoveryProperties kubernetesDiscoveryProperties() { + return discoveryProperties(true); + } + + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java new file mode 100644 index 0000000000..d5050a2b78 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2024 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.catalog.watch; + +import java.util.Set; + +import io.fabric8.kubernetes.client.KubernetesClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchWithEndpointSlicesIT.TestConfig; + +/** + * @author wind57 + */ +@SpringBootTest(classes = { KubernetesCatalogWatchAutoConfiguration.class, TestConfig.class, Application.class }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class Fabric8CatalogWatchWithEndpointSlicesIT extends Fabric8CatalogWatchBase { + + @LocalServerPort + private int port; + + @BeforeEach + void beforeEach() { + util.busybox(NAMESPACE, Phase.CREATE); + } + + /** + *
+	 *     - we deploy a busybox service with 2 replica pods
+	 *     - we use endpoint slices
+	 *     - we receive an event from KubernetesCatalogWatcher, assert what is inside it
+	 *     - delete the busybox service
+	 *     - assert that we receive an empty response
+	 * 
+ */ + @Test + void test(CapturedOutput output) { + TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch"); + TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE), port, NAMESPACE); + } + + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + return client(); + } + + @Bean + @Primary + KubernetesDiscoveryProperties kubernetesDiscoveryProperties() { + return discoveryProperties(true); + } + + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java new file mode 100644 index 0000000000..704eced47e --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java @@ -0,0 +1,98 @@ +/* + * Copyright 2012-2024 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.catalog.watch; + +import java.util.Set; + +import io.fabric8.kubernetes.client.KubernetesClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.TestConfig; + +/** + * @author wind57 + */ +@SpringBootTest(classes = { KubernetesCatalogWatchAutoConfiguration.class, TestConfig.class, Application.class }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT extends Fabric8CatalogWatchBase { + + @LocalServerPort + private int port; + + @BeforeEach + void beforeEach() { + + util.createNamespace(NAMESPACE_A); + util.createNamespace(NAMESPACE_B); + + util.busybox(NAMESPACE_A, Phase.CREATE); + util.busybox(NAMESPACE_B, Phase.CREATE); + + } + + @AfterEach + void afterEach() { + util.deleteNamespace(NAMESPACE_A); + util.deleteNamespace(NAMESPACE_B); + } + + /** + *
+	 *     - we deploy a busybox service with 2 replica pods in two namespaces : a, b
+	 *     - we use endpoints
+	 *     - we enable namespace filtering for 'default' and 'a'
+	 *     - we receive an event from KubernetesCatalogWatcher, assert what is inside it
+	 *     - delete the busybox service in 'a' and 'b'
+	 *     - assert that we receive an empty response
+	 * 
+ */ + @Test + void test(CapturedOutput output) { + TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointsCatalogWatch"); + TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE_A, NAMESPACE_B), port, NAMESPACE_A); + } + + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + return client(); + } + + @Bean + @Primary + KubernetesDiscoveryProperties kubernetesDiscoveryProperties() { + return discoveryProperties(false); + } + + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java new file mode 100644 index 0000000000..1be6cb62c7 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2024 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.catalog.watch; + +import java.util.Set; + +import io.fabric8.kubernetes.client.KubernetesClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchWithEndpointsIT.TestConfig; + +/** + * @author wind57 + */ +@SpringBootTest(classes = { KubernetesCatalogWatchAutoConfiguration.class, TestConfig.class, Application.class }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class Fabric8CatalogWatchWithEndpointsIT extends Fabric8CatalogWatchBase { + + @LocalServerPort + private int port; + + @BeforeEach + void beforeEach() { + util.busybox(NAMESPACE, Phase.CREATE); + } + + /** + *
+	 *     - we deploy a busybox service with 2 replica pods
+	 *     - we use endpoints
+	 *     - we receive an event from KubernetesCatalogWatcher, assert what is inside it
+	 *     - delete the busybox service
+	 *     - assert that we receive an empty response
+	 * 
+ */ + @Test + void test(CapturedOutput output) { + TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointsCatalogWatch"); + TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE), port, NAMESPACE); + } + + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + return client(); + } + + @Bean + @Primary + KubernetesDiscoveryProperties kubernetesDiscoveryProperties() { + return discoveryProperties(false); + } + + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithNamespacesDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithNamespacesDelegate.java deleted file mode 100644 index 69d4a06352..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithNamespacesDelegate.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2013-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.kubernetes.fabric8.catalog.watch; - -import java.time.Duration; -import java.util.List; - -import org.junit.jupiter.api.Assertions; -import org.testcontainers.k3s.K3sContainer; - -import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace; -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.core.ResolvableType; -import org.springframework.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchIT.NAMESPACE_A; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchIT.NAMESPACE_B; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.retrySpec; - -/** - * @author wind57 - */ -final class Fabric8CatalogWatchWithNamespacesDelegate { - - private Fabric8CatalogWatchWithNamespacesDelegate() { - - } - - private static final String APP_NAME = "spring-cloud-kubernetes-fabric8-client-catalog-watcher"; - - /** - *
-	 *     - we deploy one busybox service with 2 replica pods in namespace namespacea
-	 *     - we deploy one busybox service with 2 replica pods in namespace namespaceb
-	 *     - we enable the search to be made in namespacea and default ones
-	 *     - we receive an event from KubernetesCatalogWatcher, assert what is inside it
-	 *     - delete both busybox services in namespacea and namespaceb
-	 *     - assert that we receive only spring-cloud-kubernetes-fabric8-client-catalog-watcher pod
-	 * 
- */ - static void testCatalogWatchWithNamespaceFilterAndEndpoints(K3sContainer container, String imageName, Util util) { - Commons.waitForLogStatement("stateGenerator is of type: Fabric8EndpointsCatalogWatch", container, imageName); - test(util); - } - - static void testCatalogWatchWithNamespaceFilterAndEndpointSlices(K3sContainer container, String imageName, - Util util) { - Commons.waitForLogStatement("stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch", container, - imageName); - test(util); - } - - /** - * the test is the same for both endpoints and endpoint slices, the set-up for them is - * different. - */ - @SuppressWarnings("unchecked") - private static void test(Util util) { - - WebClient client = builder().baseUrl("http://localhost/result").build(); - EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[2]; - ResolvableType resolvableType = ResolvableType.forClassWithGenerics(List.class, EndpointNameAndNamespace.class); - - await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(240)).until(() -> { - List result = (List) client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType())) - .retryWhen(retrySpec()) - .block(); - - // we get 3 pods as input, but because they are sorted by name in the catalog - // watcher implementation - // we will get the first busybox instances here. - if (result != null) { - holder[0] = result.get(0); - holder[1] = result.get(1); - return true; - } - - return false; - }); - - EndpointNameAndNamespace resultOne = holder[0]; - EndpointNameAndNamespace resultTwo = holder[1]; - - Assertions.assertNotNull(resultOne); - Assertions.assertNotNull(resultTwo); - - Assertions.assertTrue(resultOne.endpointName().contains("busybox")); - Assertions.assertTrue(resultTwo.endpointName().contains("busybox")); - Assertions.assertEquals(NAMESPACE_A, resultOne.namespace()); - Assertions.assertEquals(NAMESPACE_A, resultTwo.namespace()); - - util.busybox(NAMESPACE_A, Phase.DELETE); - util.busybox(NAMESPACE_B, Phase.DELETE); - - // what we get after delete - EndpointNameAndNamespace[] afterDelete = new EndpointNameAndNamespace[1]; - - await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(240)).until(() -> { - List result = (List) client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType())) - .retryWhen(retrySpec()) - .block(); - - // we need to get the event from KubernetesCatalogWatch, but that happens - // on periodic bases. So in order to be sure we got the event we care about - // we wait until the result has a single entry, which means busybox was - // deleted - // + KubernetesCatalogWatch received the new update. - if (result != null && result.size() != 1) { - return false; - } - - // we will only receive one pod here, our own - if (result != null) { - afterDelete[0] = result.get(0); - return true; - } - - return false; - }); - - Assertions.assertTrue(afterDelete[0].endpointName().contains(APP_NAME)); - Assertions.assertEquals("default", afterDelete[0].namespace()); - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/TestUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/TestAssertions.java similarity index 87% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/TestUtil.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/TestAssertions.java index 041313c67d..fe5ddbbde7 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/TestUtil.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/TestAssertions.java @@ -14,51 +14,53 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.catalog.watch.it; +package org.springframework.cloud.kubernetes.fabric8.catalog.watch; import java.time.Duration; import java.util.List; import java.util.Objects; +import java.util.Set; import org.assertj.core.api.Assertions; import org.awaitility.Awaitility; -import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import reactor.netty.http.client.HttpClient; +import reactor.util.retry.Retry; +import reactor.util.retry.RetryBackoffSpec; +import org.springframework.boot.test.system.CapturedOutput; import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace; 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.core.ResolvableType; import org.springframework.http.HttpMethod; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; -import reactor.netty.http.client.HttpClient; -import reactor.util.retry.Retry; -import reactor.util.retry.RetryBackoffSpec; import static org.awaitility.Awaitility.await; /** * @author wind57 */ -class TestUtil { +final class TestAssertions { - private TestUtil() { + private TestAssertions() { } static void assertLogStatement(CapturedOutput output, String textToAssert) { - Awaitility.await().during(Duration.ofSeconds(5)) + Awaitility.await() + .during(Duration.ofSeconds(5)) .pollInterval(Duration.ofMillis(200)) .untilAsserted(() -> Assertions.assertThat(output.getOut()).contains(textToAssert)); } /** - * the checks are the same for both endpoints and endpoint slices, - * while the set-up for them is different. + * the checks are the same for both endpoints and endpoint slices, while the set-up + * for them is different. */ @SuppressWarnings("unchecked") - static void test(Util util, String namespace, int port) { + static void invokeAndAssert(Util util, Set namespaces, int port, String assertionNamespace) { WebClient client = builder().baseUrl("http://localhost:" + port + "/result").build(); EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[2]; @@ -92,10 +94,10 @@ static void test(Util util, String namespace, int port) { Assertions.assertThat(resultOne.endpointName()).contains("busybox"); Assertions.assertThat(resultTwo.endpointName()).contains("busybox"); - Assertions.assertThat(resultOne.namespace()).isEqualTo("default"); - Assertions.assertThat(resultTwo.namespace()).isEqualTo("default"); + Assertions.assertThat(resultOne.namespace()).isEqualTo(assertionNamespace); + Assertions.assertThat(resultTwo.namespace()).isEqualTo(assertionNamespace); - util.busybox(namespace, Phase.DELETE); + namespaces.forEach(namespace -> util.busybox(namespace, Phase.DELETE)); await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(240)).until(() -> { List result = (List) client.method(HttpMethod.GET) @@ -108,7 +110,7 @@ static void test(Util util, String namespace, int port) { // on periodic bases. So in order to be sure we got the event we care about // we wait until there is no entry, which means busybox was deleted // and KubernetesCatalogWatch received that update. - return result.isEmpty(); + return Objects.requireNonNull(result).isEmpty(); }); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Fabric8CatalogWatchIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Fabric8CatalogWatchIT.java deleted file mode 100644 index 52c37f6e01..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Fabric8CatalogWatchIT.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2012-2024 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.catalog.watch.it; - -import java.util.Map; -import java.util.Set; - -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientBuilder; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.platform.suite.api.BeforeSuite; -import org.junit.platform.suite.api.Suite; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.boot.test.system.OutputCaptureExtension; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; -import org.springframework.cloud.kubernetes.fabric8.catalog.watch.Application; -import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.testcontainers.k3s.K3sContainer; - -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; -import org.springframework.cloud.kubernetes.integration.tests.commons.Images; -import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; - -import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.it.Fabric8CatalogWatchIT.TestConfig; - -/** - * @author wind57 - */ -@Suite -@SpringBootTest( - classes = { - KubernetesCatalogWatchAutoConfiguration.class, - TestConfig.class, - Application.class - }, - properties = { - "spring.main.cloud-platform=kubernetes", - "spring.cloud.config.import-check.enabled=false", - "spring.cloud.kubernetes.discovery.catalogServicesWatchDelay=2000", - "spring.cloud.kubernetes.client.namespace=default", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" - }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT -) -@ExtendWith(OutputCaptureExtension.class) -class Fabric8CatalogWatchIT { - - private static final String NAMESPACE = "default"; - - private static final K3sContainer K3S = Commons.container(); - - private static Util util; - - @LocalServerPort - private int port; - - @BeforeSuite - static void beforeAll() { - K3S.start(); - Images.loadBusybox(K3S); - util = new Util(K3S); - util.busybox(NAMESPACE, Phase.CREATE); - } - - @Test - void test(CapturedOutput output) { - TestUtil.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointsCatalogWatch"); - TestUtil.test(util, NAMESPACE, port); - } - - @TestConfiguration - static class TestConfig { - - @Bean - @Primary - KubernetesClient kubernetesClient() { - String kubeConfigYaml = K3S.getKubeConfigYaml(); - Config config = Config.fromKubeconfig(kubeConfigYaml); - return new KubernetesClientBuilder().withConfig(config).build(); - } - - @Bean - @Primary - KubernetesDiscoveryProperties kubernetesDiscoveryProperties() { - return new KubernetesDiscoveryProperties(true, false, Set.of(), - true, 60, false, null, Set.of(443, 8443), Map.of(), null, - KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, false, false, null); - } - - } - -} - diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Sandbox.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Sandbox.java deleted file mode 100644 index 5160aafd2e..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/Sandbox.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.springframework.cloud.kubernetes.fabric8.catalog.watch.it; - -import org.junit.jupiter.api.Test; - -public class Sandbox { - - @Test - void test() { - System.out.println("running a test method"); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java deleted file mode 100644 index 860ce27d1a..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/it/SuiteSandbox.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.springframework.cloud.kubernetes.fabric8.catalog.watch.it; - -import org.junit.platform.suite.api.BeforeSuite; -import org.junit.platform.suite.api.SelectClasses; -import org.junit.platform.suite.api.Suite; - -@Suite -@SelectClasses(Sandbox.class) -public class SuiteSandbox { - - @BeforeSuite - void beforeSuite() { - System.out.println("beforeSuite"); - } - -} diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java index e19f68b7b3..1b12c3e10b 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java @@ -395,10 +395,12 @@ private void waitForDeploymentToBeDeleted(String namespace, Deployment deploymen Map matchLabels = deployment.getSpec().getSelector().getMatchLabels(); + long start = System.currentTimeMillis(); await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> { Deployment inner = client.apps().deployments().inNamespace(namespace).withName(deploymentName).get(); return inner == null; }); + System.out.println("Ended in " + (System.currentTimeMillis() - start) + "ms"); await().pollInterval(Duration.ofSeconds(1)).atMost(60, TimeUnit.SECONDS).until(() -> { List podList = client.pods().inNamespace(namespace).withLabels(matchLabels).list().getItems(); From 840fc594436515934ef2fbe9a7bf8cdd6195b942 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sat, 5 Oct 2024 18:41:35 +0300 Subject: [PATCH 04/35] fix --- .../tests/commons/fabric8_client/Util.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java index e19f68b7b3..0b582812a1 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java @@ -27,6 +27,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodListBuilder; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.ServiceAccount; @@ -151,10 +152,20 @@ public void deleteAndWait(String namespace, @Nullable Deployment deployment, Ser @Nullable Ingress ingress) { try { + long startTime = System.currentTimeMillis(); if (deployment != null) { - client.apps().deployments().inNamespace(namespace).resource(deployment).delete(); + + List deploymentPods = client.pods() + .inNamespace(namespace) + .withLabels(deployment.getSpec().getSelector().getMatchLabels()) + .list() + .getItems(); + + client.resourceList(new PodListBuilder().withItems(deploymentPods).build()).withGracePeriod(0).delete(); + client.apps().deployments().inNamespace(namespace).resource(deployment).withGracePeriod(0).delete(); waitForDeploymentToBeDeleted(namespace, deployment); } + System.out.println("Ended deployment delete in " + (System.currentTimeMillis() - startTime) + "ms"); client.services().inNamespace(namespace).resource(service).delete(); From fe07a7340d860c5aae456ed6273bcfe7966b7ee3 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sat, 5 Oct 2024 23:27:19 +0300 Subject: [PATCH 05/35] dirty --- spring-cloud-kubernetes-integration-tests/pom.xml | 1 + .../catalog/watch/Fabric8CatalogWatchBase.java | 14 +++++++------- ...atchWithEndpointSlicesAndNamespaceFilterIT.java | 4 +++- .../Fabric8CatalogWatchWithEndpointSlicesIT.java | 4 +++- ...alogWatchWithEndpointsAndNamespaceFilterIT.java | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/pom.xml b/spring-cloud-kubernetes-integration-tests/pom.xml index 261c2e1e5a..c17152c451 100644 --- a/spring-cloud-kubernetes-integration-tests/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/pom.xml @@ -77,6 +77,7 @@ + ${project.build.outputDirectory} ${testsToRun} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java index f8a35dbd31..050dee699e 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java @@ -30,17 +30,17 @@ import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; - import org.springframework.test.context.TestPropertySource; /** * @author wind57 */ -@TestPropertySource(properties = {"spring.main.cloud-platform=kubernetes", "spring.cloud.config.import-check.enabled=false", - "spring.cloud.kubernetes.discovery.catalogServicesWatchDelay=2000", - "spring.cloud.kubernetes.client.namespace=default", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG"}) +@TestPropertySource( + properties = { "spring.main.cloud-platform=kubernetes", "spring.cloud.config.import-check.enabled=false", + "spring.cloud.kubernetes.discovery.catalogServicesWatchDelay=2000", + "spring.cloud.kubernetes.client.namespace=default", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" }) @ExtendWith(OutputCaptureExtension.class) abstract class Fabric8CatalogWatchBase { @@ -62,8 +62,8 @@ protected static void beforeAll() { protected static KubernetesDiscoveryProperties discoveryProperties(boolean useEndpointSlices) { return new KubernetesDiscoveryProperties(true, false, Set.of(NAMESPACE, NAMESPACE_A), - true, 60, false, null, Set.of(443, 8443), Map.of(), null, - KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, useEndpointSlices , false, null); + true, 60, false, null, Set.of(443, 8443), + Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, useEndpointSlices, false, null); } protected static KubernetesClient client() { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java index 1ed96e1895..e410770098 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java @@ -21,6 +21,7 @@ import io.fabric8.kubernetes.client.KubernetesClient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -39,7 +40,7 @@ * @author wind57 */ @SpringBootTest(classes = { KubernetesCatalogWatchAutoConfiguration.class, TestConfig.class, Application.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT extends Fabric8CatalogWatchBase { @LocalServerPort @@ -73,6 +74,7 @@ void afterEach() { * */ @Test + @Disabled void test(CapturedOutput output) { TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch"); TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE_A, NAMESPACE_B), port, NAMESPACE_A); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java index d5050a2b78..19decebb1c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java @@ -20,6 +20,7 @@ import io.fabric8.kubernetes.client.KubernetesClient; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -38,7 +39,7 @@ * @author wind57 */ @SpringBootTest(classes = { KubernetesCatalogWatchAutoConfiguration.class, TestConfig.class, Application.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class Fabric8CatalogWatchWithEndpointSlicesIT extends Fabric8CatalogWatchBase { @LocalServerPort @@ -59,6 +60,7 @@ void beforeEach() { * */ @Test + @Disabled void test(CapturedOutput output) { TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch"); TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE), port, NAMESPACE); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java index 704eced47e..0dc13d3af8 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java @@ -39,7 +39,7 @@ * @author wind57 */ @SpringBootTest(classes = { KubernetesCatalogWatchAutoConfiguration.class, TestConfig.class, Application.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT extends Fabric8CatalogWatchBase { @LocalServerPort From 6b3200ff6d20e7d0900ee3cb22d703e596fc07ef Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 00:17:48 +0300 Subject: [PATCH 06/35] first working --- .../fabric8/catalog/watch/Fabric8CatalogWatchBase.java | 6 +++--- ...8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java | 2 -- .../watch/Fabric8CatalogWatchWithEndpointSlicesIT.java | 2 -- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java index 050dee699e..5dd33c44ba 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchBase.java @@ -61,9 +61,9 @@ protected static void beforeAll() { } protected static KubernetesDiscoveryProperties discoveryProperties(boolean useEndpointSlices) { - return new KubernetesDiscoveryProperties(true, false, Set.of(NAMESPACE, NAMESPACE_A), - true, 60, false, null, Set.of(443, 8443), - Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, useEndpointSlices, false, null); + return new KubernetesDiscoveryProperties(true, false, Set.of(NAMESPACE, NAMESPACE_A), true, 60, false, null, + Set.of(443, 8443), Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, useEndpointSlices, + false, null); } protected static KubernetesClient client() { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java index e410770098..c79f346b2f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java @@ -21,7 +21,6 @@ import io.fabric8.kubernetes.client.KubernetesClient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -74,7 +73,6 @@ void afterEach() { * */ @Test - @Disabled void test(CapturedOutput output) { TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch"); TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE_A, NAMESPACE_B), port, NAMESPACE_A); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java index 19decebb1c..908890c3e7 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java @@ -20,7 +20,6 @@ import io.fabric8.kubernetes.client.KubernetesClient; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -60,7 +59,6 @@ void beforeEach() { * */ @Test - @Disabled void test(CapturedOutput output) { TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch"); TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE), port, NAMESPACE); From e4a88a9c9a3e68ceea5ee4efdb5f41bdf318337f Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 00:28:53 +0300 Subject: [PATCH 07/35] cleanup --- docs/modules/ROOT/pages/discovery-client.adoc | 2 +- pom.xml | 1 - .../pom.xml | 7 ------- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/modules/ROOT/pages/discovery-client.adoc b/docs/modules/ROOT/pages/discovery-client.adoc index 880650e8a3..8383ff59cd 100644 --- a/docs/modules/ROOT/pages/discovery-client.adoc +++ b/docs/modules/ROOT/pages/discovery-client.adoc @@ -126,7 +126,7 @@ Once namespaces have been configured, the next question to answer is what servic spring.cloud.kubernetes.discovery.filter='#root.metadata.namespace matches "^.+A$"' ---- -which tells discovery client to only get services that have the `metadata.namespace` that ends in upper case `MySuite`. +which tells discovery client to only get services that have the `metadata.namespace` that ends in upper case `A`. If your discovery client is based on k8s-native client, then the SpEL expression must be based on `io.kubernetes.client.openapi.models.V1Service` class. The same filter showed above would work here. diff --git a/pom.xml b/pom.xml index 92ee1f450b..4e8652882b 100644 --- a/pom.xml +++ b/pom.xml @@ -166,7 +166,6 @@ ${spring-cloud-commons.version} test - diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml index d39102b11b..33be033dd6 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml @@ -59,12 +59,5 @@ - - - maven_central - Maven Central - https://repo.maven.apache.org/maven2/ - - From f88a581ef32ad0561acc396d2945e0a141fd8ece Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 00:34:42 +0300 Subject: [PATCH 08/35] cleanup --- spring-cloud-kubernetes-integration-tests/pom.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/pom.xml b/spring-cloud-kubernetes-integration-tests/pom.xml index c17152c451..d0398836d2 100644 --- a/spring-cloud-kubernetes-integration-tests/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/pom.xml @@ -32,9 +32,6 @@ org.springframework.boot spring-boot-maven-plugin - - docker.io/springcloud/${project.artifactId}:${project.version} - build-image From ed6565cee5543e51b3fdddafc7c7579851e4e94c Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 00:36:10 +0300 Subject: [PATCH 09/35] cleanup --- spring-cloud-kubernetes-integration-tests/pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spring-cloud-kubernetes-integration-tests/pom.xml b/spring-cloud-kubernetes-integration-tests/pom.xml index d0398836d2..c17152c451 100644 --- a/spring-cloud-kubernetes-integration-tests/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/pom.xml @@ -32,6 +32,9 @@ org.springframework.boot spring-boot-maven-plugin + + docker.io/springcloud/${project.artifactId}:${project.version} + build-image From 096cf995448de93485fae046acf7de6206288898 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 11:33:42 +0300 Subject: [PATCH 10/35] dirty --- .../Fabric8ApplicationDiscoveryListener.java | 2 +- .../client/discovery/Fabric8DiscoveryApp.java | 2 +- .../discovery/Fabric8DiscoveryController.java | 8 +-- .../Fabric8ReactiveDiscoveryController.java | 8 +-- .../discovery/it/Fabric8DiscoveryBase.java | 61 +++++++++++++++++++ .../it/Fabric8DiscoveryPodMetadataIT.java | 31 ++++++++++ 6 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java index 7ecafcefef..ea82be7da5 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java @@ -29,7 +29,7 @@ * @author wind57 */ @Component -public class Fabric8ApplicationDiscoveryListener implements ApplicationListener> { +class Fabric8ApplicationDiscoveryListener implements ApplicationListener> { private static final LogAccessor LOG = new LogAccessor( LogFactory.getLog(Fabric8ApplicationDiscoveryListener.class)); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryApp.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryApp.java index 1ef7ea1a55..751b2b8e0b 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryApp.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryApp.java @@ -23,7 +23,7 @@ * @author wind57 */ @SpringBootApplication -public class Fabric8DiscoveryApp { +class Fabric8DiscoveryApp { public static void main(String[] args) { SpringApplication.run(Fabric8DiscoveryApp.class, args); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java index 898c0ebf73..125e7ecccd 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java @@ -31,7 +31,7 @@ * @author wind57 */ @RestController -public class Fabric8DiscoveryController { +class Fabric8DiscoveryController { private final KubernetesDiscoveryClient discoveryClient; @@ -42,17 +42,17 @@ public Fabric8DiscoveryController(ObjectProvider disc } @GetMapping("/services") - public List allServices() { + List allServices() { return discoveryClient.getServices(); } @GetMapping("/endpoints/{serviceId}") - public List getEndPointsList(@PathVariable("serviceId") String serviceId) { + List getEndPointsList(@PathVariable("serviceId") String serviceId) { return discoveryClient.getEndPointsList(serviceId); } @GetMapping("/service-instances/{serviceId}") - public List serviceInstances(@PathVariable("serviceId") String serviceId) { + List serviceInstances(@PathVariable("serviceId") String serviceId) { return discoveryClient.getInstances(serviceId); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ReactiveDiscoveryController.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ReactiveDiscoveryController.java index d73cdfa601..1a845441ed 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ReactiveDiscoveryController.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ReactiveDiscoveryController.java @@ -31,11 +31,11 @@ * @author wind57 */ @RestController -public class Fabric8ReactiveDiscoveryController { +class Fabric8ReactiveDiscoveryController { private final KubernetesReactiveDiscoveryClient reactiveDiscoveryClient; - public Fabric8ReactiveDiscoveryController( + Fabric8ReactiveDiscoveryController( ObjectProvider reactiveDiscoveryClient) { KubernetesReactiveDiscoveryClient[] local = new KubernetesReactiveDiscoveryClient[1]; reactiveDiscoveryClient.ifAvailable(x -> local[0] = x); @@ -43,12 +43,12 @@ public Fabric8ReactiveDiscoveryController( } @GetMapping("/reactive/services") - public Mono> allServices() { + Mono> allServices() { return reactiveDiscoveryClient.getServices().collectList(); } @GetMapping("/reactive/service-instances/{serviceId}") - public Mono> serviceInstances(@PathVariable("serviceId") String serviceId) { + Mono> serviceInstances(@PathVariable("serviceId") String serviceId) { return reactiveDiscoveryClient.getInstances(serviceId).collectList(); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java new file mode 100644 index 0000000000..00132c9a08 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2024 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.client.discovery.it; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.k3s.K3sContainer; + +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; +import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; +import org.springframework.test.context.TestPropertySource; + +@TestPropertySource( + properties = { "spring.main.cloud-platform=kubernetes", "spring.cloud.config.import-check.enabled=false", + "spring.cloud.kubernetes.discovery.catalogServicesWatchDelay=2000", + "spring.cloud.kubernetes.client.namespace=default", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" }) +@ExtendWith(OutputCaptureExtension.class) +abstract class Fabric8DiscoveryBase { + + protected static final String NAMESPACE = "default"; + + protected static final String NAMESPACE_A = "a"; + + protected static final String NAMESPACE_B = "b"; + + protected static final K3sContainer K3S = Commons.container(); + + protected static Util util; + + @BeforeAll + protected static void beforeAll() { + K3S.start(); + util = new Util(K3S); + } + + protected static KubernetesClient client() { + String kubeConfigYaml = K3S.getKubeConfigYaml(); + Config config = Config.fromKubeconfig(kubeConfigYaml); + return new KubernetesClientBuilder().withConfig(config).build(); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java new file mode 100644 index 0000000000..4e56c8f3da --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012-2024 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.client.discovery.it; + +import org.junit.jupiter.api.Test; + +/** + * @author wind57 + */ +class Fabric8DiscoveryPodMetadataIT extends Fabric8DiscoveryBase { + + @Test + void test() { + + } + +} From 8824f1460b6a4674f36e407c2656f89f9f0b2ed0 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 12:13:01 +0300 Subject: [PATCH 11/35] more improvements --- ...atchWithEndpointsAndNamespaceFilterIT.java | 3 +++ .../integration/tests/commons/Images.java | 23 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java index 0dc13d3af8..9eccc88895 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.java @@ -29,6 +29,7 @@ import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; @@ -51,6 +52,8 @@ void beforeEach() { util.createNamespace(NAMESPACE_A); util.createNamespace(NAMESPACE_B); + Images.loadBusybox(K3S); + util.busybox(NAMESPACE_A, Phase.CREATE); util.busybox(NAMESPACE_B, Phase.CREATE); diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Images.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Images.java index 47021c563f..f37a17ee7e 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Images.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Images.java @@ -79,7 +79,9 @@ public static String wiremockVersion() { } public static void loadBusybox(K3sContainer container) { - Commons.load(container, BUSYBOX_TAR, BUSYBOX, busyboxVersion()); + if (!imageAlreadyInK3s(container, BUSYBOX_TAR)) { + Commons.load(container, BUSYBOX_TAR, BUSYBOX, busyboxVersion()); + } } public static void loadWiremock(K3sContainer container) { @@ -106,6 +108,25 @@ public static void loadRabbitmq(K3sContainer container) { Commons.load(container, RABBITMQ_TAR, RABBITMQ, rabbitMqVersion()); } + private static boolean imageAlreadyInK3s(K3sContainer container, String tarName) { + try { + boolean present = container.execInContainer("sh", "-c", "ctr images list | grep " + tarName) + .getStdout() + .contains(tarName); + if (present) { + System.out.println("image : " + tarName + " already in k3s, skipping"); + return true; + } + else { + System.out.println("image : " + tarName + " not in k3s"); + return false; + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + // find the image version from current-images.txt private static String imageVersion(String imageNameForDownload) { BufferedReader reader = new BufferedReader( From 7dda032d23c81adcbbc76b5c9edd722bff9a0ae7 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 12:14:14 +0300 Subject: [PATCH 12/35] more improvements --- ...ric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java | 3 +++ .../catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java | 2 ++ .../catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java | 2 ++ 3 files changed, 7 insertions(+) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java index c79f346b2f..914cc4ab62 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.java @@ -29,6 +29,7 @@ import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; @@ -51,6 +52,8 @@ void beforeEach() { util.createNamespace(NAMESPACE_A); util.createNamespace(NAMESPACE_B); + Images.loadBusybox(K3S); + util.busybox(NAMESPACE_A, Phase.CREATE); util.busybox(NAMESPACE_B, Phase.CREATE); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java index 908890c3e7..e18b810c71 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointSlicesIT.java @@ -28,6 +28,7 @@ import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; @@ -46,6 +47,7 @@ class Fabric8CatalogWatchWithEndpointSlicesIT extends Fabric8CatalogWatchBase { @BeforeEach void beforeEach() { + Images.loadBusybox(K3S); util.busybox(NAMESPACE, Phase.CREATE); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java index 1be6cb62c7..c8ca4e22ff 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/Fabric8CatalogWatchWithEndpointsIT.java @@ -28,6 +28,7 @@ import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; @@ -46,6 +47,7 @@ class Fabric8CatalogWatchWithEndpointsIT extends Fabric8CatalogWatchBase { @BeforeEach void beforeEach() { + Images.loadBusybox(K3S); util.busybox(NAMESPACE, Phase.CREATE); } From d0d3b03dda23238277eefbca4bf53a6f27910093 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 12:41:38 +0300 Subject: [PATCH 13/35] dirty --- .../pom.xml | 15 ++++ .../Fabric8ApplicationDiscoveryListener.java | 7 +- .../client/discovery/Fabric8DiscoveryApp.java | 2 +- .../Fabric8DiscoveryPodMetadataIT.java | 56 ------------ .../discovery/it/Fabric8DiscoveryBase.java | 1 - .../it/Fabric8DiscoveryPodMetadataIT.java | 85 ++++++++++++++++++- .../client/discovery/it/TestAssertions.java | 42 +++++++++ 7 files changed, 143 insertions(+), 65 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/pom.xml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/pom.xml index 79fb556322..b3f1ff57e6 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/pom.xml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/pom.xml @@ -50,6 +50,21 @@ true + + + + org.springframework.boot + spring-boot-maven-plugin + + + build-image + + true + + + + + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java index ea82be7da5..7bbf53bce5 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java @@ -16,11 +16,9 @@ package org.springframework.cloud.kubernetes.fabric8.client.discovery; -import io.fabric8.kubernetes.api.model.Pod; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; -import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; import org.springframework.context.ApplicationListener; import org.springframework.core.log.LogAccessor; import org.springframework.stereotype.Component; @@ -36,10 +34,7 @@ class Fabric8ApplicationDiscoveryListener implements ApplicationListener event) { - Pod pod = (Pod) ((KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource) event.getSource()) - .pod(); - LOG.info(() -> "received InstanceRegisteredEvent from pod with 'app' label value : " - + pod.getMetadata().getLabels().get("app")); + LOG.info(() -> "received InstanceRegisteredEvent: " + event); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryApp.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryApp.java index 751b2b8e0b..1ef7ea1a55 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryApp.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryApp.java @@ -23,7 +23,7 @@ * @author wind57 */ @SpringBootApplication -class Fabric8DiscoveryApp { +public class Fabric8DiscoveryApp { public static void main(String[] args) { SpringApplication.run(Fabric8DiscoveryApp.class, args); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java index 7fec5fdb8a..f56d229901 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java @@ -136,62 +136,6 @@ static void after() throws Exception { Commons.systemPrune(); } - @Test - void testPodMetadata() throws Exception { - - // find both pods - String[] both = K3S.execInContainer("sh", "-c", "kubectl get pods -l app=busybox -o=name --no-headers") - .getStdout() - .split("\n"); - // add a label to first pod - K3S.execInContainer("sh", "-c", - "kubectl label pods " + both[0].split("/")[1] + " custom-label=custom-label-value"); - // add annotation to the second pod - K3S.execInContainer("sh", "-c", - "kubectl annotate pods " + both[1].split("/")[1] + " custom-annotation=custom-annotation-value"); - - WebClient client = builder().baseUrl("http://localhost/service-instances/busybox-service").build(); - List serviceInstances = client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - - }) - .retryWhen(retrySpec()) - .block(); - - DefaultKubernetesServiceInstance withCustomLabel = serviceInstances.stream() - .filter(x -> x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) - .toList() - .get(0); - Assertions.assertEquals(withCustomLabel.getServiceId(), "busybox-service"); - Assertions.assertNotNull(withCustomLabel.getInstanceId()); - Assertions.assertNotNull(withCustomLabel.getHost()); - Assertions.assertEquals(withCustomLabel.getMetadata(), - Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80")); - Assertions.assertTrue(withCustomLabel.podMetadata() - .get("labels") - .entrySet() - .stream() - .anyMatch(x -> x.getKey().equals("custom-label") && x.getValue().equals("custom-label-value"))); - - DefaultKubernetesServiceInstance withCustomAnnotation = serviceInstances.stream() - .filter(x -> !x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) - .toList() - .get(0); - Assertions.assertEquals(withCustomAnnotation.getServiceId(), "busybox-service"); - Assertions.assertNotNull(withCustomAnnotation.getInstanceId()); - Assertions.assertNotNull(withCustomAnnotation.getHost()); - Assertions.assertEquals(withCustomAnnotation.getMetadata(), - 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(); testAllServicesWithBootstrap(); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java index 00132c9a08..20380d0977 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java @@ -30,7 +30,6 @@ @TestPropertySource( properties = { "spring.main.cloud-platform=kubernetes", "spring.cloud.config.import-check.enabled=false", - "spring.cloud.kubernetes.discovery.catalogServicesWatchDelay=2000", "spring.cloud.kubernetes.client.namespace=default", "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" }) @ExtendWith(OutputCaptureExtension.class) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java index 4e56c8f3da..e834fc3ba7 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java @@ -16,16 +16,99 @@ package org.springframework.cloud.kubernetes.fabric8.client.discovery.it; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; +import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +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.client.discovery.it.TestAssertions.builder; +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.retrySpec; + /** * @author wind57 */ +@SpringBootTest(classes = { Fabric8DiscoveryApp.class }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class Fabric8DiscoveryPodMetadataIT extends Fabric8DiscoveryBase { + @LocalServerPort + private int port; + + @BeforeEach + void beforeEach() { + Images.loadBusybox(K3S); + util.busybox(NAMESPACE, Phase.CREATE); + } + + @AfterEach + void afterEach() { + util.busybox(NAMESPACE, Phase.DELETE); + } + @Test - void test() { + void test() throws Exception { +// find both pods + String[] both = K3S.execInContainer("sh", "-c", "kubectl get pods -l app=busybox -o=name --no-headers") + .getStdout() + .split("\n"); + // add a label to first pod + K3S.execInContainer("sh", "-c", + "kubectl label pods " + both[0].split("/")[1] + " custom-label=custom-label-value"); + // add annotation to the second pod + K3S.execInContainer("sh", "-c", + "kubectl annotate pods " + both[1].split("/")[1] + " custom-annotation=custom-annotation-value"); + + WebClient client = builder().baseUrl("http://localhost:" + port + "/busybox-service").build(); + List serviceInstances = client.method(HttpMethod.GET) + .retrieve() + .bodyToMono(new ParameterizedTypeReference>() { + + }) + .retryWhen(retrySpec()) + .block(); + + DefaultKubernetesServiceInstance withCustomLabel = serviceInstances.stream() + .filter(x -> x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) + .toList() + .get(0); + Assertions.assertEquals(withCustomLabel.getServiceId(), "busybox-service"); + Assertions.assertNotNull(withCustomLabel.getInstanceId()); + Assertions.assertNotNull(withCustomLabel.getHost()); + Assertions.assertEquals(withCustomLabel.getMetadata(), + Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80")); + Assertions.assertTrue(withCustomLabel.podMetadata() + .get("labels") + .entrySet() + .stream() + .anyMatch(x -> x.getKey().equals("custom-label") && x.getValue().equals("custom-label-value"))); + DefaultKubernetesServiceInstance withCustomAnnotation = serviceInstances.stream() + .filter(x -> !x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) + .toList() + .get(0); + Assertions.assertEquals(withCustomAnnotation.getServiceId(), "busybox-service"); + Assertions.assertNotNull(withCustomAnnotation.getInstanceId()); + Assertions.assertNotNull(withCustomAnnotation.getHost()); + Assertions.assertEquals(withCustomAnnotation.getMetadata(), + 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"))); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java new file mode 100644 index 0000000000..5a9e738f9f --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2024 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.client.discovery.it; + +import java.time.Duration; +import java.util.Objects; + +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; + +/** + * @author wind57 + */ +class TestAssertions { + + 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); + } + +} From 947cc43658123d4394bde45fcf778a30b436afd4 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 12:42:04 +0300 Subject: [PATCH 14/35] cleanup --- .../app/watcher-endpoints-deployment.yaml | 33 ------------------- .../test/resources/app/watcher-ingress.yaml | 16 --------- .../test/resources/app/watcher-service.yaml | 14 -------- 3 files changed, 63 deletions(-) delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-endpoints-deployment.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-ingress.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-service.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-endpoints-deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-endpoints-deployment.yaml deleted file mode 100644 index fdce297c87..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-endpoints-deployment.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: spring-cloud-kubernetes-fabric8-client-catalog-watcher -spec: - selector: - matchLabels: - app: spring-cloud-kubernetes-fabric8-client-catalog-watcher - template: - metadata: - labels: - app: spring-cloud-kubernetes-fabric8-client-catalog-watcher - spec: - serviceAccountName: spring-cloud-kubernetes-serviceaccount - containers: - - name: spring-cloud-kubernetes-fabric8-client-catalog-watcher - image: docker.io/springcloud/spring-cloud-kubernetes-fabric8-client-catalog-watcher - imagePullPolicy: IfNotPresent - readinessProbe: - httpGet: - port: 8080 - path: /actuator/health/readiness - livenessProbe: - httpGet: - port: 8080 - path: /actuator/health/liveness - ports: - - containerPort: 8080 - env: - - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY - value: "DEBUG" - - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES - value: "FALSE" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-ingress.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-ingress.yaml deleted file mode 100644 index 7b0343edbf..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-ingress.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: spring-cloud-kubernetes-fabric8-client-catalog-watcher-ingress - namespace: default -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: spring-cloud-kubernetes-fabric8-client-catalog-watcher-service - port: - number: 8080 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-service.yaml deleted file mode 100644 index 6b9374dcf8..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/resources/app/watcher-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: spring-cloud-kubernetes-fabric8-client-catalog-watcher-service - name: spring-cloud-kubernetes-fabric8-client-catalog-watcher-service -spec: - ports: - - name: http - port: 8080 - targetPort: 8080 - selector: - app: spring-cloud-kubernetes-fabric8-client-catalog-watcher - type: ClusterIP From b87b82b9cc203d10ded68ccd94f386286581a37d Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 15:20:52 +0300 Subject: [PATCH 15/35] dirty --- .../discovery/Fabric8DiscoveryController.java | 1 + .../discovery/it/Fabric8DiscoveryBase.java | 11 ++-- .../it/Fabric8DiscoveryPodMetadataIT.java | 60 +++++++------------ 3 files changed, 29 insertions(+), 43 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java index 125e7ecccd..94dc3956b9 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java @@ -53,6 +53,7 @@ List getEndPointsList(@PathVariable("serviceId") String serviceId) { @GetMapping("/service-instances/{serviceId}") List serviceInstances(@PathVariable("serviceId") String serviceId) { + discoveryClient.getServices(); return discoveryClient.getInstances(serviceId); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java index 20380d0977..7e09cd6b63 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java @@ -28,19 +28,20 @@ import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; import org.springframework.test.context.TestPropertySource; +/** + * @author wind57 + */ @TestPropertySource( properties = { "spring.main.cloud-platform=kubernetes", "spring.cloud.config.import-check.enabled=false", "spring.cloud.kubernetes.client.namespace=default", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" }) + "spring.cloud.kubernetes.discovery.metadata.add-pod-labels=true", + "spring.cloud.kubernetes.discovery.metadata.add-pod-annotations=true" , + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=debug" }) @ExtendWith(OutputCaptureExtension.class) abstract class Fabric8DiscoveryBase { protected static final String NAMESPACE = "default"; - protected static final String NAMESPACE_A = "a"; - - protected static final String NAMESPACE_B = "b"; - protected static final K3sContainer K3S = Commons.container(); protected static Util util; diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java index e834fc3ba7..b59f4a856c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java @@ -19,28 +19,33 @@ import java.util.List; import java.util.Map; +import io.fabric8.kubernetes.client.KubernetesClient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; 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.client.discovery.it.TestAssertions.builder; import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.retrySpec; +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.Fabric8DiscoveryPodMetadataIT.TestConfig; /** * @author wind57 */ -@SpringBootTest(classes = { Fabric8DiscoveryApp.class }, +@SpringBootTest(classes = { Fabric8DiscoveryApp.class, TestConfig.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class Fabric8DiscoveryPodMetadataIT extends Fabric8DiscoveryBase { @@ -58,9 +63,16 @@ void afterEach() { util.busybox(NAMESPACE, Phase.DELETE); } + /** + *
+	 * 		- there is a 'busybox-service' service deployed with two pods
+	 * 		- find each of the pod, add annotation to one and labels to another
+	 * 		- call
+	 * 
+ */ @Test void test() throws Exception { -// find both pods + // find both pods String[] both = K3S.execInContainer("sh", "-c", "kubectl get pods -l app=busybox -o=name --no-headers") .getStdout() .split("\n"); @@ -70,45 +82,17 @@ void test() throws Exception { // add annotation to the second pod K3S.execInContainer("sh", "-c", "kubectl annotate pods " + both[1].split("/")[1] + " custom-annotation=custom-annotation-value"); + } - WebClient client = builder().baseUrl("http://localhost:" + port + "/busybox-service").build(); - List serviceInstances = client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - - }) - .retryWhen(retrySpec()) - .block(); + @TestConfiguration + static class TestConfig { - DefaultKubernetesServiceInstance withCustomLabel = serviceInstances.stream() - .filter(x -> x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) - .toList() - .get(0); - Assertions.assertEquals(withCustomLabel.getServiceId(), "busybox-service"); - Assertions.assertNotNull(withCustomLabel.getInstanceId()); - Assertions.assertNotNull(withCustomLabel.getHost()); - Assertions.assertEquals(withCustomLabel.getMetadata(), - Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80")); - Assertions.assertTrue(withCustomLabel.podMetadata() - .get("labels") - .entrySet() - .stream() - .anyMatch(x -> x.getKey().equals("custom-label") && x.getValue().equals("custom-label-value"))); + @Bean + @Primary + KubernetesClient kubernetesClient() { + return client(); + } - DefaultKubernetesServiceInstance withCustomAnnotation = serviceInstances.stream() - .filter(x -> !x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) - .toList() - .get(0); - Assertions.assertEquals(withCustomAnnotation.getServiceId(), "busybox-service"); - Assertions.assertNotNull(withCustomAnnotation.getInstanceId()); - Assertions.assertNotNull(withCustomAnnotation.getHost()); - Assertions.assertEquals(withCustomAnnotation.getMetadata(), - 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"))); } } From 566dfd6a0c14f8ed9e2038a3f678eb8fc624715f Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 15:26:40 +0300 Subject: [PATCH 16/35] dirty --- .../discovery/Fabric8DiscoveryController.java | 1 - .../it/Fabric8DiscoveryPodMetadataIT.java | 17 ++++++++--------- .../client/discovery/it/TestAssertions.java | 5 +++++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java index 94dc3956b9..125e7ecccd 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java @@ -53,7 +53,6 @@ List getEndPointsList(@PathVariable("serviceId") String serviceId) { @GetMapping("/service-instances/{serviceId}") List serviceInstances(@PathVariable("serviceId") String serviceId) { - discoveryClient.getServices(); return discoveryClient.getInstances(serviceId); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java index b59f4a856c..81db58ae8d 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java @@ -67,21 +67,20 @@ void afterEach() { *
 	 * 		- there is a 'busybox-service' service deployed with two pods
 	 * 		- find each of the pod, add annotation to one and labels to another
-	 * 		- call
+	 * 		- call DiscoveryClient::getInstances with this serviceId and assert fields returned
 	 * 
*/ @Test void test() throws Exception { - // find both pods - String[] both = K3S.execInContainer("sh", "-c", "kubectl get pods -l app=busybox -o=name --no-headers") + String[] busyboxPods = K3S.execInContainer("sh", "-c", "kubectl get pods -l app=busybox -o=name --no-headers") .getStdout() .split("\n"); - // add a label to first pod - K3S.execInContainer("sh", "-c", - "kubectl label pods " + both[0].split("/")[1] + " custom-label=custom-label-value"); - // add annotation to the second pod - K3S.execInContainer("sh", "-c", - "kubectl annotate pods " + both[1].split("/")[1] + " custom-annotation=custom-annotation-value"); + + String podOne = busyboxPods[0].split("/")[1]; + String podTwo = busyboxPods[1].split("/")[1]; + + K3S.execInContainer("sh", "-c", "kubectl label pods " + podOne + " my-label=my-value"); + K3S.execInContainer("sh", "-c", "kubectl annotate pods " + podTwo + " my-annotation=my-value"); } @TestConfiguration diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java index 5a9e738f9f..3fb0e17cf3 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.Objects; +import org.springframework.cloud.client.discovery.DiscoveryClient; import reactor.netty.http.client.HttpClient; import reactor.util.retry.Retry; import reactor.util.retry.RetryBackoffSpec; @@ -31,6 +32,10 @@ */ class TestAssertions { + static void assertPodMetadata(DiscoveryClient discoveryClient) { + + } + static WebClient.Builder builder() { return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); } From fe9b3f5b7f7ce30ed2e50efdc1ff4cd662a87c17 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 15:39:23 +0300 Subject: [PATCH 17/35] dirty --- .../it/Fabric8DiscoveryPodMetadataIT.java | 20 +++---- .../client/discovery/it/TestAssertions.java | 56 ++++++++++++++----- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java index 81db58ae8d..76f8dcaa85 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java @@ -16,31 +16,23 @@ package org.springframework.cloud.kubernetes.fabric8.client.discovery.it; -import java.util.List; -import java.util.Map; - import io.fabric8.kubernetes.client.KubernetesClient; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; -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.client.discovery.it.TestAssertions.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.retrySpec; import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.Fabric8DiscoveryPodMetadataIT.TestConfig; +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.assertPodMetadata; /** * @author wind57 @@ -49,8 +41,8 @@ webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class Fabric8DiscoveryPodMetadataIT extends Fabric8DiscoveryBase { - @LocalServerPort - private int port; + @Autowired + private DiscoveryClient discoveryClient; @BeforeEach void beforeEach() { @@ -81,6 +73,8 @@ void test() throws Exception { K3S.execInContainer("sh", "-c", "kubectl label pods " + podOne + " my-label=my-value"); K3S.execInContainer("sh", "-c", "kubectl annotate pods " + podTwo + " my-annotation=my-value"); + + assertPodMetadata(discoveryClient); } @TestConfiguration diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java index 3fb0e17cf3..6c73b628ea 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java @@ -16,32 +16,60 @@ package org.springframework.cloud.kubernetes.fabric8.client.discovery.it; -import java.time.Duration; -import java.util.Objects; +import java.util.List; +import java.util.Map; +import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; -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.http.client.reactive.ReactorClientHttpConnector; -import org.springframework.web.reactive.function.client.WebClient; +import static org.assertj.core.api.Assertions.assertThat; /** * @author wind57 */ -class TestAssertions { +final class TestAssertions { - static void assertPodMetadata(DiscoveryClient discoveryClient) { + private TestAssertions() { } - static WebClient.Builder builder() { - return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); - } + static void assertPodMetadata(DiscoveryClient discoveryClient) { + + List serviceInstances = discoveryClient.getInstances("busybox-service"); + + DefaultKubernetesServiceInstance withCustomLabel = serviceInstances.stream() + .map(instance -> (DefaultKubernetesServiceInstance) instance) + .filter(x -> x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) + .toList() + .get(0); + + Assertions.assertEquals(withCustomLabel.getServiceId(), "busybox-service"); + Assertions.assertNotNull(withCustomLabel.getInstanceId()); + Assertions.assertNotNull(withCustomLabel.getHost()); + Assertions.assertEquals(withCustomLabel.getMetadata(), + Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80")); + Assertions.assertTrue(withCustomLabel.podMetadata() + .get("labels") + .entrySet() + .stream() + .anyMatch(x -> x.getKey().equals("custom-label") && x.getValue().equals("custom-label-value"))); - static RetryBackoffSpec retrySpec() { - return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull); + DefaultKubernetesServiceInstance withCustomAnnotation = serviceInstances.stream() + .map(instance -> (DefaultKubernetesServiceInstance) instance) + .filter(x -> !x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) + .toList() + .get(0); + Assertions.assertEquals(withCustomAnnotation.getServiceId(), "busybox-service"); + Assertions.assertNotNull(withCustomAnnotation.getInstanceId()); + Assertions.assertNotNull(withCustomAnnotation.getHost()); + Assertions.assertEquals(withCustomAnnotation.getMetadata(), + 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"))); } } From b138b4275e986c3266cf68b91ef396fce445ab04 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 15:40:55 +0300 Subject: [PATCH 18/35] static import --- .../fabric8/catalog/watch/TestAssertions.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/TestAssertions.java index fe5ddbbde7..6d679a8918 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/fabric8/catalog/watch/TestAssertions.java @@ -37,6 +37,7 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; +import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; /** @@ -88,14 +89,14 @@ static void invokeAndAssert(Util util, Set namespaces, int port, String EndpointNameAndNamespace resultOne = holder[0]; EndpointNameAndNamespace resultTwo = holder[1]; - Assertions.assertThat(resultOne).isNotNull(); - Assertions.assertThat(resultTwo).isNotNull(); + assertThat(resultOne).isNotNull(); + assertThat(resultTwo).isNotNull(); - Assertions.assertThat(resultOne.endpointName()).contains("busybox"); - Assertions.assertThat(resultTwo.endpointName()).contains("busybox"); + assertThat(resultOne.endpointName()).contains("busybox"); + assertThat(resultTwo.endpointName()).contains("busybox"); - Assertions.assertThat(resultOne.namespace()).isEqualTo(assertionNamespace); - Assertions.assertThat(resultTwo.namespace()).isEqualTo(assertionNamespace); + assertThat(resultOne.namespace()).isEqualTo(assertionNamespace); + assertThat(resultTwo.namespace()).isEqualTo(assertionNamespace); namespaces.forEach(namespace -> util.busybox(namespace, Phase.DELETE)); From 6d7e298fb7d30b78fb3b32164c1c4d6ff62a06bd Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 15:45:14 +0300 Subject: [PATCH 19/35] dirty --- .../fabric8/client/discovery/it/TestAssertions.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java index 6c73b628ea..39c9a9ae5c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java @@ -38,17 +38,20 @@ static void assertPodMetadata(DiscoveryClient discoveryClient) { List serviceInstances = discoveryClient.getInstances("busybox-service"); + // if annotations are empty, we got the other pod, with labels here DefaultKubernetesServiceInstance withCustomLabel = serviceInstances.stream() .map(instance -> (DefaultKubernetesServiceInstance) instance) .filter(x -> x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) .toList() .get(0); - Assertions.assertEquals(withCustomLabel.getServiceId(), "busybox-service"); - Assertions.assertNotNull(withCustomLabel.getInstanceId()); - Assertions.assertNotNull(withCustomLabel.getHost()); - Assertions.assertEquals(withCustomLabel.getMetadata(), - Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80")); + assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); + assertThat(withCustomLabel.getInstanceId()).isNotNull(); + assertThat(withCustomLabel.getHost()).isNotNull(); + assertThat(withCustomLabel.getMetadata()).isEqualTo( + Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") + ); + Assertions.assertTrue(withCustomLabel.podMetadata() .get("labels") .entrySet() From c5d83f6a377faec8d9f95fd6b14878551555f6ef Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 19:55:56 +0300 Subject: [PATCH 20/35] dirty --- .../Fabric8DiscoveryBoostrapDelegate.java | 58 -------- .../discovery/Fabric8DiscoveryDelegate.java | 88 ------------ .../Fabric8DiscoveryPodMetadataIT.java | 18 --- .../it/Fabric8DiscoveryAllServicesIT.java | 128 ++++++++++++++++++ .../discovery/it/Fabric8DiscoveryBase.java | 17 +++ .../it/Fabric8DiscoveryBlockingIT.java | 48 +++++++ .../it/Fabric8DiscoveryPodMetadataIT.java | 5 +- .../client/discovery/it/TestAssertions.java | 50 +++++-- 8 files changed, 232 insertions(+), 180 deletions(-) delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBoostrapDelegate.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDelegate.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryAllServicesIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBlockingIT.java diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBoostrapDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBoostrapDelegate.java deleted file mode 100644 index 6917d0d80e..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBoostrapDelegate.java +++ /dev/null @@ -1,58 +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.client.discovery; - -import java.util.List; - -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.client.discovery.Fabric8DiscoveryClientUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.retrySpec; - -/** - * @author wind57 - */ -final class Fabric8DiscoveryBoostrapDelegate { - - /** - * KubernetesDiscoveryClient::getServices call must include the external-name-service - * also. - */ - static void testAllServicesWithBootstrap() { - WebClient client = builder().baseUrl("http://localhost/services").build(); - - List result = client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - - }) - .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")); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDelegate.java deleted file mode 100644 index 5b41469831..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDelegate.java +++ /dev/null @@ -1,88 +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.client.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.client.discovery.Fabric8DiscoveryClientUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.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 result = client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - - }) - .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 serviceInstances = client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - - }) - .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"); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java index f56d229901..c47020db55 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java @@ -137,9 +137,6 @@ static void after() throws Exception { } private void testAllOther() { - testAllServices(); - testAllServicesWithBootstrap(); - testExternalNameServiceInstance(); testBlockingConfiguration(); testDefaultConfiguration(); testReactiveConfiguration(); @@ -148,21 +145,6 @@ private void testAllOther() { namespaceFilter(); } - private void testAllServices() { - util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_ONE, Map.of("app", IMAGE_NAME)); - Fabric8DiscoveryDelegate.testAllServices(); - } - - private void testAllServicesWithBootstrap() { - util.patchWithReplace(DOCKER_IMAGE, DEPLOYMENT_NAME, NAMESPACE, BODY_ONE_WITH_BOOTSTRAP, - Map.of("app", IMAGE_NAME)); - Fabric8DiscoveryBoostrapDelegate.testAllServicesWithBootstrap(); - } - - 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); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryAllServicesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryAllServicesIT.java new file mode 100644 index 0000000000..983ef7d803 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryAllServicesIT.java @@ -0,0 +1,128 @@ +/* + * Copyright 2012-2024 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.client.discovery.it; + +import java.io.IOException; +import java.io.InputStream; + +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.assertAllServices; + +/** + * @author wind57 + */ +class Fabric8DiscoveryAllServicesIT extends Fabric8DiscoveryBase { + + @Nested + @TestPropertySource(properties = { "spring.cloud.kubernetes.discovery.include-external-name-services=true" }) + class NonBootstrap { + + @Autowired + private DiscoveryClient discoveryClient; + + @BeforeEach + void beforeEach() { + Images.loadBusybox(K3S); + util.busybox(NAMESPACE, Phase.CREATE); + externalNameServices(Phase.CREATE); + } + + @AfterEach + void afterEach() { + util.busybox(NAMESPACE, Phase.DELETE); + externalNameServices(Phase.DELETE); + } + + /** + *
+		 * 		- there are 3 services : 'busybox-service', 'kubernetes', 'external-name-service'
+		 * 		- all of them are found
+		 * 
+ */ + @Test + void test() { + assertAllServices(discoveryClient); + } + } + + @Nested + @TestPropertySource(properties = { "spring.cloud.kubernetes.discovery.include-external-name-services=true", + "spring.cloud.bootstrap.enabled=true"}) + class Bootstrap { + + @Autowired + private DiscoveryClient discoveryClient; + + @BeforeEach + void beforeEach() { + Images.loadBusybox(K3S); + util.busybox(NAMESPACE, Phase.CREATE); + externalNameServices(Phase.CREATE); + } + + @AfterEach + void afterEach() { + util.busybox(NAMESPACE, Phase.DELETE); + externalNameServices(Phase.DELETE); + } + + /** + *
+		 * 		- there are 3 services : 'busybox-service', 'kubernetes', 'external-name-service'
+		 * 		- all of them are found
+		 * 
+ */ + @Test + void test() { + assertAllServices(discoveryClient); + } + } + + private void externalNameServices(Phase phase) { + try (InputStream externalNameServiceStream = util.inputStream("external-name-service.yaml")) { + Service externalServiceName = Serialization.unmarshal(externalNameServiceStream, Service.class); + if (phase == Phase.CREATE) { + util.createAndWait(NAMESPACE, null, null, externalServiceName, null, true); + } + else { + util.deleteAndWait(NAMESPACE, null, externalServiceName, null); + } + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java index 7e09cd6b63..060d6cb245 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java @@ -21,6 +21,11 @@ import io.fabric8.kubernetes.client.KubernetesClientBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; import org.testcontainers.k3s.K3sContainer; import org.springframework.boot.test.system.OutputCaptureExtension; @@ -38,6 +43,7 @@ "spring.cloud.kubernetes.discovery.metadata.add-pod-annotations=true" , "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=debug" }) @ExtendWith(OutputCaptureExtension.class) +@SpringBootTest(classes = { Fabric8DiscoveryApp.class, Fabric8DiscoveryAllServicesIT.TestConfig.class }) abstract class Fabric8DiscoveryBase { protected static final String NAMESPACE = "default"; @@ -58,4 +64,15 @@ protected static KubernetesClient client() { return new KubernetesClientBuilder().withConfig(config).build(); } + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + return client(); + } + + } + } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBlockingIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBlockingIT.java new file mode 100644 index 0000000000..fd74fcc7fd --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBlockingIT.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2024 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.client.discovery.it; + +import java.io.IOException; +import java.io.InputStream; + +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.assertAllServices; + +/** + * @author wind57 + */ +@SpringBootTest(classes = { Fabric8DiscoveryApp.class, Fabric8DiscoveryPodMetadataIT.TestConfig.class }) +class Fabric8DiscoveryBlockingIT extends Fabric8DiscoveryBase { +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java index 76f8dcaa85..cf7ecfac84 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java @@ -37,8 +37,7 @@ /** * @author wind57 */ -@SpringBootTest(classes = { Fabric8DiscoveryApp.class, TestConfig.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest(classes = { Fabric8DiscoveryApp.class, TestConfig.class }) class Fabric8DiscoveryPodMetadataIT extends Fabric8DiscoveryBase { @Autowired @@ -58,7 +57,7 @@ void afterEach() { /** *
 	 * 		- there is a 'busybox-service' service deployed with two pods
-	 * 		- find each of the pod, add annotation to one and labels to another
+	 * 		- find each of the pod, add annotation to one, and labels to another
 	 * 		- call DiscoveryClient::getInstances with this serviceId and assert fields returned
 	 * 
*/ diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java index 39c9a9ae5c..b5241bc3c3 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java @@ -23,6 +23,8 @@ import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; +import static java.util.AbstractMap.SimpleEntry; +import static java.util.Map.Entry; import static org.assertj.core.api.Assertions.assertThat; /** @@ -44,6 +46,11 @@ static void assertPodMetadata(DiscoveryClient discoveryClient) { .filter(x -> x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) .toList() .get(0); + List> podMetadataLabels = withCustomLabel.podMetadata() + .get("labels") + .entrySet() + .stream() + .toList(); assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); assertThat(withCustomLabel.getInstanceId()).isNotNull(); @@ -51,28 +58,45 @@ static void assertPodMetadata(DiscoveryClient discoveryClient) { assertThat(withCustomLabel.getMetadata()).isEqualTo( Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") ); + assertThat(podMetadataLabels).contains(new SimpleEntry<>("my-label", "my-value")); - Assertions.assertTrue(withCustomLabel.podMetadata() - .get("labels") - .entrySet() - .stream() - .anyMatch(x -> x.getKey().equals("custom-label") && x.getValue().equals("custom-label-value"))); - + // if annotation are present, we got the one with annotations here DefaultKubernetesServiceInstance withCustomAnnotation = serviceInstances.stream() .map(instance -> (DefaultKubernetesServiceInstance) instance) .filter(x -> !x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) .toList() .get(0); - Assertions.assertEquals(withCustomAnnotation.getServiceId(), "busybox-service"); - Assertions.assertNotNull(withCustomAnnotation.getInstanceId()); - Assertions.assertNotNull(withCustomAnnotation.getHost()); - Assertions.assertEquals(withCustomAnnotation.getMetadata(), - Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80")); - Assertions.assertTrue(withCustomAnnotation.podMetadata() + List> podMetadataAnnotations = withCustomAnnotation.podMetadata() .get("annotations") .entrySet() .stream() - .anyMatch(x -> x.getKey().equals("custom-annotation") && x.getValue().equals("custom-annotation-value"))); + .toList(); + + assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); + assertThat(withCustomLabel.getInstanceId()).isNotNull(); + assertThat(withCustomLabel.getHost()).isNotNull(); + assertThat(withCustomLabel.getMetadata()).isEqualTo( + Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") + ); + assertThat(podMetadataAnnotations).contains(new SimpleEntry<>("my-annotation", "my-value")); + } + + static void assertAllServices(DiscoveryClient discoveryClient) { + + List services = discoveryClient.getServices(); + assertThat(services).containsExactlyInAnyOrder("kubernetes", "busybox-service", "external-name-service"); + + ServiceInstance externalNameInstance = discoveryClient.getInstances("external-name-service").get(0); + + assertThat(externalNameInstance.getServiceId()).isEqualTo("external-name-service"); + assertThat(externalNameInstance.getInstanceId()).isNotNull(); + assertThat(externalNameInstance.getHost()).isEqualTo("spring.io"); + assertThat(externalNameInstance.getPort()).isEqualTo(-1); + assertThat(externalNameInstance.getMetadata()).isEqualTo( + Map.of("k8s_namespace", "default", "type", "ExternalName")); + assertThat(externalNameInstance.isSecure()).isFalse(); + assertThat(externalNameInstance.getUri().toASCIIString()).isEqualTo("spring.io"); + assertThat(externalNameInstance.getScheme()).isEqualTo("http"); } } From f1fa96e34d2066d0ec07d9f580f7abc8cbc5d563 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 6 Oct 2024 22:35:31 +0300 Subject: [PATCH 21/35] dirty --- .../Fabric8ApplicationDiscoveryListener.java | 40 -- .../discovery/Fabric8DiscoveryController.java | 59 --- .../Fabric8ReactiveDiscoveryController.java | 55 --- .../Fabric8DiscoveryAllServicesIT.java | 10 +- .../{it => }/Fabric8DiscoveryBase.java | 9 +- .../{it => }/Fabric8DiscoveryBlockingIT.java | 44 ++- .../Fabric8DiscoveryClientHealthDelegate.java | 199 ---------- .../discovery/Fabric8DiscoveryClientUtil.java | 326 ---------------- ...abric8DiscoveryDefaultConfigurationIT.java | 61 +++ .../Fabric8DiscoveryFilterDelegate.java | 140 ------- ...ic8DiscoveryFilterMatchOneNamespaceIT.java | 77 ++++ ...c8DiscoveryFilterMatchTwoNamespacesIT.java | 77 ++++ .../Fabric8DiscoveryNamespaceDelegate.java | 67 ---- .../Fabric8DiscoveryPodMetadataIT.java | 226 ++---------- .../discovery/Fabric8DiscoveryReactiveIT.java | 62 ++++ .../client/discovery/TestAssertions.java | 349 ++++++++++++++++++ .../it/Fabric8DiscoveryPodMetadataIT.java | 90 ----- .../client/discovery/it/TestAssertions.java | 102 ----- .../test/resources/external-name-service.yaml | 7 - .../fabric8-discovery-deployment.yaml | 31 -- .../resources/fabric8-discovery-ingress.yaml | 16 - .../resources/fabric8-discovery-service.yaml | 14 - ...ic8-cluster-admin-serviceaccount-role.yaml | 13 - .../integration/tests/commons/Images.java | 4 +- 24 files changed, 696 insertions(+), 1382 deletions(-) delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ReactiveDiscoveryController.java rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/{it => }/Fabric8DiscoveryAllServicesIT.java (88%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/{it => }/Fabric8DiscoveryBase.java (95%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/{it => }/Fabric8DiscoveryBlockingIT.java (57%) delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryClientHealthDelegate.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryClientUtil.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDefaultConfigurationIT.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterDelegate.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryNamespaceDelegate.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryReactiveIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/TestAssertions.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/external-name-service.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-deployment.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-ingress.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-service.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java deleted file mode 100644 index 7bbf53bce5..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ApplicationDiscoveryListener.java +++ /dev/null @@ -1,40 +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.client.discovery; - -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.core.log.LogAccessor; -import org.springframework.stereotype.Component; - -/** - * @author wind57 - */ -@Component -class Fabric8ApplicationDiscoveryListener implements ApplicationListener> { - - private static final LogAccessor LOG = new LogAccessor( - LogFactory.getLog(Fabric8ApplicationDiscoveryListener.class)); - - @Override - public void onApplicationEvent(InstanceRegisteredEvent event) { - LOG.info(() -> "received InstanceRegisteredEvent: " + event); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java deleted file mode 100644 index 125e7ecccd..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryController.java +++ /dev/null @@ -1,59 +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.client.discovery; - -import java.util.List; - -import io.fabric8.kubernetes.api.model.Endpoints; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesDiscoveryClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author wind57 - */ -@RestController -class Fabric8DiscoveryController { - - private final KubernetesDiscoveryClient discoveryClient; - - public Fabric8DiscoveryController(ObjectProvider discoveryClient) { - KubernetesDiscoveryClient[] local = new KubernetesDiscoveryClient[1]; - discoveryClient.ifAvailable(x -> local[0] = x); - this.discoveryClient = local[0]; - } - - @GetMapping("/services") - List allServices() { - return discoveryClient.getServices(); - } - - @GetMapping("/endpoints/{serviceId}") - List getEndPointsList(@PathVariable("serviceId") String serviceId) { - return discoveryClient.getEndPointsList(serviceId); - } - - @GetMapping("/service-instances/{serviceId}") - List serviceInstances(@PathVariable("serviceId") String serviceId) { - return discoveryClient.getInstances(serviceId); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ReactiveDiscoveryController.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ReactiveDiscoveryController.java deleted file mode 100644 index 1a845441ed..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8ReactiveDiscoveryController.java +++ /dev/null @@ -1,55 +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.client.discovery; - -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.fabric8.discovery.reactive.KubernetesReactiveDiscoveryClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author wind57 - */ -@RestController -class Fabric8ReactiveDiscoveryController { - - private final KubernetesReactiveDiscoveryClient reactiveDiscoveryClient; - - Fabric8ReactiveDiscoveryController( - ObjectProvider reactiveDiscoveryClient) { - KubernetesReactiveDiscoveryClient[] local = new KubernetesReactiveDiscoveryClient[1]; - reactiveDiscoveryClient.ifAvailable(x -> local[0] = x); - this.reactiveDiscoveryClient = local[0]; - } - - @GetMapping("/reactive/services") - Mono> allServices() { - return reactiveDiscoveryClient.getServices().collectList(); - } - - @GetMapping("/reactive/service-instances/{serviceId}") - Mono> serviceInstances(@PathVariable("serviceId") String serviceId) { - return reactiveDiscoveryClient.getInstances(serviceId).collectList(); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryAllServicesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryAllServicesIT.java similarity index 88% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryAllServicesIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryAllServicesIT.java index 983ef7d803..5be0d2c8f2 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryAllServicesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryAllServicesIT.java @@ -14,13 +14,12 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.discovery.it; +package org.springframework.cloud.kubernetes.fabric8.client.discovery; import java.io.IOException; import java.io.InputStream; import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.utils.Serialization; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -28,17 +27,12 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; import org.springframework.test.context.TestPropertySource; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.assertAllServices; +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.TestAssertions.assertAllServices; /** * @author wind57 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBase.java similarity index 95% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBase.java index 060d6cb245..dc50137487 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBase.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.discovery.it; +package org.springframework.cloud.kubernetes.fabric8.client.discovery; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.k3s.K3sContainer; + import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; -import org.testcontainers.k3s.K3sContainer; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; @@ -43,7 +43,8 @@ "spring.cloud.kubernetes.discovery.metadata.add-pod-annotations=true" , "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=debug" }) @ExtendWith(OutputCaptureExtension.class) -@SpringBootTest(classes = { Fabric8DiscoveryApp.class, Fabric8DiscoveryAllServicesIT.TestConfig.class }) +@SpringBootTest(classes = { Fabric8DiscoveryApp.class, Fabric8DiscoveryBase.TestConfig.class }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) abstract class Fabric8DiscoveryBase { protected static final String NAMESPACE = "default"; diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBlockingIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBlockingIT.java similarity index 57% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBlockingIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBlockingIT.java index fd74fcc7fd..e0e0b57fbc 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryBlockingIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBlockingIT.java @@ -14,35 +14,45 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.discovery.it; +package org.springframework.cloud.kubernetes.fabric8.client.discovery; -import java.io.IOException; -import java.io.InputStream; - -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.utils.Serialization; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.web.server.LocalManagementPort; import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; import org.springframework.test.context.TestPropertySource; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.assertAllServices; +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.TestAssertions.assertBlockingConfiguration; /** * @author wind57 */ -@SpringBootTest(classes = { Fabric8DiscoveryApp.class, Fabric8DiscoveryPodMetadataIT.TestConfig.class }) +@TestPropertySource(properties = { "spring.cloud.discovery.reactive.enabled=false", + "logging.level.org.springframework.cloud.client.discovery.health=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG" }) class Fabric8DiscoveryBlockingIT extends Fabric8DiscoveryBase { + + @LocalManagementPort + private int port; + + @BeforeEach + void beforeEach() { + Images.loadBusybox(K3S); + util.busybox(NAMESPACE, Phase.CREATE); + } + + @AfterEach + void afterEach() { + util.busybox(NAMESPACE, Phase.DELETE); + } + + @Test + void test(CapturedOutput output) { + assertBlockingConfiguration(output, port); + } + } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryClientHealthDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryClientHealthDelegate.java deleted file mode 100644 index b9bfaa18b7..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryClientHealthDelegate.java +++ /dev/null @@ -1,199 +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.client.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.client.discovery.Fabric8DiscoveryClientUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.retrySpec; -import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.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 servicesResult = servicesClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - }) - .retryWhen(retrySpec()) - .block(); - - Assertions.assertThat(servicesResult).contains("spring-cloud-kubernetes-fabric8-client-discovery"); - Assertions.assertThat(servicesResult).contains("kubernetes"); - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryClientUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryClientUtil.java deleted file mode 100644 index 3fb07d1b47..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryClientUtil.java +++ /dev/null @@ -1,326 +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.client.discovery; - -import java.time.Duration; -import java.util.Objects; - -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; - -/** - * @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" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "FALSE" - } - ] - }] - } - } - } - } - """; - - static final String BODY_ONE_WITH_BOOTSTRAP = """ - { - "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" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "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" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_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" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "FALSE" - } - ] - }] - } - } - } - } - """; - - 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" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_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" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "FALSE" - } - ] - }] - } - } - } - } - """; - - 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" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "FALSE" - } - ] - }] - } - } - } - } - """; - - 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" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "FALSE" - } - ] - }] - } - } - } - } - """; - - 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); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDefaultConfigurationIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDefaultConfigurationIT.java new file mode 100644 index 0000000000..db439aefcd --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDefaultConfigurationIT.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2024 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.client.discovery; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.web.server.LocalManagementPort; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.TestAssertions.testDefaultConfiguration; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { + "logging.level.org.springframework.cloud.client.discovery.health.reactive=DEBUG", + "logging.level.org.springframework.cloud.client.discovery.health=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery.reactive=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG" }) +class Fabric8DiscoveryDefaultConfigurationIT extends Fabric8DiscoveryBase { + + @LocalManagementPort + private int port; + + @BeforeEach + void beforeEach() { + Images.loadBusybox(K3S); + util.busybox(NAMESPACE, Phase.CREATE); + } + + @AfterEach + void afterEach() { + util.busybox(NAMESPACE, Phase.DELETE); + } + + @Test + void test(CapturedOutput output) { + testDefaultConfiguration(output, port); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterDelegate.java deleted file mode 100644 index bbdd789ce6..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterDelegate.java +++ /dev/null @@ -1,140 +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.client.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.client.discovery.Fabric8DiscoveryClientUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.retrySpec; - -final class Fabric8DiscoveryFilterDelegate { - - private Fabric8DiscoveryFilterDelegate() { - - } - - /** - *
-	 *     - 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.
-	 * 
- */ - static void filterMatchesBothNamespacesViaThePredicate() { - - WebClient clientServices = builder().baseUrl("http://localhost/services").build(); - - @SuppressWarnings("unchecked") - List services = (List) 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 serviceInstances = client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - - }) - .retryWhen(retrySpec()) - .block(); - - Assertions.assertEquals(serviceInstances.size(), 2); - List 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")); - - } - - /** - *
-	 *     - 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.
-	 * 
- */ - static void filterMatchesOneNamespaceViaThePredicate() { - - WebClient clientServices = builder().baseUrl("http://localhost/services").build(); - - @SuppressWarnings("unchecked") - List services = (List) 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 serviceInstances = client.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - - }) - .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")); - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java new file mode 100644 index 0000000000..5176725ef1 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2024 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.client.discovery; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.TestAssertions.filterMatchesOneNamespaceViaThePredicate; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { + "spring.cloud.kubernetes.discovery.namespaces[0]=a-uat", + "spring.cloud.kubernetes.discovery.namespaces[1]=b-uat", + "spring.cloud.kubernetes.discovery.filter=#root.metadata.namespace matches 'a-uat$'", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" + +}) +class Fabric8DiscoveryFilterMatchOneNamespaceIT extends Fabric8DiscoveryBase { + + private static final String NAMESPACE_A_UAT = "a-uat"; + + private static final String NAMESPACE_B_UAT = "b-uat"; + + @Autowired + private DiscoveryClient discoveryClient; + + @BeforeEach + void beforeEach() { + Images.loadWiremock(K3S); + + util.createNamespace(NAMESPACE_A_UAT); + util.createNamespace(NAMESPACE_B_UAT); + + util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.CREATE, false); + util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.CREATE, false); + + } + + @AfterEach + void afterEach() { + + util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.DELETE, false); + util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.DELETE, false); + + util.deleteNamespace(NAMESPACE_A_UAT); + util.deleteNamespace(NAMESPACE_B_UAT); + } + + @Test + void test() { + filterMatchesOneNamespaceViaThePredicate(discoveryClient); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java new file mode 100644 index 0000000000..2945c4d6ba --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2024 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.client.discovery; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.TestAssertions.filterMatchesBothNamespacesViaThePredicate; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { + "spring.cloud.kubernetes.discovery.namespaces[0]=a-uat", + "spring.cloud.kubernetes.discovery.namespaces[1]=b-uat", + "spring.cloud.kubernetes.discovery.filter=#root.metadata.namespace matches '^.*uat$'", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" + +}) +class Fabric8DiscoveryFilterMatchTwoNamespacesIT extends Fabric8DiscoveryBase { + + private static final String NAMESPACE_A_UAT = "a-uat"; + + private static final String NAMESPACE_B_UAT = "b-uat"; + + @Autowired + private DiscoveryClient discoveryClient; + + @BeforeEach + void beforeEach() { + Images.loadWiremock(K3S); + + util.createNamespace(NAMESPACE_A_UAT); + util.createNamespace(NAMESPACE_B_UAT); + + util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.CREATE, false); + util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.CREATE, false); + + } + + @AfterEach + void afterEach() { + + util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.DELETE, false); + util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.DELETE, false); + + util.deleteNamespace(NAMESPACE_A_UAT); + util.deleteNamespace(NAMESPACE_B_UAT); + } + + @Test + void test() { + filterMatchesBothNamespacesViaThePredicate(discoveryClient); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryNamespaceDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryNamespaceDelegate.java deleted file mode 100644 index bf9c7998db..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryNamespaceDelegate.java +++ /dev/null @@ -1,67 +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.client.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.client.discovery.Fabric8DiscoveryClientUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.retrySpec; - -/** - * @author mbialkowski1 - */ -final class Fabric8DiscoveryNamespaceDelegate { - - private Fabric8DiscoveryNamespaceDelegate() { - - } - - static void namespaceFilter() { - WebClient clientServices = builder().baseUrl("http://localhost/services").build(); - - @SuppressWarnings("unchecked") - List services = (List) 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 = clientEndpoints.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - }) - .retryWhen(retrySpec()) - .block(); - - Assertions.assertEquals(endpoints.size(), 1); - Assertions.assertEquals(endpoints.get(0).getMetadata().getNamespace(), "namespace-left"); - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java index c47020db55..46d0e6ce2f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryPodMetadataIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,216 +16,56 @@ package org.springframework.cloud.kubernetes.fabric8.client.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.api.model.rbac.ClusterRoleBinding; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.utils.Serialization; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; +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.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.kubernetes.integration.tests.commons.Images; 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; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.BODY_FIVE; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.BODY_FOUR; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.BODY_ONE; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.BODY_ONE_WITH_BOOTSTRAP; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.BODY_SEVEN; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.BODY_SIX; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.BODY_THREE; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryClientUtil.BODY_TWO; -import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion; +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.TestAssertions.assertPodMetadata; /** * @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; - - private static final K3sContainer K3S = Commons.container(); +class Fabric8DiscoveryPodMetadataIT extends Fabric8DiscoveryBase { - @BeforeAll - static void beforeAll() throws Exception { - K3S.start(); - Commons.validateImage(IMAGE_NAME, K3S); - Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); + @Autowired + private DiscoveryClient discoveryClient; + @BeforeEach + void beforeEach() { Images.loadBusybox(K3S); - Images.loadWiremock(K3S); - - util = new Util(K3S); - client = util.client(); - - util.setUp(NAMESPACE); - - manifests(Phase.CREATE); - util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE, false); util.busybox(NAMESPACE, Phase.CREATE); - - util.createNamespace(NAMESPACE_A_UAT); - util.createNamespace(NAMESPACE_B_UAT); - util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.CREATE, false); - util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.CREATE, false); - - util.createNamespace(NAMESPACE_LEFT); - util.createNamespace(NAMESPACE_RIGHT); - util.wiremock(NAMESPACE_LEFT, "/wiremock", Phase.CREATE, false); - util.wiremock(NAMESPACE_RIGHT, "/wiremock", Phase.CREATE, false); } - @AfterAll - static void after() throws Exception { - util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE, false); + @AfterEach + void afterEach() { util.busybox(NAMESPACE, Phase.DELETE); - - util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.DELETE, false); - util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.DELETE, false); - util.deleteNamespace(NAMESPACE_A_UAT); - util.deleteNamespace(NAMESPACE_B_UAT); - - util.wiremock(NAMESPACE_LEFT, "/wiremock", Phase.DELETE, false); - util.wiremock(NAMESPACE_RIGHT, "/wiremock", Phase.DELETE, false); - util.deleteNamespace(NAMESPACE_LEFT); - util.deleteNamespace(NAMESPACE_RIGHT); - - manifests(Phase.DELETE); - Commons.cleanUp(IMAGE_NAME, K3S); - Commons.systemPrune(); - } - - private void testAllOther() { - testBlockingConfiguration(); - testDefaultConfiguration(); - testReactiveConfiguration(); - filterMatchesBothNamespacesViaThePredicate(); - filterMatchesOneNamespaceViaThePredicate(); - namespaceFilter(); - } - - 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 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 = Serialization.unmarshal(deploymentStream, Deployment.class); - - List existing = new ArrayList<>( - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()); - existing.add(new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_ADDPODLABELS") - .withValue("true") - .build()); - existing.add(new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_ADDPODANNOTATIONS") - .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 externalServiceName = Serialization.unmarshal(externalNameServiceStream, Service.class); - Service discoveryService = Serialization.unmarshal(discoveryServiceStream, Service.class); - Ingress ingress = Serialization.unmarshal(ingressStream, Ingress.class); - - ClusterRoleBinding clusterRoleBinding = Serialization.unmarshal(getAdminRole(), ClusterRoleBinding.class); - if (phase.equals(Phase.CREATE)) { - client.rbac().clusterRoleBindings().resource(clusterRoleBinding).create(); - util.createAndWait(NAMESPACE, IMAGE_NAME, deployment, discoveryService, ingress, true); - util.createAndWait(NAMESPACE, null, null, externalServiceName, null, true); - } - else { - client.rbac().clusterRoleBindings().resource(clusterRoleBinding).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())); } - private RetryBackoffSpec retrySpec() { - return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull); + /** + *
+	 * 		- there is a 'busybox-service' service deployed with two pods
+	 * 		- find each of the pod, add annotation to one, and labels to another
+	 * 		- call DiscoveryClient::getInstances with this serviceId and assert fields returned
+	 * 
+ */ + @Test + void test() throws Exception { + String[] busyboxPods = K3S.execInContainer("sh", "-c", "kubectl get pods -l app=busybox -o=name --no-headers") + .getStdout() + .split("\n"); + + String podOne = busyboxPods[0].split("/")[1]; + String podTwo = busyboxPods[1].split("/")[1]; + + K3S.execInContainer("sh", "-c", "kubectl label pods " + podOne + " my-label=my-value"); + K3S.execInContainer("sh", "-c", "kubectl annotate pods " + podTwo + " my-annotation=my-value"); + + assertPodMetadata(discoveryClient); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryReactiveIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryReactiveIT.java new file mode 100644 index 0000000000..28d241c070 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryReactiveIT.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2024 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.client.discovery; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.web.server.LocalManagementPort; +import org.springframework.cloud.kubernetes.integration.tests.commons.Images; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.test.context.TestPropertySource; + +import static org.springframework.cloud.kubernetes.fabric8.client.discovery.TestAssertions.testReactiveConfiguration; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { + "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG", + "logging.level.org.springframework.cloud.client.discovery.health.reactive=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery.reactive=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG", + "spring.cloud.discovery.blocking.enabled=false" +}) +class Fabric8DiscoveryReactiveIT extends Fabric8DiscoveryBase { + + @LocalManagementPort + private int port; + + @BeforeEach + void beforeEach() { + Images.loadBusybox(K3S); + util.busybox(NAMESPACE, Phase.CREATE); + } + + @AfterEach + void afterEach() { + util.busybox(NAMESPACE, Phase.DELETE); + } + + @Test + void test(CapturedOutput output) { + testReactiveConfiguration(output, port); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/TestAssertions.java new file mode 100644 index 0000000000..32640c43bb --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/TestAssertions.java @@ -0,0 +1,349 @@ +/* + * Copyright 2012-2024 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.client.discovery; + +import java.time.Duration; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.springframework.boot.test.json.BasicJsonTester; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; +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; +import reactor.netty.http.client.HttpClient; +import reactor.util.retry.Retry; +import reactor.util.retry.RetryBackoffSpec; + +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; +import static java.util.AbstractMap.SimpleEntry; +import static java.util.Map.Entry; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author wind57 + */ +final class TestAssertions { + + 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(TestAssertions.class); + + private TestAssertions() { + + } + + static void assertPodMetadata(DiscoveryClient discoveryClient) { + + List serviceInstances = discoveryClient.getInstances("busybox-service"); + + // if annotations are empty, we got the other pod, with labels here + DefaultKubernetesServiceInstance withCustomLabel = serviceInstances.stream() + .map(instance -> (DefaultKubernetesServiceInstance) instance) + .filter(x -> x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) + .toList() + .get(0); + List> podMetadataLabels = withCustomLabel.podMetadata() + .get("labels") + .entrySet() + .stream() + .toList(); + + assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); + assertThat(withCustomLabel.getInstanceId()).isNotNull(); + assertThat(withCustomLabel.getHost()).isNotNull(); + assertThat(withCustomLabel.getMetadata()).isEqualTo( + Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") + ); + assertThat(podMetadataLabels).contains(new SimpleEntry<>("my-label", "my-value")); + + // if annotation are present, we got the one with annotations here + DefaultKubernetesServiceInstance withCustomAnnotation = serviceInstances.stream() + .map(instance -> (DefaultKubernetesServiceInstance) instance) + .filter(x -> !x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) + .toList() + .get(0); + List> podMetadataAnnotations = withCustomAnnotation.podMetadata() + .get("annotations") + .entrySet() + .stream() + .toList(); + + assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); + assertThat(withCustomLabel.getInstanceId()).isNotNull(); + assertThat(withCustomLabel.getHost()).isNotNull(); + assertThat(withCustomLabel.getMetadata()).isEqualTo( + Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") + ); + assertThat(podMetadataAnnotations).contains(new SimpleEntry<>("my-annotation", "my-value")); + } + + static void assertAllServices(DiscoveryClient discoveryClient) { + + List services = discoveryClient.getServices(); + assertThat(services).containsExactlyInAnyOrder("kubernetes", "busybox-service", "external-name-service"); + + ServiceInstance externalNameInstance = discoveryClient.getInstances("external-name-service").get(0); + + assertThat(externalNameInstance.getServiceId()).isEqualTo("external-name-service"); + assertThat(externalNameInstance.getInstanceId()).isNotNull(); + assertThat(externalNameInstance.getHost()).isEqualTo("spring.io"); + assertThat(externalNameInstance.getPort()).isEqualTo(-1); + assertThat(externalNameInstance.getMetadata()).isEqualTo( + Map.of("k8s_namespace", "default", "type", "ExternalName")); + assertThat(externalNameInstance.isSecure()).isFalse(); + assertThat(externalNameInstance.getUri().toASCIIString()).isEqualTo("spring.io"); + assertThat(externalNameInstance.getScheme()).isEqualTo("http"); + } + + /** + * 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 assertBlockingConfiguration(CapturedOutput output, int port) { + + waitForLogStatement(output, "Will publish InstanceRegisteredEvent from blocking implementation"); + waitForLogStatement(output, "publishing InstanceRegisteredEvent"); + waitForLogStatement(output, "Discovery Client has been initialized"); + + + WebClient healthClient = builder().baseUrl("http://localhost:" + port + "/actuator/health").build(); + + String healthResult = healthClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block(); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue("$.components.discoveryComposite.status") + .isEqualTo("UP"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue(BLOCKING_STATUS) + .isEqualTo("UP"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathArrayValue("$.components.discoveryComposite.components.discoveryClient.details.services") + .containsExactlyInAnyOrder("kubernetes", "busybox-service"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)).doesNotHaveJsonPath(REACTIVE_STATUS); + + } + + /** + * Both blocking and reactive are enabled. + */ + static void testDefaultConfiguration(CapturedOutput output, int port) { + + waitForLogStatement(output, "Will publish InstanceRegisteredEvent from blocking implementation"); + waitForLogStatement(output, "Will publish InstanceRegisteredEvent from reactive implementation"); + waitForLogStatement(output, "publishing InstanceRegisteredEvent"); + waitForLogStatement(output, "Discovery Client has been initialized"); + + WebClient healthClient = builder().baseUrl("http://localhost:" + port + "/actuator/health").build(); + + String healthResult = healthClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block(); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue("$.components.discoveryComposite.status") + .isEqualTo("UP"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue("$.components.discoveryComposite.components.discoveryClient.status") + .isEqualTo("UP"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathArrayValue("$.components.discoveryComposite.components.discoveryClient.details.services") + .containsExactlyInAnyOrder("kubernetes", "busybox-service"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status") + .isEqualTo("UP"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue( + "$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].status") + .isEqualTo("UP"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathArrayValue( + "$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].details.services") + .containsExactlyInAnyOrder("kubernetes", "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 reactive discovery + * client was initialized. + */ + static void testReactiveConfiguration(CapturedOutput output, int port) { + + waitForLogStatement(output, "Will publish InstanceRegisteredEvent from reactive implementation"); + waitForLogStatement(output, "publishing InstanceRegisteredEvent"); + waitForLogStatement(output, "Discovery Client has been initialized"); + + WebClient healthClient = builder().baseUrl("http://localhost:" + port + "/actuator/health").build(); + + String healthResult = healthClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block(); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status") + .isEqualTo("UP"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue(REACTIVE_STATUS) + .isEqualTo("UP"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathArrayValue( + "$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].details.services") + .containsExactlyInAnyOrder("kubernetes", "busybox-service"); + + assertThat(BASIC_JSON_TESTER.from(healthResult)).doesNotHaveJsonPath(BLOCKING_STATUS); + + // test for services also: + WebClient servicesClient = builder().baseUrl("http://localhost:" + port + "/reactive/services").build(); + + List servicesResult = servicesClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(new ParameterizedTypeReference>() { + }) + .retryWhen(retrySpec()) + .block(); + + assertThat(servicesResult).contains("busybox-service"); + assertThat(servicesResult).contains("kubernetes"); + + } + + /** + *
+	 *     - 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.
+	 * 
+ */ + static void filterMatchesBothNamespacesViaThePredicate(DiscoveryClient discoveryClient) { + + List services = discoveryClient.getServices(); + + assertThat(Objects.requireNonNull(services).size()).isEqualTo(1); + assertThat(services).contains("service-wiremock"); + + List serviceInstances = discoveryClient.getInstances("service-wiremock") + .stream() + .map(x -> (DefaultKubernetesServiceInstance) x) + .toList(); + + assertThat(Objects.requireNonNull(serviceInstances).size()).isEqualTo(2); + List sorted = serviceInstances.stream() + .sorted(Comparator.comparing(DefaultKubernetesServiceInstance::getNamespace)) + .toList(); + + DefaultKubernetesServiceInstance first = sorted.get(0); + assertThat(first.getServiceId()).isEqualTo("service-wiremock"); + assertThat(first.getInstanceId()).isNotNull(); + assertThat(first.getPort()).isEqualTo(8080); + assertThat(first.getNamespace()).isEqualTo("a-uat"); + assertThat(first.getMetadata()).isEqualTo( + Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP")); + + DefaultKubernetesServiceInstance second = sorted.get(1); + assertThat(second.getServiceId()).isEqualTo("service-wiremock"); + assertThat(second.getInstanceId()).isNotNull(); + assertThat(second.getPort()).isEqualTo(8080); + assertThat(second.getNamespace()).isEqualTo("b-uat"); + assertThat(second.getMetadata()).isEqualTo( + Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "b-uat", "type", "ClusterIP")); + + } + + /** + *
+	 *     - 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.
+	 * 
+ */ + static void filterMatchesOneNamespaceViaThePredicate(DiscoveryClient discoveryClient) { + + List services = discoveryClient.getServices(); + assertThat(services.size()).isEqualTo(1); + assertThat(services).contains("service-wiremock"); + + List serviceInstances = discoveryClient.getInstances("service-wiremock") + .stream() + .map(x -> (DefaultKubernetesServiceInstance) x) + .toList(); + + assertThat(serviceInstances.size()).isEqualTo(1); + + DefaultKubernetesServiceInstance first = serviceInstances.get(0); + assertThat(first.getServiceId()).isEqualTo("service-wiremock"); + assertThat(first.getInstanceId()).isNotNull(); + assertThat(first.getPort()).isEqualTo(8080); + assertThat(first.getNamespace()).isEqualTo("a-uat"); + assertThat(first.getMetadata()).isEqualTo( + Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP")); + + } + + private static void waitForLogStatement(CapturedOutput output, String message) { + await().pollInterval(Duration.ofSeconds(1)) + .atMost(Duration.ofSeconds(30)) + .until(() -> output.getOut().contains(message)); + } + + private static WebClient.Builder builder() { + return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); + } + + private static RetryBackoffSpec retrySpec() { + return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java deleted file mode 100644 index cf7ecfac84..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/Fabric8DiscoveryPodMetadataIT.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2012-2024 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.client.discovery.it; - -import io.fabric8.kubernetes.client.KubernetesClient; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.kubernetes.fabric8.client.discovery.Fabric8DiscoveryApp; -import org.springframework.cloud.kubernetes.integration.tests.commons.Images; -import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; - -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.Fabric8DiscoveryPodMetadataIT.TestConfig; -import static org.springframework.cloud.kubernetes.fabric8.client.discovery.it.TestAssertions.assertPodMetadata; - -/** - * @author wind57 - */ -@SpringBootTest(classes = { Fabric8DiscoveryApp.class, TestConfig.class }) -class Fabric8DiscoveryPodMetadataIT extends Fabric8DiscoveryBase { - - @Autowired - private DiscoveryClient discoveryClient; - - @BeforeEach - void beforeEach() { - Images.loadBusybox(K3S); - util.busybox(NAMESPACE, Phase.CREATE); - } - - @AfterEach - void afterEach() { - util.busybox(NAMESPACE, Phase.DELETE); - } - - /** - *
-	 * 		- there is a 'busybox-service' service deployed with two pods
-	 * 		- find each of the pod, add annotation to one, and labels to another
-	 * 		- call DiscoveryClient::getInstances with this serviceId and assert fields returned
-	 * 
- */ - @Test - void test() throws Exception { - String[] busyboxPods = K3S.execInContainer("sh", "-c", "kubectl get pods -l app=busybox -o=name --no-headers") - .getStdout() - .split("\n"); - - String podOne = busyboxPods[0].split("/")[1]; - String podTwo = busyboxPods[1].split("/")[1]; - - K3S.execInContainer("sh", "-c", "kubectl label pods " + podOne + " my-label=my-value"); - K3S.execInContainer("sh", "-c", "kubectl annotate pods " + podTwo + " my-annotation=my-value"); - - assertPodMetadata(discoveryClient); - } - - @TestConfiguration - static class TestConfig { - - @Bean - @Primary - KubernetesClient kubernetesClient() { - return client(); - } - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java deleted file mode 100644 index b5241bc3c3..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/it/TestAssertions.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2012-2024 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.client.discovery.it; - -import java.util.List; -import java.util.Map; - -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; - -import static java.util.AbstractMap.SimpleEntry; -import static java.util.Map.Entry; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author wind57 - */ -final class TestAssertions { - - private TestAssertions() { - - } - - static void assertPodMetadata(DiscoveryClient discoveryClient) { - - List serviceInstances = discoveryClient.getInstances("busybox-service"); - - // if annotations are empty, we got the other pod, with labels here - DefaultKubernetesServiceInstance withCustomLabel = serviceInstances.stream() - .map(instance -> (DefaultKubernetesServiceInstance) instance) - .filter(x -> x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) - .toList() - .get(0); - List> podMetadataLabels = withCustomLabel.podMetadata() - .get("labels") - .entrySet() - .stream() - .toList(); - - assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); - assertThat(withCustomLabel.getInstanceId()).isNotNull(); - assertThat(withCustomLabel.getHost()).isNotNull(); - assertThat(withCustomLabel.getMetadata()).isEqualTo( - Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") - ); - assertThat(podMetadataLabels).contains(new SimpleEntry<>("my-label", "my-value")); - - // if annotation are present, we got the one with annotations here - DefaultKubernetesServiceInstance withCustomAnnotation = serviceInstances.stream() - .map(instance -> (DefaultKubernetesServiceInstance) instance) - .filter(x -> !x.podMetadata().getOrDefault("annotations", Map.of()).isEmpty()) - .toList() - .get(0); - List> podMetadataAnnotations = withCustomAnnotation.podMetadata() - .get("annotations") - .entrySet() - .stream() - .toList(); - - assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); - assertThat(withCustomLabel.getInstanceId()).isNotNull(); - assertThat(withCustomLabel.getHost()).isNotNull(); - assertThat(withCustomLabel.getMetadata()).isEqualTo( - Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") - ); - assertThat(podMetadataAnnotations).contains(new SimpleEntry<>("my-annotation", "my-value")); - } - - static void assertAllServices(DiscoveryClient discoveryClient) { - - List services = discoveryClient.getServices(); - assertThat(services).containsExactlyInAnyOrder("kubernetes", "busybox-service", "external-name-service"); - - ServiceInstance externalNameInstance = discoveryClient.getInstances("external-name-service").get(0); - - assertThat(externalNameInstance.getServiceId()).isEqualTo("external-name-service"); - assertThat(externalNameInstance.getInstanceId()).isNotNull(); - assertThat(externalNameInstance.getHost()).isEqualTo("spring.io"); - assertThat(externalNameInstance.getPort()).isEqualTo(-1); - assertThat(externalNameInstance.getMetadata()).isEqualTo( - Map.of("k8s_namespace", "default", "type", "ExternalName")); - assertThat(externalNameInstance.isSecure()).isFalse(); - assertThat(externalNameInstance.getUri().toASCIIString()).isEqualTo("spring.io"); - assertThat(externalNameInstance.getScheme()).isEqualTo("http"); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/external-name-service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/external-name-service.yaml deleted file mode 100644 index 649d5da75c..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/external-name-service.yaml +++ /dev/null @@ -1,7 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: external-name-service -spec: - type: ExternalName - externalName: spring.io diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-deployment.yaml deleted file mode 100644 index 47f0a16f38..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-deployment.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: spring-cloud-kubernetes-fabric8-client-discovery-deployment -spec: - selector: - matchLabels: - app: spring-cloud-kubernetes-fabric8-client-discovery - template: - metadata: - labels: - app: spring-cloud-kubernetes-fabric8-client-discovery - spec: - serviceAccountName: spring-cloud-kubernetes-serviceaccount - containers: - - name: spring-cloud-kubernetes-fabric8-client-discovery - image: docker.io/springcloud/spring-cloud-kubernetes-fabric8-client-discovery - imagePullPolicy: IfNotPresent - readinessProbe: - httpGet: - port: 8080 - path: /actuator/health/readiness - livenessProbe: - httpGet: - port: 8080 - path: /actuator/health/liveness - ports: - - containerPort: 8080 - env: - - name: SPRING_CLOUD_BOOTSTRAP_ENABLED - value: "FALSE" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-ingress.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-ingress.yaml deleted file mode 100644 index 334508bf16..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-ingress.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: spring-cloud-kubernetes-fabric8-client-discovery-ingress - namespace: default -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: spring-cloud-kubernetes-fabric8-client-discovery - port: - number: 8080 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-service.yaml deleted file mode 100644 index bea511d5eb..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/fabric8-discovery-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: spring-cloud-kubernetes-fabric8-client-discovery - name: spring-cloud-kubernetes-fabric8-client-discovery -spec: - ports: - - name: http - port: 8080 - targetPort: 8080 - selector: - app: spring-cloud-kubernetes-fabric8-client-discovery - type: ClusterIP diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml deleted file mode 100644 index d8eec94946..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - creationTimestamp: null - name: admin-default -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: spring-cloud-kubernetes-serviceaccount - namespace: default diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Images.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Images.java index f37a17ee7e..4a39540a1b 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Images.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Images.java @@ -85,7 +85,9 @@ public static void loadBusybox(K3sContainer container) { } public static void loadWiremock(K3sContainer container) { - Commons.load(container, WIREMOCK_TAR, WIREMOCK, wiremockVersion()); + if (!imageAlreadyInK3s(container, WIREMOCK_TAR)) { + Commons.load(container, WIREMOCK_TAR, WIREMOCK, wiremockVersion()); + } } public static void loadIstioCtl(K3sContainer container) { From b0dcb451a8d8f78ab0f1943a5023b63e40c1ffab Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 7 Oct 2024 10:28:35 +0300 Subject: [PATCH 22/35] first ready --- .../Fabric8DiscoveryAllServicesIT.java | 32 ++++---- .../discovery/Fabric8DiscoveryBase.java | 14 ++-- .../discovery/Fabric8DiscoveryBlockingIT.java | 4 +- ...abric8DiscoveryDefaultConfigurationIT.java | 11 ++- ...ic8DiscoveryFilterMatchOneNamespaceIT.java | 9 +-- ...c8DiscoveryFilterMatchTwoNamespacesIT.java | 9 +-- .../discovery/Fabric8DiscoveryReactiveIT.java | 19 +++-- .../client/discovery/TestAssertions.java | 79 ++++++++----------- .../test/resources/external-name-service.yaml | 7 ++ 9 files changed, 87 insertions(+), 97 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/external-name-service.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryAllServicesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryAllServicesIT.java index 5be0d2c8f2..404d2f767d 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryAllServicesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryAllServicesIT.java @@ -39,6 +39,21 @@ */ class Fabric8DiscoveryAllServicesIT extends Fabric8DiscoveryBase { + private void externalNameServices(Phase phase) { + try (InputStream externalNameServiceStream = util.inputStream("external-name-service.yaml")) { + Service externalServiceName = Serialization.unmarshal(externalNameServiceStream, Service.class); + if (phase == Phase.CREATE) { + util.createAndWait(NAMESPACE, null, null, externalServiceName, null, true); + } + else { + util.deleteAndWait(NAMESPACE, null, externalServiceName, null); + } + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + @Nested @TestPropertySource(properties = { "spring.cloud.kubernetes.discovery.include-external-name-services=true" }) class NonBootstrap { @@ -69,11 +84,12 @@ void afterEach() { void test() { assertAllServices(discoveryClient); } + } @Nested @TestPropertySource(properties = { "spring.cloud.kubernetes.discovery.include-external-name-services=true", - "spring.cloud.bootstrap.enabled=true"}) + "spring.cloud.bootstrap.enabled=true" }) class Bootstrap { @Autowired @@ -102,21 +118,7 @@ void afterEach() { void test() { assertAllServices(discoveryClient); } - } - private void externalNameServices(Phase phase) { - try (InputStream externalNameServiceStream = util.inputStream("external-name-service.yaml")) { - Service externalServiceName = Serialization.unmarshal(externalNameServiceStream, Service.class); - if (phase == Phase.CREATE) { - util.createAndWait(NAMESPACE, null, null, externalServiceName, null, true); - } - else { - util.deleteAndWait(NAMESPACE, null, externalServiceName, null); - } - } - catch (IOException e) { - throw new RuntimeException(e); - } } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBase.java index dc50137487..0b385736e3 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBase.java @@ -25,26 +25,24 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; - import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; import org.springframework.test.context.TestPropertySource; /** * @author wind57 */ -@TestPropertySource( - properties = { "spring.main.cloud-platform=kubernetes", "spring.cloud.config.import-check.enabled=false", - "spring.cloud.kubernetes.client.namespace=default", +@TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", + "spring.cloud.config.import-check.enabled=false", "spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.discovery.metadata.add-pod-labels=true", - "spring.cloud.kubernetes.discovery.metadata.add-pod-annotations=true" , + "spring.cloud.kubernetes.discovery.metadata.add-pod-annotations=true", "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=debug" }) @ExtendWith(OutputCaptureExtension.class) @SpringBootTest(classes = { Fabric8DiscoveryApp.class, Fabric8DiscoveryBase.TestConfig.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) abstract class Fabric8DiscoveryBase { protected static final String NAMESPACE = "default"; diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBlockingIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBlockingIT.java index e0e0b57fbc..3c4d3cc7d5 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBlockingIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryBlockingIT.java @@ -32,8 +32,8 @@ * @author wind57 */ @TestPropertySource(properties = { "spring.cloud.discovery.reactive.enabled=false", - "logging.level.org.springframework.cloud.client.discovery.health=DEBUG", - "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG" }) + "logging.level.org.springframework.cloud.client.discovery.health=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG" }) class Fabric8DiscoveryBlockingIT extends Fabric8DiscoveryBase { @LocalManagementPort diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDefaultConfigurationIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDefaultConfigurationIT.java index db439aefcd..438ae6e8fa 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDefaultConfigurationIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryDefaultConfigurationIT.java @@ -31,12 +31,11 @@ /** * @author wind57 */ -@TestPropertySource(properties = { - "logging.level.org.springframework.cloud.client.discovery.health.reactive=DEBUG", - "logging.level.org.springframework.cloud.client.discovery.health=DEBUG", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery.reactive=DEBUG", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG", - "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG" }) +@TestPropertySource(properties = { "logging.level.org.springframework.cloud.client.discovery.health.reactive=DEBUG", + "logging.level.org.springframework.cloud.client.discovery.health=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery.reactive=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG" }) class Fabric8DiscoveryDefaultConfigurationIT extends Fabric8DiscoveryBase { @LocalManagementPort diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java index 5176725ef1..f7716bd6dd 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java @@ -31,11 +31,10 @@ /** * @author wind57 */ -@TestPropertySource(properties = { - "spring.cloud.kubernetes.discovery.namespaces[0]=a-uat", - "spring.cloud.kubernetes.discovery.namespaces[1]=b-uat", - "spring.cloud.kubernetes.discovery.filter=#root.metadata.namespace matches 'a-uat$'", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" +@TestPropertySource(properties = { "spring.cloud.kubernetes.discovery.namespaces[0]=a-uat", + "spring.cloud.kubernetes.discovery.namespaces[1]=b-uat", + "spring.cloud.kubernetes.discovery.filter=#root.metadata.namespace matches 'a-uat$'", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" }) class Fabric8DiscoveryFilterMatchOneNamespaceIT extends Fabric8DiscoveryBase { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java index 2945c4d6ba..4872c2091e 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java @@ -31,11 +31,10 @@ /** * @author wind57 */ -@TestPropertySource(properties = { - "spring.cloud.kubernetes.discovery.namespaces[0]=a-uat", - "spring.cloud.kubernetes.discovery.namespaces[1]=b-uat", - "spring.cloud.kubernetes.discovery.filter=#root.metadata.namespace matches '^.*uat$'", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" +@TestPropertySource(properties = { "spring.cloud.kubernetes.discovery.namespaces[0]=a-uat", + "spring.cloud.kubernetes.discovery.namespaces[1]=b-uat", + "spring.cloud.kubernetes.discovery.filter=#root.metadata.namespace matches '^.*uat$'", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" }) class Fabric8DiscoveryFilterMatchTwoNamespacesIT extends Fabric8DiscoveryBase { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryReactiveIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryReactiveIT.java index 28d241c070..12cf477f20 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryReactiveIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryReactiveIT.java @@ -20,8 +20,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.web.server.LocalManagementPort; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; import org.springframework.cloud.kubernetes.integration.tests.commons.Images; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.test.context.TestPropertySource; @@ -31,18 +33,19 @@ /** * @author wind57 */ -@TestPropertySource(properties = { - "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG", - "logging.level.org.springframework.cloud.client.discovery.health.reactive=DEBUG", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery.reactive=DEBUG", - "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG", - "spring.cloud.discovery.blocking.enabled=false" -}) +@TestPropertySource(properties = { "logging.level.org.springframework.cloud.kubernetes.commons.discovery=DEBUG", + "logging.level.org.springframework.cloud.client.discovery.health.reactive=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery.reactive=DEBUG", + "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG", + "spring.cloud.discovery.blocking.enabled=false" }) class Fabric8DiscoveryReactiveIT extends Fabric8DiscoveryBase { @LocalManagementPort private int port; + @Autowired + private ReactiveDiscoveryClient discoveryClient; + @BeforeEach void beforeEach() { Images.loadBusybox(K3S); @@ -56,7 +59,7 @@ void afterEach() { @Test void test(CapturedOutput output) { - testReactiveConfiguration(output, port); + testReactiveConfiguration(discoveryClient, output, port); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/TestAssertions.java index 32640c43bb..64ca0ec8bb 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/TestAssertions.java @@ -22,23 +22,24 @@ import java.util.Map; import java.util.Objects; +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.boot.test.system.CapturedOutput; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; -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; -import reactor.netty.http.client.HttpClient; -import reactor.util.retry.Retry; -import reactor.util.retry.RetryBackoffSpec; -import static org.testcontainers.shaded.org.awaitility.Awaitility.await; import static java.util.AbstractMap.SimpleEntry; import static java.util.Map.Entry; import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; /** * @author wind57 @@ -66,17 +67,16 @@ static void assertPodMetadata(DiscoveryClient discoveryClient) { .toList() .get(0); List> podMetadataLabels = withCustomLabel.podMetadata() - .get("labels") - .entrySet() - .stream() - .toList(); + .get("labels") + .entrySet() + .stream() + .toList(); assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); assertThat(withCustomLabel.getInstanceId()).isNotNull(); assertThat(withCustomLabel.getHost()).isNotNull(); - assertThat(withCustomLabel.getMetadata()).isEqualTo( - Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") - ); + assertThat(withCustomLabel.getMetadata()) + .isEqualTo(Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80")); assertThat(podMetadataLabels).contains(new SimpleEntry<>("my-label", "my-value")); // if annotation are present, we got the one with annotations here @@ -94,9 +94,8 @@ static void assertPodMetadata(DiscoveryClient discoveryClient) { assertThat(withCustomLabel.getServiceId()).isEqualTo("busybox-service"); assertThat(withCustomLabel.getInstanceId()).isNotNull(); assertThat(withCustomLabel.getHost()).isNotNull(); - assertThat(withCustomLabel.getMetadata()).isEqualTo( - Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80") - ); + assertThat(withCustomLabel.getMetadata()) + .isEqualTo(Map.of("k8s_namespace", "default", "type", "ClusterIP", "port.busybox-port", "80")); assertThat(podMetadataAnnotations).contains(new SimpleEntry<>("my-annotation", "my-value")); } @@ -111,8 +110,8 @@ static void assertAllServices(DiscoveryClient discoveryClient) { assertThat(externalNameInstance.getInstanceId()).isNotNull(); assertThat(externalNameInstance.getHost()).isEqualTo("spring.io"); assertThat(externalNameInstance.getPort()).isEqualTo(-1); - assertThat(externalNameInstance.getMetadata()).isEqualTo( - Map.of("k8s_namespace", "default", "type", "ExternalName")); + assertThat(externalNameInstance.getMetadata()) + .isEqualTo(Map.of("k8s_namespace", "default", "type", "ExternalName")); assertThat(externalNameInstance.isSecure()).isFalse(); assertThat(externalNameInstance.getUri().toASCIIString()).isEqualTo("spring.io"); assertThat(externalNameInstance.getScheme()).isEqualTo("http"); @@ -132,7 +131,6 @@ static void assertBlockingConfiguration(CapturedOutput output, int port) { waitForLogStatement(output, "publishing InstanceRegisteredEvent"); waitForLogStatement(output, "Discovery Client has been initialized"); - WebClient healthClient = builder().baseUrl("http://localhost:" + port + "/actuator/health").build(); String healthResult = healthClient.method(HttpMethod.GET) @@ -145,9 +143,7 @@ static void assertBlockingConfiguration(CapturedOutput output, int port) { .extractingJsonPathStringValue("$.components.discoveryComposite.status") .isEqualTo("UP"); - assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathStringValue(BLOCKING_STATUS) - .isEqualTo("UP"); + assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue(BLOCKING_STATUS).isEqualTo("UP"); assertThat(BASIC_JSON_TESTER.from(healthResult)) .extractingJsonPathArrayValue("$.components.discoveryComposite.components.discoveryClient.details.services") @@ -191,13 +187,11 @@ static void testDefaultConfiguration(CapturedOutput output, int port) { .extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status") .isEqualTo("UP"); - assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathStringValue( + assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue( "$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].status") .isEqualTo("UP"); - assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathArrayValue( + assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathArrayValue( "$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].details.services") .containsExactlyInAnyOrder("kubernetes", "busybox-service"); } @@ -210,7 +204,7 @@ static void testDefaultConfiguration(CapturedOutput output, int port) { * We assert for logs and call '/health' endpoint to see that reactive discovery * client was initialized. */ - static void testReactiveConfiguration(CapturedOutput output, int port) { + static void testReactiveConfiguration(ReactiveDiscoveryClient discoveryClient, CapturedOutput output, int port) { waitForLogStatement(output, "Will publish InstanceRegisteredEvent from reactive implementation"); waitForLogStatement(output, "publishing InstanceRegisteredEvent"); @@ -228,29 +222,18 @@ static void testReactiveConfiguration(CapturedOutput output, int port) { .extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status") .isEqualTo("UP"); - assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathStringValue(REACTIVE_STATUS) - .isEqualTo("UP"); + assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue(REACTIVE_STATUS).isEqualTo("UP"); - assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathArrayValue( + assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathArrayValue( "$.components.reactiveDiscoveryClients.components.['Fabric8 Kubernetes Reactive Discovery Client'].details.services") .containsExactlyInAnyOrder("kubernetes", "busybox-service"); assertThat(BASIC_JSON_TESTER.from(healthResult)).doesNotHaveJsonPath(BLOCKING_STATUS); - // test for services also: - WebClient servicesClient = builder().baseUrl("http://localhost:" + port + "/reactive/services").build(); + List services = discoveryClient.getServices().toStream().toList(); - List servicesResult = servicesClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - }) - .retryWhen(retrySpec()) - .block(); - - assertThat(servicesResult).contains("busybox-service"); - assertThat(servicesResult).contains("kubernetes"); + assertThat(services).contains("busybox-service"); + assertThat(services).contains("kubernetes"); } @@ -287,7 +270,7 @@ static void filterMatchesBothNamespacesViaThePredicate(DiscoveryClient discovery assertThat(first.getPort()).isEqualTo(8080); assertThat(first.getNamespace()).isEqualTo("a-uat"); assertThat(first.getMetadata()).isEqualTo( - Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP")); + Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP")); DefaultKubernetesServiceInstance second = sorted.get(1); assertThat(second.getServiceId()).isEqualTo("service-wiremock"); @@ -295,7 +278,7 @@ static void filterMatchesBothNamespacesViaThePredicate(DiscoveryClient discovery assertThat(second.getPort()).isEqualTo(8080); assertThat(second.getNamespace()).isEqualTo("b-uat"); assertThat(second.getMetadata()).isEqualTo( - Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "b-uat", "type", "ClusterIP")); + Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "b-uat", "type", "ClusterIP")); } @@ -316,9 +299,9 @@ static void filterMatchesOneNamespaceViaThePredicate(DiscoveryClient discoveryCl assertThat(services).contains("service-wiremock"); List serviceInstances = discoveryClient.getInstances("service-wiremock") - .stream() - .map(x -> (DefaultKubernetesServiceInstance) x) - .toList(); + .stream() + .map(x -> (DefaultKubernetesServiceInstance) x) + .toList(); assertThat(serviceInstances.size()).isEqualTo(1); @@ -328,7 +311,7 @@ static void filterMatchesOneNamespaceViaThePredicate(DiscoveryClient discoveryCl assertThat(first.getPort()).isEqualTo(8080); assertThat(first.getNamespace()).isEqualTo("a-uat"); assertThat(first.getMetadata()).isEqualTo( - Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP")); + Map.of("app", "service-wiremock", "port.http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP")); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/external-name-service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/external-name-service.yaml new file mode 100644 index 0000000000..649d5da75c --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/resources/external-name-service.yaml @@ -0,0 +1,7 @@ +kind: Service +apiVersion: v1 +metadata: + name: external-name-service +spec: + type: ExternalName + externalName: spring.io From fed55590d0a46ceac8ac85b8ccbec931a35c8411 Mon Sep 17 00:00:00 2001 From: wind57 Date: Wed, 9 Oct 2024 22:11:10 +0300 Subject: [PATCH 23/35] started basic work --- .../reload/it/Fabric8EventReloadBase.java | 67 +++++++++++++++++ ...rmFromOneNamespaceEventNotTriggeredIT.java | 74 +++++++++++++++++++ .../client/reload/it/TestAssertions.java | 37 ++++++++++ .../src/test/resources/deployment.yaml | 35 --------- .../src/test/resources/ingress.yaml | 16 ---- .../src/test/resources/service.yaml | 14 ---- 6 files changed, 178 insertions(+), 65 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/deployment.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/ingress.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/service.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java new file mode 100644 index 0000000000..2a9d68a7f7 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2024 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.client.reload.it; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.cloud.kubernetes.fabric8.client.reload.App; +import org.testcontainers.k3s.K3sContainer; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; +import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +@ExtendWith(OutputCaptureExtension.class) +@SpringBootTest(classes = { App.class, Fabric8EventReloadBase.TestConfig.class }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +abstract class Fabric8EventReloadBase { + + protected static final K3sContainer K3S = Commons.container(); + + protected static Util util; + + @BeforeAll + protected static void beforeAll() { + K3S.start(); + util = new Util(K3S); + } + + protected static KubernetesClient client() { + String kubeConfigYaml = K3S.getKubeConfigYaml(); + Config config = Config.fromKubeconfig(kubeConfigYaml); + return new KubernetesClientBuilder().withConfig(config).build(); + } + + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + return client(); + } + + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java new file mode 100644 index 0000000000..bf55570736 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2024 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.client.reload.it; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.test.context.TestPropertySource; + +import java.io.InputStream; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", + "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", + "spring.cloud.bootstrap.enabled=true", "spring.cloud.kubernetes.client.namespace=default" }) +class Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT extends Fabric8EventReloadBase { + + @BeforeEach + void beforeEach() { + util.createNamespace("left"); + util.createNamespace("right"); + + leftAndRightConfigMap(Phase.CREATE); + } + + @AfterEach + void afterEach() { + leftAndRightConfigMap(Phase.DELETE); + + util.deleteNamespace("left"); + util.deleteNamespace("right"); + } + + @Test + void test() { + + } + + private void leftAndRightConfigMap(Phase phase) { + InputStream leftConfigMapStream = util.inputStream("left-configmap.yaml"); + InputStream rightConfigMapStream = util.inputStream("right-configmap.yaml"); + ConfigMap leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); + ConfigMap rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); + + if (phase.equals(Phase.CREATE)) { + util.createAndWait("left", leftConfigMap, null); + util.createAndWait("right", rightConfigMap, null); + } + else { + util.deleteAndWait("left", leftConfigMap, null); + util.deleteAndWait("right", rightConfigMap, null); + } + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java new file mode 100644 index 0000000000..def980b73e --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2024 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.client.reload.it; + +import org.springframework.boot.test.system.CapturedOutput; + +/** + * @author wind57 + */ +final class TestAssertions { + + private TestAssertions() { + + } + + /** + * assert that 'left' is present, and IFF it is, assert that 'right' is not + */ + static void assertReloadLogStatements(String left, String right, CapturedOutput output) { + + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/deployment.yaml deleted file mode 100644 index aba13d5127..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/deployment.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: spring-cloud-kubernetes-fabric8-client-reload -spec: - selector: - matchLabels: - app: spring-cloud-kubernetes-fabric8-client-reload - template: - metadata: - labels: - app: spring-cloud-kubernetes-fabric8-client-reload - spec: - serviceAccountName: spring-cloud-kubernetes-serviceaccount - containers: - - name: spring-cloud-kubernetes-fabric8-client-reload - image: docker.io/springcloud/spring-cloud-kubernetes-fabric8-client-reload - imagePullPolicy: IfNotPresent - readinessProbe: - httpGet: - port: 8080 - path: /actuator/health/readiness - livenessProbe: - httpGet: - port: 8080 - path: /actuator/health/liveness - ports: - - containerPort: 8080 - env: - - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_CONFIG_RELOAD - value: "DEBUG" - - name: SPRING_PROFILES_ACTIVE - value: "one" - - name: SPRING_CLOUD_BOOTSTRAP_ENABLED - value: "TRUE" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/ingress.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/ingress.yaml deleted file mode 100644 index d6e9e49a9e..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/ingress.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: spring-cloud-kubernetes-fabric8-client-reload-ingress - namespace: default -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: spring-cloud-kubernetes-fabric8-client-reload - port: - number: 8080 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/service.yaml deleted file mode 100644 index 3f73ad0763..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: spring-cloud-kubernetes-fabric8-client-reload - name: spring-cloud-kubernetes-fabric8-client-reload -spec: - ports: - - name: http - port: 8080 - targetPort: 8080 - selector: - app: spring-cloud-kubernetes-fabric8-client-reload - type: ClusterIP From 6668e411e334f329516be3e26c29a813964642da Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 11 Oct 2024 10:42:39 +0300 Subject: [PATCH 24/35] dirty --- .../client/reload/Fabric8EventReloadIT.java | 8 -- .../fabric8/client/reload/TestUtil.java | 2 +- .../reload/it/Fabric8EventReloadBase.java | 26 +---- ...rmFromOneNamespaceEventNotTriggeredIT.java | 107 +++++++++++++++--- .../client/reload/it/TestAssertions.java | 21 ++++ .../test/resources/META-INF/spring.factories | 2 + .../src/test/resources/application-one.yaml | 12 ++ .../src/test/resources/logback-test.xml | 2 + .../resources/{ => manifests}/configmap.yaml | 0 .../{ => manifests}/left-configmap.yaml | 0 .../right-configmap-with-label.yaml | 0 .../{ => manifests}/right-configmap.yaml | 0 .../resources/{ => manifests}/secret.yaml | 0 13 files changed, 132 insertions(+), 48 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-one.yaml rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/{ => manifests}/configmap.yaml (100%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/{ => manifests}/left-configmap.yaml (100%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/{ => manifests}/right-configmap-with-label.yaml (100%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/{ => manifests}/right-configmap.yaml (100%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/{ => manifests}/secret.yaml (100%) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java index 780ad9b644..727a7c8c43 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java @@ -89,14 +89,6 @@ static void afterAll() throws Exception { manifests(Phase.DELETE); } - /** - *
-	 *     - there are two namespaces : left and right
-	 *     - each of the namespaces has one configmap
-	 *     - we watch the "left" namespace, but make a change in the configmap in the right namespace
-	 *     - as such, no event is triggered and "left-configmap" stays as-is
-	 * 
- */ @Test void testInformFromOneNamespaceEventNotTriggered() { Commons.assertReloadLogStatements("added configmap informer for namespace", diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java index 7ede854c6f..4282e05084 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java @@ -314,7 +314,7 @@ static void reCreateSources(Util util, KubernetesClient client) { } static void replaceConfigMap(KubernetesClient client, ConfigMap configMap, String namespace) { - client.configMaps().inNamespace(namespace).resource(configMap).createOrReplace(); + client.configMaps().inNamespace(namespace).resource(configMap).serverSideApply(); } static void patchOne(Util util, String dockerImage, String deploymentName, String namespace) { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java index 2a9d68a7f7..ffc1724f20 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java @@ -16,25 +16,18 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload.it; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.cloud.kubernetes.fabric8.client.reload.App; import org.testcontainers.k3s.K3sContainer; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; @ExtendWith(OutputCaptureExtension.class) -@SpringBootTest(classes = { App.class, Fabric8EventReloadBase.TestConfig.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest(classes = { App.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) abstract class Fabric8EventReloadBase { protected static final K3sContainer K3S = Commons.container(); @@ -47,21 +40,4 @@ protected static void beforeAll() { util = new Util(K3S); } - protected static KubernetesClient client() { - String kubeConfigYaml = K3S.getKubeConfigYaml(); - Config config = Config.fromKubeconfig(kubeConfigYaml); - return new KubernetesClientBuilder().withConfig(config).build(); - } - - @TestConfiguration - static class TestConfig { - - @Bean - @Primary - KubernetesClient kubernetesClient() { - return client(); - } - - } - } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java index bf55570736..96e4d1baa7 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java @@ -16,15 +16,36 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +import java.io.InputStream; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.TimeUnit; + import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.utils.Serialization; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.system.CapturedOutput; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; -import java.io.InputStream; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; /** * @author wind57 @@ -32,35 +53,80 @@ @TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", "spring.cloud.bootstrap.enabled=true", "spring.cloud.kubernetes.client.namespace=default" }) +@ActiveProfiles("one") class Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT extends Fabric8EventReloadBase { - @BeforeEach - void beforeEach() { + private static ConfigMap leftConfigMap; + + private static ConfigMap rightConfigMap; + + @Autowired + private Environment environment; + + @Autowired + private KubernetesClient kubernetesClient; + + @BeforeAll + static void beforeAllLocal() { + InputStream leftConfigMapStream = util.inputStream("manifests/left-configmap.yaml"); + InputStream rightConfigMapStream = util.inputStream("manifests/right-configmap.yaml"); + leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); + rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); + util.createNamespace("left"); util.createNamespace("right"); leftAndRightConfigMap(Phase.CREATE); } - @AfterEach - void afterEach() { + @AfterAll + static void afterAllLocal() { leftAndRightConfigMap(Phase.DELETE); util.deleteNamespace("left"); util.deleteNamespace("right"); } + /** + *
+	 *     - there are two namespaces : left and right
+	 *     - each of the namespaces has one configmap
+	 *     - we watch the "left" namespace, but make a change in the configmap in the "right" namespace
+	 *     - as such, no event is triggered and "left-configmap" stays as-is
+	 * 
+ */ @Test - void test() { + void test(CapturedOutput output) throws Exception { + assertReloadLogStatements("added configmap informer for namespace", + "added secret informer for namespace", output); + String currentLeftValue = environment.getProperty("left.value"); + assertThat(currentLeftValue).isEqualTo("left-initial"); - } + String currentRightValue = environment.getProperty("right.value"); + assertThat(currentRightValue).isEqualTo("right-initial"); + + // then deploy a new version of right-configmap + ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build()) + .withData(Map.of("right.value", "right-after-change")) + .build(); - private void leftAndRightConfigMap(Phase phase) { - InputStream leftConfigMapStream = util.inputStream("left-configmap.yaml"); - InputStream rightConfigMapStream = util.inputStream("right-configmap.yaml"); - ConfigMap leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); - ConfigMap rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); + replaceConfigMap(kubernetesClient, rightConfigMapAfterChange, "right"); + TimeUnit.SECONDS.sleep(10); + +// await().pollThread(run -> { +// Thread t = new Thread(run); +// return t; +// }).atMost(Duration.ofSeconds(30)) +// .pollInterval(Duration.ofSeconds(1)) +// .until(() -> environment.getProperty("right.value").equals("right-after-change")); + + System.out.println("yes"); + + } + + private static void leftAndRightConfigMap(Phase phase) { if (phase.equals(Phase.CREATE)) { util.createAndWait("left", leftConfigMap, null); util.createAndWait("right", rightConfigMap, null); @@ -71,4 +137,17 @@ private void leftAndRightConfigMap(Phase phase) { } } + @TestConfiguration + public static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + String kubeConfigYaml = K3S.getKubeConfigYaml(); + Config config = Config.fromKubeconfig(kubeConfigYaml); + return new KubernetesClientBuilder().withConfig(config).build(); + } + + } + } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java index def980b73e..2c6ca5c353 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java @@ -16,8 +16,14 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; import org.springframework.boot.test.system.CapturedOutput; +import java.time.Duration; + +import static org.testcontainers.shaded.org.awaitility.Awaitility.await; + /** * @author wind57 */ @@ -32,6 +38,21 @@ private TestAssertions() { */ static void assertReloadLogStatements(String left, String right, CapturedOutput output) { + await().pollDelay(Duration.ofSeconds(5)) + .atMost(Duration.ofSeconds(15)) + .pollInterval(Duration.ofSeconds(1)) + .until(() -> { + boolean leftIsPresent = output.getOut().contains(left); + if (leftIsPresent) { + boolean rightIsPresent = output.getOut().contains(right); + return !rightIsPresent; + } + return false; + }); + } + + static void replaceConfigMap(KubernetesClient client, ConfigMap configMap, String namespace) { + client.configMaps().inNamespace(namespace).resource(configMap).createOrReplace(); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories new file mode 100644 index 0000000000..1ae5656b5e --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.kubernetes.fabric8.client.reload.it.Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.TestConfig diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-one.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-one.yaml new file mode 100644 index 0000000000..b1cc878981 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-one.yaml @@ -0,0 +1,12 @@ +spring: + application: + name: event-reload + cloud: + kubernetes: + reload: + enabled: true + strategy: refresh + mode: event + namespaces: + - left + monitoring-config-maps: true diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/logback-test.xml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/logback-test.xml index ee24334373..0f9de3d918 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/logback-test.xml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/logback-test.xml @@ -12,4 +12,6 @@ + +
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/configmap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/configmap.yaml similarity index 100% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/configmap.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/configmap.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/left-configmap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/left-configmap.yaml similarity index 100% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/left-configmap.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/left-configmap.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/right-configmap-with-label.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/right-configmap-with-label.yaml similarity index 100% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/right-configmap-with-label.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/right-configmap-with-label.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/right-configmap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/right-configmap.yaml similarity index 100% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/right-configmap.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/right-configmap.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/secret.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/secret.yaml similarity index 100% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/secret.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/secret.yaml From 3a0f108a3cf79b94c91b18b845ef22c4b430307d Mon Sep 17 00:00:00 2001 From: wind57 Date: Sat, 12 Oct 2024 16:47:50 +0300 Subject: [PATCH 25/35] dirty --- .../client/reload/Fabric8EventReloadIT.java | 43 ----- .../reload/it/Fabric8EventReloadBase.java | 4 +- ...rmFromOneNamespaceEventNotTriggeredIT.java | 63 +++---- ...nformFromOneNamespaceEventTriggeredIT.java | 163 ++++++++++++++++++ .../client/reload/it/TestAssertions.java | 18 +- .../resources/application-two.yaml | 6 +- .../resources/bootstrap-one.yaml | 4 - .../resources/bootstrap-two.yaml | 4 - 8 files changed, 206 insertions(+), 99 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/{main => test}/resources/application-two.yaml (76%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/{main => test}/resources/bootstrap-one.yaml (84%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/{main => test}/resources/bootstrap-two.yaml (84%) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java index 727a7c8c43..b2b4739198 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java @@ -91,49 +91,6 @@ static void afterAll() throws Exception { @Test void testInformFromOneNamespaceEventNotTriggered() { - Commons.assertReloadLogStatements("added configmap informer for namespace", - "added secret informer for namespace", IMAGE_NAME); - - WebClient webClient = TestUtil.builder().baseUrl("http://localhost/left").build(); - String result = webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - - // we first read the initial value from the left-configmap - Assertions.assertEquals("left-initial", result); - - // then read the value from the right-configmap - webClient = TestUtil.builder().baseUrl("http://localhost/right").build(); - result = webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - Assertions.assertEquals("right-initial", result); - - // then deploy a new version of right-configmap - ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build()) - .withData(Map.of("right.value", "right-after-change")) - .build(); - - TestUtil.replaceConfigMap(client, rightConfigMapAfterChange, "right"); - - webClient = TestUtil.builder().baseUrl("http://localhost/left").build(); - - WebClient finalWebClient = webClient; - await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(30)).until(() -> { - String innerResult = finalWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - // left configmap has not changed, no restart of app has happened - return "left-initial".equals(innerResult); - }); - testInformFromOneNamespaceEventTriggered(); testInform(); testInformFromOneNamespaceEventTriggeredSecretsDisabled(); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java index ffc1724f20..2ece0ad1a5 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java @@ -18,16 +18,16 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.cloud.kubernetes.fabric8.client.reload.App; import org.testcontainers.k3s.K3sContainer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.fabric8.client.reload.App; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; @ExtendWith(OutputCaptureExtension.class) -@SpringBootTest(classes = { App.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest(classes = { App.class }) abstract class Fabric8EventReloadBase { protected static final K3sContainer K3S = Commons.container(); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java index 96e4d1baa7..fbfb81531f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java @@ -17,9 +17,7 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload.it; import java.io.InputStream; -import java.time.Duration; import java.util.Map; -import java.util.concurrent.TimeUnit; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; @@ -42,22 +40,24 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.configMap; import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; -import static org.testcontainers.shaded.org.awaitility.Awaitility.await; /** * @author wind57 */ @TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", - "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", - "spring.cloud.bootstrap.enabled=true", "spring.cloud.kubernetes.client.namespace=default" }) + "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", + "spring.cloud.bootstrap.enabled=true", "spring.cloud.kubernetes.client.namespace=default" }) @ActiveProfiles("one") class Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT extends Fabric8EventReloadBase { - private static ConfigMap leftConfigMap; + public static final String LEFT_NAMESPACE = "left"; + public static final String RIGHT_NAMESPACE = "right"; + private static ConfigMap leftConfigMap; private static ConfigMap rightConfigMap; @Autowired @@ -73,18 +73,20 @@ static void beforeAllLocal() { leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); - util.createNamespace("left"); - util.createNamespace("right"); + util.createNamespace(LEFT_NAMESPACE); + util.createNamespace(RIGHT_NAMESPACE); - leftAndRightConfigMap(Phase.CREATE); + configMap(Phase.CREATE, util, leftConfigMap, LEFT_NAMESPACE); + configMap(Phase.CREATE, util, rightConfigMap, RIGHT_NAMESPACE); } @AfterAll static void afterAllLocal() { - leftAndRightConfigMap(Phase.DELETE); + configMap(Phase.DELETE, util, leftConfigMap, LEFT_NAMESPACE); + configMap(Phase.DELETE, util, rightConfigMap, RIGHT_NAMESPACE); - util.deleteNamespace("left"); - util.deleteNamespace("right"); + util.deleteNamespace(LEFT_NAMESPACE); + util.deleteNamespace(RIGHT_NAMESPACE); } /** @@ -96,9 +98,10 @@ static void afterAllLocal() { * */ @Test - void test(CapturedOutput output) throws Exception { - assertReloadLogStatements("added configmap informer for namespace", - "added secret informer for namespace", output); + void test(CapturedOutput output) { + assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", + output); + String currentLeftValue = environment.getProperty("left.value"); assertThat(currentLeftValue).isEqualTo("left-initial"); @@ -107,34 +110,18 @@ void test(CapturedOutput output) throws Exception { // then deploy a new version of right-configmap ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build()) + .withMetadata(new ObjectMetaBuilder().withNamespace(RIGHT_NAMESPACE).withName("right-configmap").build()) .withData(Map.of("right.value", "right-after-change")) .build(); - replaceConfigMap(kubernetesClient, rightConfigMapAfterChange, "right"); + replaceConfigMap(kubernetesClient, rightConfigMapAfterChange, RIGHT_NAMESPACE); - TimeUnit.SECONDS.sleep(10); + String afterUpdateLeftValue = environment.getProperty("left.value"); + assertThat(afterUpdateLeftValue).isEqualTo("left-initial"); -// await().pollThread(run -> { -// Thread t = new Thread(run); -// return t; -// }).atMost(Duration.ofSeconds(30)) -// .pollInterval(Duration.ofSeconds(1)) -// .until(() -> environment.getProperty("right.value").equals("right-after-change")); + String afterUpdateRightValue = environment.getProperty("right.value"); + assertThat(afterUpdateRightValue).isEqualTo("right-initial"); - System.out.println("yes"); - - } - - private static void leftAndRightConfigMap(Phase phase) { - if (phase.equals(Phase.CREATE)) { - util.createAndWait("left", leftConfigMap, null); - util.createAndWait("right", rightConfigMap, null); - } - else { - util.deleteAndWait("left", leftConfigMap, null); - util.deleteAndWait("right", rightConfigMap, null); - } } @TestConfiguration diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java new file mode 100644 index 0000000000..738ede3edd --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java @@ -0,0 +1,163 @@ +/* + * Copyright 2012-2024 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.client.reload.it; + +import java.io.InputStream; +import java.time.Duration; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.cloud.kubernetes.fabric8.client.reload.RightProperties; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.configMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", + "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", + "spring.cloud.bootstrap.enabled=true", "spring.cloud.kubernetes.client.namespace=default" }) +@ActiveProfiles("two") +class Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT extends Fabric8EventReloadBase { + + private static final String LEFT_NAMESPACE = "left"; + private static final String RIGHT_NAMESPACE = "right"; + private static final String DEFAULT_NAMESPACE = "default"; + + private static ConfigMap leftConfigMap; + private static ConfigMap rightConfigMap; + private static ConfigMap configMap; + + @Autowired + private Environment environment; + + @Autowired + private KubernetesClient kubernetesClient; + + @Autowired + private RightProperties rightProperties; + + @Autowired + private ApplicationContext applicationContext; + + @BeforeAll + static void beforeAllLocal() { + InputStream leftConfigMapStream = util.inputStream("manifests/left-configmap.yaml"); + InputStream rightConfigMapStream = util.inputStream("manifests/right-configmap.yaml"); + InputStream configMapStream = util.inputStream("manifests/configmap.yaml"); + + leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); + rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); + configMap = Serialization.unmarshal(configMapStream, ConfigMap.class); + + util.createNamespace(LEFT_NAMESPACE); + util.createNamespace(RIGHT_NAMESPACE); + + configMap(Phase.CREATE, util, leftConfigMap, LEFT_NAMESPACE); + configMap(Phase.CREATE, util, rightConfigMap, RIGHT_NAMESPACE); + configMap(Phase.CREATE, util, configMap, DEFAULT_NAMESPACE); + } + + @AfterAll + static void afterAllLocal() { + configMap(Phase.DELETE, util, leftConfigMap, LEFT_NAMESPACE); + configMap(Phase.DELETE, util, rightConfigMap, RIGHT_NAMESPACE); + configMap(Phase.DELETE, util, configMap, DEFAULT_NAMESPACE); + + util.deleteNamespace("left"); + util.deleteNamespace("right"); + } + + /** + *
+	 * - there are two namespaces : left and right
+	 * - each of the namespaces has one configmap
+	 * - we watch the "right" namespace and make a change in the configmap in the same
+	 * namespace
+	 * - as such, event is triggered and we see the updated value
+	 * 
+ */ + @Test + void test(CapturedOutput output) { + assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", + output); + + String currentLeftValue = environment.getProperty("left.value"); + assertThat(currentLeftValue).isEqualTo("left-initial"); + + String currentRightValue = environment.getProperty("right.value"); + assertThat(currentRightValue).isEqualTo("right-initial"); + + // then deploy a new version of right-configmap + ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder().withNamespace(RIGHT_NAMESPACE).withName("right-configmap").build()) + .withData(Map.of("right.value", "right-after-change")) + .build(); + + replaceConfigMap(kubernetesClient, rightConfigMapAfterChange, RIGHT_NAMESPACE); + + String afterUpdateLeftValue = environment.getProperty("left.value"); + assertThat(afterUpdateLeftValue).isEqualTo("left-initial"); + + await().atMost(Duration.ofSeconds(15000)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> { + String afterUpdateRightValue = rightProperties.getValue(); + return afterUpdateRightValue.equals("right-after-change"); + }); + + } + + @TestConfiguration + public static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + String kubeConfigYaml = K3S.getKubeConfigYaml(); + Config config = Config.fromKubeconfig(kubeConfigYaml); + return new KubernetesClientBuilder().withConfig(config).build(); + } + + } + + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java index 2c6ca5c353..5d10f840aa 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java @@ -16,11 +16,14 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +import java.time.Duration; + import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; -import org.springframework.boot.test.system.CapturedOutput; -import java.time.Duration; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; import static org.testcontainers.shaded.org.awaitility.Awaitility.await; @@ -52,7 +55,16 @@ static void assertReloadLogStatements(String left, String right, CapturedOutput } static void replaceConfigMap(KubernetesClient client, ConfigMap configMap, String namespace) { - client.configMaps().inNamespace(namespace).resource(configMap).createOrReplace(); + client.configMaps().inNamespace(namespace).resource(configMap).update(); + } + + static void configMap(Phase phase, Util util, ConfigMap configMap, String namespace) { + if (phase.equals(Phase.CREATE)) { + util.createAndWait(namespace, configMap, null); + } + else { + util.deleteAndWait(namespace, configMap, null); + } } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-two.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-two.yaml similarity index 76% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-two.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-two.yaml index 43ae273796..f8071e884a 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-two.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-two.yaml @@ -1,7 +1,3 @@ -logging: - level: - root: DEBUG - spring: application: name: event-reload @@ -9,7 +5,7 @@ spring: kubernetes: reload: enabled: true - strategy: shutdown + strategy: refresh mode: event namespaces: - right diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-one.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-one.yaml similarity index 84% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-one.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-one.yaml index 9265fce12e..17ce4c7193 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-one.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-one.yaml @@ -1,7 +1,3 @@ -logging: - level: - root: DEBUG - spring: cloud: kubernetes: diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-two.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-two.yaml similarity index 84% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-two.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-two.yaml index 9265fce12e..17ce4c7193 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-two.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-two.yaml @@ -1,7 +1,3 @@ -logging: - level: - root: DEBUG - spring: cloud: kubernetes: From 62f4ccd52627d8f53aa787b832af995e0a5a15e2 Mon Sep 17 00:00:00 2001 From: wind57 Date: Wed, 4 Dec 2024 08:41:02 +0200 Subject: [PATCH 26/35] dirty --- .../fabric8/client/reload/ConfigMapProperties.java | 6 +++--- .../fabric8/client/reload/Controller.java | 14 +++++++------- .../fabric8/client/reload/LeftProperties.java | 6 +++--- .../client/reload/RightWithLabelsProperties.java | 6 +++--- .../fabric8/client/reload/SecretProperties.java | 6 +++--- .../client/reload/it/Fabric8EventReloadBase.java | 9 +++++++++ ...loadInformFromOneNamespaceEventTriggeredIT.java | 8 ++------ 7 files changed, 30 insertions(+), 25 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapProperties.java index bb3fb709e1..5376e65c88 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapProperties.java @@ -22,15 +22,15 @@ * @author wind57 */ @ConfigurationProperties("from.properties") -public class ConfigMapProperties { +class ConfigMapProperties { private String key; - public String getKey() { + String getKey() { return key; } - public void setKey(String key1) { + void setKey(String key1) { this.key = key1; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java index 12469a7fff..ab0af15644 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java @@ -23,7 +23,7 @@ * @author wind57 */ @RestController -public class Controller { +class Controller { private final LeftProperties leftProperties; @@ -35,7 +35,7 @@ public class Controller { private final SecretProperties secretProperties; - public Controller(LeftProperties leftProperties, RightProperties rightProperties, + Controller(LeftProperties leftProperties, RightProperties rightProperties, RightWithLabelsProperties rightWithLabelsProperties, ConfigMapProperties configMapProperties, SecretProperties secretProperties) { this.leftProperties = leftProperties; @@ -46,27 +46,27 @@ public Controller(LeftProperties leftProperties, RightProperties rightProperties } @GetMapping("/left") - public String left() { + String left() { return leftProperties.getValue(); } @GetMapping("/right") - public String right() { + String right() { return rightProperties.getValue(); } @GetMapping("/with-label") - public String witLabel() { + String witLabel() { return rightWithLabelsProperties.getValue(); } @GetMapping("/key") - public String key() { + String key() { return configMapProperties.getKey(); } @GetMapping("/key-from-secret") - public String keyFromSecret() { + String keyFromSecret() { return secretProperties.getKey(); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/LeftProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/LeftProperties.java index e844191a2c..ee4c809285 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/LeftProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/LeftProperties.java @@ -22,15 +22,15 @@ * @author wind57 */ @ConfigurationProperties("left") -public class LeftProperties { +class LeftProperties { private String value; - public String getValue() { + String getValue() { return value; } - public void setValue(String value) { + void setValue(String value) { this.value = value; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/RightWithLabelsProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/RightWithLabelsProperties.java index a300b765d9..7bdf7cd20a 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/RightWithLabelsProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/RightWithLabelsProperties.java @@ -22,15 +22,15 @@ * @author wind57 */ @ConfigurationProperties("right.with.label") -public class RightWithLabelsProperties { +class RightWithLabelsProperties { private String value; - public String getValue() { + String getValue() { return value; } - public void setValue(String value) { + void setValue(String value) { this.value = value; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretProperties.java index 2a58879973..204dd71a1c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretProperties.java @@ -22,15 +22,15 @@ * @author wind57 */ @ConfigurationProperties("from.secret.properties") -public class SecretProperties { +class SecretProperties { private String key; - public String getKey() { + String getKey() { return key; } - public void setKey(String key) { + void setKey(String key) { this.key = key; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java index 2ece0ad1a5..f2754681f9 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java @@ -16,6 +16,9 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.k3s.K3sContainer; @@ -40,4 +43,10 @@ protected static void beforeAll() { util = new Util(K3S); } + protected static KubernetesClient kubernetesClient() { + String kubeConfigYaml = K3S.getKubeConfigYaml(); + Config config = Config.fromKubeconfig(kubeConfigYaml); + return new KubernetesClientBuilder().withConfig(config).build(); + } + } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java index 738ede3edd..8f369ffcf4 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java @@ -23,9 +23,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.utils.Serialization; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -147,14 +145,12 @@ void test(CapturedOutput output) { } @TestConfiguration - public static class TestConfig { + static class TestConfig { @Bean @Primary KubernetesClient kubernetesClient() { - String kubeConfigYaml = K3S.getKubeConfigYaml(); - Config config = Config.fromKubeconfig(kubeConfigYaml); - return new KubernetesClientBuilder().withConfig(config).build(); + return kubernetesClient(); } } From d8579ac08c5f96548a9cf7404c47c9bb3ac547b5 Mon Sep 17 00:00:00 2001 From: wind57 Date: Thu, 12 Dec 2024 22:07:56 +0200 Subject: [PATCH 27/35] dirty --- ...ic8DiscoveryFilterMatchOneNamespaceIT.java | 1 - ...c8DiscoveryFilterMatchTwoNamespacesIT.java | 1 - .../fabric8/client/reload/Controller.java | 73 ------- .../fabric8/client/reload/LeftProperties.java | 6 +- .../reload/RightWithLabelsProperties.java | 6 +- .../client/reload/SecretProperties.java | 6 +- .../src/main/resources/application-one.yaml | 10 +- .../resources/application-two.yaml | 3 + .../resources/application-with-secret.yaml | 7 +- .../src/main/resources/bootstrap-one.yaml | 13 ++ .../src/main/resources/bootstrap-three.yaml | 12 +- .../resources/bootstrap-two.yaml | 7 + .../DataChangesInConfigMapReloadDelegate.java | 119 ----------- .../client/reload/Fabric8EventReloadIT.java | 200 ------------------ .../reload/SecretsEventsReloadDelegate.java | 111 ---------- .../fabric8/client/reload/TestUtil.java | 100 --------- .../reload/it/Fabric8EventReloadBase.java | 22 +- ...c8EventReloadDataChangesInConfigMapIT.java | 140 ++++++++++++ ...rmFromOneNamespaceEventNotTriggeredIT.java | 140 ------------ ...T.java => Fabric8EventReloadInformIT.java} | 56 ++--- .../Fabric8EventReloadInformWithLabelIT.java | 148 +++++++++++++ .../reload/it/Fabric8EventReloadSecretIT.java | 121 +++++++++++ .../client/reload/it/TestAssertions.java | 14 ++ .../test/resources/META-INF/spring.factories | 2 +- .../src/test/resources/application-one.yaml | 12 -- .../src/test/resources/bootstrap-one.yaml | 9 - 26 files changed, 499 insertions(+), 840 deletions(-) delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/{test => main}/resources/application-two.yaml (85%) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-one.yaml rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/{test => main}/resources/bootstrap-two.yaml (51%) delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/DataChangesInConfigMapReloadDelegate.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretsEventsReloadDelegate.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/{Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java => Fabric8EventReloadInformIT.java} (72%) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-one.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-one.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java index f7716bd6dd..1a8393f71c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchOneNamespaceIT.java @@ -35,7 +35,6 @@ "spring.cloud.kubernetes.discovery.namespaces[1]=b-uat", "spring.cloud.kubernetes.discovery.filter=#root.metadata.namespace matches 'a-uat$'", "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" - }) class Fabric8DiscoveryFilterMatchOneNamespaceIT extends Fabric8DiscoveryBase { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java index 4872c2091e..83e84f74ea 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterMatchTwoNamespacesIT.java @@ -35,7 +35,6 @@ "spring.cloud.kubernetes.discovery.namespaces[1]=b-uat", "spring.cloud.kubernetes.discovery.filter=#root.metadata.namespace matches '^.*uat$'", "logging.level.org.springframework.cloud.kubernetes.fabric8.discovery=DEBUG" - }) class Fabric8DiscoveryFilterMatchTwoNamespacesIT extends Fabric8DiscoveryBase { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java deleted file mode 100644 index ab0af15644..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.kubernetes.fabric8.client.reload; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author wind57 - */ -@RestController -class Controller { - - private final LeftProperties leftProperties; - - private final RightProperties rightProperties; - - private final RightWithLabelsProperties rightWithLabelsProperties; - - private final ConfigMapProperties configMapProperties; - - private final SecretProperties secretProperties; - - Controller(LeftProperties leftProperties, RightProperties rightProperties, - RightWithLabelsProperties rightWithLabelsProperties, ConfigMapProperties configMapProperties, - SecretProperties secretProperties) { - this.leftProperties = leftProperties; - this.rightProperties = rightProperties; - this.rightWithLabelsProperties = rightWithLabelsProperties; - this.configMapProperties = configMapProperties; - this.secretProperties = secretProperties; - } - - @GetMapping("/left") - String left() { - return leftProperties.getValue(); - } - - @GetMapping("/right") - String right() { - return rightProperties.getValue(); - } - - @GetMapping("/with-label") - String witLabel() { - return rightWithLabelsProperties.getValue(); - } - - @GetMapping("/key") - String key() { - return configMapProperties.getKey(); - } - - @GetMapping("/key-from-secret") - String keyFromSecret() { - return secretProperties.getKey(); - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/LeftProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/LeftProperties.java index ee4c809285..e844191a2c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/LeftProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/LeftProperties.java @@ -22,15 +22,15 @@ * @author wind57 */ @ConfigurationProperties("left") -class LeftProperties { +public class LeftProperties { private String value; - String getValue() { + public String getValue() { return value; } - void setValue(String value) { + public void setValue(String value) { this.value = value; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/RightWithLabelsProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/RightWithLabelsProperties.java index 7bdf7cd20a..a300b765d9 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/RightWithLabelsProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/RightWithLabelsProperties.java @@ -22,15 +22,15 @@ * @author wind57 */ @ConfigurationProperties("right.with.label") -class RightWithLabelsProperties { +public class RightWithLabelsProperties { private String value; - String getValue() { + public String getValue() { return value; } - void setValue(String value) { + public void setValue(String value) { this.value = value; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretProperties.java index 204dd71a1c..2a58879973 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretProperties.java @@ -22,15 +22,15 @@ * @author wind57 */ @ConfigurationProperties("from.secret.properties") -class SecretProperties { +public class SecretProperties { private String key; - String getKey() { + public String getKey() { return key; } - void setKey(String key) { + public void setKey(String key) { this.key = key; } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-one.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-one.yaml index 988bfb2127..041768896c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-one.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-one.yaml @@ -1,16 +1,14 @@ -logging: - level: - root: DEBUG - spring: application: name: event-reload cloud: kubernetes: + secrets: + enabled: false reload: enabled: true - strategy: shutdown + strategy: refresh mode: event namespaces: - - left + - right monitoring-config-maps: true diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-two.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-two.yaml similarity index 85% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-two.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-two.yaml index f8071e884a..1945840e95 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-two.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-two.yaml @@ -10,3 +10,6 @@ spring: namespaces: - right monitoring-config-maps: true + + main: + cloud-platform: kubernetes diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-with-secret.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-with-secret.yaml index bd33ead110..176f34c6a5 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-with-secret.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-with-secret.yaml @@ -1,7 +1,3 @@ -logging: - level: - root: DEBUG - spring: application: name: event-reload @@ -15,6 +11,9 @@ spring: strategy: shutdown mode: event monitoring-configMaps: false + config: + enabled: false + enable-api: false secrets: enabled: true enable-api: true diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-one.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-one.yaml new file mode 100644 index 0000000000..035e8e8410 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-one.yaml @@ -0,0 +1,13 @@ +spring: + cloud: + kubernetes: + config: + sources: + - namespace: right + name: right-configmap + + # otherwise on context refresh we lose this property + # and test fails, since beans are not wired. + main: + cloud-platform: kubernetes + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-three.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-three.yaml index 5c5f360585..2463e20a97 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-three.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-three.yaml @@ -1,15 +1,15 @@ -logging: - level: - root: DEBUG - spring: cloud: kubernetes: config: sources: - - namespace: left - name: left-configmap - namespace: right name: right-configmap - namespace: right name: right-configmap-with-label + + + # otherwise on context refresh we lose this property + # and test fails, since beans are not wired. + main: + cloud-platform: kubernetes diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-two.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-two.yaml similarity index 51% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-two.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-two.yaml index 17ce4c7193..c4c058b62e 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-two.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/bootstrap-two.yaml @@ -1,9 +1,16 @@ spring: cloud: kubernetes: + secrets: + enabled: false config: sources: - namespace: left name: left-configmap - namespace: right name: right-configmap + + # otherwise on context refresh we lose this property + # and test fails, since beans are not wired. + main: + cloud-platform: kubernetes diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/DataChangesInConfigMapReloadDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/DataChangesInConfigMapReloadDelegate.java deleted file mode 100644 index 26698fe617..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/DataChangesInConfigMapReloadDelegate.java +++ /dev/null @@ -1,119 +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.client.reload; - -import java.time.Duration; -import java.util.Map; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.junit.jupiter.api.Assertions; -import org.testcontainers.k3s.K3sContainer; - -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; -import org.springframework.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestUtil.logs; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestUtil.replaceConfigMap; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestUtil.retrySpec; - -final class DataChangesInConfigMapReloadDelegate { - - private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-reload"; - - private static final String LEFT_NAMESPACE = "left"; - - /** - *
-	 *     - configMap with no labels and data: left.value = left-initial exists in namespace left
-	 *     - we assert that we can read it correctly first, by invoking localhost/left
-	 *
-	 *     - then we change the configmap by adding a label, this in turn does not
-	 *       change the result of localhost/left, because the data has not changed.
-	 *
-	 *     - then we change data inside the config map, and we must see the updated value
-	 * 
- */ - static void testDataChangesInConfigMap(KubernetesClient client, K3sContainer container, String appLabelValue) { - Commons.assertReloadLogStatements("added configmap informer for namespace", - "added secret informer for namespace", IMAGE_NAME); - - WebClient webClient = builder().baseUrl("http://localhost/" + LEFT_NAMESPACE).build(); - String result = webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(retrySpec()) - .block(); - - // we first read the initial value from the left-configmap - Assertions.assertEquals("left-initial", result); - - // then deploy a new version of left-configmap, but without changing its data, - // only add a label - ConfigMap configMap = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withLabels(Map.of("new-label", "abc")) - .withNamespace("left") - .withName("left-configmap") - .build()) - .withData(Map.of("left.value", "left-initial")) - .build(); - - replaceConfigMap(client, configMap, "left"); - - await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { - WebClient innerWebClient = builder().baseUrl("http://localhost/" + LEFT_NAMESPACE).build(); - String innerResult = innerWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(retrySpec()) - .block(); - return "left-initial".equals(innerResult); - }); - - String logs = logs(container, appLabelValue); - Assertions.assertTrue(logs.contains("ConfigMap left-configmap was updated in namespace left")); - Assertions.assertTrue(logs.contains("data in configmap has not changed, will not reload")); - - // change data - configMap = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withLabels(Map.of("new-label", "abc")) - .withNamespace("left") - .withName("left-configmap") - .build()) - .withData(Map.of("left.value", "left-after-change")) - .build(); - - replaceConfigMap(client, configMap, "left"); - - await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { - WebClient innerWebClient = builder().baseUrl("http://localhost/" + LEFT_NAMESPACE).build(); - String innerResult = innerWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(retrySpec()) - .block(); - return "left-after-change".equals(innerResult); - }); - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java index b2b4739198..748cd9214f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java @@ -91,205 +91,10 @@ static void afterAll() throws Exception { @Test void testInformFromOneNamespaceEventNotTriggered() { - testInformFromOneNamespaceEventTriggered(); - testInform(); - testInformFromOneNamespaceEventTriggeredSecretsDisabled(); - testDataChangesInConfigMap(); testConfigMapMountPollingReload(); testPollingReloadConfigMapWithBootstrap(); - testSecretReload(); } - /** - *
-	 * - there are two namespaces : left and right
-	 * - each of the namespaces has one configmap
-	 * - we watch the "right" namespace and make a change in the configmap in the same
-	 * namespace
-	 * - as such, event is triggered and we see the updated value
-	 * 
- */ - void testInformFromOneNamespaceEventTriggered() { - - TestUtil.reCreateSources(util, client); - TestUtil.patchOne(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - - Commons.assertReloadLogStatements("added configmap informer for namespace", - "added secret informer for namespace", IMAGE_NAME); - - // read the value from the right-configmap - WebClient webClient = TestUtil.builder().baseUrl("http://localhost/right").build(); - String result = webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - Assertions.assertEquals("right-initial", result); - - // then deploy a new version of right-configmap - ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build()) - .withData(Map.of("right.value", "right-after-change")) - .build(); - - TestUtil.replaceConfigMap(client, rightConfigMapAfterChange, "right"); - - String[] resultAfterChange = new String[1]; - await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { - WebClient innerWebClient = TestUtil.builder().baseUrl("http://localhost/right").build(); - String innerResult = innerWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - resultAfterChange[0] = innerResult; - return innerResult != null; - }); - Assertions.assertEquals("right-after-change", resultAfterChange[0]); - } - - /** - *
-	 * - there are two namespaces : left and right (though we do not care about the left
-	 * one)
-	 * - left has one configmap : left-configmap
-	 * - right has two configmaps: right-configmap, right-configmap-with-label
-	 * - we watch the "right" namespace, but enable tagging; which means that only
-	 * right-configmap-with-label triggers changes.
-	 * 
- */ - void testInform() { - - TestUtil.reCreateSources(util, client); - TestUtil.patchTwo(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - - Commons.assertReloadLogStatements("added configmap informer for namespace", - "added secret informer for namespace", IMAGE_NAME); - - // read the initial value from the right-configmap - WebClient rightWebClient = TestUtil.builder().baseUrl("http://localhost/right").build(); - String rightResult = rightWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - Assertions.assertEquals("right-initial", rightResult); - - // then read the initial value from the right-with-label-configmap - WebClient rightWithLabelWebClient = TestUtil.builder().baseUrl("http://localhost/with-label").build(); - String rightWithLabelResult = rightWithLabelWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - Assertions.assertEquals("right-with-label-initial", rightWithLabelResult); - - // then deploy a new version of right-configmap - ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build()) - .withData(Map.of("right.value", "right-after-change")) - .build(); - - TestUtil.replaceConfigMap(client, rightConfigMapAfterChange, "right"); - - // nothing changes in our app, because we are watching only labeled configmaps - await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(30)).until(() -> { - String innerRightResult = rightWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - return "right-initial".equals(innerRightResult); - }); - - // then deploy a new version of right-with-label-configmap - ConfigMap rightWithLabelConfigMapAfterChange = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap-with-label").build()) - .withData(Map.of("right.with.label.value", "right-with-label-after-change")) - .build(); - - TestUtil.replaceConfigMap(client, rightWithLabelConfigMapAfterChange, "right"); - - // since we have changed a labeled configmap, app will restart and pick up the new - // value - String[] resultAfterChange = new String[1]; - await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { - WebClient innerWebClient = TestUtil.builder().baseUrl("http://localhost/with-label").build(); - String innerResult = innerWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - resultAfterChange[0] = innerResult; - return innerResult != null; - }); - Assertions.assertEquals("right-with-label-after-change", resultAfterChange[0]); - - // right-configmap now will see the new value also, but only because the other - // configmap has triggered the restart - rightResult = rightWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - Assertions.assertEquals("right-after-change", rightResult); - } - - /** - *
-	 * - there are two namespaces : left and right
-	 * - each of the namespaces has one configmap
-	 * - secrets are disabled
-	 * - we watch the "right" namespace and make a change in the configmap in the same
-	 * namespace
-	 * - as such, event is triggered and we see the updated value
-	 * 
- */ - void testInformFromOneNamespaceEventTriggeredSecretsDisabled() { - - TestUtil.reCreateSources(util, client); - TestUtil.patchThree(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - - Commons.assertReloadLogStatements("added configmap informer for namespace", - "added secret informer for namespace", IMAGE_NAME); - - // read the value from the right-configmap - WebClient webClient = TestUtil.builder().baseUrl("http://localhost/right").build(); - String result = webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - Assertions.assertEquals("right-initial", result); - - // then deploy a new version of right-configmap - ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build()) - .withData(Map.of("right.value", "right-after-change")) - .build(); - - TestUtil.replaceConfigMap(client, rightConfigMapAfterChange, "right"); - - String[] resultAfterChange = new String[1]; - await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { - WebClient innerWebClient = TestUtil.builder().baseUrl("http://localhost/right").build(); - String innerResult = innerWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - resultAfterChange[0] = innerResult; - return innerResult != null; - }); - Assertions.assertEquals("right-after-change", resultAfterChange[0]); - - } - - void testDataChangesInConfigMap() { - TestUtil.reCreateSources(util, client); - TestUtil.patchFour(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - DataChangesInConfigMapReloadDelegate.testDataChangesInConfigMap(client, K3S, IMAGE_NAME); - } void testConfigMapMountPollingReload() { TestUtil.reCreateSources(util, client); @@ -304,11 +109,6 @@ void testPollingReloadConfigMapWithBootstrap() { IMAGE_NAME); } - void testSecretReload() { - TestUtil.patchSeven(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - SecretsEventsReloadDelegate.testSecretReload(client, K3S, IMAGE_NAME); - } - private static void manifests(Phase phase) { InputStream deploymentStream = util.inputStream("deployment.yaml"); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretsEventsReloadDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretsEventsReloadDelegate.java deleted file mode 100644 index 281608211a..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/SecretsEventsReloadDelegate.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2013-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.kubernetes.fabric8.client.reload; - -import java.time.Duration; -import java.util.Base64; -import java.util.Map; - -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.SecretBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.junit.jupiter.api.Assertions; -import org.testcontainers.k3s.K3sContainer; - -import org.springframework.cloud.kubernetes.commons.config.Constants; -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; -import org.springframework.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestUtil.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestUtil.retrySpec; - -/** - * @author wind57 - */ -final class SecretsEventsReloadDelegate { - - /** - *
-	 *     - secret with no labels and data: from.secret.properties.key = secret-initial exists in namespace default
-	 *     - we assert that we can read it correctly first, by invoking localhost/key.
-	 *
-	 *     - then we change the secret by adding a label, this in turn does not
-	 *       change the result of localhost/key, because the data has not changed.
-	 *
-	 *     - then we change data inside the secret, and we must see the updated value.
-	 * 
- */ - static void testSecretReload(KubernetesClient client, K3sContainer container, String appLabelValue) { - Commons.assertReloadLogStatements("added secret informer for namespace", - "added configmap informer for namespace", appLabelValue); - - WebClient webClient = builder().baseUrl("http://localhost/key-from-secret").build(); - String result = webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(retrySpec()) - .block(); - Assertions.assertEquals("secret-initial", result); - - Secret secret = new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withLabels(Map.of("letter", "a")) - .withNamespace("default") - .withName("event-reload") - .build()) - .withData(Map.of(Constants.APPLICATION_PROPERTIES, - Base64.getEncoder().encodeToString("from.secret.properties.key=secret-initial".getBytes()))) - .build(); - client.secrets().inNamespace("default").resource(secret).createOrReplace(); - - await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { - WebClient innerWebClient = builder().baseUrl("http://localhost/key-from-secret").build(); - String innerResult = innerWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(retrySpec()) - .block(); - return "secret-initial".equals(innerResult); - }); - - Commons.waitForLogStatement("Secret event-reload was updated in namespace default", container, appLabelValue); - Commons.waitForLogStatement("data in secret has not changed, will not reload", container, appLabelValue); - - // change data - secret = new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withNamespace("default").withName("event-reload").build()) - .withData(Map.of(Constants.APPLICATION_PROPERTIES, - Base64.getEncoder().encodeToString("from.secret.properties.key=secret-initial-changed".getBytes()))) - .build(); - - client.secrets().inNamespace("default").resource(secret).createOrReplace(); - - await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { - WebClient innerWebClient = builder().baseUrl("http://localhost/key-from-secret").build(); - String innerResult = innerWebClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(retrySpec()) - .block(); - return "secret-initial-changed".equals(innerResult); - }); - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java index 4282e05084..4a9d03e3bf 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java @@ -42,96 +42,8 @@ final class TestUtil { private static final Map POD_LABELS = Map.of("app", "spring-cloud-kubernetes-fabric8-client-reload"); - private static final String BODY_ONE = """ - { - "spec": { - "template": { - "spec": { - "containers": [{ - "name": "spring-cloud-kubernetes-fabric8-client-configmap-event-reload", - "image": "image_name_here", - "env": [ - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY", - "value": "DEBUG" - }, - { - "name": "SPRING_PROFILES_ACTIVE", - "value": "two" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "TRUE" - } - ] - }] - } - } - } - } - """; - private static final String BODY_TWO = """ - { - "spec": { - "template": { - "spec": { - "containers": [{ - "name": "spring-cloud-kubernetes-fabric8-client-configmap-event-reload", - "image": "image_name_here", - "env": [ - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY", - "value": "DEBUG" - }, - { - "name": "SPRING_PROFILES_ACTIVE", - "value": "three" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "TRUE" - } - ] - }] - } - } - } - } - """; - private static final String BODY_THREE = """ - { - "spec": { - "template": { - "spec": { - "containers": [{ - "name": "spring-cloud-kubernetes-fabric8-client-configmap-event-reload", - "image": "image_name_here", - "env": [ - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY", - "value": "DEBUG" - }, - { - "name": "SPRING_PROFILES_ACTIVE", - "value": "two" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_SECRETS_ENABLED", - "value": "FALSE" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "TRUE" - } - ] - }] - } - } - } - } - """; private static final String BODY_FOUR = """ { @@ -317,18 +229,6 @@ static void replaceConfigMap(KubernetesClient client, ConfigMap configMap, Strin client.configMaps().inNamespace(namespace).resource(configMap).serverSideApply(); } - static void patchOne(Util util, String dockerImage, String deploymentName, String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_ONE, POD_LABELS); - } - - static void patchTwo(Util util, String dockerImage, String deploymentName, String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_TWO, POD_LABELS); - } - - static void patchThree(Util util, String dockerImage, String deploymentName, String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_THREE, POD_LABELS); - } - static void patchFour(Util util, String dockerImage, String deploymentName, String namespace) { util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_FOUR, POD_LABELS); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java index f2754681f9..41fda572e9 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java @@ -21,16 +21,19 @@ import io.fabric8.kubernetes.client.KubernetesClientBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cloud.kubernetes.fabric8.client.reload.App; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; import org.testcontainers.k3s.K3sContainer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.OutputCaptureExtension; -import org.springframework.cloud.kubernetes.fabric8.client.reload.App; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; +@SpringBootTest(classes = { App.class, Fabric8EventReloadBase.TestConfig.class }) @ExtendWith(OutputCaptureExtension.class) -@SpringBootTest(classes = { App.class }) abstract class Fabric8EventReloadBase { protected static final K3sContainer K3S = Commons.container(); @@ -43,10 +46,17 @@ protected static void beforeAll() { util = new Util(K3S); } - protected static KubernetesClient kubernetesClient() { - String kubeConfigYaml = K3S.getKubeConfigYaml(); - Config config = Config.fromKubeconfig(kubeConfigYaml); - return new KubernetesClientBuilder().withConfig(config).build(); + @TestConfiguration + static class TestConfig { + + @Bean + @Primary + KubernetesClient kubernetesClient() { + String kubeConfigYaml = K3S.getKubeConfigYaml(); + Config config = Config.fromKubeconfig(kubeConfigYaml); + return new KubernetesClientBuilder().withConfig(config).build(); + } + } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java new file mode 100644 index 0000000000..c4aa6182b8 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java @@ -0,0 +1,140 @@ +/* + * Copyright 2012-2024 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.client.reload.it; + +import java.io.InputStream; +import java.time.Duration; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.cloud.kubernetes.fabric8.client.reload.RightProperties; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.configMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", + "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", + "spring.cloud.bootstrap.enabled=true" }) +@ActiveProfiles("one") +class Fabric8EventReloadDataChangesInConfigMapIT extends Fabric8EventReloadBase { + + private static final String NAMESPACE = "right"; + + private static ConfigMap configMap; + + @Autowired + private RightProperties properties; + + @Autowired + private KubernetesClient kubernetesClient; + + @BeforeAll + static void beforeAllLocal() { + InputStream rightConfigMapStream = util.inputStream("manifests/right-configmap.yaml"); + configMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); + + util.createNamespace(NAMESPACE); + configMap(Phase.CREATE, util, configMap, NAMESPACE); + } + + @AfterAll + static void afterAllLocal() { + configMap(Phase.DELETE, util, configMap, NAMESPACE); + util.deleteNamespace(NAMESPACE); + } + + + /** + *
+	 *     - configMap with no labels and data: right.value = right-initial exists in namespace right
+	 *
+	 *     - then we change the configmap by adding a label, this in turn does not
+	 *       change the result, because the data has not changed.
+	 *
+	 *     - then we change data inside the config map, and we must see the updated value
+	 * 
+ */ + @Test + void test(CapturedOutput output) { + + assertReloadLogStatements("added configmap informer for namespace", + "added secret informer for namespace", output); + + // we first read the initial value from configmap + assertThat(properties.getValue()).isEqualTo("right-initial"); + + // then deploy a new version of right-configmap, but without changing its data, + // only add a label + ConfigMap configMap = new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder().withLabels(Map.of("new-label", "abc")) + .withNamespace(NAMESPACE) + .withName("right-configmap") + .build()) + .withData(Map.of("right.value", "right-initial")) + .build(); + + replaceConfigMap(kubernetesClient, configMap, NAMESPACE); + + await().atMost(Duration.ofSeconds(60)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> output.getOut().contains("ConfigMap right-configmap was updated in namespace right")); + + await().atMost(Duration.ofSeconds(60)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> output.getOut().contains("data in configmap has not changed, will not reload")); + + assertThat(properties.getValue()).isEqualTo("right-initial"); + + // change data + configMap = new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder().withLabels(Map.of("new-label", "abc")) + .withNamespace(NAMESPACE) + .withName("right-configmap") + .build()) + .withData(Map.of("right.value", "right-after-change")) + .build(); + + replaceConfigMap(kubernetesClient, configMap, NAMESPACE); + + await().atMost(Duration.ofSeconds(60)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> { + String afterUpdateRightValue = properties.getValue(); + return afterUpdateRightValue.equals("right-after-change"); + }); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java deleted file mode 100644 index fbfb81531f..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2012-2024 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.client.reload.it; - -import java.io.InputStream; -import java.util.Map; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientBuilder; -import io.fabric8.kubernetes.client.utils.Serialization; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.Environment; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.TestPropertySource; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.configMap; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; - -/** - * @author wind57 - */ -@TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", - "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", - "spring.cloud.bootstrap.enabled=true", "spring.cloud.kubernetes.client.namespace=default" }) -@ActiveProfiles("one") -class Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT extends Fabric8EventReloadBase { - - public static final String LEFT_NAMESPACE = "left"; - public static final String RIGHT_NAMESPACE = "right"; - - private static ConfigMap leftConfigMap; - private static ConfigMap rightConfigMap; - - @Autowired - private Environment environment; - - @Autowired - private KubernetesClient kubernetesClient; - - @BeforeAll - static void beforeAllLocal() { - InputStream leftConfigMapStream = util.inputStream("manifests/left-configmap.yaml"); - InputStream rightConfigMapStream = util.inputStream("manifests/right-configmap.yaml"); - leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); - rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); - - util.createNamespace(LEFT_NAMESPACE); - util.createNamespace(RIGHT_NAMESPACE); - - configMap(Phase.CREATE, util, leftConfigMap, LEFT_NAMESPACE); - configMap(Phase.CREATE, util, rightConfigMap, RIGHT_NAMESPACE); - } - - @AfterAll - static void afterAllLocal() { - configMap(Phase.DELETE, util, leftConfigMap, LEFT_NAMESPACE); - configMap(Phase.DELETE, util, rightConfigMap, RIGHT_NAMESPACE); - - util.deleteNamespace(LEFT_NAMESPACE); - util.deleteNamespace(RIGHT_NAMESPACE); - } - - /** - *
-	 *     - there are two namespaces : left and right
-	 *     - each of the namespaces has one configmap
-	 *     - we watch the "left" namespace, but make a change in the configmap in the "right" namespace
-	 *     - as such, no event is triggered and "left-configmap" stays as-is
-	 * 
- */ - @Test - void test(CapturedOutput output) { - assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", - output); - - String currentLeftValue = environment.getProperty("left.value"); - assertThat(currentLeftValue).isEqualTo("left-initial"); - - String currentRightValue = environment.getProperty("right.value"); - assertThat(currentRightValue).isEqualTo("right-initial"); - - // then deploy a new version of right-configmap - ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder().withNamespace(RIGHT_NAMESPACE).withName("right-configmap").build()) - .withData(Map.of("right.value", "right-after-change")) - .build(); - - replaceConfigMap(kubernetesClient, rightConfigMapAfterChange, RIGHT_NAMESPACE); - - String afterUpdateLeftValue = environment.getProperty("left.value"); - assertThat(afterUpdateLeftValue).isEqualTo("left-initial"); - - String afterUpdateRightValue = environment.getProperty("right.value"); - assertThat(afterUpdateRightValue).isEqualTo("right-initial"); - - } - - @TestConfiguration - public static class TestConfig { - - @Bean - @Primary - KubernetesClient kubernetesClient() { - String kubeConfigYaml = K3S.getKubeConfigYaml(); - Config config = Config.fromKubeconfig(kubeConfigYaml); - return new KubernetesClientBuilder().withConfig(config).build(); - } - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java similarity index 72% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java index 8f369ffcf4..4fa6254922 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java @@ -30,14 +30,10 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.cloud.kubernetes.fabric8.client.reload.LeftProperties; import org.springframework.cloud.kubernetes.fabric8.client.reload.RightProperties; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.Environment; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; @@ -52,56 +48,47 @@ */ @TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", - "spring.cloud.bootstrap.enabled=true", "spring.cloud.kubernetes.client.namespace=default" }) + "spring.cloud.bootstrap.enabled=true" }) @ActiveProfiles("two") -class Fabric8EventReloadInformFromOneNamespaceEventTriggeredIT extends Fabric8EventReloadBase { +class Fabric8EventReloadInformIT extends Fabric8EventReloadBase { private static final String LEFT_NAMESPACE = "left"; private static final String RIGHT_NAMESPACE = "right"; - private static final String DEFAULT_NAMESPACE = "default"; private static ConfigMap leftConfigMap; private static ConfigMap rightConfigMap; - private static ConfigMap configMap; @Autowired - private Environment environment; - - @Autowired - private KubernetesClient kubernetesClient; + private LeftProperties leftProperties; @Autowired private RightProperties rightProperties; @Autowired - private ApplicationContext applicationContext; + private KubernetesClient kubernetesClient; @BeforeAll static void beforeAllLocal() { InputStream leftConfigMapStream = util.inputStream("manifests/left-configmap.yaml"); InputStream rightConfigMapStream = util.inputStream("manifests/right-configmap.yaml"); - InputStream configMapStream = util.inputStream("manifests/configmap.yaml"); leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); - configMap = Serialization.unmarshal(configMapStream, ConfigMap.class); util.createNamespace(LEFT_NAMESPACE); util.createNamespace(RIGHT_NAMESPACE); configMap(Phase.CREATE, util, leftConfigMap, LEFT_NAMESPACE); configMap(Phase.CREATE, util, rightConfigMap, RIGHT_NAMESPACE); - configMap(Phase.CREATE, util, configMap, DEFAULT_NAMESPACE); } @AfterAll static void afterAllLocal() { configMap(Phase.DELETE, util, leftConfigMap, LEFT_NAMESPACE); configMap(Phase.DELETE, util, rightConfigMap, RIGHT_NAMESPACE); - configMap(Phase.DELETE, util, configMap, DEFAULT_NAMESPACE); - util.deleteNamespace("left"); - util.deleteNamespace("right"); + util.deleteNamespace(LEFT_NAMESPACE); + util.deleteNamespace(RIGHT_NAMESPACE); } /** @@ -110,7 +97,7 @@ static void afterAllLocal() { * - each of the namespaces has one configmap * - we watch the "right" namespace and make a change in the configmap in the same * namespace - * - as such, event is triggered and we see the updated value + * - as such, event is triggered (refresh happens) and we see the updated value * */ @Test @@ -118,11 +105,9 @@ void test(CapturedOutput output) { assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", output); - String currentLeftValue = environment.getProperty("left.value"); - assertThat(currentLeftValue).isEqualTo("left-initial"); - - String currentRightValue = environment.getProperty("right.value"); - assertThat(currentRightValue).isEqualTo("right-initial"); + // first we read these with default values + assertThat(leftProperties.getValue()).isEqualTo("left-initial"); + assertThat(rightProperties.getValue()).isEqualTo("right-initial"); // then deploy a new version of right-configmap ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() @@ -132,28 +117,15 @@ void test(CapturedOutput output) { replaceConfigMap(kubernetesClient, rightConfigMapAfterChange, RIGHT_NAMESPACE); - String afterUpdateLeftValue = environment.getProperty("left.value"); - assertThat(afterUpdateLeftValue).isEqualTo("left-initial"); - - await().atMost(Duration.ofSeconds(15000)) + await().atMost(Duration.ofSeconds(60)) .pollDelay(Duration.ofSeconds(1)) .until(() -> { String afterUpdateRightValue = rightProperties.getValue(); return afterUpdateRightValue.equals("right-after-change"); }); + // left does not change + assertThat(leftProperties.getValue()).isEqualTo("left-initial"); } - @TestConfiguration - static class TestConfig { - - @Bean - @Primary - KubernetesClient kubernetesClient() { - return kubernetesClient(); - } - - } - - } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java new file mode 100644 index 0000000000..f8d467de84 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java @@ -0,0 +1,148 @@ +/* + * Copyright 2012-2024 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.client.reload.it; + +import java.io.InputStream; +import java.time.Duration; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.cloud.kubernetes.fabric8.client.reload.RightProperties; +import org.springframework.cloud.kubernetes.fabric8.client.reload.RightWithLabelsProperties; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.configMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", + "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", + "spring.cloud.bootstrap.enabled=true" }) +@ActiveProfiles("three") +class Fabric8EventReloadInformWithLabelIT extends Fabric8EventReloadBase { + + private static final String RIGHT_NAMESPACE = "right"; + + private static ConfigMap rightConfigMap; + + private static ConfigMap rightConfigMapWithLabel; + + @Autowired + private KubernetesClient kubernetesClient; + + @Autowired + private RightProperties rightProperties; + + @Autowired + private RightWithLabelsProperties rightWithLabelsProperties; + + @BeforeAll + static void beforeAllLocal() { + InputStream rightConfigMapStream = util.inputStream("manifests/right-configmap.yaml"); + InputStream rightConfigMapWithLabelStream = util.inputStream("manifests/right-configmap-with-label.yaml"); + + rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); + rightConfigMapWithLabel = Serialization.unmarshal(rightConfigMapWithLabelStream, ConfigMap.class); + + util.createNamespace(RIGHT_NAMESPACE); + + configMap(Phase.CREATE, util, rightConfigMap, RIGHT_NAMESPACE); + configMap(Phase.CREATE, util, rightConfigMapWithLabel, RIGHT_NAMESPACE); + } + + @AfterAll + static void afterAllLocal() { + configMap(Phase.DELETE, util, rightConfigMap, RIGHT_NAMESPACE); + configMap(Phase.DELETE, util, rightConfigMapWithLabel, RIGHT_NAMESPACE); + util.deleteNamespace(RIGHT_NAMESPACE); + } + + /** + *
+	 * - there is one namespace : right
+	 * - right has two configmaps: right-configmap, right-configmap-with-label
+	 * - we watch the "right" namespace, but enable tagging; which means that only
+	 * right-configmap-with-label triggers changes.
+	 * 
+ */ + @Test + void test(CapturedOutput output) { + assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", + output); + + // read the initial value from the right-configmap + assertThat(rightProperties.getValue()).isEqualTo("right-initial"); + + // then read the initial value from the right-with-label-configmap + assertThat(rightWithLabelsProperties.getValue()).isEqualTo("right-with-label-initial"); + + // then deploy a new version of right-configmap + ConfigMap rightConfigMapAfterChange = new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build()) + .withData(Map.of("right.value", "right-after-change")) + .build(); + + replaceConfigMap(kubernetesClient, rightConfigMapAfterChange, RIGHT_NAMESPACE); + + // nothing changes in our app, because we are watching only labeled configmaps + assertThat(rightProperties.getValue()).isEqualTo("right-initial"); + assertThat(rightWithLabelsProperties.getValue()).isEqualTo("right-with-label-initial"); + + // then deploy a new version of right-with-label-configmap + ConfigMap rightWithLabelConfigMapAfterChange = new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap-with-label").build()) + .withData(Map.of("right.with.label.value", "right-with-label-after-change")) + .build(); + + replaceConfigMap(kubernetesClient, rightWithLabelConfigMapAfterChange, RIGHT_NAMESPACE); + + // since we have changed a labeled configmap, app will restart and pick up the new value + await().atMost(Duration.ofSeconds(60)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> { + String afterUpdateRightValue = rightWithLabelsProperties.getValue(); + return afterUpdateRightValue.equals("right-with-label-after-change"); + }); + + // right-configmap now will see the new value also, but only because the other + // configmap has triggered the restart + await().atMost(Duration.ofSeconds(60)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> { + String afterUpdateRightValue = rightProperties.getValue(); + return afterUpdateRightValue.equals("right-after-change"); + }); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java new file mode 100644 index 0000000000..0d9014d569 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java @@ -0,0 +1,121 @@ +/* + * Copyright 2012-2024 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.client.reload.it; + +import java.io.InputStream; +import java.time.Duration; +import java.util.Base64; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.cloud.kubernetes.commons.config.Constants; +import org.springframework.cloud.kubernetes.fabric8.client.reload.SecretProperties; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.secret; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceSecret; + +/** + * @author wind57 + */ +@TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", + "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug" }) +@ActiveProfiles("with-secret") +class Fabric8EventReloadSecretIT extends Fabric8EventReloadBase { + + private static final String NAMESPACE = "default"; + + private static Secret secret; + + @Autowired + private KubernetesClient kubernetesClient; + + @Autowired + private SecretProperties secretProperties; + + @BeforeAll + static void beforeAllLocal() { + InputStream secretStream = util.inputStream("manifests/secret.yaml"); + secret = Serialization.unmarshal(secretStream, Secret.class); + secret(Phase.CREATE, util, secret, NAMESPACE); + } + + @AfterAll + static void afterAllLocal() { + secret(Phase.DELETE, util, secret, NAMESPACE); + } + + /** + *
+	 *     - secret with no labels and data: from.secret.properties.key = secret-initial exists in namespace default
+	 *
+	 *     - then we change the secret by adding a label, this in turn does not
+	 *       change the result
+	 *
+	 *     - then we change data inside the secret, and we must see the updated value.
+	 * 
+ */ + @Test + void test(CapturedOutput output) { + assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", + output); + assertThat(secretProperties.getKey()).isEqualTo("secret-initial"); + + Secret secret = new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withLabels(Map.of("letter", "a")) + .withNamespace("default") + .withName("event-reload") + .build()) + .withData(Map.of(Constants.APPLICATION_PROPERTIES, + Base64.getEncoder().encodeToString("from.secret.properties.key=secret-initial".getBytes()))) + .build(); + replaceSecret(kubernetesClient, secret, NAMESPACE); + + assertThat(output.getOut()).contains("Secret event-reload was updated in namespace default"); + assertThat(output.getOut()).contains("data in secret has not changed, will not reload"); + assertThat(secretProperties.getKey()).isEqualTo("secret-initial"); + + + // change data + secret = new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withNamespace("default").withName("event-reload").build()) + .withData(Map.of(Constants.APPLICATION_PROPERTIES, + Base64.getEncoder().encodeToString("from.secret.properties.key=secret-initial-changed".getBytes()))) + .build(); + replaceSecret(kubernetesClient, secret, NAMESPACE); + + await().atMost(Duration.ofSeconds(60)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> secretProperties.getKey().equals("right-after-change")); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java index 5d10f840aa..5feadd1504 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java @@ -19,6 +19,7 @@ import java.time.Duration; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import org.springframework.boot.test.system.CapturedOutput; @@ -58,6 +59,10 @@ static void replaceConfigMap(KubernetesClient client, ConfigMap configMap, Strin client.configMaps().inNamespace(namespace).resource(configMap).update(); } + static void replaceSecret(KubernetesClient client, Secret secret, String namespace) { + client.secrets().inNamespace(namespace).resource(secret).update(); + } + static void configMap(Phase phase, Util util, ConfigMap configMap, String namespace) { if (phase.equals(Phase.CREATE)) { util.createAndWait(namespace, configMap, null); @@ -67,4 +72,13 @@ static void configMap(Phase phase, Util util, ConfigMap configMap, String namesp } } + static void secret(Phase phase, Util util, Secret secret, String namespace) { + if (phase.equals(Phase.CREATE)) { + util.createAndWait(namespace, null, secret); + } + else { + util.deleteAndWait(namespace, null, secret); + } + } + } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories index 1ae5656b5e..1d3fcdf8fd 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ -org.springframework.cloud.kubernetes.fabric8.client.reload.it.Fabric8EventReloadInformFromOneNamespaceEventNotTriggeredIT.TestConfig +org.springframework.cloud.kubernetes.fabric8.client.reload.it.Fabric8EventReloadBase.TestConfig diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-one.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-one.yaml deleted file mode 100644 index b1cc878981..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/application-one.yaml +++ /dev/null @@ -1,12 +0,0 @@ -spring: - application: - name: event-reload - cloud: - kubernetes: - reload: - enabled: true - strategy: refresh - mode: event - namespaces: - - left - monitoring-config-maps: true diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-one.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-one.yaml deleted file mode 100644 index 17ce4c7193..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/bootstrap-one.yaml +++ /dev/null @@ -1,9 +0,0 @@ -spring: - cloud: - kubernetes: - config: - sources: - - namespace: left - name: left-configmap - - namespace: right - name: right-configmap From c201be4e1b1e4e8949816ee11c8e2ebc3a2d5025 Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 20 Dec 2024 13:39:34 +0200 Subject: [PATCH 28/35] dirty --- .../resources/application-with-secret.yaml | 6 +- ...pEnabledPollingReloadConfigMapMountIT.java | 148 ++++++++++++++++++ .../reload/it/Fabric8EventReloadBase.java | 5 +- .../reload/it/Fabric8EventReloadSecretIT.java | 37 ++++- 4 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-with-secret.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-with-secret.yaml index 176f34c6a5..ffd1487b59 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-with-secret.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-with-secret.yaml @@ -8,7 +8,7 @@ spring: reload: enabled: true monitoring-secrets: true - strategy: shutdown + strategy: refresh mode: event monitoring-configMaps: false config: @@ -18,4 +18,8 @@ spring: enabled: true enable-api: true + # otherwise we can't refresh in the test + main: + cloud-platform: kubernetes + diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java new file mode 100644 index 0000000000..904ca50666 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java @@ -0,0 +1,148 @@ +/* + * 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.client.reload.it; + +import java.io.InputStream; +import java.time.Duration; +import java.util.Map; +import java.util.Set; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.Secret; +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 io.fabric8.kubernetes.client.utils.Serialization; +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.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.testcontainers.k3s.K3sContainer; + +import org.springframework.cloud.kubernetes.commons.config.Constants; +import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; +import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; +import org.springframework.http.HttpMethod; +import org.springframework.web.reactive.function.client.WebClient; + +import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion; + +/** + * @author wind57 + */ +class BootstrapEnabledPollingReloadConfigMapMountIT { + + private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-reload"; + + private static final String DOCKER_IMAGE = "docker.io/springcloud/" + IMAGE_NAME + ":" + pomVersion(); + + private static final String NAMESPACE = "default"; + + private static final K3sContainer K3S = Commons.container(); + + private static Util util; + + private static KubernetesClient client; + + @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.createNamespace("left"); + util.createNamespace("right"); + util.setUpClusterWide(NAMESPACE, Set.of("left", "right")); + util.setUp(NAMESPACE); + + manifests(Phase.CREATE); + } + + @AfterAll + static void afterAll() throws Exception { + util.deleteNamespace("left"); + util.deleteNamespace("right"); + Commons.cleanUp(IMAGE_NAME, K3S); + Commons.systemPrune(); + + manifests(Phase.DELETE); + } + + /** + *
+	 *     - we have bootstrap enabled, which means we will 'locate' property sources
+	 *       from config maps.
+	 *     - there are no explicit config maps to search for, but what we will also read,
+	 *     	 is 'spring.cloud.kubernetes.config.paths', which we have set to
+	 *     	 '/tmp/application.properties'
+	 *       in this test. That is populated by the volumeMounts (see deployment-mount.yaml)
+	 *     - we first assert that we are actually reading the path based source via (1), (2) and (3).
+	 *
+	 *     - we then change the config map content, wait for k8s to pick it up and replace them
+	 *     - our polling will then detect that change, and trigger a reload.
+	 * 
+ */ + @Test + void test() { + + } + + private static void manifests(Phase phase) { + + InputStream deploymentStream = util.inputStream("deployment.yaml"); + InputStream serviceStream = util.inputStream("service.yaml"); + InputStream ingressStream = util.inputStream("ingress.yaml"); + InputStream leftConfigMapStream = util.inputStream("left-configmap.yaml"); + InputStream rightConfigMapStream = util.inputStream("right-configmap.yaml"); + InputStream rightWithLabelConfigMapStream = util.inputStream("right-configmap-with-label.yaml"); + InputStream configMapAsStream = util.inputStream("configmap.yaml"); + InputStream secretAsStream = util.inputStream("secret.yaml"); + + Deployment deployment = Serialization.unmarshal(deploymentStream, Deployment.class); + + Service service = Serialization.unmarshal(serviceStream, Service.class); + Ingress ingress = Serialization.unmarshal(ingressStream, Ingress.class); + ConfigMap leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); + ConfigMap rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); + ConfigMap rightWithLabelConfigMap = Serialization.unmarshal(rightWithLabelConfigMapStream, ConfigMap.class); + ConfigMap configMap = Serialization.unmarshal(configMapAsStream, ConfigMap.class); + Secret secret = Serialization.unmarshal(secretAsStream, Secret.class); + + if (phase.equals(Phase.CREATE)) { + util.createAndWait("left", leftConfigMap, null); + util.createAndWait("right", rightConfigMap, null); + util.createAndWait("right", rightWithLabelConfigMap, null); + util.createAndWait(NAMESPACE, configMap, secret); + util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); + } + else { + util.deleteAndWait("left", leftConfigMap, null); + util.deleteAndWait("right", rightConfigMap, null); + util.deleteAndWait("right", rightWithLabelConfigMap, null); + util.deleteAndWait(NAMESPACE, configMap, secret); + util.deleteAndWait(NAMESPACE, deployment, service, ingress); + } + + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java index 41fda572e9..0c56f2c588 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java @@ -32,7 +32,10 @@ import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; -@SpringBootTest(classes = { App.class, Fabric8EventReloadBase.TestConfig.class }) +/** + * @author wind57 + */ +@SpringBootTest(classes = { App.class }) @ExtendWith(OutputCaptureExtension.class) abstract class Fabric8EventReloadBase { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java index 0d9014d569..992442bc37 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java @@ -24,7 +24,9 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.SecretBuilder; +import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.utils.Serialization; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -48,7 +50,8 @@ * @author wind57 */ @TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", - "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug" }) + "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", + "spring.cloud.kubernetes.client.namespace=default" }) @ActiveProfiles("with-secret") class Fabric8EventReloadSecretIT extends Fabric8EventReloadBase { @@ -64,6 +67,25 @@ class Fabric8EventReloadSecretIT extends Fabric8EventReloadBase { @BeforeAll static void beforeAllLocal() { + + // set system properties very early, so that when 'Fabric8ConfigDataLocationResolver' + // loads KubernetesClient from Config, these would be already present + Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml()); + String caCertData = config.getCaCertData(); + String clientCertData = config.getClientCertData(); + String clientKeyData = config.getClientKeyData(); + String clientKeyAlgo = config.getClientKeyAlgo(); + String clientKeyPass = config.getClientKeyPassphrase(); + String masterUrl = new KubernetesClientBuilder().withConfig(config) + .build().getConfiguration().getMasterUrl(); + + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, masterUrl); + System.setProperty(Config.KUBERNETES_CA_CERTIFICATE_DATA_SYSTEM_PROPERTY, caCertData); + System.setProperty(Config.KUBERNETES_CLIENT_CERTIFICATE_DATA_SYSTEM_PROPERTY, clientCertData); + System.setProperty(Config.KUBERNETES_CLIENT_KEY_DATA_SYSTEM_PROPERTY, clientKeyData); + System.setProperty(Config.KUBERNETES_CLIENT_KEY_ALGO_SYSTEM_PROPERTY, clientKeyAlgo); + System.setProperty(Config.KUBERNETES_CLIENT_KEY_PASSPHRASE_SYSTEM_PROPERTY, clientKeyPass); + InputStream secretStream = util.inputStream("manifests/secret.yaml"); secret = Serialization.unmarshal(secretStream, Secret.class); secret(Phase.CREATE, util, secret, NAMESPACE); @@ -86,7 +108,7 @@ static void afterAllLocal() { */ @Test void test(CapturedOutput output) { - assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", + assertReloadLogStatements("added secret informer for namespace", "added configmap informer for namespace", output); assertThat(secretProperties.getKey()).isEqualTo("secret-initial"); @@ -100,8 +122,13 @@ void test(CapturedOutput output) { .build(); replaceSecret(kubernetesClient, secret, NAMESPACE); - assertThat(output.getOut()).contains("Secret event-reload was updated in namespace default"); - assertThat(output.getOut()).contains("data in secret has not changed, will not reload"); + await().atMost(Duration.ofSeconds(60)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> output.getOut().contains("Secret event-reload was updated in namespace default")); + + await().atMost(Duration.ofSeconds(60)) + .pollDelay(Duration.ofSeconds(1)) + .until(() -> output.getOut().contains("data in secret has not changed, will not reload")); assertThat(secretProperties.getKey()).isEqualTo("secret-initial"); @@ -115,7 +142,7 @@ void test(CapturedOutput output) { await().atMost(Duration.ofSeconds(60)) .pollDelay(Duration.ofSeconds(1)) - .until(() -> secretProperties.getKey().equals("right-after-change")); + .until(() -> secretProperties.getKey().equals("secret-initial-changed")); } } From f7919276bad03028926ff6cd807a6689f4c9c356 Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 20 Dec 2024 14:10:27 +0200 Subject: [PATCH 29/35] dirty --- ...pEnabledPollingReloadConfigMapMountIT.java | 29 +----------- .../test/resources/manifests/deployment.yaml | 45 +++++++++++++++++++ .../src/test/resources/manifests/ingress.yaml | 16 +++++++ .../src/test/resources/manifests/service.yaml | 14 ++++++ 4 files changed, 77 insertions(+), 27 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/ingress.yaml create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/service.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java index 904ca50666..d92b9a362f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java @@ -51,8 +51,6 @@ class BootstrapEnabledPollingReloadConfigMapMountIT { private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-reload"; - private static final String DOCKER_IMAGE = "docker.io/springcloud/" + IMAGE_NAME + ":" + pomVersion(); - private static final String NAMESPACE = "default"; private static final K3sContainer K3S = Commons.container(); @@ -69,22 +67,13 @@ static void beforeAll() throws Exception { util = new Util(K3S); client = util.client(); - - util.createNamespace("left"); - util.createNamespace("right"); - util.setUpClusterWide(NAMESPACE, Set.of("left", "right")); util.setUp(NAMESPACE); - manifests(Phase.CREATE); } @AfterAll static void afterAll() throws Exception { - util.deleteNamespace("left"); - util.deleteNamespace("right"); Commons.cleanUp(IMAGE_NAME, K3S); - Commons.systemPrune(); - manifests(Phase.DELETE); } @@ -112,34 +101,20 @@ private static void manifests(Phase phase) { InputStream deploymentStream = util.inputStream("deployment.yaml"); InputStream serviceStream = util.inputStream("service.yaml"); InputStream ingressStream = util.inputStream("ingress.yaml"); - InputStream leftConfigMapStream = util.inputStream("left-configmap.yaml"); - InputStream rightConfigMapStream = util.inputStream("right-configmap.yaml"); - InputStream rightWithLabelConfigMapStream = util.inputStream("right-configmap-with-label.yaml"); InputStream configMapAsStream = util.inputStream("configmap.yaml"); - InputStream secretAsStream = util.inputStream("secret.yaml"); Deployment deployment = Serialization.unmarshal(deploymentStream, Deployment.class); Service service = Serialization.unmarshal(serviceStream, Service.class); Ingress ingress = Serialization.unmarshal(ingressStream, Ingress.class); - ConfigMap leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); - ConfigMap rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); - ConfigMap rightWithLabelConfigMap = Serialization.unmarshal(rightWithLabelConfigMapStream, ConfigMap.class); ConfigMap configMap = Serialization.unmarshal(configMapAsStream, ConfigMap.class); - Secret secret = Serialization.unmarshal(secretAsStream, Secret.class); if (phase.equals(Phase.CREATE)) { - util.createAndWait("left", leftConfigMap, null); - util.createAndWait("right", rightConfigMap, null); - util.createAndWait("right", rightWithLabelConfigMap, null); - util.createAndWait(NAMESPACE, configMap, secret); + util.createAndWait(NAMESPACE, configMap, null); util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); } else { - util.deleteAndWait("left", leftConfigMap, null); - util.deleteAndWait("right", rightConfigMap, null); - util.deleteAndWait("right", rightWithLabelConfigMap, null); - util.deleteAndWait(NAMESPACE, configMap, secret); + util.deleteAndWait(NAMESPACE, configMap, null); util.deleteAndWait(NAMESPACE, deployment, service, ingress); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml new file mode 100644 index 0000000000..44c958bf92 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: spring-cloud-kubernetes-fabric8-client-reload +spec: + selector: + matchLabels: + app: spring-cloud-kubernetes-fabric8-client-reload + template: + metadata: + labels: + app: spring-cloud-kubernetes-fabric8-client-reload + spec: + serviceAccountName: spring-cloud-kubernetes-serviceaccount + containers: + - name: spring-cloud-kubernetes-fabric8-client-reload + image: docker.io/springcloud/spring-cloud-kubernetes-fabric8-client-reload + imagePullPolicy: IfNotPresent + readinessProbe: + httpGet: + port: 8080 + path: /actuator/health/readiness + livenessProbe: + httpGet: + port: 8080 + path: /actuator/health/liveness + ports: + - containerPort: 8080 + env: + - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_CONFIG_RELOAD + value: "DEBUG" + - name: SPRING_PROFILES_ACTIVE + value: "one" + - name: SPRING_CLOUD_BOOTSTRAP_ENABLED + value: "TRUE" + + volumeMounts: + - mountPath: /tmp + name: "config-map-volume" + + volumes: + - name: "config-map-volume" + configMap: + defaultMode: 420 + name: "poll-reload" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/ingress.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/ingress.yaml new file mode 100644 index 0000000000..d6e9e49a9e --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/ingress.yaml @@ -0,0 +1,16 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: spring-cloud-kubernetes-fabric8-client-reload-ingress + namespace: default +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: spring-cloud-kubernetes-fabric8-client-reload + port: + number: 8080 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/service.yaml new file mode 100644 index 0000000000..3f73ad0763 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: spring-cloud-kubernetes-fabric8-client-reload + name: spring-cloud-kubernetes-fabric8-client-reload +spec: + ports: + - name: http + port: 8080 + targetPort: 8080 + selector: + app: spring-cloud-kubernetes-fabric8-client-reload + type: ClusterIP From f1e077450735aa668df320527555b76beb6c0a77 Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 20 Dec 2024 18:45:48 +0200 Subject: [PATCH 30/35] dirty --- .../fabric8/client/reload/Controller.java | 39 ++++++++++ .../src/main/resources/application-mount.yaml | 2 - .../main/resources/application-no-mount.yaml | 4 - ...edPollingReloadConfigMapMountDelegate.java | 78 +++++++++---------- .../ConfigMapMountPollingReloadDelegate.java | 78 +++++++++---------- .../client/reload/Fabric8EventReloadIT.java | 9 --- .../fabric8/client/reload/TestUtil.java | 18 ----- ...pEnabledPollingReloadConfigMapMountIT.java | 46 ++++++++--- .../reload/it/Fabric8EventReloadBase.java | 8 +- ...c8EventReloadDataChangesInConfigMapIT.java | 19 ++--- .../reload/it/Fabric8EventReloadInformIT.java | 18 ++--- .../Fabric8EventReloadInformWithLabelIT.java | 29 ++++--- .../reload/it/Fabric8EventReloadSecretIT.java | 19 +++-- .../client/reload/it/TestAssertions.java | 14 ++++ .../test/resources/manifests/deployment.yaml | 4 +- 15 files changed, 206 insertions(+), 179 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java new file mode 100644 index 0000000000..5a190760cd --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Controller.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.client.reload; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author wind57 + */ +@RestController +class Controller { + + private final ConfigMapProperties configMapProperties; + + Controller(ConfigMapProperties configMapProperties) { + this.configMapProperties = configMapProperties; + } + + @GetMapping("/key") + String key() { + return configMapProperties.getKey(); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-mount.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-mount.yaml index cd1765f5c8..da2863ab51 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-mount.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-mount.yaml @@ -12,6 +12,4 @@ spring: config: paths: - /tmp/application.properties - config: - import: "kubernetes:" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-no-mount.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-no-mount.yaml index baf0f12d35..12bc7a05fd 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-no-mount.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-no-mount.yaml @@ -1,7 +1,3 @@ -logging: - level: - root: DEBUG - spring: application: name: poll-reload diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountDelegate.java index 0b6c397967..6d280196ad 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountDelegate.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountDelegate.java @@ -16,23 +16,10 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload; -import java.io.InputStream; -import java.time.Duration; -import java.util.Map; - -import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.utils.Serialization; -import org.junit.jupiter.api.Assertions; import org.testcontainers.k3s.K3sContainer; -import org.springframework.cloud.kubernetes.commons.config.Constants; -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; -import org.springframework.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.awaitility.Awaitility.await; final class BootstrapEnabledPollingReloadConfigMapMountDelegate { @@ -52,36 +39,41 @@ final class BootstrapEnabledPollingReloadConfigMapMountDelegate { */ static void testPollingReloadConfigMapWithBootstrap(KubernetesClient client, Util util, K3sContainer container, String appLabelValue) { - // (1) - Commons.waitForLogStatement("paths property sources : [/tmp/application.properties]", container, appLabelValue); - // (2) - Commons.waitForLogStatement("will add file-based property source : /tmp/application.properties", container, - appLabelValue); - // (3) - WebClient webClient = TestUtil.builder().baseUrl("http://localhost/key").build(); - String result = webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - - // we first read the initial value from the configmap - Assertions.assertEquals("as-mount-initial", result); - - // replace data in configmap and wait for k8s to pick it up - // our polling will detect that and restart the app - InputStream configMapStream = util.inputStream("configmap.yaml"); - ConfigMap configMap = Serialization.unmarshal(configMapStream, ConfigMap.class); - configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, "from.properties.key=as-mount-changed")); - client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); - - await().timeout(Duration.ofSeconds(360)) - .until(() -> webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block() - .equals("as-mount-changed")); + // // (1) + // Commons.waitForLogStatement("paths property sources : + // [/tmp/application.properties]", container, appLabelValue); + // // (2) + // Commons.waitForLogStatement("will add file-based property source : + // /tmp/application.properties", container, + // appLabelValue); + // // (3) + // WebClient webClient = + // TestUtil.builder().baseUrl("http://localhost/key").build(); + // String result = webClient.method(HttpMethod.GET) + // .retrieve() + // .bodyToMono(String.class) + // .retryWhen(TestUtil.retrySpec()) + // .block(); + // + // // we first read the initial value from the configmap + // Assertions.assertEquals("as-mount-initial", result); + // + // // replace data in configmap and wait for k8s to pick it up + // // our polling will detect that and restart the app + // InputStream configMapStream = util.inputStream("configmap.yaml"); + // ConfigMap configMap = Serialization.unmarshal(configMapStream, + // ConfigMap.class); + // configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, + // "from.properties.key=as-mount-changed")); + // client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); + // + // await().timeout(Duration.ofSeconds(360)) + // .until(() -> webClient.method(HttpMethod.GET) + // .retrieve() + // .bodyToMono(String.class) + // .retryWhen(TestUtil.retrySpec()) + // .block() + // .equals("as-mount-changed")); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegate.java index c0f5fc5d3f..e1668bcba6 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegate.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegate.java @@ -16,23 +16,10 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload; -import java.io.InputStream; -import java.time.Duration; -import java.util.Map; - -import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.utils.Serialization; -import org.junit.jupiter.api.Assertions; import org.testcontainers.k3s.K3sContainer; -import org.springframework.cloud.kubernetes.commons.config.Constants; -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; -import org.springframework.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.awaitility.Awaitility.await; /** * @author wind57 @@ -56,36 +43,41 @@ final class ConfigMapMountPollingReloadDelegate { */ static void testConfigMapMountPollingReload(KubernetesClient client, Util util, K3sContainer container, String appLabelValue) { - // (1) - Commons.waitForLogStatement("paths property sources : [/tmp/application.properties]", container, appLabelValue); - // (2) - Commons.waitForLogStatement("will add file-based property source : /tmp/application.properties", container, - appLabelValue); - // (3) - WebClient webClient = TestUtil.builder().baseUrl("http://localhost/key").build(); - String result = webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block(); - - // we first read the initial value from the configmap - Assertions.assertEquals("as-mount-initial", result); - - // replace data in configmap and wait for k8s to pick it up - // our polling will detect that and restart the app - InputStream configMapStream = util.inputStream("configmap.yaml"); - ConfigMap configMap = Serialization.unmarshal(configMapStream, ConfigMap.class); - configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, "from.properties.key=as-mount-changed")); - client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); - - await().timeout(Duration.ofSeconds(360)) - .until(() -> webClient.method(HttpMethod.GET) - .retrieve() - .bodyToMono(String.class) - .retryWhen(TestUtil.retrySpec()) - .block() - .equals("as-mount-changed")); + // // (1) + // Commons.waitForLogStatement("paths property sources : + // [/tmp/application.properties]", container, appLabelValue); + // // (2) + // Commons.waitForLogStatement("will add file-based property source : + // /tmp/application.properties", container, + // appLabelValue); + // // (3) + // WebClient webClient = + // TestUtil.builder().baseUrl("http://localhost/key").build(); + // String result = webClient.method(HttpMethod.GET) + // .retrieve() + // .bodyToMono(String.class) + // .retryWhen(TestUtil.retrySpec()) + // .block(); + // + // // we first read the initial value from the configmap + // Assertions.assertEquals("as-mount-initial", result); + // + // // replace data in configmap and wait for k8s to pick it up + // // our polling will detect that and restart the app + // InputStream configMapStream = util.inputStream("configmap.yaml"); + // ConfigMap configMap = Serialization.unmarshal(configMapStream, + // ConfigMap.class); + // configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, + // "from.properties.key=as-mount-changed")); + // client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); + // + // await().timeout(Duration.ofSeconds(360)) + // .until(() -> webClient.method(HttpMethod.GET) + // .retrieve() + // .bodyToMono(String.class) + // .retryWhen(TestUtil.retrySpec()) + // .block() + // .equals("as-mount-changed")); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java index 748cd9214f..ffa17c723b 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java @@ -17,13 +17,9 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload; import java.io.InputStream; -import java.time.Duration; -import java.util.Map; import java.util.Set; import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.apps.Deployment; @@ -31,7 +27,6 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.utils.Serialization; 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; @@ -39,10 +34,7 @@ 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.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; -import static org.awaitility.Awaitility.await; import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion; /** @@ -95,7 +87,6 @@ void testInformFromOneNamespaceEventNotTriggered() { testPollingReloadConfigMapWithBootstrap(); } - void testConfigMapMountPollingReload() { TestUtil.reCreateSources(util, client); TestUtil.patchFive(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java index 4a9d03e3bf..7a69a2163e 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java @@ -17,22 +17,15 @@ package org.springframework.cloud.kubernetes.fabric8.client.reload; import java.io.InputStream; -import java.time.Duration; import java.util.Map; -import java.util.Objects; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.utils.Serialization; 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.integration.tests.commons.fabric8_client.Util; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; -import org.springframework.web.reactive.function.client.WebClient; /** * @author wind57 @@ -42,9 +35,6 @@ final class TestUtil { private static final Map POD_LABELS = Map.of("app", "spring-cloud-kubernetes-fabric8-client-reload"); - - - private static final String BODY_FOUR = """ { "spec": { @@ -245,14 +235,6 @@ static void patchSeven(Util util, String dockerImage, String deploymentName, Str util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_SEVEN, POD_LABELS); } - static WebClient.Builder builder() { - return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); - } - - static RetryBackoffSpec retrySpec() { - return Retry.fixedDelay(120, Duration.ofSeconds(2)).filter(Objects::nonNull); - } - static String logs(K3sContainer container, String appLabelValue) { try { String appPodName = container diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java index d92b9a362f..9b954578a6 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java @@ -19,10 +19,8 @@ import java.io.InputStream; import java.time.Duration; import java.util.Map; -import java.util.Set; import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; @@ -32,17 +30,18 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.testcontainers.k3s.K3sContainer; import org.springframework.cloud.kubernetes.commons.config.Constants; 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.http.HttpMethod; import org.springframework.web.reactive.function.client.WebClient; import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.builder; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.retrySpec; /** * @author wind57 @@ -93,15 +92,44 @@ static void afterAll() throws Exception { */ @Test void test() { - + // (1) + Commons.waitForLogStatement("paths property sources : [/tmp/application.properties]", K3S, IMAGE_NAME); + // (2) + Commons.waitForLogStatement("will add file-based property source : /tmp/application.properties", K3S, + IMAGE_NAME); + // (3) + WebClient webClient = builder().baseUrl("http://localhost/key").build(); + String result = webClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block(); + + // we first read the initial value from the configmap + Assertions.assertEquals("as-mount-initial", result); + + // replace data in configmap and wait for k8s to pick it up + // our polling will detect that and restart the app + InputStream configMapStream = util.inputStream("configmap.yaml"); + ConfigMap configMap = Serialization.unmarshal(configMapStream, ConfigMap.class); + configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, "from.properties.key=as-mount-changed")); + client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); + + await().timeout(Duration.ofSeconds(360)) + .until(() -> webClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block() + .equals("as-mount-changed")); } private static void manifests(Phase phase) { - InputStream deploymentStream = util.inputStream("deployment.yaml"); - InputStream serviceStream = util.inputStream("service.yaml"); - InputStream ingressStream = util.inputStream("ingress.yaml"); - InputStream configMapAsStream = util.inputStream("configmap.yaml"); + InputStream deploymentStream = util.inputStream("manifests/deployment.yaml"); + InputStream serviceStream = util.inputStream("manifests/service.yaml"); + InputStream ingressStream = util.inputStream("manifests/ingress.yaml"); + InputStream configMapAsStream = util.inputStream("manifests/configmap.yaml"); Deployment deployment = Serialization.unmarshal(deploymentStream, Deployment.class); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java index 0c56f2c588..d1d2498c01 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java @@ -21,16 +21,16 @@ import io.fabric8.kubernetes.client.KubernetesClientBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.kubernetes.fabric8.client.reload.App; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; import org.testcontainers.k3s.K3sContainer; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.fabric8.client.reload.App; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; /** * @author wind57 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java index c4aa6182b8..30c3727aa4 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java @@ -46,8 +46,8 @@ * @author wind57 */ @TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes", - "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", - "spring.cloud.bootstrap.enabled=true" }) + "logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug", + "spring.cloud.bootstrap.enabled=true" }) @ActiveProfiles("one") class Fabric8EventReloadDataChangesInConfigMapIT extends Fabric8EventReloadBase { @@ -76,7 +76,6 @@ static void afterAllLocal() { util.deleteNamespace(NAMESPACE); } - /** *
 	 *     - configMap with no labels and data: right.value = right-initial exists in namespace right
@@ -90,8 +89,8 @@ static void afterAllLocal() {
 	@Test
 	void test(CapturedOutput output) {
 
-		assertReloadLogStatements("added configmap informer for namespace",
-			"added secret informer for namespace", output);
+		assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace",
+				output);
 
 		// we first read the initial value from configmap
 		assertThat(properties.getValue()).isEqualTo("right-initial");
@@ -129,12 +128,10 @@ void test(CapturedOutput output) {
 
 		replaceConfigMap(kubernetesClient, configMap, NAMESPACE);
 
-		await().atMost(Duration.ofSeconds(60))
-			.pollDelay(Duration.ofSeconds(1))
-			.until(() -> {
-				String afterUpdateRightValue = properties.getValue();
-				return afterUpdateRightValue.equals("right-after-change");
-			});
+		await().atMost(Duration.ofSeconds(60)).pollDelay(Duration.ofSeconds(1)).until(() -> {
+			String afterUpdateRightValue = properties.getValue();
+			return afterUpdateRightValue.equals("right-after-change");
+		});
 	}
 
 }
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java
index 4fa6254922..7821938760 100644
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java
+++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java
@@ -47,15 +47,17 @@
  * @author wind57
  */
 @TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes",
-	"logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug",
-	"spring.cloud.bootstrap.enabled=true" })
+		"logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug",
+		"spring.cloud.bootstrap.enabled=true" })
 @ActiveProfiles("two")
 class Fabric8EventReloadInformIT extends Fabric8EventReloadBase {
 
 	private static final String LEFT_NAMESPACE = "left";
+
 	private static final String RIGHT_NAMESPACE = "right";
 
 	private static ConfigMap leftConfigMap;
+
 	private static ConfigMap rightConfigMap;
 
 	@Autowired
@@ -103,7 +105,7 @@ static void afterAllLocal() {
 	@Test
 	void test(CapturedOutput output) {
 		assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace",
-			output);
+				output);
 
 		// first we read these with default values
 		assertThat(leftProperties.getValue()).isEqualTo("left-initial");
@@ -117,12 +119,10 @@ void test(CapturedOutput output) {
 
 		replaceConfigMap(kubernetesClient, rightConfigMapAfterChange, RIGHT_NAMESPACE);
 
-		await().atMost(Duration.ofSeconds(60))
-			.pollDelay(Duration.ofSeconds(1))
-			.until(() -> {
-				String afterUpdateRightValue = rightProperties.getValue();
-				return afterUpdateRightValue.equals("right-after-change");
-			});
+		await().atMost(Duration.ofSeconds(60)).pollDelay(Duration.ofSeconds(1)).until(() -> {
+			String afterUpdateRightValue = rightProperties.getValue();
+			return afterUpdateRightValue.equals("right-after-change");
+		});
 
 		// left does not change
 		assertThat(leftProperties.getValue()).isEqualTo("left-initial");
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java
index f8d467de84..b1c263cbdc 100644
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java
+++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java
@@ -47,8 +47,8 @@
  * @author wind57
  */
 @TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes",
-	"logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug",
-	"spring.cloud.bootstrap.enabled=true" })
+		"logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug",
+		"spring.cloud.bootstrap.enabled=true" })
 @ActiveProfiles("three")
 class Fabric8EventReloadInformWithLabelIT extends Fabric8EventReloadBase {
 
@@ -99,7 +99,7 @@ static void afterAllLocal() {
 	@Test
 	void test(CapturedOutput output) {
 		assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace",
-			output);
+				output);
 
 		// read the initial value from the right-configmap
 		assertThat(rightProperties.getValue()).isEqualTo("right-initial");
@@ -127,22 +127,19 @@ void test(CapturedOutput output) {
 
 		replaceConfigMap(kubernetesClient, rightWithLabelConfigMapAfterChange, RIGHT_NAMESPACE);
 
-		// since we have changed a labeled configmap, app will restart and pick up the new value
-		await().atMost(Duration.ofSeconds(60))
-			.pollDelay(Duration.ofSeconds(1))
-			.until(() -> {
-				String afterUpdateRightValue = rightWithLabelsProperties.getValue();
-				return afterUpdateRightValue.equals("right-with-label-after-change");
-			});
+		// since we have changed a labeled configmap, app will restart and pick up the new
+		// value
+		await().atMost(Duration.ofSeconds(60)).pollDelay(Duration.ofSeconds(1)).until(() -> {
+			String afterUpdateRightValue = rightWithLabelsProperties.getValue();
+			return afterUpdateRightValue.equals("right-with-label-after-change");
+		});
 
 		// right-configmap now will see the new value also, but only because the other
 		// configmap has triggered the restart
-		await().atMost(Duration.ofSeconds(60))
-			.pollDelay(Duration.ofSeconds(1))
-			.until(() -> {
-				String afterUpdateRightValue = rightProperties.getValue();
-				return afterUpdateRightValue.equals("right-after-change");
-			});
+		await().atMost(Duration.ofSeconds(60)).pollDelay(Duration.ofSeconds(1)).until(() -> {
+			String afterUpdateRightValue = rightProperties.getValue();
+			return afterUpdateRightValue.equals("right-after-change");
+		});
 	}
 
 }
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java
index 992442bc37..77f2fb0ab4 100644
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java
+++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java
@@ -43,15 +43,15 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.awaitility.Awaitility.await;
 import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements;
-import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.secret;
 import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceSecret;
+import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.secret;
 
 /**
  * @author wind57
  */
 @TestPropertySource(properties = { "spring.main.cloud-platform=kubernetes",
-	"logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug",
-	"spring.cloud.kubernetes.client.namespace=default" })
+		"logging.level.org.springframework.cloud.kubernetes.fabric8.config.reload=debug",
+		"spring.cloud.kubernetes.client.namespace=default" })
 @ActiveProfiles("with-secret")
 class Fabric8EventReloadSecretIT extends Fabric8EventReloadBase {
 
@@ -68,7 +68,8 @@ class Fabric8EventReloadSecretIT extends Fabric8EventReloadBase {
 	@BeforeAll
 	static void beforeAllLocal() {
 
-		// set system properties very early, so that when 'Fabric8ConfigDataLocationResolver'
+		// set system properties very early, so that when
+		// 'Fabric8ConfigDataLocationResolver'
 		// loads KubernetesClient from Config, these would be already present
 		Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
 		String caCertData = config.getCaCertData();
@@ -76,8 +77,7 @@ static void beforeAllLocal() {
 		String clientKeyData = config.getClientKeyData();
 		String clientKeyAlgo = config.getClientKeyAlgo();
 		String clientKeyPass = config.getClientKeyPassphrase();
-		String masterUrl = new KubernetesClientBuilder().withConfig(config)
-			.build().getConfiguration().getMasterUrl();
+		String masterUrl = new KubernetesClientBuilder().withConfig(config).build().getConfiguration().getMasterUrl();
 
 		System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, masterUrl);
 		System.setProperty(Config.KUBERNETES_CA_CERTIFICATE_DATA_SYSTEM_PROPERTY, caCertData);
@@ -109,7 +109,7 @@ static void afterAllLocal() {
 	@Test
 	void test(CapturedOutput output) {
 		assertReloadLogStatements("added secret informer for namespace", "added configmap informer for namespace",
-			output);
+				output);
 		assertThat(secretProperties.getKey()).isEqualTo("secret-initial");
 
 		Secret secret = new SecretBuilder()
@@ -118,7 +118,7 @@ void test(CapturedOutput output) {
 				.withName("event-reload")
 				.build())
 			.withData(Map.of(Constants.APPLICATION_PROPERTIES,
-				Base64.getEncoder().encodeToString("from.secret.properties.key=secret-initial".getBytes())))
+					Base64.getEncoder().encodeToString("from.secret.properties.key=secret-initial".getBytes())))
 			.build();
 		replaceSecret(kubernetesClient, secret, NAMESPACE);
 
@@ -131,12 +131,11 @@ void test(CapturedOutput output) {
 			.until(() -> output.getOut().contains("data in secret has not changed, will not reload"));
 		assertThat(secretProperties.getKey()).isEqualTo("secret-initial");
 
-
 		// change data
 		secret = new SecretBuilder()
 			.withMetadata(new ObjectMetaBuilder().withNamespace("default").withName("event-reload").build())
 			.withData(Map.of(Constants.APPLICATION_PROPERTIES,
-				Base64.getEncoder().encodeToString("from.secret.properties.key=secret-initial-changed".getBytes())))
+					Base64.getEncoder().encodeToString("from.secret.properties.key=secret-initial-changed".getBytes())))
 			.build();
 		replaceSecret(kubernetesClient, secret, NAMESPACE);
 
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java
index 5feadd1504..20e2b4561a 100644
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java
+++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java
@@ -17,14 +17,20 @@
 package org.springframework.cloud.kubernetes.fabric8.client.reload.it;
 
 import java.time.Duration;
+import java.util.Objects;
 
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.api.model.Secret;
 import io.fabric8.kubernetes.client.KubernetesClient;
+import reactor.netty.http.client.HttpClient;
+import reactor.util.retry.Retry;
+import reactor.util.retry.RetryBackoffSpec;
 
 import org.springframework.boot.test.system.CapturedOutput;
 import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
 import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.web.reactive.function.client.WebClient;
 
 import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
 
@@ -81,4 +87,12 @@ static void secret(Phase phase, Util util, Secret secret, String namespace) {
 		}
 	}
 
+	static WebClient.Builder builder() {
+		return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
+	}
+
+	static RetryBackoffSpec retrySpec() {
+		return Retry.fixedDelay(120, Duration.ofSeconds(2)).filter(Objects::nonNull);
+	}
+
 }
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml
index 44c958bf92..3e7ee3750c 100644
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml
+++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml
@@ -30,9 +30,11 @@ spec:
             - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_CONFIG_RELOAD
               value: "DEBUG"
             - name: SPRING_PROFILES_ACTIVE
-              value: "one"
+              value: "mount"
             - name: SPRING_CLOUD_BOOTSTRAP_ENABLED
               value: "TRUE"
+            - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG
+              value: "DEBUG"
 
           volumeMounts:
             - mountPath: /tmp

From b1c3c3837f4adf883f54c3f668f3abba0996518b Mon Sep 17 00:00:00 2001
From: wind57 
Date: Fri, 20 Dec 2024 19:11:02 +0200
Subject: [PATCH 31/35] fix

---
 .../src/main/resources/application-mount.yaml |   3 +-
 ...edPollingReloadConfigMapMountDelegate.java |  80 ------
 ...pEnabledPollingReloadConfigMapMountIT.java |  44 +--
 .../ConfigMapMountPollingReloadDelegate.java  |  84 ------
 ...ConfigMapMountPollingReloadDelegateIT.java | 126 +++++++++
 .../{it => }/Fabric8EventReloadBase.java      |   3 +-
 ...c8EventReloadDataChangesInConfigMapIT.java |   9 +-
 .../client/reload/Fabric8EventReloadIT.java   | 141 ----------
 .../{it => }/Fabric8EventReloadInformIT.java  |  10 +-
 .../Fabric8EventReloadInformWithLabelIT.java  |  10 +-
 .../{it => }/Fabric8EventReloadSecretIT.java  |   9 +-
 .../reload/{it => }/TestAssertions.java       |  31 ++-
 .../fabric8/client/reload/TestUtil.java       | 254 ------------------
 .../test/resources/manifests/deployment.yaml  |   2 +-
 14 files changed, 185 insertions(+), 621 deletions(-)
 delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountDelegate.java
 rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/{it => }/BootstrapEnabledPollingReloadConfigMapMountIT.java (74%)
 delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegate.java
 create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java
 rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/{it => }/Fabric8EventReloadBase.java (96%)
 rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/{it => }/Fabric8EventReloadDataChangesInConfigMapIT.java (95%)
 delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java
 rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/{it => }/Fabric8EventReloadInformIT.java (93%)
 rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/{it => }/Fabric8EventReloadInformWithLabelIT.java (94%)
 rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/{it => }/Fabric8EventReloadSecretIT.java (96%)
 rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/{it => }/TestAssertions.java (71%)
 delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java

diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-mount.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-mount.yaml
index da2863ab51..a035e13da0 100644
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-mount.yaml
+++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/main/resources/application-mount.yaml
@@ -12,4 +12,5 @@ spring:
       config:
         paths:
           - /tmp/application.properties
-
+  config:
+    import: "kubernetes:"
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountDelegate.java
deleted file mode 100644
index 6d280196ad..0000000000
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountDelegate.java
+++ /dev/null
@@ -1,80 +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.client.reload;
-
-import io.fabric8.kubernetes.client.KubernetesClient;
-import org.testcontainers.k3s.K3sContainer;
-
-import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
-
-final class BootstrapEnabledPollingReloadConfigMapMountDelegate {
-
-	/**
-	 * 
-	 *     - we have bootstrap enabled, which means we will 'locate' property sources
-	 *       from config maps.
-	 *     - there are no explicit config maps to search for, but what we will also read,
-	 *     	 is 'spring.cloud.kubernetes.config.paths', which we have set to
-	 *     	 '/tmp/application.properties'
-	 *       in this test. That is populated by the volumeMounts (see deployment-mount.yaml)
-	 *     - we first assert that we are actually reading the path based source via (1), (2) and (3).
-	 *
-	 *     - we then change the config map content, wait for k8s to pick it up and replace them
-	 *     - our polling will then detect that change, and trigger a reload.
-	 * 
- */ - static void testPollingReloadConfigMapWithBootstrap(KubernetesClient client, Util util, K3sContainer container, - String appLabelValue) { - // // (1) - // Commons.waitForLogStatement("paths property sources : - // [/tmp/application.properties]", container, appLabelValue); - // // (2) - // Commons.waitForLogStatement("will add file-based property source : - // /tmp/application.properties", container, - // appLabelValue); - // // (3) - // WebClient webClient = - // TestUtil.builder().baseUrl("http://localhost/key").build(); - // String result = webClient.method(HttpMethod.GET) - // .retrieve() - // .bodyToMono(String.class) - // .retryWhen(TestUtil.retrySpec()) - // .block(); - // - // // we first read the initial value from the configmap - // Assertions.assertEquals("as-mount-initial", result); - // - // // replace data in configmap and wait for k8s to pick it up - // // our polling will detect that and restart the app - // InputStream configMapStream = util.inputStream("configmap.yaml"); - // ConfigMap configMap = Serialization.unmarshal(configMapStream, - // ConfigMap.class); - // configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, - // "from.properties.key=as-mount-changed")); - // client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); - // - // await().timeout(Duration.ofSeconds(360)) - // .until(() -> webClient.method(HttpMethod.GET) - // .retrieve() - // .bodyToMono(String.class) - // .retryWhen(TestUtil.retrySpec()) - // .block() - // .equals("as-mount-changed")); - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountIT.java similarity index 74% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountIT.java index 9b954578a6..ad957804eb 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/BootstrapEnabledPollingReloadConfigMapMountIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountIT.java @@ -14,20 +14,16 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +package org.springframework.cloud.kubernetes.fabric8.client.reload; import java.io.InputStream; import java.time.Duration; import java.util.Map; import io.fabric8.kubernetes.api.model.ConfigMap; -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 io.fabric8.kubernetes.client.utils.Serialization; 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; @@ -39,9 +35,11 @@ import org.springframework.http.HttpMethod; import org.springframework.web.reactive.function.client.WebClient; +import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.builder; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.retrySpec; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.builder; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.manifests; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.retrySpec; /** * @author wind57 @@ -67,13 +65,13 @@ static void beforeAll() throws Exception { util = new Util(K3S); client = util.client(); util.setUp(NAMESPACE); - manifests(Phase.CREATE); + manifests(Phase.CREATE, util, NAMESPACE); } @AfterAll static void afterAll() throws Exception { Commons.cleanUp(IMAGE_NAME, K3S); - manifests(Phase.DELETE); + manifests(Phase.DELETE, util, NAMESPACE); } /** @@ -106,11 +104,11 @@ void test() { .block(); // we first read the initial value from the configmap - Assertions.assertEquals("as-mount-initial", result); + assertThat(result).isEqualTo("as-mount-initial"); // replace data in configmap and wait for k8s to pick it up // our polling will detect that and restart the app - InputStream configMapStream = util.inputStream("configmap.yaml"); + InputStream configMapStream = util.inputStream("manifests/configmap.yaml"); ConfigMap configMap = Serialization.unmarshal(configMapStream, ConfigMap.class); configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, "from.properties.key=as-mount-changed")); client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); @@ -124,28 +122,4 @@ void test() { .equals("as-mount-changed")); } - private static void manifests(Phase phase) { - - InputStream deploymentStream = util.inputStream("manifests/deployment.yaml"); - InputStream serviceStream = util.inputStream("manifests/service.yaml"); - InputStream ingressStream = util.inputStream("manifests/ingress.yaml"); - InputStream configMapAsStream = util.inputStream("manifests/configmap.yaml"); - - Deployment deployment = Serialization.unmarshal(deploymentStream, Deployment.class); - - Service service = Serialization.unmarshal(serviceStream, Service.class); - Ingress ingress = Serialization.unmarshal(ingressStream, Ingress.class); - ConfigMap configMap = Serialization.unmarshal(configMapAsStream, ConfigMap.class); - - if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, configMap, null); - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); - } - else { - util.deleteAndWait(NAMESPACE, configMap, null); - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } - - } - } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegate.java deleted file mode 100644 index e1668bcba6..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegate.java +++ /dev/null @@ -1,84 +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.client.reload; - -import io.fabric8.kubernetes.client.KubernetesClient; -import org.testcontainers.k3s.K3sContainer; - -import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; - -/** - * @author wind57 - */ -final class ConfigMapMountPollingReloadDelegate { - - /** - *
-	 *     - we have "spring.config.import: kubernetes", which means we will 'locate' property sources
-	 *       from config maps.
-	 *     - the property above means that at the moment we will be searching for config maps that only
-	 *       match the application name, in this specific test there is no such config map.
-	 *     - what we will also read, is 'spring.cloud.kubernetes.config.paths', which we have set to
-	 *     	 '/tmp/application.properties'
-	 *       in this test. That is populated by the volumeMounts (see deployment-mount.yaml)
-	 *     - we first assert that we are actually reading the path based source via (1), (2) and (3).
-	 *
-	 *     - we then change the config map content, wait for k8s to pick it up and replace them
-	 *     - our polling will then detect that change, and trigger a reload.
-	 * 
- */ - static void testConfigMapMountPollingReload(KubernetesClient client, Util util, K3sContainer container, - String appLabelValue) { - // // (1) - // Commons.waitForLogStatement("paths property sources : - // [/tmp/application.properties]", container, appLabelValue); - // // (2) - // Commons.waitForLogStatement("will add file-based property source : - // /tmp/application.properties", container, - // appLabelValue); - // // (3) - // WebClient webClient = - // TestUtil.builder().baseUrl("http://localhost/key").build(); - // String result = webClient.method(HttpMethod.GET) - // .retrieve() - // .bodyToMono(String.class) - // .retryWhen(TestUtil.retrySpec()) - // .block(); - // - // // we first read the initial value from the configmap - // Assertions.assertEquals("as-mount-initial", result); - // - // // replace data in configmap and wait for k8s to pick it up - // // our polling will detect that and restart the app - // InputStream configMapStream = util.inputStream("configmap.yaml"); - // ConfigMap configMap = Serialization.unmarshal(configMapStream, - // ConfigMap.class); - // configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, - // "from.properties.key=as-mount-changed")); - // client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); - // - // await().timeout(Duration.ofSeconds(360)) - // .until(() -> webClient.method(HttpMethod.GET) - // .retrieve() - // .bodyToMono(String.class) - // .retryWhen(TestUtil.retrySpec()) - // .block() - // .equals("as-mount-changed")); - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java new file mode 100644 index 0000000000..e560c2b1e4 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java @@ -0,0 +1,126 @@ +/* + * 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.client.reload; + +import java.io.InputStream; +import java.time.Duration; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.k3s.K3sContainer; + +import org.springframework.cloud.kubernetes.commons.config.Constants; +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.http.HttpMethod; +import org.springframework.web.reactive.function.client.WebClient; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.builder; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.manifests; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.retrySpec; + +/** + * @author wind57 + */ +class ConfigMapMountPollingReloadDelegateIT { + + private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-reload"; + + private static final String NAMESPACE = "default"; + + private static final K3sContainer K3S = Commons.container(); + + private static Util util; + + private static KubernetesClient client; + + @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, NAMESPACE); + } + + @AfterAll + static void afterAll() throws Exception { + Commons.cleanUp(IMAGE_NAME, K3S); + manifests(Phase.DELETE, util, NAMESPACE); + } + + /** + *
+	 *     - we have "spring.config.import: kubernetes", which means we will 'locate' property sources
+	 *       from config maps.
+	 *     - the property above means that at the moment we will be searching for config maps that only
+	 *       match the application name, in this specific test there is no such config map.
+	 *     - what we will also read, is 'spring.cloud.kubernetes.config.paths', which we have set to
+	 *     	 '/tmp/application.properties'
+	 *       in this test. That is populated by the volumeMounts (see deployment-mount.yaml)
+	 *     - we first assert that we are actually reading the path based source via (1), (2) and (3).
+	 *
+	 *     - we then change the config map content, wait for k8s to pick it up and replace them
+	 *     - our polling will then detect that change, and trigger a reload.
+	 * 
+ */ + @Test + void test() { + // (1) + Commons.waitForLogStatement("paths property sources : [/tmp/application.properties]", K3S, IMAGE_NAME); + // (2) + Commons.waitForLogStatement("will add file-based property source : /tmp/application.properties", K3S, + IMAGE_NAME); + // (3) + WebClient webClient = builder().baseUrl("http://localhost/key").build(); + String result = webClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block(); + + // we first read the initial value from the configmap + assertThat(result).isEqualTo("as-mount-initial"); + + // replace data in configmap and wait for k8s to pick it up + // our polling will detect that and restart the app + InputStream configMapStream = util.inputStream("manifests/configmap.yaml"); + ConfigMap configMap = Serialization.unmarshal(configMapStream, ConfigMap.class); + configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, "from.properties.key=as-mount-changed")); + client.configMaps().inNamespace("default").resource(configMap).createOrReplace(); + + await().timeout(Duration.ofSeconds(360)) + .until(() -> webClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block() + .equals("as-mount-changed")); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadBase.java similarity index 96% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadBase.java index d1d2498c01..4b71862666 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadBase.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +package org.springframework.cloud.kubernetes.fabric8.client.reload; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; @@ -26,7 +26,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.system.OutputCaptureExtension; -import org.springframework.cloud.kubernetes.fabric8.client.reload.App; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; import org.springframework.context.annotation.Bean; diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadDataChangesInConfigMapIT.java similarity index 95% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadDataChangesInConfigMapIT.java index 30c3727aa4..b39d08746a 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadDataChangesInConfigMapIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadDataChangesInConfigMapIT.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +package org.springframework.cloud.kubernetes.fabric8.client.reload; import java.io.InputStream; import java.time.Duration; @@ -31,16 +31,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.cloud.kubernetes.fabric8.client.reload.RightProperties; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.configMap; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.configMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.replaceConfigMap; /** * @author wind57 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java deleted file mode 100644 index ffa17c723b..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadIT.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2013-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.kubernetes.fabric8.client.reload; - -import java.io.InputStream; -import java.util.Set; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.Secret; -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 io.fabric8.kubernetes.client.utils.Serialization; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.testcontainers.k3s.K3sContainer; - -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 static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion; - -/** - * @author wind57 - */ -class Fabric8EventReloadIT { - - private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-reload"; - - private static final String DOCKER_IMAGE = "docker.io/springcloud/" + IMAGE_NAME + ":" + pomVersion(); - - private static final String NAMESPACE = "default"; - - private static final K3sContainer K3S = Commons.container(); - - private static Util util; - - private static KubernetesClient client; - - @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.createNamespace("left"); - util.createNamespace("right"); - util.setUpClusterWide(NAMESPACE, Set.of("left", "right")); - util.setUp(NAMESPACE); - - manifests(Phase.CREATE); - } - - @AfterAll - static void afterAll() throws Exception { - util.deleteNamespace("left"); - util.deleteNamespace("right"); - Commons.cleanUp(IMAGE_NAME, K3S); - Commons.systemPrune(); - - manifests(Phase.DELETE); - } - - @Test - void testInformFromOneNamespaceEventNotTriggered() { - testConfigMapMountPollingReload(); - testPollingReloadConfigMapWithBootstrap(); - } - - void testConfigMapMountPollingReload() { - TestUtil.reCreateSources(util, client); - TestUtil.patchFive(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - ConfigMapMountPollingReloadDelegate.testConfigMapMountPollingReload(client, util, K3S, IMAGE_NAME); - } - - void testPollingReloadConfigMapWithBootstrap() { - TestUtil.reCreateSources(util, client); - TestUtil.patchSix(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE); - BootstrapEnabledPollingReloadConfigMapMountDelegate.testPollingReloadConfigMapWithBootstrap(client, util, K3S, - IMAGE_NAME); - } - - private static void manifests(Phase phase) { - - InputStream deploymentStream = util.inputStream("deployment.yaml"); - InputStream serviceStream = util.inputStream("service.yaml"); - InputStream ingressStream = util.inputStream("ingress.yaml"); - InputStream leftConfigMapStream = util.inputStream("left-configmap.yaml"); - InputStream rightConfigMapStream = util.inputStream("right-configmap.yaml"); - InputStream rightWithLabelConfigMapStream = util.inputStream("right-configmap-with-label.yaml"); - InputStream configMapAsStream = util.inputStream("configmap.yaml"); - InputStream secretAsStream = util.inputStream("secret.yaml"); - - Deployment deployment = Serialization.unmarshal(deploymentStream, Deployment.class); - - Service service = Serialization.unmarshal(serviceStream, Service.class); - Ingress ingress = Serialization.unmarshal(ingressStream, Ingress.class); - ConfigMap leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); - ConfigMap rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); - ConfigMap rightWithLabelConfigMap = Serialization.unmarshal(rightWithLabelConfigMapStream, ConfigMap.class); - ConfigMap configMap = Serialization.unmarshal(configMapAsStream, ConfigMap.class); - Secret secret = Serialization.unmarshal(secretAsStream, Secret.class); - - if (phase.equals(Phase.CREATE)) { - util.createAndWait("left", leftConfigMap, null); - util.createAndWait("right", rightConfigMap, null); - util.createAndWait("right", rightWithLabelConfigMap, null); - util.createAndWait(NAMESPACE, configMap, secret); - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); - } - else { - util.deleteAndWait("left", leftConfigMap, null); - util.deleteAndWait("right", rightConfigMap, null); - util.deleteAndWait("right", rightWithLabelConfigMap, null); - util.deleteAndWait(NAMESPACE, configMap, secret); - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } - - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadInformIT.java similarity index 93% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadInformIT.java index 7821938760..ceeff4968e 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadInformIT.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +package org.springframework.cloud.kubernetes.fabric8.client.reload; import java.io.InputStream; import java.time.Duration; @@ -31,17 +31,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.cloud.kubernetes.fabric8.client.reload.LeftProperties; -import org.springframework.cloud.kubernetes.fabric8.client.reload.RightProperties; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.configMap; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.configMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.replaceConfigMap; /** * @author wind57 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadInformWithLabelIT.java similarity index 94% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadInformWithLabelIT.java index b1c263cbdc..044742bbe5 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadInformWithLabelIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadInformWithLabelIT.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +package org.springframework.cloud.kubernetes.fabric8.client.reload; import java.io.InputStream; import java.time.Duration; @@ -31,17 +31,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.system.CapturedOutput; -import org.springframework.cloud.kubernetes.fabric8.client.reload.RightProperties; -import org.springframework.cloud.kubernetes.fabric8.client.reload.RightWithLabelsProperties; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.configMap; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceConfigMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.configMap; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.replaceConfigMap; /** * @author wind57 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadSecretIT.java similarity index 96% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadSecretIT.java index 77f2fb0ab4..f9d869ac58 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/Fabric8EventReloadSecretIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/Fabric8EventReloadSecretIT.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +package org.springframework.cloud.kubernetes.fabric8.client.reload; import java.io.InputStream; import java.time.Duration; @@ -35,16 +35,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.cloud.kubernetes.commons.config.Constants; -import org.springframework.cloud.kubernetes.fabric8.client.reload.SecretProperties; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.assertReloadLogStatements; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.replaceSecret; -import static org.springframework.cloud.kubernetes.fabric8.client.reload.it.TestAssertions.secret; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.assertReloadLogStatements; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.replaceSecret; +import static org.springframework.cloud.kubernetes.fabric8.client.reload.TestAssertions.secret; /** * @author wind57 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestAssertions.java similarity index 71% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestAssertions.java index 20e2b4561a..fb4103eb17 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/it/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestAssertions.java @@ -14,14 +14,19 @@ * limitations under the License. */ -package org.springframework.cloud.kubernetes.fabric8.client.reload.it; +package org.springframework.cloud.kubernetes.fabric8.client.reload; +import java.io.InputStream; import java.time.Duration; import java.util.Objects; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.Secret; +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 io.fabric8.kubernetes.client.utils.Serialization; import reactor.netty.http.client.HttpClient; import reactor.util.retry.Retry; import reactor.util.retry.RetryBackoffSpec; @@ -87,6 +92,30 @@ static void secret(Phase phase, Util util, Secret secret, String namespace) { } } + static void manifests(Phase phase, Util util, String namespace) { + + InputStream deploymentStream = util.inputStream("manifests/deployment.yaml"); + InputStream serviceStream = util.inputStream("manifests/service.yaml"); + InputStream ingressStream = util.inputStream("manifests/ingress.yaml"); + InputStream configMapAsStream = util.inputStream("manifests/configmap.yaml"); + + Deployment deployment = Serialization.unmarshal(deploymentStream, Deployment.class); + + Service service = Serialization.unmarshal(serviceStream, Service.class); + Ingress ingress = Serialization.unmarshal(ingressStream, Ingress.class); + ConfigMap configMap = Serialization.unmarshal(configMapAsStream, ConfigMap.class); + + if (phase.equals(Phase.CREATE)) { + util.createAndWait(namespace, configMap, null); + util.createAndWait(namespace, null, deployment, service, ingress, true); + } + else { + util.deleteAndWait(namespace, configMap, null); + util.deleteAndWait(namespace, deployment, service, ingress); + } + + } + static WebClient.Builder builder() { return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java deleted file mode 100644 index 7a69a2163e..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/TestUtil.java +++ /dev/null @@ -1,254 +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.client.reload; - -import java.io.InputStream; -import java.util.Map; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.utils.Serialization; -import org.testcontainers.containers.Container; -import org.testcontainers.k3s.K3sContainer; - -import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util; - -/** - * @author wind57 - */ -final class TestUtil { - - private static final Map POD_LABELS = Map.of("app", - "spring-cloud-kubernetes-fabric8-client-reload"); - - private static final String BODY_FOUR = """ - { - "spec": { - "template": { - "spec": { - "containers": [{ - "name": "spring-cloud-kubernetes-fabric8-client-configmap-event-reload", - "image": "image_name_here", - "env": [ - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_CONFIG_RELOAD", - "value": "DEBUG" - }, - { - "name": "SPRING_PROFILES_ACTIVE", - "value": "one" - }, - { - "name": "SPRING_CLOUD_KUBERNETES_SECRETS_ENABLED", - "value": "FALSE" - }, - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD", - "value": "DEBUG" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "TRUE" - } - ] - }] - } - } - } - } - """; - - private static final String BODY_FIVE = """ - { - "spec": { - "template": { - "spec": { - "volumes": [ - { - "configMap": { - "defaultMode": 420, - "name": "poll-reload" - }, - "name": "config-map-volume" - } - ], - "containers": [{ - "volumeMounts": [ - { - "mountPath": "/tmp", - "name": "config-map-volume" - } - ], - "name": "spring-cloud-kubernetes-fabric8-client-configmap-event-reload", - "image": "image_name_here", - "env": [ - { - "name": "SPRING_PROFILES_ACTIVE", - "value": "mount" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "FALSE" - }, - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG_RELOAD", - "value": "DEBUG" - }, - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG", - "value": "DEBUG" - }, - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS", - "value": "DEBUG" - } - ] - }] - } - } - } - } - """; - - private static final String BODY_SIX = """ - { - "spec": { - "template": { - "spec": { - "volumes": [ - { - "configMap": { - "defaultMode": 420, - "name": "poll-reload" - }, - "name": "config-map-volume" - } - ], - "containers": [{ - "volumeMounts": [ - { - "mountPath": "/tmp", - "name": "config-map-volume" - } - ], - "name": "spring-cloud-kubernetes-fabric8-client-configmap-event-reload", - "image": "image_name_here", - "env": [ - { - "name": "SPRING_PROFILES_ACTIVE", - "value": "with-bootstrap" - }, - { - "name": "SPRING_CLOUD_BOOTSTRAP_ENABLED", - "value": "TRUE" - }, - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG_RELOAD", - "value": "DEBUG" - }, - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG", - "value": "DEBUG" - }, - { - "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS", - "value": "DEBUG" - } - ] - }] - } - } - } - } - """; - - private static final String BODY_SEVEN = """ - { - "spec": { - "template": { - "spec": { - "containers": [{ - "name": "spring-cloud-kubernetes-fabric8-client-configmap-event-reload", - "image": "image_name_here", - "env": [ - { - "name": "SPRING_PROFILES_ACTIVE", - "value": "with-secret" - } - ] - }] - } - } - } - } - """; - - private TestUtil() { - - } - - static void reCreateSources(Util util, KubernetesClient client) { - InputStream leftConfigMapStream = util.inputStream("left-configmap.yaml"); - InputStream rightConfigMapStream = util.inputStream("right-configmap.yaml"); - InputStream configMapStream = util.inputStream("configmap.yaml"); - - ConfigMap leftConfigMap = Serialization.unmarshal(leftConfigMapStream, ConfigMap.class); - ConfigMap rightConfigMap = Serialization.unmarshal(rightConfigMapStream, ConfigMap.class); - ConfigMap configMap = Serialization.unmarshal(configMapStream, ConfigMap.class); - - replaceConfigMap(client, leftConfigMap, "left"); - replaceConfigMap(client, rightConfigMap, "right"); - replaceConfigMap(client, configMap, "default"); - } - - static void replaceConfigMap(KubernetesClient client, ConfigMap configMap, String namespace) { - client.configMaps().inNamespace(namespace).resource(configMap).serverSideApply(); - } - - static void patchFour(Util util, String dockerImage, String deploymentName, String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_FOUR, POD_LABELS); - } - - static void patchFive(Util util, String dockerImage, String deploymentName, String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_FIVE, POD_LABELS); - } - - static void patchSix(Util util, String dockerImage, String deploymentName, String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_SIX, POD_LABELS); - } - - static void patchSeven(Util util, String dockerImage, String deploymentName, String namespace) { - util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_SEVEN, POD_LABELS); - } - - static String logs(K3sContainer container, String appLabelValue) { - try { - String appPodName = container - .execInContainer("sh", "-c", - "kubectl get pods -l app=" + appLabelValue + " -o=name --no-headers | tr -d '\n'") - .getStdout(); - - Container.ExecResult execResult = container.execInContainer("sh", "-c", - "kubectl logs " + appPodName.trim()); - return execResult.getStdout(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml index 3e7ee3750c..a430c69bf2 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/manifests/deployment.yaml @@ -30,7 +30,7 @@ spec: - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_CONFIG_RELOAD value: "DEBUG" - name: SPRING_PROFILES_ACTIVE - value: "mount" + value: "with-bootstrap" - name: SPRING_CLOUD_BOOTSTRAP_ENABLED value: "TRUE" - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG From 9b4dc97e69e98deb4f0ca74c988d2b08370aab23 Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 20 Dec 2024 19:26:51 +0200 Subject: [PATCH 32/35] fix --- .../reload/BootstrapEnabledPollingReloadConfigMapMountIT.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountIT.java index ad957804eb..144462bb8f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/BootstrapEnabledPollingReloadConfigMapMountIT.java @@ -69,8 +69,7 @@ static void beforeAll() throws Exception { } @AfterAll - static void afterAll() throws Exception { - Commons.cleanUp(IMAGE_NAME, K3S); + static void afterAll() { manifests(Phase.DELETE, util, NAMESPACE); } From ef10f74a5b619436d0fc2e0e91fd6c6642c9e3ba Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 20 Dec 2024 20:52:20 +0200 Subject: [PATCH 33/35] fix --- .../client/reload/ConfigMapMountPollingReloadDelegateIT.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java index e560c2b1e4..ac6361e2f3 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java @@ -69,8 +69,7 @@ static void beforeAll() throws Exception { } @AfterAll - static void afterAll() throws Exception { - Commons.cleanUp(IMAGE_NAME, K3S); + static void afterAll(){ manifests(Phase.DELETE, util, NAMESPACE); } From 9dff1340d24caee330d762a385519708767853aa Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 20 Dec 2024 20:57:09 +0200 Subject: [PATCH 34/35] fix --- .../client/reload/ConfigMapMountPollingReloadDelegateIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java index ac6361e2f3..c6a21e6500 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/reload/ConfigMapMountPollingReloadDelegateIT.java @@ -69,7 +69,7 @@ static void beforeAll() throws Exception { } @AfterAll - static void afterAll(){ + static void afterAll() { manifests(Phase.DELETE, util, NAMESPACE); } From a5564fb9717df310b2b20a3decd3df42c8233ee8 Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 20 Dec 2024 22:13:31 +0200 Subject: [PATCH 35/35] fix --- .../src/test/resources/META-INF/spring.factories | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories index 1d3fcdf8fd..cc4154d5ba 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-reload/src/test/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ -org.springframework.cloud.kubernetes.fabric8.client.reload.it.Fabric8EventReloadBase.TestConfig +org.springframework.cloud.kubernetes.fabric8.client.reload.Fabric8EventReloadBase.TestConfig