From 9b2c890eb621be7f1d12e821b37fd0ad609abedf Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Sun, 6 Oct 2024 14:30:09 -0400 Subject: [PATCH 1/5] Check if testcontainers.properties exists before removing it --- scripts/integration-tests.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/integration-tests.sh b/scripts/integration-tests.sh index e3990fe6..7a517159 100755 --- a/scripts/integration-tests.sh +++ b/scripts/integration-tests.sh @@ -1,7 +1,9 @@ #!/usr/bin/env bash set -e -rm ~/.testcontainers.properties +if [ -f "~/.testcontainers.properties" ]; then + rm ~/.testcontainers.properties +fi echo 'testcontainers.reuse.enable=true' > ~/.testcontainers.properties ./mvnw clean install -B -Pdocs ${@} From 7b62798d9122cd865fee4df37aad5bbdd3158257 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:57:59 -0400 Subject: [PATCH 2/5] Bump @antora/collector-extension in /docs (#1761) --- updated-dependencies: - dependency-name: "@antora/collector-extension" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/package.json b/docs/package.json index f067320c..37c37659 100644 --- a/docs/package.json +++ b/docs/package.json @@ -2,7 +2,7 @@ "dependencies": { "antora": "3.2.0-alpha.4", "@antora/atlas-extension": "1.0.0-alpha.2", - "@antora/collector-extension": "1.0.0-alpha.3", + "@antora/collector-extension": "1.0.0-beta.3", "@asciidoctor/tabs": "1.0.0-beta.6", "@springio/antora-extensions": "1.11.1", "@springio/asciidoctor-extensions": "1.0.0-alpha.14" From a27bf65c55b32353c162298d42e2ec4a7638ebe3 Mon Sep 17 00:00:00 2001 From: erabii Date: Tue, 15 Oct 2024 17:21:50 +0300 Subject: [PATCH 3/5] Refactor fabric8 catalog watch integration tests (1) (#1755) --- .../pom.xml | 1 + .../pom.xml | 16 ++ .../watch/Fabric8CatalogWatchBase.java | 75 ++++++ .../catalog/watch/Fabric8CatalogWatchIT.java | 254 ------------------ .../watch/Fabric8CatalogWatchUtil.java | 156 ----------- ...ithEndpointSlicesAndNamespaceFilterIT.java | 101 +++++++ ...bric8CatalogWatchWithEndpointSlicesIT.java | 86 ++++++ ...atchWithEndpointsAndNamespaceFilterIT.java | 101 +++++++ .../Fabric8CatalogWatchWithEndpointsIT.java | 86 ++++++ ...ic8CatalogWatchWithNamespacesDelegate.java | 150 ----------- .../fabric8/catalog/watch/TestAssertions.java | 127 +++++++++ .../app/watcher-endpoints-deployment.yaml | 33 --- .../test/resources/app/watcher-ingress.yaml | 16 -- .../test/resources/app/watcher-service.yaml | 14 - .../integration/tests/commons/Images.java | 23 +- .../tests/commons/fabric8_client/Util.java | 2 + 16 files changed, 617 insertions(+), 624 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 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/TestAssertions.java 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/pom.xml b/spring-cloud-kubernetes-integration-tests/pom.xml index 261c2e1e..c17152c4 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/pom.xml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-catalog-watcher/pom.xml index 0c1e1e3e..33be033d 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 @@ -42,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 00000000..5dd33c44 --- /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 652699ea..00000000 --- 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,254 +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 { - assertLogStatement(); - 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); - } - - /** - * 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"); - 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 16b9f818..00000000 --- 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,156 +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 00000000..914cc4ab --- /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,101 @@ +/* + * 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.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.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); + + Images.loadBusybox(K3S); + + 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 00000000..e18b810c --- /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,86 @@ +/* + * 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.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.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() { + Images.loadBusybox(K3S); + 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 00000000..9eccc888 --- /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,101 @@ +/* + * 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.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.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); + + Images.loadBusybox(K3S); + + 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 00000000..c8ca4e22 --- /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,86 @@ +/* + * 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.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.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() { + Images.loadBusybox(K3S); + 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 69d4a063..00000000 --- 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/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 new file mode 100644 index 00000000..6d679a89 --- /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/TestAssertions.java @@ -0,0 +1,127 @@ +/* + * 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.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 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 static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +/** + * @author wind57 + */ +final class TestAssertions { + + private TestAssertions() { + + } + + 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 invokeAndAssert(Util util, Set namespaces, int port, String assertionNamespace) { + + 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]; + + assertThat(resultOne).isNotNull(); + assertThat(resultTwo).isNotNull(); + + assertThat(resultOne.endpointName()).contains("busybox"); + assertThat(resultTwo.endpointName()).contains("busybox"); + + assertThat(resultOne.namespace()).isEqualTo(assertionNamespace); + assertThat(resultTwo.namespace()).isEqualTo(assertionNamespace); + + 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) + .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 Objects.requireNonNull(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); + } + +} 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 fdce297c..00000000 --- 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 7b0343ed..00000000 --- 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 6b9374dc..00000000 --- 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 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 47021c56..f37a17ee 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 final class Images { } 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 final class Images { 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( 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 0b582812..cff24e87 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 @@ -406,10 +406,12 @@ public final class Util { 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 8d02d1a1b663bb69cb3a2cb4f9d9c917efa95309 Mon Sep 17 00:00:00 2001 From: erabii Date: Wed, 16 Oct 2024 02:28:19 +0300 Subject: [PATCH 4/5] simplify a little (#1764) --- ...WatchWithEndpointSlicesAndNamespaceFilterIT.java | 7 +++++-- .../Fabric8CatalogWatchWithEndpointSlicesIT.java | 13 +++++++++++-- ...talogWatchWithEndpointsAndNamespaceFilterIT.java | 7 +++++-- .../watch/Fabric8CatalogWatchWithEndpointsIT.java | 13 +++++++++++-- .../integration/tests/commons/Commons.java | 4 ++-- 5 files changed, 34 insertions(+), 10 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/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 914cc4ab..a1b5a79c 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 @@ -35,6 +35,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT.TestConfig; +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.TestAssertions.assertLogStatement; +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.TestAssertions.invokeAndAssert; /** * @author wind57 @@ -61,6 +63,7 @@ class Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT extends Fabric8C @AfterEach void afterEach() { + // busybox is deleted as part of the test itself, thus not seen here util.deleteNamespace(NAMESPACE_A); util.deleteNamespace(NAMESPACE_B); } @@ -77,8 +80,8 @@ class Fabric8CatalogWatchWithEndpointSlicesAndNamespaceFilterIT extends Fabric8C */ @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); + assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch"); + invokeAndAssert(util, Set.of(NAMESPACE_A, NAMESPACE_B), port, NAMESPACE_A); } @TestConfiguration 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 e18b810c..9ffdcc03 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 @@ -19,6 +19,7 @@ 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; @@ -34,6 +35,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchWithEndpointSlicesIT.TestConfig; +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.TestAssertions.assertLogStatement; +import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.TestAssertions.invokeAndAssert; /** * @author wind57 @@ -51,6 +54,12 @@ class Fabric8CatalogWatchWithEndpointSlicesIT extends Fabric8CatalogWatchBase { util.busybox(NAMESPACE, Phase.CREATE); } + @AfterEach + void afterEach() { + // empty on purpose + // busybox is deleted as part of the test itself, thus not seen here + } + /** *
 	 *     - we deploy a busybox service with 2 replica pods
