diff --git a/.github/workflows/composites/maven-build-with-dry-run-for-tests/action.yaml b/.github/workflows/composites/maven-build-with-dry-run-for-tests/action.yaml index 15b6b7f1..aec179be 100644 --- a/.github/workflows/composites/maven-build-with-dry-run-for-tests/action.yaml +++ b/.github/workflows/composites/maven-build-with-dry-run-for-tests/action.yaml @@ -13,6 +13,7 @@ runs: -T 1C > /tmp/tests.txt - name: show result + if: always() shell: bash run: cat /tmp/tests.txt 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 0d85418f..b3deeb1c 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 @@ -31,7 +31,10 @@ import io.kubernetes.client.openapi.models.V1Service; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.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; @@ -51,6 +54,7 @@ import org.springframework.web.reactive.function.client.WebClient; /** * @author wind57 */ +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) class KubernetesClientDiscoveryClientIT { private static final String NAMESPACE = "default"; @@ -61,6 +65,12 @@ class KubernetesClientDiscoveryClientIT { private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-discovery-it"; + private static final String DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-discovery-deployment-it"; + + private static final String NAMESPACE_A_UAT = "a-uat"; + + private static final String NAMESPACE_B_UAT = "b-uat"; + private static Util util; private static final K3sContainer K3S = Commons.container(); @@ -72,11 +82,13 @@ class KubernetesClientDiscoveryClientIT { Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); util = new Util(K3S); - Commons.systemPrune(); + util.setUp(NAMESPACE); + manifests(Phase.CREATE); } @AfterAll - static void after() throws Exception { + static void afterAll() throws Exception { + manifests(Phase.DELETE); Commons.cleanUp(IMAGE_NAME, K3S); } @@ -85,11 +97,9 @@ class KubernetesClientDiscoveryClientIT { * explicit namespace and 'default' must be picked-up. */ @Test + @Order(1) void testSimple() { - // set-up - util.setUp(NAMESPACE); - manifests(false, null, Phase.CREATE); util.busybox(NAMESPACE, Phase.CREATE); Assertions.assertTrue(logs().contains("serviceSharedInformer will use namespace : default")); @@ -148,7 +158,6 @@ class KubernetesClientDiscoveryClientIT { // clean-up util.busybox(NAMESPACE, Phase.DELETE); - manifests(false, null, Phase.DELETE); } /** @@ -161,13 +170,17 @@ class KubernetesClientDiscoveryClientIT { * */ @Test + @Order(2) void testAllNamespaces() { util.createNamespace(NAMESPACE_A); util.createNamespace(NAMESPACE_B); util.setUpClusterWideClusterRoleBinding(NAMESPACE); util.wiremock(NAMESPACE_A, "/wiremock", Phase.CREATE); util.busybox(NAMESPACE_B, Phase.CREATE); - manifests(true, null, 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")); @@ -193,12 +206,9 @@ class KubernetesClientDiscoveryClientIT { Assertions.assertEquals(resultForNonExistentService.size(), 0); - manifests(true, null, Phase.DELETE); - util.wiremock(NAMESPACE_A, "/wiremock", Phase.DELETE); + // do not remove wiremock in namespace a, it is required in the next test util.busybox(NAMESPACE_B, Phase.DELETE); util.deleteClusterWideClusterRoleBinding(NAMESPACE); - util.deleteNamespace(NAMESPACE_A); - util.deleteNamespace(NAMESPACE_B); } /** @@ -211,13 +221,14 @@ class KubernetesClientDiscoveryClientIT { * */ @Test + @Order(3) void testSpecificNamespace() { - util.createNamespace(NAMESPACE_A); - util.createNamespace(NAMESPACE_B); util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A)); - util.wiremock(NAMESPACE_A, "/wiremock", Phase.CREATE); util.wiremock(NAMESPACE_B, "/wiremock", Phase.CREATE); - manifests(false, NAMESPACE_A, Phase.CREATE); + + 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"); @@ -259,7 +270,6 @@ class KubernetesClientDiscoveryClientIT { Assertions.assertEquals(resultForNonExistentService.size(), 0); - manifests(false, NAMESPACE_A, Phase.DELETE); util.wiremock(NAMESPACE_A, "/wiremock", Phase.DELETE); util.wiremock(NAMESPACE_B, "/wiremock", Phase.DELETE); util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A)); @@ -267,41 +277,122 @@ class KubernetesClientDiscoveryClientIT { util.deleteNamespace(NAMESPACE_B); } - private static void manifests(boolean allNamespaces, String clientSpecificNamespace, Phase phase) { + @Test + @Order(4) + void testSimplePodMetadata() { + 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(); + } + + @Test + @Order(5) + 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); + + } + + /** + *
+	 *     - service "wiremock" is present in namespace "a-uat"
+	 *     - service "wiremock" is present in namespace "b-uat"
+	 *
+	 *     - we search with a predicate : "#root.metadata.namespace matches '^uat.*$'"
+	 *
+	 *     As such, both services are found via 'getInstances' call.
+	 * 
+ */ + @Test + @Order(6) + void filterMatchesBothNamespacesViaThePredicate() { + + // 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); + } + + @Test + @Order(7) + void testBlockingConfiguration() { + + // filter tests are done, clean-up a bit to prepare everything for health tests + deleteNamespacesAndWiremock(); + + 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); + } + + @Test + @Order(8) + 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); + } + + @Test + @Order(9) + 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); + } + + private void deleteNamespacesAndWiremock() { + util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.DELETE); + util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.DELETE); + util.deleteNamespace(NAMESPACE_A_UAT); + util.deleteNamespace(NAMESPACE_B_UAT); + } + + private static void manifests(Phase phase) { V1Deployment deployment = (V1Deployment) util.yaml("kubernetes-discovery-deployment.yaml"); V1Service service = (V1Service) util.yaml("kubernetes-discovery-service.yaml"); V1Ingress ingress = (V1Ingress) util.yaml("kubernetes-discovery-ingress.yaml"); - List envVars = new ArrayList<>( - Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) - .orElse(List.of())); - V1EnvVar debugLevel = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY") - .value("DEBUG"); - - V1EnvVar debugLevelForClient = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT") - .value("DEBUG"); - - if (allNamespaces) { - V1EnvVar allNamespacesVar = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_ALL_NAMESPACES") - .value("TRUE"); - envVars.add(allNamespacesVar); + if (phase.equals(Phase.DELETE)) { + util.deleteAndWait(NAMESPACE, deployment, service, ingress); + return; } - if (clientSpecificNamespace != null) { - V1EnvVar clientNamespace = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0") - .value(NAMESPACE_A); - envVars.add(clientNamespace); - } - envVars.add(debugLevel); - envVars.add(debugLevelForClient); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); - if (phase.equals(Phase.CREATE)) { + + List envVars = new ArrayList<>( + Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) + .orElse(List.of())); + V1EnvVar debugLevel = new V1EnvVar() + .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY").value("DEBUG"); + + V1EnvVar debugLevelForClient = new V1EnvVar() + .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT").value("DEBUG"); + + envVars.add(debugLevel); + envVars.add(debugLevelForClient); + deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); + util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); } - else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } + } private WebClient.Builder builder() { 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 new file mode 100644 index 00000000..55093d05 --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryClientUtils.java @@ -0,0 +1,442 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.discovery.it; + +import 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; + +/** + * @author wind57 + */ +final class KubernetesClientDiscoveryClientUtils { + + private static final LogAccessor LOG = new LogAccessor( + LogFactory.getLog(KubernetesClientDiscoveryClientUtils.class)); + + // patch the filter so that it matches both namespaces + private static final String BODY_ONE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [{ + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_FILTER", + "value": "#root.metadata.namespace matches '^.*uat$'" + }] + }] + } + } + } + } + """; + + private static final String BODY_TWO = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [ + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_DISCOVERY", + "value": "DEBUG" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH_REACTIVE", + "value": "DEBUG" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_REACTIVE", + "value": "DEBUG" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY", + "value": "DEBUG" + }, + { + "name": "SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED", + "value": "FALSE" + }, + { + "name": "SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED", + "value": "TRUE" + } + ] + }] + } + } + } + } + """; + + // this one patches on top of BODY_TWO, so it essentially enables both blocking and + // reactive implementations + // and adds proper packages in DEBUG mode, so that we could assert logs. + private static final String BODY_THREE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [{ + "name": "SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED", + "value": "TRUE" + }] + }] + } + } + } + } + """; + + // this one patches on top of BODY_TWO, so it essentially enables both blocking and + // reactive implementations + // and adds proper packages in DEBUG mode, so that we could assert logs. + private static final String BODY_FOUR = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "image": "image_name_here", + "env": [ + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_DISCOVERY", + "value": "DEBUG" + }, + { + "name": "SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED", + "value": "FALSE" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH", + "value": "DEBUG" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY", + "value": "DEBUG" + } + ] + }] + } + } + } + } + """; + + // patch to include all namespaces + private static final String BODY_FIVE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [{ + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_ALL_NAMESPACES", + "value": "TRUE" + }] + }] + } + } + } + } + """; + + // disable all namespaces and include a single namespace to be discoverable + private static final String BODY_SIX = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [ + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0", + "value": "a" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_ALL_NAMESPACES", + "value": "FALSE" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_SEVEN = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "image": "image_name_here", + "env": [ + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY", + "value": "DEBUG" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_ADDLABELS", + "value": "TRUE" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_LABELSPREFIX", + "value": "label-" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_ADDANNOTATIONS", + "value": "TRUE" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_ANNOTATIONSPREFIX", + "value": "annotation-" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_EIGHT = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [ + { + "name": "SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED", + "value": "TRUE" + }, + { + "name": "SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED", + "value": "FALSE" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_NINE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [ + { + "name": "SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED", + "value": "TRUE" + }, + { + "name": "SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED", + "value": "TRUE" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_TEN = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [ + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1", + "value": "b" + }, + { + "name": "SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED", + "value": "FALSE" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_ELEVEN = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "env": [ + { + "name": "SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED", + "value": "TRUE" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_TWELVE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-discovery", + "image": "image_name_here", + "env": [ + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0", + "value": "a-uat" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1", + "value": "b-uat" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_FILTER", + "value": "#root.metadata.namespace matches 'a-uat$'" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY", + "value": "DEBUG" + } + ] + }] + } + } + } + } + """; + + private KubernetesClientDiscoveryClientUtils() { + + } + + static void patchForTwoNamespacesMatchViaThePredicate(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_ONE); + } + + static void patchForReactiveHealth(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_TWO); + } + + static void patchForBlockingAndReactiveHealth(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_THREE); + } + + // notice the usage of 'PATCH_FORMAT_JSON_MERGE_PATCH' here, it will not merge + // env variables + static void patchForBlockingHealth(String image, String deploymentName, String namespace) { + patchWithReplace(image, deploymentName, namespace, BODY_FOUR); + } + + // add SPRING_CLOUD_KUBERNETES_DISCOVERY_ALL_NAMESPACES=TRUE + static void patchForAllNamespaces(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_FIVE); + } + + static void patchForSingleNamespace(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_SIX); + } + + static void patchForPodMetadata(String imageName, String deploymentName, String namespace) { + patchWithReplace(imageName, deploymentName, namespace, BODY_SEVEN); + } + + static void patchForReactiveOnly(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_EIGHT); + } + + static void patchForBlockingAndReactive(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_NINE); + } + + static void patchForTwoNamespacesBlockingOnly(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_TEN); + } + + static void patchToAddBlockingSupport(String deploymentName, String namespace) { + patchWithMerge(deploymentName, namespace, BODY_ELEVEN); + } + + static void patchForUATNamespacesTests(String image, String deploymentName, String namespace) { + 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/KubernetesClientDiscoveryFilterIT.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 similarity index 60% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryFilterIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryFilterITDelegate.java index 93c27a57..3fafb389 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryFilterIT.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 @@ -17,31 +17,18 @@ package org.springframework.cloud.kubernetes.client.discovery.it; import java.time.Duration; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; -import io.kubernetes.client.openapi.models.V1Deployment; -import io.kubernetes.client.openapi.models.V1EnvVar; -import io.kubernetes.client.openapi.models.V1Ingress; -import io.kubernetes.client.openapi.models.V1Service; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; import reactor.util.retry.Retry; import reactor.util.retry.RetryBackoffSpec; import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util; import org.springframework.core.ParameterizedTypeReference; @@ -52,11 +39,7 @@ import org.springframework.web.reactive.function.client.WebClient; /** * @author wind57 */ -class KubernetesClientDiscoveryFilterIT { - - private static final String FILTER_BOTH_NAMESPACES = "#root.metadata.namespace matches '^.*uat$'"; - - private static final String FILTER_SINGLE_NAMESPACE = "#root.metadata.namespace matches 'a-uat$'"; +class KubernetesClientDiscoveryFilterITDelegate { private static final String NAMESPACE_A_UAT = "a-uat"; @@ -64,42 +47,42 @@ class KubernetesClientDiscoveryFilterIT { private static final String NAMESPACE = "default"; - private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-discovery-it"; + private static final String DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-discovery-deployment-it"; - private static Util util; + void filterMatchesOneNamespaceViaThePredicate(Util util) { - private static final K3sContainer K3S = Commons.container(); - - @BeforeAll - static void beforeAll() throws Exception { - K3S.start(); - Commons.validateImage(IMAGE_NAME, K3S); - Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); - - util = new Util(K3S); - Commons.systemPrune(); - } - - @BeforeEach - void beforeEach() { + // set-up for this test and the next one util.createNamespace(NAMESPACE_A_UAT); util.createNamespace(NAMESPACE_B_UAT); util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A_UAT, NAMESPACE_B_UAT)); util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.CREATE); util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.CREATE); - } - @AfterEach - void afterEach() { - util.wiremock(NAMESPACE_A_UAT, "/wiremock", Phase.DELETE); - util.wiremock(NAMESPACE_B_UAT, "/wiremock", Phase.DELETE); - util.deleteNamespace(NAMESPACE_A_UAT); - util.deleteNamespace(NAMESPACE_B_UAT); - } + WebClient clientServices = builder().baseUrl("http://localhost/services").build(); + + @SuppressWarnings("unchecked") + List services = (List) clientServices.method(HttpMethod.GET).retrieve().bodyToMono(List.class) + .retryWhen(retrySpec()).block(); + + Assertions.assertEquals(services.size(), 1); + Assertions.assertTrue(services.contains("service-wiremock")); + + WebClient client = builder().baseUrl("http://localhost/service-instances/service-wiremock").build(); + List serviceInstances = client.method(HttpMethod.GET).retrieve() + .bodyToMono(new ParameterizedTypeReference>() { + + }).retryWhen(retrySpec()).block(); + + Assertions.assertEquals(serviceInstances.size(), 1); + + DefaultKubernetesServiceInstance first = serviceInstances.get(0); + Assertions.assertEquals(first.getServiceId(), "service-wiremock"); + Assertions.assertNotNull(first.getInstanceId()); + Assertions.assertEquals(first.getPort(), 8080); + Assertions.assertEquals(first.getNamespace(), "a-uat"); + Assertions.assertEquals(first.getMetadata(), + Map.of("app", "service-wiremock", "http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP")); - @AfterAll - static void after() throws Exception { - Commons.cleanUp(IMAGE_NAME, K3S); } /** @@ -112,10 +95,12 @@ class KubernetesClientDiscoveryFilterIT { * As such, both services are found via 'getInstances' call. * */ - @Test - void filterMatchesBothNamespacesViaThePredicate() { + void filterMatchesBothNamespacesViaThePredicate(Util util) { - manifests(Phase.CREATE, FILTER_BOTH_NAMESPACES); + // 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(); @@ -152,80 +137,6 @@ class KubernetesClientDiscoveryFilterIT { Assertions.assertEquals(second.getMetadata(), Map.of("app", "service-wiremock", "http", "8080", "k8s_namespace", "b-uat", "type", "ClusterIP")); - manifests(Phase.DELETE, FILTER_BOTH_NAMESPACES); - } - - /** - *
-	 *     - service "wiremock" is present in namespace "a-uat"
-	 *     - service "wiremock" is present in namespace "b-uat"
-	 *
-	 *     - we search with a predicate : "#root.metadata.namespace matches 'a-uat$'"
-	 *
-	 *     As such, only service from 'a-uat' namespace matches.
-	 * 
- */ - @Test - void filterMatchesOneNamespaceViaThePredicate() { - manifests(Phase.CREATE, FILTER_SINGLE_NAMESPACE); - - WebClient clientServices = builder().baseUrl("http://localhost/services").build(); - - @SuppressWarnings("unchecked") - List services = (List) clientServices.method(HttpMethod.GET).retrieve().bodyToMono(List.class) - .retryWhen(retrySpec()).block(); - - Assertions.assertEquals(services.size(), 1); - Assertions.assertTrue(services.contains("service-wiremock")); - - WebClient client = builder().baseUrl("http://localhost/service-instances/service-wiremock").build(); - List serviceInstances = client.method(HttpMethod.GET).retrieve() - .bodyToMono(new ParameterizedTypeReference>() { - - }).retryWhen(retrySpec()).block(); - - Assertions.assertEquals(serviceInstances.size(), 1); - - DefaultKubernetesServiceInstance first = serviceInstances.get(0); - Assertions.assertEquals(first.getServiceId(), "service-wiremock"); - Assertions.assertNotNull(first.getInstanceId()); - Assertions.assertEquals(first.getPort(), 8080); - Assertions.assertEquals(first.getNamespace(), "a-uat"); - Assertions.assertEquals(first.getMetadata(), - Map.of("app", "service-wiremock", "http", "8080", "k8s_namespace", "a-uat", "type", "ClusterIP")); - - manifests(Phase.DELETE, FILTER_SINGLE_NAMESPACE); - } - - private static void manifests(Phase phase, String serviceFilter) { - - V1Deployment deployment = (V1Deployment) util.yaml("kubernetes-discovery-deployment.yaml"); - V1Service service = (V1Service) util.yaml("kubernetes-discovery-service.yaml"); - V1Ingress ingress = (V1Ingress) util.yaml("kubernetes-discovery-ingress.yaml"); - - List envVars = new ArrayList<>( - Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) - .orElse(List.of())); - V1EnvVar namespaceAUat = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0") - .value(NAMESPACE_A_UAT); - V1EnvVar namespaceBUat = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1") - .value(NAMESPACE_B_UAT); - V1EnvVar filter = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_FILTER").value(serviceFilter); - V1EnvVar debug = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY") - .value("DEBUG"); - envVars.add(namespaceAUat); - envVars.add(namespaceBUat); - envVars.add(filter); - envVars.add(debug); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); - - if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); - } - else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } - } private WebClient.Builder builder() { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryHealthIT.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 similarity index 59% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryHealthIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryHealthITDelegate.java index 13adeafa..8c46e2bb 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryHealthIT.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 @@ -17,19 +17,12 @@ 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.concurrent.TimeUnit; -import io.kubernetes.client.openapi.models.V1Deployment; -import io.kubernetes.client.openapi.models.V1EnvVar; -import io.kubernetes.client.openapi.models.V1Ingress; -import io.kubernetes.client.openapi.models.V1Service; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; import org.testcontainers.containers.Container; import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; @@ -37,18 +30,18 @@ import reactor.util.retry.Retry; import reactor.util.retry.RetryBackoffSpec; import org.springframework.boot.test.json.BasicJsonTester; -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; -import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; +import static org.awaitility.Awaitility.await; + /** * @author wind57 */ -class KubernetesClientDiscoveryHealthIT { +class KubernetesClientDiscoveryHealthITDelegate { private static final String REACTIVE_STATUS = "$.components.reactiveDiscoveryClients.components.['Kubernetes Reactive Discovery Client'].status"; @@ -58,28 +51,10 @@ class KubernetesClientDiscoveryHealthIT { private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-discovery-it"; + private static final String DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-discovery-deployment-it"; + private static final BasicJsonTester BASIC_JSON_TESTER = new BasicJsonTester( - KubernetesClientDiscoveryHealthIT.class); - - private static Util util; - - private static final K3sContainer K3S = Commons.container(); - - @BeforeAll - static void beforeAll() throws Exception { - K3S.start(); - Commons.validateImage(IMAGE_NAME, K3S); - Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); - - util = new Util(K3S); - Commons.systemPrune(); - util.setUp(NAMESPACE); - } - - @AfterAll - static void after() throws Exception { - Commons.cleanUp(IMAGE_NAME, K3S); - } + KubernetesClientDiscoveryHealthITDelegate.class); /** * Reactive is disabled, only blocking is active. As such, @@ -89,15 +64,12 @@ class KubernetesClientDiscoveryHealthIT { * We assert for logs and call '/health' endpoint to see that blocking discovery * client was initialized. */ - @Test - void testBlockingConfiguration() { + void testBlockingConfiguration(K3sContainer container) { - manifests(true, false, Phase.CREATE); - - assertLogStatement("Will publish InstanceRegisteredEvent from blocking implementation"); - assertLogStatement("publishing InstanceRegisteredEvent"); - assertLogStatement("Discovery Client has been initialized"); - assertLogStatement( + assertLogStatement(container, "Will publish InstanceRegisteredEvent from blocking implementation"); + assertLogStatement(container, "publishing InstanceRegisteredEvent"); + assertLogStatement(container, "Discovery Client has been initialized"); + assertLogStatement(container, "received InstanceRegisteredEvent from pod with 'app' label value : spring-cloud-kubernetes-client-discovery-it"); WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build(); @@ -118,52 +90,6 @@ class KubernetesClientDiscoveryHealthIT { Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).doesNotHaveJsonPath(REACTIVE_STATUS); - manifests(true, false, Phase.DELETE); - } - - /** - * Both blocking and reactive are enabled. - */ - @Test - void testDefaultConfiguration() { - - manifests(false, false, Phase.CREATE); - - assertLogStatement("Will publish InstanceRegisteredEvent from blocking implementation"); - assertLogStatement("publishing InstanceRegisteredEvent"); - assertLogStatement("Discovery Client has been initialized"); - assertLogStatement( - "received InstanceRegisteredEvent from pod with 'app' label value : spring-cloud-kubernetes-client-discovery-it"); - - WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build(); - - String healthResult = healthClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class) - .retryWhen(retrySpec()).block(); - - Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathStringValue("$.components.discoveryComposite.status").isEqualTo("UP"); - - Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathStringValue("$.components.discoveryComposite.components.discoveryClient.status") - .isEqualTo("UP"); - - Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathArrayValue( - "$.components.discoveryComposite.components.discoveryClient.details.services") - .containsExactlyInAnyOrder("spring-cloud-kubernetes-client-discovery-it", "kubernetes"); - - Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)) - .extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status").isEqualTo("UP"); - - Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue( - "$.components.reactiveDiscoveryClients.components.['Kubernetes Reactive Discovery Client'].status") - .isEqualTo("UP"); - - Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathArrayValue( - "$.components.reactiveDiscoveryClients.components.['Kubernetes Reactive Discovery Client'].details.services") - .containsExactlyInAnyOrder("spring-cloud-kubernetes-client-discovery-it", "kubernetes"); - - manifests(false, false, Phase.DELETE); } /** @@ -174,15 +100,16 @@ class KubernetesClientDiscoveryHealthIT { * We assert for logs and call '/health' endpoint to see that blocking discovery * client was initialized. */ - @Test - void testReactiveConfiguration() { + void testReactiveConfiguration(Util util, K3sContainer container) { - manifests(false, true, Phase.CREATE); + KubernetesClientDiscoveryClientUtils.patchForReactiveHealth(DEPLOYMENT_NAME, NAMESPACE); + util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, + Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); - assertLogStatement("Will publish InstanceRegisteredEvent from reactive implementation"); - assertLogStatement("publishing InstanceRegisteredEvent"); - assertLogStatement("Discovery Client has been initialized"); - assertLogStatement( + assertLogStatement(container, "Will publish InstanceRegisteredEvent from reactive implementation"); + assertLogStatement(container, "publishing InstanceRegisteredEvent"); + assertLogStatement(container, "Discovery Client has been initialized"); + assertLogStatement(container, "received InstanceRegisteredEvent from pod with 'app' label value : spring-cloud-kubernetes-client-discovery-it"); WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build(); @@ -213,69 +140,51 @@ class KubernetesClientDiscoveryHealthIT { Assertions.assertThat(servicesResult).contains("spring-cloud-kubernetes-client-discovery-it"); Assertions.assertThat(servicesResult).contains("kubernetes"); - manifests(false, true, Phase.DELETE); } - private static void manifests(boolean disableReactive, boolean disableBlocking, Phase phase) { - V1Deployment deployment = (V1Deployment) util.yaml("kubernetes-discovery-deployment.yaml"); - V1Service service = (V1Service) util.yaml("kubernetes-discovery-service.yaml"); - V1Ingress ingress = (V1Ingress) util.yaml("kubernetes-discovery-ingress.yaml"); + /** + * Both blocking and reactive are enabled. + */ + void testDefaultConfiguration(Util util, K3sContainer container) { - List envVars = new ArrayList<>( - Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) - .orElse(List.of())); + KubernetesClientDiscoveryClientUtils.patchForBlockingAndReactiveHealth(DEPLOYMENT_NAME, NAMESPACE); + util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE, + Map.of("app", "spring-cloud-kubernetes-client-discovery-it")); - V1EnvVar debugLevelForCommons = new V1EnvVar() - .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_DISCOVERY").value("DEBUG"); + assertLogStatement(container, "Will publish InstanceRegisteredEvent from blocking implementation"); + assertLogStatement(container, "publishing InstanceRegisteredEvent"); + assertLogStatement(container, "Discovery Client has been initialized"); + assertLogStatement(container, + "received InstanceRegisteredEvent from pod with 'app' label value : spring-cloud-kubernetes-client-discovery-it"); - if (!disableBlocking) { - V1EnvVar debugBlockingEnvVar = new V1EnvVar() - .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH").value("DEBUG"); + WebClient healthClient = builder().baseUrl("http://localhost/actuator/health").build(); - V1EnvVar debugLevelForBlocking = new V1EnvVar() - .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY").value("DEBUG"); + String healthResult = healthClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class) + .retryWhen(retrySpec()).block(); - envVars.add(debugBlockingEnvVar); - envVars.add(debugLevelForBlocking); - } + Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue("$.components.discoveryComposite.status").isEqualTo("UP"); - if (!disableReactive) { - V1EnvVar debugReactiveEnvVar = new V1EnvVar() - .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_CLIENT_DISCOVERY_HEALTH_REACTIVE").value("DEBUG"); + Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue("$.components.discoveryComposite.components.discoveryClient.status") + .isEqualTo("UP"); - V1EnvVar debugLevelForReactive = new V1EnvVar() - .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_REACTIVE") - .value("DEBUG"); + Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathArrayValue( + "$.components.discoveryComposite.components.discoveryClient.details.services") + .containsExactlyInAnyOrder("spring-cloud-kubernetes-client-discovery-it", "kubernetes"); - V1EnvVar debugLevelForBlocking = new V1EnvVar() - .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY").value("DEBUG"); + Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)) + .extractingJsonPathStringValue("$.components.reactiveDiscoveryClients.status").isEqualTo("UP"); - envVars.add(debugReactiveEnvVar); - envVars.add(debugLevelForBlocking); - envVars.add(debugLevelForReactive); - } + Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathStringValue( + "$.components.reactiveDiscoveryClients.components.['Kubernetes Reactive Discovery Client'].status") + .isEqualTo("UP"); - if (disableBlocking) { - V1EnvVar disableBlockingEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED") - .value("FALSE"); - envVars.add(disableBlockingEnvVar); - } + Assertions.assertThat(BASIC_JSON_TESTER.from(healthResult)).extractingJsonPathArrayValue( + "$.components.reactiveDiscoveryClients.components.['Kubernetes Reactive Discovery Client'].details.services") + .containsExactlyInAnyOrder("spring-cloud-kubernetes-client-discovery-it", "kubernetes"); - if (disableReactive) { - V1EnvVar disableReactiveEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED") - .value("FALSE"); - envVars.add(disableReactiveEnvVar); - } - - envVars.add(debugLevelForCommons); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); - - if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); - } - else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } } private WebClient.Builder builder() { @@ -286,14 +195,18 @@ class KubernetesClientDiscoveryHealthIT { return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull); } - private void assertLogStatement(String message) { + private void assertLogStatement(K3sContainer container, String message) { try { - String appPodName = K3S.execInContainer("sh", "-c", + String appPodName = container.execInContainer("sh", "-c", "kubectl get pods -l app=" + IMAGE_NAME + " -o=name --no-headers | tr -d '\n'").getStdout(); - Container.ExecResult execResult = K3S.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim()); - String ok = execResult.getStdout(); - Assertions.assertThat(ok).contains(message); + await().pollDelay(Duration.ofSeconds(4)).pollInterval(Duration.ofSeconds(1)).atMost(20, TimeUnit.SECONDS) + .until(() -> { + Container.ExecResult execResult = container.execInContainer("sh", "-c", + "kubectl logs " + appPodName.trim()); + String ok = execResult.getStdout(); + return ok.contains(message); + }); } catch (Exception e) { e.printStackTrace(); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryMultipleSelectiveNamespacesIT.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 similarity index 67% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryMultipleSelectiveNamespacesIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate.java index 328383eb..5e63e348 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryMultipleSelectiveNamespacesIT.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 @@ -17,21 +17,11 @@ package org.springframework.cloud.kubernetes.client.discovery.it; import java.time.Duration; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import io.kubernetes.client.openapi.models.V1Deployment; -import io.kubernetes.client.openapi.models.V1EnvVar; -import io.kubernetes.client.openapi.models.V1Ingress; -import io.kubernetes.client.openapi.models.V1Service; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; import org.testcontainers.containers.Container; import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; @@ -39,9 +29,6 @@ import reactor.util.retry.Retry; import reactor.util.retry.RetryBackoffSpec; import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; -import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.client.reactive.ReactorClientHttpConnector; @@ -50,64 +37,22 @@ import org.springframework.web.reactive.function.client.WebClient; /** * @author wind57 */ -class KubernetesClientDiscoveryMultipleSelectiveNamespacesIT { +class KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate { private static final String BLOCKING_PUBLISH = "Will publish InstanceRegisteredEvent from blocking implementation"; private static final String REACTIVE_PUBLISH = "Will publish InstanceRegisteredEvent from reactive implementation"; - private static final String NAMESPACE = "default"; - - private static final String NAMESPACE_A = "a"; - - private static final String NAMESPACE_B = "b"; - private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-discovery-it"; - private static Util util; - - private static final K3sContainer K3S = Commons.container(); - - @BeforeAll - static void beforeAll() throws Exception { - K3S.start(); - Commons.validateImage(IMAGE_NAME, K3S); - Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); - - util = new Util(K3S); - Commons.systemPrune(); - - util.createNamespace(NAMESPACE_A); - util.createNamespace(NAMESPACE_B); - util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B)); - util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE); - util.wiremock(NAMESPACE_A, "/wiremock", Phase.CREATE); - util.wiremock(NAMESPACE_B, "/wiremock", Phase.CREATE); - } - - @AfterAll - static void afterAll() throws Exception { - Commons.cleanUp(IMAGE_NAME, K3S); - - util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE); - util.wiremock(NAMESPACE_A, "/wiremock", Phase.DELETE); - util.wiremock(NAMESPACE_B, "/wiremock", Phase.DELETE); - util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B)); - util.deleteNamespace(NAMESPACE_A); - util.deleteNamespace(NAMESPACE_B); - } - /** * Deploy wiremock in 3 namespaces: default, a, b. Search in selective namespaces 'a' * and 'b' with blocking enabled and reactive disabled, as such find services and it's * instances. */ - @Test - void testTwoNamespacesBlockingOnly() { + void testTwoNamespacesBlockingOnly(K3sContainer container) { - manifests(Phase.CREATE, false, true); - - String logs = logs(); + String logs = logs(container); Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]")); Assertions.assertTrue( logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]")); @@ -124,8 +69,6 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesIT { blockingCheck(); - manifests(Phase.DELETE, false, true); - } /** @@ -133,12 +76,9 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesIT { * and 'b' with blocking disabled and reactive enabled, as such find services and it's * instances. */ - @Test - void testTwoNamespaceReactiveOnly() { + void testTwoNamespaceReactiveOnly(K3sContainer container) { - manifests(Phase.CREATE, true, false); - - String logs = logs(); + String logs = logs(container); Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]")); Assertions.assertTrue( logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]")); @@ -155,8 +95,6 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesIT { reactiveCheck(); - manifests(Phase.DELETE, true, false); - } /** @@ -164,12 +102,9 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesIT { * and 'b' with blocking enabled and reactive enabled, as such find services and its * service instances. */ - @Test - void testTwoNamespacesBothBlockingAndReactive() { + void testTwoNamespacesBothBlockingAndReactive(K3sContainer container) { - manifests(Phase.CREATE, false, false); - - String logs = logs(); + String logs = logs(container); Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]")); Assertions.assertTrue( logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]")); @@ -187,56 +122,15 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesIT { blockingCheck(); reactiveCheck(); - manifests(Phase.DELETE, false, false); - } - private static void manifests(Phase phase, boolean disableBlocking, boolean disableReactive) { - V1Deployment deployment = (V1Deployment) util.yaml("kubernetes-discovery-deployment.yaml"); - V1Service service = (V1Service) util.yaml("kubernetes-discovery-service.yaml"); - V1Ingress ingress = (V1Ingress) util.yaml("kubernetes-discovery-ingress.yaml"); - - List envVars = new ArrayList<>( - Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) - .orElse(List.of())); - V1EnvVar debugLevel = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY") - .value("DEBUG"); - V1EnvVar selectiveNamespaceA = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0") - .value(NAMESPACE_A); - V1EnvVar selectiveNamespaceB = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1") - .value(NAMESPACE_B); - - if (disableReactive) { - V1EnvVar disableReactiveEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED") - .value("FALSE"); - envVars.add(disableReactiveEnvVar); - } - - if (disableBlocking) { - V1EnvVar disableBlockingEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED") - .value("FALSE"); - envVars.add(disableBlockingEnvVar); - } - - envVars.add(debugLevel); - envVars.add(selectiveNamespaceA); - envVars.add(selectiveNamespaceB); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); - - if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); - } - else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } - } - - private String logs() { + private String logs(K3sContainer container) { try { - String appPodName = K3S.execInContainer("sh", "-c", + String appPodName = container.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()); + Container.ExecResult execResult = container.execInContainer("sh", "-c", + "kubectl logs " + appPodName.trim()); return execResult.getStdout(); } catch (Exception 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/KubernetesClientDiscoveryPodMetadataIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryPodMetadataITDelegate.java similarity index 51% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryPodMetadataIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryPodMetadataITDelegate.java index bdfaccaa..5bfebc47 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryPodMetadataIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoveryPodMetadataITDelegate.java @@ -17,31 +17,16 @@ 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 io.kubernetes.client.openapi.models.V1Deployment; -import io.kubernetes.client.openapi.models.V1EnvVar; -import io.kubernetes.client.openapi.models.V1Ingress; -import io.kubernetes.client.openapi.models.V1Service; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; import reactor.util.retry.Retry; import reactor.util.retry.RetryBackoffSpec; import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; -import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; -import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.client.reactive.ReactorClientHttpConnector; @@ -50,47 +35,12 @@ import org.springframework.web.reactive.function.client.WebClient; /** * @author wind57 */ -class KubernetesClientDiscoveryPodMetadataIT { - - private static final String NAMESPACE = "default"; - - private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-discovery-it"; - - private static Util util; - - private static final K3sContainer K3S = Commons.container(); - - @BeforeAll - static void beforeAll() throws Exception { - K3S.start(); - Commons.validateImage(IMAGE_NAME, K3S); - Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); - - util = new Util(K3S); - Commons.systemPrune(); - } - - @AfterAll - static void after() throws Exception { - Commons.cleanUp(IMAGE_NAME, K3S); - } - - @BeforeEach - void beforeEach() { - util.setUp(NAMESPACE); - manifests(Phase.CREATE); - } - - @AfterEach - void afterEach() { - manifests(Phase.DELETE); - } +class KubernetesClientDiscoveryPodMetadataITDelegate { /** * Three services are deployed in the default namespace. We do not configure any * explicit namespace and 'default' must be picked-up. */ - @Test void testSimple() { WebClient servicesClient = builder().baseUrl("http://localhost/services").build(); @@ -126,40 +76,6 @@ class KubernetesClientDiscoveryPodMetadataIT { } - private static void manifests(Phase phase) { - V1Deployment deployment = (V1Deployment) util.yaml("kubernetes-discovery-deployment.yaml"); - V1Service service = (V1Service) util.yaml("kubernetes-discovery-service.yaml"); - V1Ingress ingress = (V1Ingress) util.yaml("kubernetes-discovery-ingress.yaml"); - - List envVars = new ArrayList<>( - Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) - .orElse(List.of())); - V1EnvVar debugLevel = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY") - .value("DEBUG"); - V1EnvVar includeLabelMetadata = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_ADDLABELS") - .value("TRUE"); - V1EnvVar includeLabelMetadataPrefix = new V1EnvVar() - .name("SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_LABELSPREFIX").value("label-"); - V1EnvVar includeAnnotationsMetadata = new V1EnvVar() - .name("SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_ADDANNOTATIONS").value("TRUE"); - V1EnvVar includeAnnotationsMetadataPrefix = new V1EnvVar() - .name("SPRING_CLOUD_KUBERNETES_DISCOVERY_METADATA_ANNOTATIONSPREFIX").value("annotation-"); - - envVars.add(debugLevel); - envVars.add(includeLabelMetadata); - envVars.add(includeLabelMetadataPrefix); - envVars.add(includeAnnotationsMetadata); - envVars.add(includeAnnotationsMetadataPrefix); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); - - if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); - } - else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } - } - private WebClient.Builder builder() { return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoverySingleSelectiveNamespaceIT.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 similarity index 70% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoverySingleSelectiveNamespaceIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoverySelectiveNamespacesIT.java index 458b204f..f6e8f021 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/java/org/springframework/cloud/kubernetes/client/discovery/it/KubernetesClientDiscoverySingleSelectiveNamespaceIT.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,6 +19,7 @@ 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; @@ -30,7 +31,10 @@ import io.kubernetes.client.openapi.models.V1Service; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.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; @@ -49,7 +53,8 @@ import org.springframework.web.reactive.function.client.WebClient; /** * @author wind57 */ -class KubernetesClientDiscoverySingleSelectiveNamespaceIT { +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class KubernetesClientDiscoverySelectiveNamespacesIT { private static final String BLOCKING_PUBLISH = "Will publish InstanceRegisteredEvent from blocking implementation"; @@ -63,6 +68,8 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-discovery-it"; + private static final String DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-discovery-deployment-it"; + private static Util util; private static final K3sContainer K3S = Commons.container(); @@ -74,7 +81,6 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); util = new Util(K3S); - Commons.systemPrune(); util.createNamespace(NAMESPACE_A); util.createNamespace(NAMESPACE_B); @@ -82,10 +88,11 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE); util.wiremock(NAMESPACE_A, "/wiremock", Phase.CREATE); util.wiremock(NAMESPACE_B, "/wiremock", Phase.CREATE); + manifests(Phase.CREATE); } @AfterAll - static void after() throws Exception { + static void afterAll() throws Exception { Commons.cleanUp(IMAGE_NAME, K3S); util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE); @@ -94,6 +101,7 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B)); util.deleteNamespace(NAMESPACE_A); util.deleteNamespace(NAMESPACE_B); + manifests(Phase.DELETE); } /** @@ -102,10 +110,9 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { * its service instance. */ @Test + @Order(1) void testOneNamespaceBlockingOnly() { - manifests(Phase.CREATE, false, true); - String logs = logs(); Assertions.assertTrue(logs.contains("using selective namespaces : [a]")); Assertions.assertTrue( @@ -121,8 +128,6 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { blockingCheck(); - manifests(Phase.DELETE, false, true); - } /** @@ -131,9 +136,12 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { * its service instance. */ @Test + @Order(2) void testOneNamespaceReactiveOnly() { - manifests(Phase.CREATE, true, false); + 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]")); @@ -150,8 +158,6 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { reactiveCheck(); - manifests(Phase.DELETE, true, false); - } /** @@ -160,9 +166,12 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { * its service instance. */ @Test + @Order(3) void testOneNamespaceBothBlockingAndReactive() { - manifests(Phase.CREATE, false, false); + 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]")); @@ -180,44 +189,101 @@ class KubernetesClientDiscoverySingleSelectiveNamespaceIT { blockingCheck(); reactiveCheck(); - manifests(Phase.DELETE, false, false); - } - private static void manifests(Phase phase, boolean disableBlocking, boolean disableReactive) { + /** + * previous test already has:
+	 *     - SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0 = a
+	 *     - SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED = TRUE
+	 *     - SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED = TRUE
+	 *
+	 *     All we need to patch for is:
+	 *     -  add one more namespace to track, via SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1 = b
+	 *     - disable reactive, via SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED = FALSE
+	 *
+	 *    As such, two namespaces + blocking only, is achieved.
+	 * 
+ */ + @Test + @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); + } + + /** + * previous test already has:
+	 *     - SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0 = a
+	 *     - SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1 = b
+	 *     - SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED = FALSE
+	 *     - SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED = TRUE
+	 *
+	 *     We invert the reactive and blocking in this test via patching.
+	 *
+	 *    As such, two namespaces + reactive only, is achieved.
+	 * 
+ */ + @Test + @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); + } + + /** + * previous test already has:
+	 *     - SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0 = a
+	 *     - SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1 = b
+	 *     - SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED = TRUE
+	 *     - SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED = FALSE
+	 *
+	 *     We invert the blocking support.
+	 *
+	 *    As such, two namespaces + blocking and reactive, is achieved.
+	 * 
+ */ + @Test + @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); + } + + private static void manifests(Phase phase) { V1Deployment deployment = (V1Deployment) util.yaml("kubernetes-discovery-deployment.yaml"); V1Service service = (V1Service) util.yaml("kubernetes-discovery-service.yaml"); V1Ingress ingress = (V1Ingress) util.yaml("kubernetes-discovery-ingress.yaml"); - List envVars = new ArrayList<>( - Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) - .orElse(List.of())); - V1EnvVar debugLevel = new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY") - .value("DEBUG"); - V1EnvVar selectiveNamespaceA = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0") - .value(NAMESPACE_A); - if (disableReactive) { + if (phase.equals(Phase.DELETE)) { + util.deleteAndWait(NAMESPACE, deployment, service, ingress); + return; + } + + if (phase.equals(Phase.CREATE)) { + List envVars = new ArrayList<>( + Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) + .orElse(List.of())); + V1EnvVar debugLevel = new V1EnvVar() + .name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY").value("DEBUG"); + V1EnvVar selectiveNamespaceA = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0") + .value(NAMESPACE_A); + V1EnvVar disableReactiveEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_REACTIVE_ENABLED") .value("FALSE"); envVars.add(disableReactiveEnvVar); - } - if (disableBlocking) { - V1EnvVar disableBlockingEnvVar = new V1EnvVar().name("SPRING_CLOUD_DISCOVERY_BLOCKING_ENABLED") - .value("FALSE"); - envVars.add(disableBlockingEnvVar); - } - - envVars.add(debugLevel); - envVars.add(selectiveNamespaceA); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); - - if (phase.equals(Phase.CREATE)) { + envVars.add(debugLevel); + envVars.add(selectiveNamespaceA); + deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); } - else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } } private void reactiveCheck() { diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/resources/kubernetes-discovery-deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/resources/kubernetes-discovery-deployment.yaml index 3fe5f0a2..bb8eaa28 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/resources/kubernetes-discovery-deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-discovery-it/src/test/resources/kubernetes-discovery-deployment.yaml @@ -20,15 +20,15 @@ spec: httpGet: port: 8080 path: /actuator/health/readiness - initialDelaySeconds: 10 + initialDelaySeconds: 15 periodSeconds: 2 - failureThreshold: 3 + failureThreshold: 5 livenessProbe: httpGet: port: 8080 path: /actuator/health/liveness - initialDelaySeconds: 10 + initialDelaySeconds: 15 periodSeconds: 2 - failureThreshold: 3 + failureThreshold: 5 ports: - containerPort: 8080 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 3fb9903b..dd737834 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 @@ -162,19 +162,6 @@ public final class Commons { CONTAINER.execInContainer("crictl", "rmi", image); } - /** - * equivalent of 'docker system prune', but for crictl. - */ - public static void systemPrune() { - try { - CONTAINER.execInContainer("sh", "-c", - "crictl ps -a | grep -v Running | awk '{print $1}' | xargs crictl rm && crictl rmi --prune"); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - /** * validates that the provided image does exist in the local docker registry. */ 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 dbb0ebe8..e46aa618 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 @@ -24,6 +24,7 @@ import java.net.HttpURLConnection; import java.time.Duration; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -135,10 +136,6 @@ public final class Util { if (e instanceof ApiException apiException) { System.out.println(apiException.getResponseBody()); } - e.printStackTrace(); - LOG.info("events : " + events()); - LOG.info("describe deployment : " + describeDeployment(deployment)); - LOG.info("node events : " + nodeEvents()); throw new RuntimeException(e); } } @@ -593,6 +590,46 @@ public final class Util { return availableReplicas != null && availableReplicas >= 1; } + public void waitForDeploymentAfterPatch(String deploymentName, String namespace, Map labels) { + try { + await().pollDelay(Duration.ofSeconds(4)).pollInterval(Duration.ofSeconds(3)).atMost(60, TimeUnit.SECONDS) + .until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace, labels)); + } + catch (Exception e) { + if (e instanceof ApiException apiException) { + LOG.error("Error: "); + LOG.error(apiException.getResponseBody()); + } + throw new RuntimeException(e); + } + + } + + private boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace, Map labels) + throws ApiException { + + V1DeploymentList deployments = appsV1Api.listNamespacedDeployment(namespace, null, null, null, + "metadata.name=" + deploymentName, null, null, null, null, null, null); + if (deployments.getItems().size() < 1) { + fail("No deployment with name " + deploymentName); + } + + 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 numberOfPods = coreV1Api.listNamespacedPod(namespace, null, null, null, null, labelSelector(labels), null, + null, null, null, null).getItems().size(); + + if (numberOfPods != replicas) { + LOG.info("number of pods not yet stabilized"); + return false; + } + + return replicas == Optional.ofNullable(deployment.getStatus().getAvailableReplicas()).orElse(0); + + } + private static void notExistsHandler(CheckedSupplier callee, CheckedSupplier defaulter) throws Exception { try { callee.get(); @@ -612,38 +649,6 @@ public final class Util { return labels.entrySet().stream().map(en -> en.getKey() + "=" + en.getValue()).collect(Collectors.joining(",")); } - private String events() { - try { - return container.execInContainer("sh", "-c", "kubectl get events").getStdout(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - private String nodeEvents() { - try { - String nodeName = container.execInContainer("sh", "-c", "kubectl get nodes --no-headers | awk '{print $1}'") - .getStdout(); - LOG.info("nodeName : " + nodeName); - return container.execInContainer("sh", "-c", "kubectl describe node " + nodeName).getStdout(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - private String describeDeployment(V1Deployment deployment) { - try { - return container - .execInContainer("sh", "-c", "kubectl describe deployment " + deployment.getMetadata().getName()) - .getStdout(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - private interface CheckedSupplier { T get() throws Exception; diff --git a/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-deployment.yaml b/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-deployment.yaml index 1799c71d..90e096cf 100644 --- a/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-deployment.yaml +++ b/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-deployment.yaml @@ -20,9 +20,15 @@ spec: httpGet: port: 8080 path: /__admin/mappings + initialDelaySeconds: 3 + periodSeconds: 2 + failureThreshold: 1 livenessProbe: httpGet: port: 8080 path: /__admin/mappings + initialDelaySeconds: 3 + periodSeconds: 2 + failureThreshold: 1 ports: - containerPort: 8080