From f957d0453bfef5cd9b319ea1a8748b889dd0b4b1 Mon Sep 17 00:00:00 2001 From: erabii Date: Tue, 12 Sep 2023 00:04:21 +0300 Subject: [PATCH] Simply k8s client configmap event reload it (7) (#1425) --- .../KubernetesClientCatalogWatchUtils.java | 11 +- .../config/it/ConfigMapAndSecretIT.java | 9 +- .../event/reload/ConfigMapEventReloadIT.java | 73 +++--- .../reload/ConfigMapEventReloadITUtil.java | 247 ++++++++++++++++++ ...DataChangesInConfigMapReloadDelegate.java} | 98 +------ .../test/resources/{one => }/deployment.yaml | 0 .../src/test/resources/left-configmap.yaml | 2 +- .../resources/right-configmap-with-label.yaml | 2 +- .../src/test/resources/right-configmap.yaml | 2 +- .../src/test/resources/three/deployment.yaml | 33 --- .../src/test/resources/two/deployment.yaml | 33 --- .../KubernetesClientDiscoveryClientUtils.java | 31 +-- .../tests/commons/native_client/Util.java | 26 +- 13 files changed, 344 insertions(+), 223 deletions(-) create mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadITUtil.java rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/{DataChangesInConfigMapReloadIT.java => DataChangesInConfigMapReloadDelegate.java} (64%) rename spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/{one => }/deployment.yaml (100%) delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/three/deployment.yaml delete mode 100644 spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/two/deployment.yaml 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 index d5a2637496..02fd587271 100644 --- 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 @@ -16,6 +16,8 @@ package org.springframework.cloud.kubernetes.client.catalog; +import java.util.Map; + import static org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util.patchWithReplace; /** @@ -23,6 +25,9 @@ */ final class KubernetesClientCatalogWatchUtils { + private static final Map POD_LABELS = Map.of("app", + "spring-cloud-kubernetes-client-catalog-watcher"); + private KubernetesClientCatalogWatchUtils() { } @@ -119,15 +124,15 @@ private KubernetesClientCatalogWatchUtils() { """; static void patchForEndpointSlices(String deploymentName, String namespace, String imageName) { - patchWithReplace(imageName, deploymentName, namespace, BODY_ONE); + patchWithReplace(imageName, deploymentName, namespace, BODY_ONE, POD_LABELS); } static void patchForEndpointsNamespaces(String deploymentName, String namespace, String imageName) { - patchWithReplace(imageName, deploymentName, namespace, BODY_TWO); + patchWithReplace(imageName, deploymentName, namespace, BODY_TWO, POD_LABELS); } static void patchForEndpointSlicesNamespaces(String deploymentName, String namespace, String imageName) { - patchWithReplace(imageName, deploymentName, namespace, BODY_THREE); + patchWithReplace(imageName, deploymentName, namespace, BODY_THREE, POD_LABELS); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java index 2e16ead09a..26ada12e66 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-config-it/src/test/java/org/springframework/cloud/kubernetes/client/config/it/ConfigMapAndSecretIT.java @@ -52,6 +52,8 @@ */ class ConfigMapAndSecretIT { + private static final Map POD_LABELS = Map.of("app", "spring-cloud-kubernetes-client-config-it"); + private static final String BODY = """ { "spec": { @@ -134,9 +136,8 @@ void testConfigMapAndSecretRefresh() throws Exception { WebClient propertyClient = builder.baseUrl(PROPERTY_URL).build(); await().timeout(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)) - .ignoreException(WebClientResponseException.BadGateway.class) - .until(() -> propertyClient - .method(HttpMethod.GET).retrieve().bodyToMono(String.class).block().equals("from-config-map")); + .ignoreException(WebClientResponseException.BadGateway.class).until(() -> propertyClient + .method(HttpMethod.GET).retrieve().bodyToMono(String.class).block().equals("from-config-map")); WebClient secretClient = builder.baseUrl(SECRET_URL).build(); String secret = secretClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()) @@ -206,7 +207,7 @@ private RetryBackoffSpec retrySpec() { private static void patchForPollingReload() { patchWithReplace(ConfigMapAndSecretIT.DOCKER_IMAGE, ConfigMapAndSecretIT.APP_NAME + "-deployment", - ConfigMapAndSecretIT.NAMESPACE, BODY); + ConfigMapAndSecretIT.NAMESPACE, BODY, POD_LABELS); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadIT.java index ffa58a0d78..c61cd8216c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadIT.java @@ -17,11 +17,8 @@ package org.springframework.cloud.kubernetes.client.configmap.event.reload; 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; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; @@ -31,7 +28,6 @@ import io.kubernetes.client.openapi.models.V1ConfigMap; import io.kubernetes.client.openapi.models.V1ConfigMapBuilder; 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.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1Service; @@ -52,6 +48,10 @@ import org.springframework.web.reactive.function.client.WebClient; import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.client.configmap.event.reload.ConfigMapEventReloadITUtil.patchOne; +import static org.springframework.cloud.kubernetes.client.configmap.event.reload.ConfigMapEventReloadITUtil.patchThree; +import static org.springframework.cloud.kubernetes.client.configmap.event.reload.ConfigMapEventReloadITUtil.patchTwo; +import static org.springframework.cloud.kubernetes.client.configmap.event.reload.DataChangesInConfigMapReloadDelegate.testSimple; /** * @author wind57 @@ -60,6 +60,8 @@ class ConfigMapEventReloadIT { private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-configmap-event-reload"; + private static final String DOCKER_IMAGE = "docker.io/springcloud/" + IMAGE_NAME + ":" + Commons.pomVersion(); + private static final String NAMESPACE = "default"; private static final K3sContainer K3S = Commons.container(); @@ -83,6 +85,7 @@ static void beforeAll() throws Exception { @AfterAll static void afterAll() throws Exception { util.deleteClusterWide(NAMESPACE, Set.of("left", "right")); + manifests(Phase.DELETE); util.deleteNamespace("left"); util.deleteNamespace("right"); Commons.cleanUp(IMAGE_NAME, K3S); @@ -99,7 +102,7 @@ static void afterAll() throws Exception { */ @Test void testInformFromOneNamespaceEventNotTriggered() throws Exception { - manifests("one", Phase.CREATE, false); + manifests(Phase.CREATE); Commons.assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", IMAGE_NAME); @@ -130,7 +133,11 @@ void testInformFromOneNamespaceEventNotTriggered() throws Exception { // left configmap has not changed, no restart of app has happened Assertions.assertEquals("left-initial", result); - manifests("one", Phase.DELETE, false); + testInformFromOneNamespaceEventTriggered(); + testInform(); + testInformFromOneNamespaceEventTriggeredSecretsDisabled(); + testSimple(DOCKER_IMAGE); + } /** @@ -141,9 +148,9 @@ void testInformFromOneNamespaceEventNotTriggered() throws Exception { * - as such, event is triggered and we see the updated value * */ - @Test void testInformFromOneNamespaceEventTriggered() throws Exception { - manifests("two", Phase.CREATE, false); + recreateConfigMaps(); + patchOne("spring-cloud-kubernetes-client-configmap-deployment-event-reload", NAMESPACE, DOCKER_IMAGE); Commons.assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", IMAGE_NAME); @@ -170,8 +177,6 @@ void testInformFromOneNamespaceEventTriggered() throws Exception { return innerResult != null; }); Assertions.assertEquals("right-after-change", resultAfterChange[0]); - - manifests("two", Phase.DELETE, false); } /** @@ -183,9 +188,12 @@ void testInformFromOneNamespaceEventTriggered() throws Exception { * right-configmap-with-label triggers changes. * */ - @Test void testInform() throws Exception { - manifests("three", Phase.CREATE, false); + recreateConfigMaps(); + V1ConfigMap rightWithLabelConfigMap = (V1ConfigMap) util.yaml("right-configmap-with-label.yaml"); + util.createAndWait("right", rightWithLabelConfigMap, null); + patchTwo("spring-cloud-kubernetes-client-configmap-deployment-event-reload", NAMESPACE, DOCKER_IMAGE); + Commons.assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", IMAGE_NAME); @@ -240,8 +248,7 @@ void testInform() throws Exception { rightResult = rightWebClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()) .block(); Assertions.assertEquals("right-after-change", rightResult); - - manifests("three", Phase.DELETE, false); + util.deleteAndWait("right", rightWithLabelConfigMap, null); } /** @@ -252,9 +259,9 @@ void testInform() throws Exception { * - as such, event is triggered and we see the updated value * */ - @Test void testInformFromOneNamespaceEventTriggeredSecretsDisabled() throws Exception { - manifests("two", Phase.CREATE, true); + recreateConfigMaps(); + patchThree("spring-cloud-kubernetes-client-configmap-deployment-event-reload", NAMESPACE, DOCKER_IMAGE); Commons.assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", IMAGE_NAME); @@ -281,49 +288,39 @@ void testInformFromOneNamespaceEventTriggeredSecretsDisabled() throws Exception return innerResult != null; }); Assertions.assertEquals("right-after-change", resultAfterChange[0]); + } + + private void recreateConfigMaps() { + V1ConfigMap leftConfigMap = (V1ConfigMap) util.yaml("left-configmap.yaml"); + V1ConfigMap rightConfigMap = (V1ConfigMap) util.yaml("right-configmap.yaml"); - manifests("two", Phase.DELETE, true); + util.deleteAndWait("left", leftConfigMap, null); + util.deleteAndWait("right", rightConfigMap, null); + + util.createAndWait("left", leftConfigMap, null); + util.createAndWait("right", rightConfigMap, null); } - private static void manifests(String deploymentRoot, Phase phase, boolean secretsDisabled) { + private static void manifests(Phase phase) { try { V1ConfigMap leftConfigMap = (V1ConfigMap) util.yaml("left-configmap.yaml"); V1ConfigMap rightConfigMap = (V1ConfigMap) util.yaml("right-configmap.yaml"); - V1ConfigMap rightWithLabelConfigMap = (V1ConfigMap) util.yaml("right-configmap-with-label.yaml"); - V1Deployment deployment = (V1Deployment) util.yaml(deploymentRoot + "/deployment.yaml"); + V1Deployment deployment = (V1Deployment) util.yaml("deployment.yaml"); V1Service service = (V1Service) util.yaml("service.yaml"); V1Ingress ingress = (V1Ingress) util.yaml("ingress.yaml"); - List envVars = new ArrayList<>( - Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) - .orElse(List.of())); - - if (secretsDisabled) { - V1EnvVar secretsDisabledEnvVar = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_SECRETS_ENABLED") - .value("FALSE"); - envVars.add(secretsDisabledEnvVar); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); - } - if (phase.equals(Phase.CREATE)) { util.createAndWait("left", leftConfigMap, null); util.createAndWait("right", rightConfigMap, null); - - if ("three".equals(deploymentRoot)) { - util.createAndWait("right", rightWithLabelConfigMap, null); - } util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); } if (phase.equals(Phase.DELETE)) { util.deleteAndWait("left", leftConfigMap, null); util.deleteAndWait("right", rightConfigMap, null); - if ("three".equals(deploymentRoot)) { - util.deleteAndWait("right", rightWithLabelConfigMap, null); - } util.deleteAndWait(NAMESPACE, deployment, service, ingress); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadITUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadITUtil.java new file mode 100644 index 0000000000..7d16ae060b --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadITUtil.java @@ -0,0 +1,247 @@ +/* + * 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.configmap.event.reload; + +import java.util.Map; + +import static org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util.patchWithReplace; + +/** + * @author wind57 + */ +final class ConfigMapEventReloadITUtil { + + private static final Map POD_LABELS = Map.of("app", + "spring-cloud-kubernetes-client-configmap-event-reload"); + + private ConfigMapEventReloadITUtil() { + + } + + private static final String BODY_ONE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-configmap-event-reload", + "image": "image_name_here", + "livenessProbe": { + "failureThreshold": 3, + "httpGet": { + "path": "/actuator/health/liveness", + "port": 8080, + "scheme": "HTTP" + }, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1 + }, + "readinessProbe": { + "failureThreshold": 3, + "httpGet": { + "path": "/actuator/health/readiness", + "port": 8080, + "scheme": "HTTP" + }, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1 + }, + "env": [ + { + "name": "SPRING_PROFILES_ACTIVE", + "value": "two" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD", + "value": "DEBUG" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_TWO = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-configmap-event-reload", + "image": "image_name_here", + "livenessProbe": { + "failureThreshold": 3, + "httpGet": { + "path": "/actuator/health/liveness", + "port": 8080, + "scheme": "HTTP" + }, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1 + }, + "readinessProbe": { + "failureThreshold": 3, + "httpGet": { + "path": "/actuator/health/readiness", + "port": 8080, + "scheme": "HTTP" + }, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1 + }, + "env": [ + { + "name": "SPRING_PROFILES_ACTIVE", + "value": "three" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD", + "value": "DEBUG" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_THREE = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-configmap-event-reload", + "image": "image_name_here", + "livenessProbe": { + "failureThreshold": 3, + "httpGet": { + "path": "/actuator/health/liveness", + "port": 8080, + "scheme": "HTTP" + }, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1 + }, + "readinessProbe": { + "failureThreshold": 3, + "httpGet": { + "path": "/actuator/health/readiness", + "port": 8080, + "scheme": "HTTP" + }, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1 + }, + "env": [ + { + "name": "SPRING_PROFILES_ACTIVE", + "value": "two" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD", + "value": "DEBUG" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_SECRETS_ENABLED", + "value": "FALSE" + } + ] + }] + } + } + } + } + """; + + private static final String BODY_FOUR = """ + { + "spec": { + "template": { + "spec": { + "containers": [{ + "name": "spring-cloud-kubernetes-client-configmap-event-reload", + "image": "image_name_here", + "livenessProbe": { + "failureThreshold": 3, + "httpGet": { + "path": "/actuator/health/liveness", + "port": 8080, + "scheme": "HTTP" + }, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1 + }, + "readinessProbe": { + "failureThreshold": 3, + "httpGet": { + "path": "/actuator/health/readiness", + "port": 8080, + "scheme": "HTTP" + }, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 1 + }, + "env": [ + { + "name": "SPRING_PROFILES_ACTIVE", + "value": "one" + }, + { + "name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD", + "value": "DEBUG" + }, + { + "name": "SPRING_CLOUD_KUBERNETES_SECRETS_ENABLED", + "value": "FALSE" + } + ] + }] + } + } + } + } + """; + + static void patchOne(String deploymentName, String namespace, String imageName) { + patchWithReplace(imageName, deploymentName, namespace, BODY_ONE, POD_LABELS); + } + + static void patchTwo(String deploymentName, String namespace, String imageName) { + patchWithReplace(imageName, deploymentName, namespace, BODY_TWO, POD_LABELS); + } + + static void patchThree(String deploymentName, String namespace, String imageName) { + patchWithReplace(imageName, deploymentName, namespace, BODY_THREE, POD_LABELS); + } + + static void patchFour(String deploymentName, String namespace, String imageName) { + patchWithReplace(imageName, deploymentName, namespace, BODY_FOUR, POD_LABELS); + } + +} diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/DataChangesInConfigMapReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/DataChangesInConfigMapReloadDelegate.java similarity index 64% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/DataChangesInConfigMapReloadIT.java rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/DataChangesInConfigMapReloadDelegate.java index 3189e69b22..b23e296145 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/DataChangesInConfigMapReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/DataChangesInConfigMapReloadDelegate.java @@ -17,26 +17,15 @@ package org.springframework.cloud.kubernetes.client.configmap.event.reload; 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; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.apis.CoreV1Api; import io.kubernetes.client.openapi.models.V1ConfigMap; import io.kubernetes.client.openapi.models.V1ConfigMapBuilder; -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.V1ObjectMetaBuilder; -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; @@ -44,15 +33,14 @@ import reactor.util.retry.RetryBackoffSpec; import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; -import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; -import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util; 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; +import static org.springframework.cloud.kubernetes.client.configmap.event.reload.ConfigMapEventReloadITUtil.patchFour; -class DataChangesInConfigMapReloadIT { +class DataChangesInConfigMapReloadDelegate { private static final String IMAGE_NAME = "spring-cloud-kubernetes-client-configmap-event-reload"; @@ -62,30 +50,6 @@ class DataChangesInConfigMapReloadIT { private static final K3sContainer K3S = Commons.container(); - private static Util util; - - private static CoreV1Api api; - - @BeforeAll - static void beforeAll() throws Exception { - K3S.start(); - Commons.validateImage(IMAGE_NAME, K3S); - Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); - - util = new Util(K3S); - api = new CoreV1Api(); - - util.createNamespace(LEFT_NAMESPACE); - util.setUpClusterWide(NAMESPACE, Set.of(LEFT_NAMESPACE)); - } - - @AfterAll - static void afterAll() throws Exception { - util.deleteNamespace(LEFT_NAMESPACE); - Commons.cleanUp(IMAGE_NAME, K3S); - Commons.systemPrune(); - } - /** *
 	 *     - configMap with no labels and data: left.value = left-initial exists in namespace left
@@ -97,9 +61,9 @@ static void afterAll() throws Exception {
 	 *     - then we change data inside the config map, and we must see the updated value
 	 * 
*/ - @Test - void testSimple() { - manifests(Phase.CREATE); + static void testSimple(String dockerImage) { + + patchFour("spring-cloud-kubernetes-client-configmap-deployment-event-reload", NAMESPACE, dockerImage); Commons.assertReloadLogStatements("added configmap informer for namespace", "added secret informer for namespace", IMAGE_NAME); @@ -116,7 +80,7 @@ void testSimple() { .withLabels(Map.of("new-label", "abc")).withNamespace("left").withName("left-configmap").build()) .withData(Map.of("left.value", "left-initial")).build(); - replaceConfigMap(configMap, "left-configmap"); + replaceConfigMap(configMap); await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { WebClient innerWebClient = builder().baseUrl("http://localhost/" + LEFT_NAMESPACE).build(); @@ -135,7 +99,7 @@ void testSimple() { .withName("left-configmap").build()) .withData(Map.of("left.value", "left-after-change")).build(); - replaceConfigMap(configMap, "left-configmap"); + replaceConfigMap(configMap); await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> { WebClient innerWebClient = builder().baseUrl("http://localhost/" + LEFT_NAMESPACE).build(); @@ -144,46 +108,9 @@ void testSimple() { return "left-after-change".equals(innerResult); }); - manifests(Phase.DELETE); - } - - private static void manifests(Phase phase) { - - try { - - V1ConfigMap leftConfigMap = (V1ConfigMap) util.yaml("left-configmap.yaml"); - - V1Deployment deployment = (V1Deployment) util.yaml("one/deployment.yaml"); - V1Service service = (V1Service) util.yaml("service.yaml"); - V1Ingress ingress = (V1Ingress) util.yaml("ingress.yaml"); - - List envVars = new ArrayList<>( - Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()) - .orElse(List.of())); - - V1EnvVar secretsDisabledEnvVar = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_SECRETS_ENABLED") - .value("FALSE"); - envVars.add(secretsDisabledEnvVar); - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); - - if (phase.equals(Phase.CREATE)) { - util.createAndWait(LEFT_NAMESPACE, leftConfigMap, null); - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); - } - - if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(LEFT_NAMESPACE, leftConfigMap, null); - util.deleteAndWait(NAMESPACE, deployment, service, ingress); - } - - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - private String logs() { + private static String logs() { try { String appPodName = K3S.execInContainer("sh", "-c", "kubectl get pods -l app=" + IMAGE_NAME + " -o=name --no-headers | tr -d '\n'").getStdout(); @@ -197,17 +124,18 @@ private String logs() { } } - 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(120, Duration.ofSeconds(2)).filter(Objects::nonNull); } - private static void replaceConfigMap(V1ConfigMap configMap, String name) { + private static void replaceConfigMap(V1ConfigMap configMap) { try { - api.replaceNamespacedConfigMap(name, LEFT_NAMESPACE, configMap, null, null, null, null); + new CoreV1Api().replaceNamespacedConfigMap("left-configmap", LEFT_NAMESPACE, configMap, null, null, null, + null); } catch (ApiException e) { throw new RuntimeException(e); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/one/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/deployment.yaml similarity index 100% rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/one/deployment.yaml rename to spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/deployment.yaml diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/left-configmap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/left-configmap.yaml index 560954befa..a72d8af5dc 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/left-configmap.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/left-configmap.yaml @@ -4,4 +4,4 @@ metadata: name: left-configmap namespace: left data: - left.value: left-initial + left.value: "left-initial" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/right-configmap-with-label.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/right-configmap-with-label.yaml index 56531a6352..658ebca4c2 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/right-configmap-with-label.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/right-configmap-with-label.yaml @@ -6,4 +6,4 @@ metadata: labels: spring.cloud.kubernetes.config.informer.enabled: true data: - right.with.label.value: right-with-label-initial + right.with.label.value: "right-with-label-initial" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/right-configmap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/right-configmap.yaml index a14273279b..0ede471da5 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/right-configmap.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/right-configmap.yaml @@ -4,4 +4,4 @@ metadata: name: right-configmap namespace: right data: - right.value: right-initial + right.value: "right-initial" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/three/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/three/deployment.yaml deleted file mode 100644 index f2f457750c..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/three/deployment.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: spring-cloud-kubernetes-client-configmap-deployment-event-reload -spec: - selector: - matchLabels: - app: spring-cloud-kubernetes-client-configmap-event-reload - template: - metadata: - labels: - app: spring-cloud-kubernetes-client-configmap-event-reload - spec: - serviceAccountName: spring-cloud-kubernetes-serviceaccount - containers: - - name: spring-cloud-kubernetes-client-configmap-event-reload - image: docker.io/springcloud/spring-cloud-kubernetes-client-configmap-event-reload - imagePullPolicy: IfNotPresent - readinessProbe: - httpGet: - port: 8080 - path: /actuator/health/readiness - livenessProbe: - httpGet: - port: 8080 - path: /actuator/health/liveness - ports: - - containerPort: 8080 - env: - - name: SPRING_PROFILES_ACTIVE - value: three - - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD - value: DEBUG diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/two/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/two/deployment.yaml deleted file mode 100644 index caa0d04139..0000000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/two/deployment.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: spring-cloud-kubernetes-client-configmap-deployment-event-reload -spec: - selector: - matchLabels: - app: spring-cloud-kubernetes-client-configmap-event-reload - template: - metadata: - labels: - app: spring-cloud-kubernetes-client-configmap-event-reload - spec: - serviceAccountName: spring-cloud-kubernetes-serviceaccount - containers: - - name: spring-cloud-kubernetes-client-configmap-event-reload - image: docker.io/springcloud/spring-cloud-kubernetes-client-configmap-event-reload - imagePullPolicy: IfNotPresent - readinessProbe: - httpGet: - port: 8080 - path: /actuator/health/readiness - livenessProbe: - httpGet: - port: 8080 - path: /actuator/health/liveness - ports: - - containerPort: 8080 - env: - - name: SPRING_PROFILES_ACTIVE - value: two - - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD - value: DEBUG 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 0c92e83941..8a6406a3af 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,9 +16,7 @@ package org.springframework.cloud.kubernetes.client.discovery.it; -import org.apache.commons.logging.LogFactory; - -import org.springframework.core.log.LogAccessor; +import java.util.Map; 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; @@ -28,8 +26,7 @@ */ final class KubernetesClientDiscoveryClientUtils { - private static final LogAccessor LOG = new LogAccessor( - LogFactory.getLog(KubernetesClientDiscoveryClientUtils.class)); + private static final Map POD_LABELS = Map.of("app", "spring-cloud-kubernetes-client-discovery-it"); // patch the filter so that it matches both namespaces private static final String BODY_ONE = """ @@ -358,54 +355,54 @@ private KubernetesClientDiscoveryClientUtils() { } static void patchForTwoNamespacesMatchViaThePredicate(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_ONE); + patchWithMerge(deploymentName, namespace, BODY_ONE, POD_LABELS); } static void patchForReactiveHealth(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_TWO); + patchWithMerge(deploymentName, namespace, BODY_TWO, POD_LABELS); } static void patchForBlockingAndReactiveHealth(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_THREE); + patchWithMerge(deploymentName, namespace, BODY_THREE, POD_LABELS); } // 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); + patchWithReplace(image, deploymentName, namespace, BODY_FOUR, POD_LABELS); } // add SPRING_CLOUD_KUBERNETES_DISCOVERY_ALL_NAMESPACES=TRUE static void patchForAllNamespaces(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_FIVE); + patchWithMerge(deploymentName, namespace, BODY_FIVE, POD_LABELS); } static void patchForSingleNamespace(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_SIX); + patchWithMerge(deploymentName, namespace, BODY_SIX, POD_LABELS); } static void patchForPodMetadata(String imageName, String deploymentName, String namespace) { - patchWithReplace(imageName, deploymentName, namespace, BODY_SEVEN); + patchWithReplace(imageName, deploymentName, namespace, BODY_SEVEN, POD_LABELS); } static void patchForReactiveOnly(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_EIGHT); + patchWithMerge(deploymentName, namespace, BODY_EIGHT, POD_LABELS); } static void patchForBlockingAndReactive(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_NINE); + patchWithMerge(deploymentName, namespace, BODY_NINE, POD_LABELS); } static void patchForTwoNamespacesBlockingOnly(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_TEN); + patchWithMerge(deploymentName, namespace, BODY_TEN, POD_LABELS); } static void patchToAddBlockingSupport(String deploymentName, String namespace) { - patchWithMerge(deploymentName, namespace, BODY_ELEVEN); + patchWithMerge(deploymentName, namespace, BODY_ELEVEN, POD_LABELS); } static void patchForUATNamespacesTests(String image, String deploymentName, String namespace) { - patchWithReplace(image, deploymentName, namespace, BODY_TWELVE); + patchWithReplace(image, deploymentName, namespace, BODY_TWELVE, POD_LABELS); } } 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 097b95eb6d..8cbc176535 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 @@ -438,7 +438,8 @@ public void wiremock(String namespace, String path, Phase phase) { } - public static void patchWithMerge(String deploymentName, String namespace, String patchBody) { + public static void patchWithMerge(String deploymentName, String namespace, String patchBody, + Map podLabels) { try { PatchUtils.patch(V1Deployment.class, () -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, @@ -450,10 +451,11 @@ public static void patchWithMerge(String deploymentName, String namespace, Strin throw new RuntimeException(e); } - waitForDeploymentAfterPatch(deploymentName, namespace); + waitForDeploymentAfterPatch(deploymentName, namespace, podLabels); } - public static void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody) { + public static void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody, + Map podLabels) { String body = patchBody.replace("image_name_here", imageName); try { @@ -467,7 +469,7 @@ public static void patchWithReplace(String imageName, String deploymentName, Str throw new RuntimeException(e); } - waitForDeploymentAfterPatch(deploymentName, namespace); + waitForDeploymentAfterPatch(deploymentName, namespace, podLabels); } @@ -625,10 +627,11 @@ private boolean isDeploymentReady(String deploymentName, String namespace) throw return availableReplicas != null && availableReplicas >= 1; } - private static void waitForDeploymentAfterPatch(String deploymentName, String namespace) { + private static void waitForDeploymentAfterPatch(String deploymentName, String namespace, + Map podLabels) { try { await().pollDelay(Duration.ofSeconds(4)).pollInterval(Duration.ofSeconds(3)).atMost(60, TimeUnit.SECONDS) - .until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace)); + .until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace, podLabels)); } catch (Exception e) { if (e instanceof ApiException apiException) { @@ -640,7 +643,8 @@ private static void waitForDeploymentAfterPatch(String deploymentName, String na } - private static boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace) throws ApiException { + private static boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace, + Map podLabels) throws ApiException { V1DeploymentList deployments = new AppsV1Api().listNamespacedDeployment(namespace, null, null, null, "metadata.name=" + deploymentName, null, null, null, null, null, null); @@ -654,6 +658,14 @@ private static boolean isDeploymentReadyAfterPatch(String deploymentName, String int readyReplicas = Optional.ofNullable(deployment.getStatus().getReadyReplicas()).orElse(0); if (readyReplicas != replicas) { + LOG.info("ready replicas not yet same as replicas"); + return false; + } + + int pods = new CoreV1Api().listNamespacedPod(namespace, null, null, null, null, labelSelector(podLabels), null, + null, null, null, null).getItems().size(); + + if (pods != replicas) { LOG.info("number of pods not yet stabilized"); return false; }