@@ -62,8 +71,8 @@ class Fabric8CatalogWatchWithEndpointSlicesIT extends Fabric8CatalogWatchBase {
 	 */
 	@Test
 	void test(CapturedOutput output) {
-		TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch");
-		TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE), port, NAMESPACE);
+		assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch");
+		invokeAndAssert(util, Set.of(NAMESPACE), port, NAMESPACE);
 	}
 
 	@TestConfiguration
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 9eccc888..cd37be17 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
@@ -35,6 +35,8 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Primary;
 
 import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT.TestConfig;
+import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.TestAssertions.assertLogStatement;
+import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.TestAssertions.invokeAndAssert;
 
 /**
  * @author wind57
@@ -61,6 +63,7 @@ class Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT extends Fabric8Catalo
 
 	@AfterEach
 	void afterEach() {
+		// busybox is deleted as part of the assertions, thus not seen here
 		util.deleteNamespace(NAMESPACE_A);
 		util.deleteNamespace(NAMESPACE_B);
 	}
@@ -77,8 +80,8 @@ class Fabric8CatalogWatchWithEndpointsAndNamespaceFilterIT extends Fabric8Catalo
 	 */
 	@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);
+		assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointsCatalogWatch");
+		invokeAndAssert(util, Set.of(NAMESPACE_A, NAMESPACE_B), port, NAMESPACE_A);
 	}
 
 	@TestConfiguration
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 c8ca4e22..efed4f5a 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
@@ -19,6 +19,7 @@ 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;
 
