diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchIT.java index 80984cc2..4918f807 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchIT.java @@ -19,6 +19,7 @@ package org.springframework.cloud.kubernetes.client.catalog; import java.time.Duration; import java.util.List; import java.util.Objects; +import java.util.Set; import io.kubernetes.client.openapi.models.V1Deployment; import io.kubernetes.client.openapi.models.V1Ingress; @@ -27,7 +28,10 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; import reactor.util.retry.Retry; @@ -44,18 +48,29 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.client.catalog.KubernetesClientCatalogWatchUtils.patchForEndpointSlices; +import static org.springframework.cloud.kubernetes.client.catalog.KubernetesClientCatalogWatchUtils.patchForEndpointSlicesNamespaces; +import static org.springframework.cloud.kubernetes.client.catalog.KubernetesClientCatalogWatchUtils.patchForEndpointsNamespaces; +import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.waitForLogStatement; /** * @author wind57 */ +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) class KubernetesClientCatalogWatchIT { private static final String APP_NAME = "spring-cloud-kubernetes-client-catalog-watcher"; private static final String NAMESPACE = "default"; + private static final String NAMESPACE_A = "namespacea"; + + private static final String NAMESPACE_B = "namespaceb"; + private static final K3sContainer K3S = Commons.container(); + private static final String DOCKER_IMAGE = "docker.io/springcloud/" + APP_NAME + ":" + Commons.pomVersion(); + private static Util util; @BeforeAll @@ -65,10 +80,15 @@ class KubernetesClientCatalogWatchIT { Commons.loadSpringCloudKubernetesImage(APP_NAME, K3S); util = new Util(K3S); util.setUp(NAMESPACE); + app(Phase.CREATE); } @AfterAll static void afterAll() { + util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE_A, NAMESPACE_B)); + util.deleteNamespace(NAMESPACE_A); + util.deleteNamespace(NAMESPACE_B); + app(Phase.DELETE); Commons.systemPrune(); } @@ -86,31 +106,35 @@ class KubernetesClientCatalogWatchIT { * */ @Test - void testCatalogWatchWithEndpoints() throws Exception { - app(false, Phase.CREATE); - assertLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch"); + @Order(1) + void testCatalogWatchWithEndpoints() { + waitForLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch", K3S, APP_NAME); test(); - app(false, Phase.DELETE); } @Test - void testCatalogWatchWithEndpointSlices() throws Exception { - app(true, Phase.CREATE); - assertLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch"); + @Order(2) + void testCatalogWatchWithEndpointSlices() { + patchForEndpointSlices(APP_NAME, NAMESPACE, DOCKER_IMAGE); + waitForLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch", K3S, APP_NAME); test(); - app(true, Phase.DELETE); + + testCatalogWatchWithEndpointsNamespaces(); } - /** - * 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(String log) throws Exception { - String appPodName = K3S.execInContainer("kubectl", "get", "pods", "-l", - "app=spring-cloud-kubernetes-client-catalog-watcher", "-o=name", "--no-headers").getStdout(); - String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout(); - Assertions.assertTrue(allLogs.contains(log)); + void testCatalogWatchWithEndpointsNamespaces() { + util.createNamespace(NAMESPACE_A); + util.createNamespace(NAMESPACE_B); + util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE_A, NAMESPACE_B)); + util.busybox(NAMESPACE_A, Phase.CREATE); + util.busybox(NAMESPACE_B, Phase.CREATE); + patchForEndpointsNamespaces(APP_NAME, NAMESPACE, DOCKER_IMAGE); + KubernetesClientCatalogWatchNamespacesDelegate.testCatalogWatchWithEndpointsNamespaces(); + + util.busybox(NAMESPACE_A, Phase.CREATE); + util.busybox(NAMESPACE_B, Phase.CREATE); + patchForEndpointSlicesNamespaces(APP_NAME, NAMESPACE, DOCKER_IMAGE); + KubernetesClientCatalogWatchNamespacesDelegate.testCatalogWatchWithEndpointSlicesNamespaces(); } /** @@ -131,7 +155,11 @@ class KubernetesClientCatalogWatchIT { // 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; @@ -184,10 +212,8 @@ class KubernetesClientCatalogWatchIT { } - private static void app(boolean useEndpointSlices, Phase phase) { - V1Deployment deployment = useEndpointSlices - ? (V1Deployment) util.yaml("app/watcher-endpoint-slices-deployment.yaml") - : (V1Deployment) util.yaml("app/watcher-endpoints-deployment.yaml"); + private static void app(Phase phase) { + V1Deployment deployment = (V1Deployment) util.yaml("app/watcher-deployment.yaml"); V1Service service = (V1Service) util.yaml("app/watcher-service.yaml"); V1Ingress ingress = (V1Ingress) util.yaml("app/watcher-ingress.yaml"); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchNamespacesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchNamespacesDelegate.java similarity index 58% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchNamespacesIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchNamespacesDelegate.java index 00c98ba6..30ba34b2 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchNamespacesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchNamespacesDelegate.java @@ -17,24 +17,12 @@ package org.springframework.cloud.kubernetes.client.catalog; import java.time.Duration; -import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Objects; -import java.util.Set; -import io.kubernetes.client.openapi.models.V1Deployment; -import io.kubernetes.client.openapi.models.V1EnvVar; -import io.kubernetes.client.openapi.models.V1EnvVarBuilder; -import io.kubernetes.client.openapi.models.V1Ingress; -import io.kubernetes.client.openapi.models.V1Service; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; import reactor.util.retry.Retry; @@ -51,8 +39,13 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.waitForLogStatement; -public class KubernetesClientCatalogWatchNamespacesIT { +final class KubernetesClientCatalogWatchNamespacesDelegate { + + private KubernetesClientCatalogWatchNamespacesDelegate() { + + } private static final String APP_NAME = "spring-cloud-kubernetes-client-catalog-watcher"; @@ -60,42 +53,10 @@ public class KubernetesClientCatalogWatchNamespacesIT { private static final String NAMESPACE_B = "namespaceb"; - private static final String NAMESPACE_DEFAULT = "default"; - private static final K3sContainer K3S = Commons.container(); private static Util util; - @BeforeAll - static void beforeAll() throws Exception { - K3S.start(); - Commons.validateImage(APP_NAME, K3S); - Commons.loadSpringCloudKubernetesImage(APP_NAME, K3S); - util = new Util(K3S); - util.setUp(NAMESPACE_DEFAULT); - } - - @AfterAll - static void afterAll() { - Commons.systemPrune(); - } - - @BeforeEach - void beforeEach() { - util.createNamespace(NAMESPACE_A); - util.createNamespace(NAMESPACE_B); - util.setUpClusterWide(NAMESPACE_DEFAULT, Set.of(NAMESPACE_A, NAMESPACE_B)); - util.busybox(NAMESPACE_A, Phase.CREATE); - util.busybox(NAMESPACE_B, Phase.CREATE); - } - - @AfterEach - void afterEach() { - util.deleteClusterWide(NAMESPACE_DEFAULT, Set.of(NAMESPACE_A, NAMESPACE_B)); - util.deleteNamespace(NAMESPACE_A); - util.deleteNamespace(NAMESPACE_B); - } - /** *
 	 *     - we deploy one busybox service with 2 replica pods in namespace namespacea
@@ -106,39 +67,21 @@ public class KubernetesClientCatalogWatchNamespacesIT {
 	 *     - assert that we receive only spring-cloud-kubernetes-client-catalog-watcher pod
 	 * 
*/ - @Test - void testCatalogWatchWithEndpoints() throws Exception { - app(false, Phase.CREATE); - assertLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch"); - test(); - app(false, Phase.DELETE); + static void testCatalogWatchWithEndpointsNamespaces() { + waitForLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch", K3S, APP_NAME); + testForNamespacesFilter(); } - @Test - void testCatalogWatchWithEndpointSlices() throws Exception { - app(true, Phase.CREATE); - assertLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch"); - test(); - app(true, Phase.DELETE); - } - - /** - * 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(String log) throws Exception { - String appPodName = K3S.execInContainer("kubectl", "get", "pods", "-l", - "app=spring-cloud-kubernetes-client-catalog-watcher", "-o=name", "--no-headers").getStdout(); - String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout(); - Assertions.assertTrue(allLogs.contains(log)); + static void testCatalogWatchWithEndpointSlicesNamespaces() { + waitForLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch", K3S, APP_NAME); + testForNamespacesFilter(); } /** * the test is the same for both endpoints and endpoint slices, the set-up for them is * different. */ - private void test() { + private static void testForNamespacesFilter() { WebClient client = builder().baseUrl("http://localhost/result").build(); EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[4]; @@ -180,6 +123,7 @@ public class KubernetesClientCatalogWatchNamespacesIT { Assertions.assertEquals(NAMESPACE_B, sorted.get(2).namespace()); Assertions.assertEquals(NAMESPACE_B, sorted.get(3).namespace()); + util = new Util(K3S); util.busybox(NAMESPACE_A, Phase.DELETE); util.busybox(NAMESPACE_B, Phase.DELETE); @@ -195,37 +139,11 @@ public class KubernetesClientCatalogWatchNamespacesIT { } - private void app(boolean useEndpointSlices, Phase phase) { - V1Deployment deployment = useEndpointSlices - ? (V1Deployment) util.yaml("app/watcher-endpoint-slices-deployment.yaml") - : (V1Deployment) util.yaml("app/watcher-endpoints-deployment.yaml"); - V1Service service = (V1Service) util.yaml("app/watcher-service.yaml"); - V1Ingress ingress = (V1Ingress) util.yaml("app/watcher-ingress.yaml"); - - if (phase.equals(Phase.CREATE)) { - V1EnvVar one = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0") - .withValue(NAMESPACE_A).build(); - - V1EnvVar two = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1") - .withValue(NAMESPACE_B).build(); - - List existing = new ArrayList<>( - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()); - existing.add(one); - existing.add(two); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(existing); - util.createAndWait(NAMESPACE_DEFAULT, null, deployment, service, ingress, true); - } - else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE_DEFAULT, deployment, service, ingress); - } - } - - private WebClient.Builder builder() { + private static WebClient.Builder builder() { return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); } - private RetryBackoffSpec retrySpec() { + 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-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchUtils.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchUtils.java new file mode 100644 index 00000000..d5a26374 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/java/org/springframework/cloud/kubernetes/client/catalog/KubernetesClientCatalogWatchUtils.java @@ -0,0 +1,133 @@ +/* + * 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.client.catalog; + +import static org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util.patchWithReplace; + +/** + * @author wind57 + */ +final class KubernetesClientCatalogWatchUtils { + + private KubernetesClientCatalogWatchUtils() { + + } + + private static final String BODY_ONE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-catalog-watcher", + "image": "image_name_here", + "env": [ + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES", + "value": "TRUE" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG", + "value": "DEBUG" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_TWO = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-catalog-watcher", + "image": "image_name_here", + "env": [ + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES", + "value": "FALSE" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG", + "value": "DEBUG" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0", + "value": "namespacea" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1", + "value": "namespaceb" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_THREE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-catalog-watcher", + "image": "image_name_here", + "env": [ + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES", + "value": "TRUE" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG", + "value": "DEBUG" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0", + "value": "namespacea" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1", + "value": "namespaceb" + } + ] + }] + } + } + } + } + """; + + static void patchForEndpointSlices(String deploymentName, String namespace, String imageName) { + patchWithReplace(imageName, deploymentName, namespace, BODY_ONE); + } + + static void patchForEndpointsNamespaces(String deploymentName, String namespace, String imageName) { + patchWithReplace(imageName, deploymentName, namespace, BODY_TWO); + } + + static void patchForEndpointSlicesNamespaces(String deploymentName, String namespace, String imageName) { + patchWithReplace(imageName, deploymentName, namespace, BODY_THREE); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/resources/app/watcher-endpoint-slices-deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/resources/app/watcher-deployment.yaml similarity index 94% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/resources/app/watcher-endpoint-slices-deployment.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/resources/app/watcher-deployment.yaml index 4d97f2c5..e0b9b534 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/resources/app/watcher-endpoint-slices-deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/resources/app/watcher-deployment.yaml @@ -28,6 +28,6 @@ spec: - containerPort: 8080 env: - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG - value: DEBUG + value: "DEBUG" - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES - value: true + value: "FALSE" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/resources/app/watcher-endpoints-deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-catalog-watcher/src/test/resources/app/watcher-endpoints-deployment.yaml deleted file mode 100644 index c7b8a770..00000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-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-client-catalog-watcher -spec: - selector: - matchLabels: - app: spring-cloud-kubernetes-client-catalog-watcher - template: - metadata: - labels: - app: spring-cloud-kubernetes-client-catalog-watcher - spec: - serviceAccountName: spring-cloud-kubernetes-serviceaccount - containers: - - name: spring-cloud-kubernetes-client-catalog-watcher - image: docker.io/springcloud/spring-cloud-kubernetes-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_CLIENT_DISCOVERY_CATALOG - value: DEBUG - - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES - value: false diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientIT.java index 89dbd32d..cfadb181 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientIT.java @@ -35,7 +35,6 @@ import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import org.testcontainers.containers.Container; import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; import reactor.util.retry.Retry; @@ -103,7 +102,7 @@ class KubernetesClientDiscoveryClientIT { util.busybox(NAMESPACE, Phase.CREATE); - Assertions.assertTrue(logs().contains("serviceSharedInformer will use namespace : default")); + Commons.waitForLogStatement("serviceSharedInformer will use namespace : default", K3S, IMAGE_NAME); WebClient servicesClient = builder().baseUrl("http://localhost/services").build(); @@ -180,10 +179,8 @@ class KubernetesClientDiscoveryClientIT { util.busybox(NAMESPACE_B, Phase.CREATE); KubernetesClientDiscoveryClientUtils.patchForAllNamespaces(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); - Assertions.assertTrue(logs().contains("serviceSharedInformer will use all-namespaces")); + Commons.waitForLogStatement("serviceSharedInformer will use all-namespaces", K3S, IMAGE_NAME); WebClient servicesClient = builder().baseUrl("http://localhost/services").build(); List servicesResult = servicesClient.method(HttpMethod.GET).retrieve() @@ -228,16 +225,14 @@ class KubernetesClientDiscoveryClientIT { util.wiremock(NAMESPACE_B, "/wiremock", Phase.CREATE); KubernetesClientDiscoveryClientUtils.patchForSingleNamespace(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); // first check that wiremock service is present in both namespaces a and b assertServicePresentInNamespaces(List.of("a", "b"), "service-wiremock", "service-wiremock"); - String logs = logs(); - Assertions.assertTrue(logs.contains("using selective namespaces : [a]")); - Assertions.assertTrue(logs.contains("reading pod in namespace : default")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a")); + + Commons.waitForLogStatement("using selective namespaces : [a]", K3S, IMAGE_NAME); + Commons.waitForLogStatement("reading pod in namespace : default", K3S, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : a", K3S, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", K3S, IMAGE_NAME); WebClient servicesClient = builder().baseUrl("http://localhost/services").build(); List servicesResult = servicesClient.method(HttpMethod.GET).retrieve() @@ -284,8 +279,6 @@ class KubernetesClientDiscoveryClientIT { util.setUp(NAMESPACE); String imageName = "docker.io/springcloud/spring-cloud-kubernetes-client-discovery-it:" + Commons.pomVersion(); KubernetesClientDiscoveryClientUtils.patchForPodMetadata(imageName, DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryPodMetadataITDelegate().testSimple(); } @@ -294,8 +287,6 @@ class KubernetesClientDiscoveryClientIT { void filterMatchesOneNamespaceViaThePredicate() { String imageName = "docker.io/springcloud/spring-cloud-kubernetes-client-discovery-it:" + Commons.pomVersion(); KubernetesClientDiscoveryClientUtils.patchForUATNamespacesTests(imageName, DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryFilterITDelegate().filterMatchesOneNamespaceViaThePredicate(util); } @@ -316,8 +307,6 @@ class KubernetesClientDiscoveryClientIT { // patch the deployment to change what namespaces are take into account KubernetesClientDiscoveryClientUtils.patchForTwoNamespacesMatchViaThePredicate(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryFilterITDelegate().filterMatchesBothNamespacesViaThePredicate(util); } @@ -331,8 +320,6 @@ class KubernetesClientDiscoveryClientIT { String imageName = "docker.io/springcloud/spring-cloud-kubernetes-client-discovery-it:" + Commons.pomVersion(); KubernetesClientDiscoveryClientUtils.patchForBlockingHealth(imageName, DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryHealthITDelegate().testBlockingConfiguration(K3S); } @@ -342,8 +329,6 @@ class KubernetesClientDiscoveryClientIT { void testReactiveConfiguration() { KubernetesClientDiscoveryClientUtils.patchForReactiveHealth(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryHealthITDelegate().testReactiveConfiguration(util, K3S); } @@ -353,8 +338,6 @@ class KubernetesClientDiscoveryClientIT { void testDefaultConfiguration() { KubernetesClientDiscoveryClientUtils.patchForBlockingAndReactiveHealth(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryHealthITDelegate().testDefaultConfiguration(util, K3S); } @@ -404,21 +387,6 @@ class KubernetesClientDiscoveryClientIT { return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull); } - private String logs() { - try { - String appPodName = K3S.execInContainer("sh", "-c", - "kubectl get pods -l app=" + IMAGE_NAME + " -o=name --no-headers | tr -d '\n'").getStdout(); - - Container.ExecResult execResult = K3S.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim()); - return execResult.getStdout(); - } - catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - - } - private void assertServicePresentInNamespaces(List namespaces, String value, String serviceName) { namespaces.forEach(x -> { try { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientUtils.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientUtils.java index 55093d05..0c92e839 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientUtils.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientUtils.java @@ -16,16 +16,13 @@ package org.springframework.cloud.kubernetes.client.discovery.it; -import io.kubernetes.client.custom.V1Patch; -import io.kubernetes.client.openapi.ApiException; -import io.kubernetes.client.openapi.apis.AppsV1Api; -import io.kubernetes.client.openapi.apis.CoreV1Api; -import io.kubernetes.client.openapi.models.V1Deployment; -import io.kubernetes.client.util.PatchUtils; import org.apache.commons.logging.LogFactory; import org.springframework.core.log.LogAccessor; +import static org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util.patchWithMerge; +import static org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util.patchWithReplace; + /** * @author wind57 */ @@ -411,32 +408,4 @@ final class KubernetesClientDiscoveryClientUtils { patchWithReplace(image, deploymentName, namespace, BODY_TWELVE); } - private static void patchWithMerge(String deploymentName, String namespace, String patchBody) { - try { - PatchUtils.patch(V1Deployment.class, - () -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, - new V1Patch(patchBody), null, null, null, null, null, null), - V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, new CoreV1Api().getApiClient()); - } - catch (ApiException e) { - LOG.error(() -> "error : " + e.getResponseBody()); - throw new RuntimeException(e); - } - } - - private static void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody) { - String body = patchBody.replace("image_name_here", imageName); - - try { - PatchUtils.patch(V1Deployment.class, - () -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, new V1Patch(body), - null, null, null, null, null, null), - V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, new CoreV1Api().getApiClient()); - } - catch (ApiException e) { - LOG.error(() -> "error : " + e.getResponseBody()); - throw new RuntimeException(e); - } - } - } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryFilterITDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryFilterITDelegate.java index 3fafb389..21e25911 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryFilterITDelegate.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryFilterITDelegate.java @@ -99,8 +99,6 @@ class KubernetesClientDiscoveryFilterITDelegate { // patch the deployment to change what namespaces are take into account KubernetesClientDiscoveryClientUtils.patchForTwoNamespacesMatchViaThePredicate(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); WebClient clientServices = builder().baseUrl("http://localhost/services").build(); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryHealthITDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryHealthITDelegate.java index 8c46e2bb..e06b5df7 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryHealthITDelegate.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryHealthITDelegate.java @@ -18,7 +18,6 @@ package org.springframework.cloud.kubernetes.client.discovery.it; import java.time.Duration; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -103,8 +102,6 @@ class KubernetesClientDiscoveryHealthITDelegate { void testReactiveConfiguration(Util util, K3sContainer container) { KubernetesClientDiscoveryClientUtils.patchForReactiveHealth(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); assertLogStatement(container, "Will publish InstanceRegisteredEvent from reactive implementation"); assertLogStatement(container, "publishing InstanceRegisteredEvent"); @@ -148,8 +145,6 @@ class KubernetesClientDiscoveryHealthITDelegate { void testDefaultConfiguration(Util util, K3sContainer container) { KubernetesClientDiscoveryClientUtils.patchForBlockingAndReactiveHealth(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); assertLogStatement(container, "Will publish InstanceRegisteredEvent from blocking implementation"); assertLogStatement(container, "publishing InstanceRegisteredEvent"); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate.java index 5e63e348..6d36632b 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate.java @@ -29,6 +29,7 @@ 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.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.client.reactive.ReactorClientHttpConnector; @@ -52,20 +53,21 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate { */ void testTwoNamespacesBlockingOnly(K3sContainer container) { - String logs = logs(container); - Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b")); + Commons.waitForLogStatement("using selective namespaces : [a, b]", container, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]", + container, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]", + container, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]", + container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : a", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : b", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : b", container, IMAGE_NAME); // this tiny checks makes sure that blocking is enabled and reactive is disabled. - Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH)); - Assertions.assertFalse(logs.contains(REACTIVE_PUBLISH)); + Commons.waitForLogStatement(BLOCKING_PUBLISH, container, IMAGE_NAME); + Assertions.assertFalse(logs(container).contains(REACTIVE_PUBLISH)); blockingCheck(); @@ -78,20 +80,19 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate { */ void testTwoNamespaceReactiveOnly(K3sContainer container) { - String logs = logs(container); - Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b")); + Commons.waitForLogStatement("using selective namespaces : [a, b]", container, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]", + container, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]", + container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : a", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : b", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : b", container, IMAGE_NAME); // this tiny checks makes sure that blocking is disabled and reactive is enabled. - Assertions.assertFalse(logs.contains(BLOCKING_PUBLISH)); - Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH)); + Commons.waitForLogStatement(REACTIVE_PUBLISH, container, IMAGE_NAME); + Assertions.assertFalse(logs(container).contains(BLOCKING_PUBLISH)); reactiveCheck(); @@ -104,20 +105,19 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate { */ void testTwoNamespacesBothBlockingAndReactive(K3sContainer container) { - String logs = logs(container); - Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b")); + Commons.waitForLogStatement("using selective namespaces : [a, b]", container, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]", + container, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]", + container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : a", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : b", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", container, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : b", container, IMAGE_NAME); // this tiny checks makes sure that blocking is enabled and reactive is enabled. - Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH)); - Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH)); + Commons.waitForLogStatement(BLOCKING_PUBLISH, container, IMAGE_NAME); + Assertions.assertTrue(logs(container).contains(REACTIVE_PUBLISH)); blockingCheck(); reactiveCheck(); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoverySelectiveNamespacesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoverySelectiveNamespacesIT.java index 1ce488ec..406b273b 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoverySelectiveNamespacesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoverySelectiveNamespacesIT.java @@ -19,7 +19,6 @@ package org.springframework.cloud.kubernetes.client.discovery.it; import java.time.Duration; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -114,18 +113,17 @@ class KubernetesClientDiscoverySelectiveNamespacesIT { @Order(1) void testOneNamespaceBlockingOnly() { - String logs = logs(); - Assertions.assertTrue(logs.contains("using selective namespaces : [a]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a")); + Commons.waitForLogStatement("using selective namespaces : [a]", K3S, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]", K3S, + IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]", K3S, + IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : a", K3S, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", K3S, IMAGE_NAME); // this tiny checks makes sure that blocking is enabled and reactive is disabled. - Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH)); - Assertions.assertFalse(logs.contains(REACTIVE_PUBLISH)); + Commons.waitForLogStatement(BLOCKING_PUBLISH, K3S, IMAGE_NAME); + Assertions.assertFalse(logs().contains(REACTIVE_PUBLISH)); blockingCheck(); @@ -141,21 +139,16 @@ class KubernetesClientDiscoverySelectiveNamespacesIT { void testOneNamespaceReactiveOnly() { KubernetesClientDiscoveryClientUtils.patchForReactiveOnly(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); - String logs = logs(); - Assertions.assertTrue(logs.contains("using selective namespaces : [a]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a")); + Commons.waitForLogStatement("using selective namespaces : [a]", K3S, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]", K3S, + IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : a", K3S, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", K3S, IMAGE_NAME); // this tiny checks makes sure that reactive is enabled and blocking is disabled. - Assertions.assertFalse(logs.contains(BLOCKING_PUBLISH)); - Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH)); + Commons.waitForLogStatement(REACTIVE_PUBLISH, K3S, IMAGE_NAME); + Assertions.assertFalse(logs().contains(BLOCKING_PUBLISH)); reactiveCheck(); @@ -171,21 +164,18 @@ class KubernetesClientDiscoverySelectiveNamespacesIT { void testOneNamespaceBothBlockingAndReactive() { KubernetesClientDiscoveryClientUtils.patchForBlockingAndReactive(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); - String logs = logs(); - Assertions.assertTrue(logs.contains("using selective namespaces : [a]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]")); - Assertions.assertTrue( - logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]")); - Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a")); - Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a")); + Commons.waitForLogStatement("using selective namespaces : [a]", K3S, IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]", K3S, + IMAGE_NAME); + Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]", K3S, + IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for services) in namespace : a", K3S, IMAGE_NAME); + Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", K3S, IMAGE_NAME); // this tiny checks makes sure that blocking and reactive is enabled. - Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH)); - Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH)); + Commons.waitForLogStatement(BLOCKING_PUBLISH, K3S, IMAGE_NAME); + Commons.waitForLogStatement(REACTIVE_PUBLISH, K3S, IMAGE_NAME); blockingCheck(); reactiveCheck(); @@ -209,8 +199,6 @@ class KubernetesClientDiscoverySelectiveNamespacesIT { @Order(4) void testTwoNamespacesBlockingOnly() { KubernetesClientDiscoveryClientUtils.patchForTwoNamespacesBlockingOnly(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate().testTwoNamespacesBlockingOnly(K3S); } @@ -230,8 +218,6 @@ class KubernetesClientDiscoverySelectiveNamespacesIT { @Order(5) void testTwoNamespacesReactiveOnly() { KubernetesClientDiscoveryClientUtils.patchForReactiveOnly(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate().testTwoNamespaceReactiveOnly(K3S); } @@ -251,8 +237,6 @@ class KubernetesClientDiscoverySelectiveNamespacesIT { @Order(6) void testTwoNamespacesBothBlockingAndReactive() { KubernetesClientDiscoveryClientUtils.patchToAddBlockingSupport(DEPLOYMENT_NAME, NAMESPACE); - util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, - Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); new KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate() .testTwoNamespacesBothBlockingAndReactive(K3S); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8DiscoveryClientHealthDelegate.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8DiscoveryClientHealthDelegate.java index 34912c2b..4e2a932d 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8DiscoveryClientHealthDelegate.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8DiscoveryClientHealthDelegate.java @@ -28,7 +28,7 @@ import org.springframework.web.reactive.function.client.WebClient; import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.builder; import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.retrySpec; -import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.waitForLogStatement; +import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.waitForLogStatement; /** * @author wind57 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8DiscoveryClientUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8DiscoveryClientUtil.java index c0a81948..15949ff3 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8DiscoveryClientUtil.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8DiscoveryClientUtil.java @@ -19,7 +19,6 @@ package org.springframework.cloud.kubernetes.fabric8.discovery; import java.time.Duration; import java.util.Objects; -import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; import reactor.util.retry.Retry; import reactor.util.retry.RetryBackoffSpec; @@ -27,8 +26,6 @@ import reactor.util.retry.RetryBackoffSpec; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; -import static org.awaitility.Awaitility.await; - /** * @author wind57 */ @@ -269,21 +266,4 @@ final class Fabric8DiscoveryClientUtil { return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull); } - static void waitForLogStatement(String message, K3sContainer k3sContainer, String imageName) { - try { - String appPodName = k3sContainer.execInContainer("sh", "-c", - "kubectl get pods -l app=" + imageName + " -o=name --no-headers | tr -d '\n'").getStdout(); - - await().atMost(Duration.ofMinutes(2)).pollInterval(Duration.ofSeconds(2)).until(() -> { - String execResult = k3sContainer.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim()) - .getStdout(); - return execResult.contains(message); - }); - } - catch (Exception e) { - throw new RuntimeException(e); - } - - } - } 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 222bb1d6..f22caa9b 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 @@ -229,6 +229,31 @@ public final class Commons { return null; } + /** + * the assumption is that there is only a single pod that is 'Running'. + */ + public static void waitForLogStatement(String message, K3sContainer k3sContainer, String imageName) { + try { + + await().atMost(Duration.ofMinutes(2)).pollInterval(Duration.ofSeconds(4)).until(() -> { + + String appPodName = k3sContainer.execInContainer("sh", "-c", + "kubectl get pods -l app=" + imageName + + " -o custom-columns=POD:metadata.name,STATUS:status.phase" + + " | grep -i 'running' | awk '{print $1}' | tr -d '\n' ") + .getStdout(); + + String execResult = k3sContainer.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim()) + .getStdout(); + return execResult.contains(message); + }); + } + catch (Exception e) { + throw new RuntimeException(e); + } + + } + /** * A K3sContainer, but with fixed port mappings. This is needed because of the nature * of some integration tests. diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java index 0cddfbbb..097b95eb 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.openapi.ApiClient; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.Configuration; @@ -51,6 +52,7 @@ import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.openapi.models.V1ServiceAccount; import io.kubernetes.client.util.Config; +import io.kubernetes.client.util.PatchUtils; import io.kubernetes.client.util.Yaml; import jakarta.annotation.Nullable; import org.apache.commons.logging.Log; @@ -436,6 +438,39 @@ public final class Util { } + public static void patchWithMerge(String deploymentName, String namespace, String patchBody) { + try { + PatchUtils.patch(V1Deployment.class, + () -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, + new V1Patch(patchBody), null, null, null, null, null, null), + V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, new CoreV1Api().getApiClient()); + } + catch (ApiException e) { + LOG.error("error : " + e.getResponseBody()); + throw new RuntimeException(e); + } + + waitForDeploymentAfterPatch(deploymentName, namespace); + } + + public static void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody) { + String body = patchBody.replace("image_name_here", imageName); + + try { + PatchUtils.patch(V1Deployment.class, + () -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, new V1Patch(body), + null, null, null, null, null, null), + V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, new CoreV1Api().getApiClient()); + } + catch (ApiException e) { + LOG.error("error : " + e.getResponseBody()); + throw new RuntimeException(e); + } + + waitForDeploymentAfterPatch(deploymentName, namespace); + + } + private String deploymentName(V1Deployment deployment) { return deployment.getMetadata().getName(); } @@ -590,10 +625,10 @@ public final class Util { return availableReplicas != null && availableReplicas >= 1; } - public void waitForDeploymentAfterPatch(String deploymentName, String namespace, Map labels) { + private static void waitForDeploymentAfterPatch(String deploymentName, String namespace) { try { await().pollDelay(Duration.ofSeconds(4)).pollInterval(Duration.ofSeconds(3)).atMost(60, TimeUnit.SECONDS) - .until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace, labels)); + .until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace)); } catch (Exception e) { if (e instanceof ApiException apiException) { @@ -605,10 +640,9 @@ public final class Util { } - private boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace, Map labels) - throws ApiException { + private static boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace) throws ApiException { - V1DeploymentList deployments = appsV1Api.listNamespacedDeployment(namespace, null, null, null, + V1DeploymentList deployments = new AppsV1Api().listNamespacedDeployment(namespace, null, null, null, "metadata.name=" + deploymentName, null, null, null, null, null, null); if (deployments.getItems().isEmpty()) { fail("No deployment with name " + deploymentName); @@ -617,17 +651,14 @@ public final class Util { V1Deployment deployment = deployments.getItems().get(0); // if no replicas are defined, it means only 1 is needed int replicas = Optional.ofNullable(deployment.getSpec().getReplicas()).orElse(1); + int readyReplicas = Optional.ofNullable(deployment.getStatus().getReadyReplicas()).orElse(0); - int numberOfPods = coreV1Api.listNamespacedPod(namespace, null, null, null, null, labelSelector(labels), null, - null, null, null, null).getItems().size(); - - if (numberOfPods != replicas) { + if (readyReplicas != replicas) { LOG.info("number of pods not yet stabilized"); return false; } - return replicas == Optional.ofNullable(deployment.getStatus().getAvailableReplicas()).orElse(0); - + return true; } private static void notExistsHandler(CheckedSupplier callee, CheckedSupplier defaulter) throws Exception {