@@ -34,6 +35,8 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Primary;
 
 import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchWithEndpointsIT.TestConfig;
+import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.TestAssertions.assertLogStatement;
+import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.TestAssertions.invokeAndAssert;
 
 /**
  * @author wind57
@@ -51,6 +54,12 @@ class Fabric8CatalogWatchWithEndpointsIT extends Fabric8CatalogWatchBase {
 		util.busybox(NAMESPACE, Phase.CREATE);
 	}
 
+	@AfterEach
+	void afterEach() {
+		// empty on purpose
+		// busybox is deleted as part of the test itself, thus not seen here
+	}
+
 	/**
 	 * 
 	 *     - we deploy a busybox service with 2 replica pods
@@ -62,8 +71,8 @@ class Fabric8CatalogWatchWithEndpointsIT extends Fabric8CatalogWatchBase {
 	 */
 	@Test
 	void test(CapturedOutput output) {
-		TestAssertions.assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointsCatalogWatch");
-		TestAssertions.invokeAndAssert(util, Set.of(NAMESPACE), port, NAMESPACE);
+		assertLogStatement(output, "stateGenerator is of type: Fabric8EndpointsCatalogWatch");
+		invokeAndAssert(util, Set.of(NAMESPACE), port, NAMESPACE);
 	}
 
 	@TestConfiguration
diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java
index 4fc2136f..a2037445 100644
--- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java
+++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java
@@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
 import java.time.Duration;
 import java.util.Arrays;
 import java.util.List;
@@ -159,8 +160,7 @@ public final class Commons {
 			InputStream imageStream = saveImageCmd.withTag(tag).exec();
 
 			Path imagePath = Paths.get(TEMP_FOLDER + "/" + tarName + ".tar");
-			Files.deleteIfExists(imagePath);
-			Files.copy(imageStream, imagePath);
+			Files.copy(imageStream, imagePath, StandardCopyOption.REPLACE_EXISTING);
 			// import image with ctr. this works because TEMP_FOLDER is mounted in the
 			// container
 			await().atMost(Duration.ofMinutes(2)).pollInterval(Duration.ofSeconds(1)).until(() -> {

From 7e824ba146dc3cf9f4c73427b9c8ba01887282b2 Mon Sep 17 00:00:00 2001
From: erabii 
Date: Wed, 16 Oct 2024 15:43:44 +0300
Subject: [PATCH 5/5] clean (#1765)

---
 .../integration/tests/commons/Commons.java    | 78 ++----------------
 .../integration/tests/commons/Constants.java  | 45 +++++++++++
 .../tests/commons/FixedPortsK3sContainer.java | 80 +++++++++++++++++++
 .../tests/commons/fabric8_client/Util.java    |  3 -
 4 files changed, 131 insertions(+), 75 deletions(-)
 create mode 100644 spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Constants.java
 create mode 100644 spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/FixedPortsK3sContainer.java

diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java
index a2037445..a36d64b6 100644
--- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java
+++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java
@@ -27,22 +27,18 @@ import java.nio.file.StandardCopyOption;
 import java.time.Duration;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
 import com.github.dockerjava.api.command.ListImagesCmd;
 import com.github.dockerjava.api.command.PullImageCmd;
 import com.github.dockerjava.api.command.SaveImageCmd;
-import com.github.dockerjava.api.model.Bind;
-import com.github.dockerjava.api.model.HostConfig;
 import com.github.dockerjava.api.model.Image;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.junit.jupiter.api.Assertions;
 import org.testcontainers.containers.Container;
 import org.testcontainers.k3s.K3sContainer;
-import org.testcontainers.utility.DockerImageName;
 
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.util.ReflectionUtils;
@@ -50,6 +46,10 @@ import org.springframework.util.StreamUtils;
 import org.springframework.util.StringUtils;
 
 import static org.awaitility.Awaitility.await;
+import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.KUBERNETES_VERSION_FILE;
+import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.TEMP_FOLDER;
+import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.TMP_IMAGES;
+import static org.springframework.cloud.kubernetes.integration.tests.commons.FixedPortsK3sContainer.CONTAINER;
 
 /**
  * A few commons things that can be re-used across clients. This is meant to be used for
@@ -61,44 +61,10 @@ public final class Commons {
 
 	private static final Log LOG = LogFactory.getLog(Commons.class);
 
-	/**
-	 * this path is generated by the pipeline of github actions.
-	 */
-	private static final String TMP_IMAGES = "/tmp/docker/images";
-
 	private Commons() {
 		throw new AssertionError("No instance provided");
 	}
 
-	private static final String KUBERNETES_VERSION_FILE = "META-INF/springcloudkubernetes-version.txt";
-
-	/**
-	 * Rancher version to use for test-containers.
-	 */
-	public static final String RANCHER = "rancher/k3s:v1.28.8-k3s1";
-
-	/**
-	 * Command to use when starting rancher. Without "server" option, traefik is not
-	 * installed
-	 */
-	public static final String RANCHER_COMMAND = "server";
-
-	/**
-	 * Test containers exposed ports.
-	 */
-	public static final int[] EXPOSED_PORTS = new int[] { 80, 6443, 8080, 8888, 9092 };
-
-	/**
-	 * Temporary folder where to load images.
-	 */
-	public static final String TEMP_FOLDER = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath();
-
-	private static final K3sContainer CONTAINER = new FixedPortsK3sContainer(DockerImageName.parse(Commons.RANCHER))
-		.configureFixedPorts()
-		.addBinds()
-		.withCommand(Commons.RANCHER_COMMAND)
-		.withReuse(true);
-
 	public static K3sContainer container() {
 		return CONTAINER;
 	}
@@ -180,7 +146,7 @@ public final class Commons {
 	 * either get the tar from '/tmp/docker/images', or pull the image.
 	 */
 	public static void load(K3sContainer container, String tarName, String imageNameForDownload, String imageVersion) {
-		File dockerImagesRootDir = Paths.get(Commons.TMP_IMAGES).toFile();
+		File dockerImagesRootDir = Paths.get(TMP_IMAGES).toFile();
 		if (dockerImagesRootDir.exists() && dockerImagesRootDir.isDirectory()) {
 			File[] tars = dockerImagesRootDir.listFiles();
 			if (tars != null && tars.length > 0) {
@@ -189,8 +155,7 @@ public final class Commons {
 					.filter(x -> x.contains(tarName))
 					.findFirst();
 				if (found.isPresent()) {
-					LOG.info("running in github actions, will load from : " + Commons.TMP_IMAGES + " tar : "
-							+ found.get());
+					LOG.info("running in github actions, will load from : " + TMP_IMAGES + " tar : " + found.get());
 					Commons.loadImageFromPath(found.get(), container);
 					return;
 				}
@@ -321,35 +286,4 @@ public final class Commons {
 
 	}
 
-	/**
-	 * A K3sContainer, but with fixed port mappings. This is needed because of the nature
-	 * of some integration tests.
-	 *
-	 * @author wind57
-	 */
-	private static final class FixedPortsK3sContainer extends K3sContainer {
-
-		private FixedPortsK3sContainer(DockerImageName dockerImageName) {
-			super(dockerImageName);
-		}
-
-		private FixedPortsK3sContainer configureFixedPorts() {
-			for (int port : Commons.EXPOSED_PORTS) {
-				super.addFixedExposedPort(port, port);
-			}
-			return this;
-		}
-
-		private FixedPortsK3sContainer addBinds() {
-			super.withCreateContainerCmdModifier(cmd -> {
-				HostConfig hostConfig = Objects.requireNonNull(cmd.getHostConfig());
-				hostConfig.withBinds(Bind.parse(TEMP_FOLDER + ":" + TEMP_FOLDER),
-						Bind.parse(TMP_IMAGES + ":" + TMP_IMAGES));
-			});
-
-			return this;
-		}
-
-	}
-
 }
diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Constants.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Constants.java
new file mode 100644
index 00000000..c15171b6
--- /dev/null
+++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Constants.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013-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.integration.tests.commons;
+
+import java.io.File;
+
+/**
+ * @author wind57
+ */
+final class Constants {
+
+	private Constants() {
+
+	}
+
+	/**
+	 * this path is generated by the pipeline of github actions.
+	 */
+	static final String TMP_IMAGES = "/tmp/docker/images";
+
+	/**
+	 * Temporary folder where to load images.
+	 */
+	static final String TEMP_FOLDER = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath();
+
+	/**
+	 * where is the version situated.
+	 */
+	static final String KUBERNETES_VERSION_FILE = "META-INF/springcloudkubernetes-version.txt";
+
+}
diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/FixedPortsK3sContainer.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/FixedPortsK3sContainer.java
new file mode 100644
index 00000000..05c275db
--- /dev/null
+++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/FixedPortsK3sContainer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013-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.integration.tests.commons;
+
+import java.util.Objects;
+
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.HostConfig;
+import org.testcontainers.k3s.K3sContainer;
+import org.testcontainers.utility.DockerImageName;
+
+import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.TEMP_FOLDER;
+import static org.springframework.cloud.kubernetes.integration.tests.commons.Constants.TMP_IMAGES;
+
+/**
+ * A K3sContainer, but with fixed port mappings. This is needed because of the nature of
+ * some integration tests.
+ *
+ * @author wind57
+ */
+final class FixedPortsK3sContainer extends K3sContainer {
+
+	/**
+	 * Test containers exposed ports.
+	 */
+	private static final int[] EXPOSED_PORTS = new int[] { 80, 6443, 8080, 8888, 9092 };
+
+	/**
+	 * Rancher version to use for test-containers.
+	 */
+	private static final String RANCHER_VERSION = "rancher/k3s:v1.28.8-k3s1";
+
+	/**
+	 * Command to use when starting rancher. Without "server" option, traefik is not
+	 * installed
+	 */
+	private static final String RANCHER_COMMAND = "server";
+
+	static final K3sContainer CONTAINER = new FixedPortsK3sContainer(DockerImageName.parse(RANCHER_VERSION))
+		.configureFixedPorts()
+		.addBinds()
+		.withCommand(RANCHER_COMMAND)
+		.withReuse(true);
+
+	FixedPortsK3sContainer(DockerImageName dockerImageName) {
+		super(dockerImageName);
+	}
+
+	FixedPortsK3sContainer configureFixedPorts() {
+		for (int port : EXPOSED_PORTS) {
+			super.addFixedExposedPort(port, port);
+		}
+		return this;
+	}
+
+	FixedPortsK3sContainer addBinds() {
+		super.withCreateContainerCmdModifier(cmd -> {
+			HostConfig hostConfig = Objects.requireNonNull(cmd.getHostConfig());
+			hostConfig.withBinds(Bind.parse(TEMP_FOLDER + ":" + TEMP_FOLDER),
+					Bind.parse(TMP_IMAGES + ":" + TMP_IMAGES));
+		});
+
+		return this;
+	}
+
+}
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 cff24e87..bc1bfa43 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
@@ -65,9 +65,6 @@ public final class Util {
 
 	private static final Log LOG = LogFactory.getLog(Util.class);
 
-	/** Image we get {@code istioctl} from in order to install Istio. */
-	public static final String ISTIO_ISTIOCTL = "istio/istioctl";
-
 	private final K3sContainer container;
 
 	private final KubernetesClient client;