diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadUtil.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadUtil.java index c8df2821..f066da4c 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadUtil.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadUtil.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.cloud.bootstrap.config.BootstrapPropertySource; import org.springframework.cloud.bootstrap.config.PropertySourceLocator; import org.springframework.cloud.kubernetes.commons.config.MountConfigMapPropertySource; +import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySource; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; @@ -110,6 +111,10 @@ public final class ConfigReloadUtil { // we know that the type is correct here managedSources.add((S) mountConfigMapPropertySource); } + else if (source instanceof SecretsPropertySource secretsPropertySource) { + // we know that the type is correct here + managedSources.add((S) secretsPropertySource); + } else if (source instanceof BootstrapPropertySource bootstrapPropertySource) { PropertySource propertySource = bootstrapPropertySource.getDelegate(); LOG.debug(() -> "bootstrap delegate class : " + propertySource.getClass()); @@ -120,6 +125,10 @@ public final class ConfigReloadUtil { // we know that the type is correct here managedSources.add((S) mountConfigMapPropertySource); } + else if (propertySource instanceof SecretsPropertySource secretsPropertySource) { + // we know that the type is correct here + managedSources.add((S) secretsPropertySource); + } } } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadUtilTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadUtilTests.java index c5cfd2f1..7bcfd361 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadUtilTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadUtilTests.java @@ -27,6 +27,8 @@ import org.junit.jupiter.api.Test; import org.springframework.cloud.bootstrap.config.BootstrapPropertySource; import org.springframework.cloud.kubernetes.commons.config.MountConfigMapPropertySource; +import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySource; +import org.springframework.cloud.kubernetes.commons.config.SourceData; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MapPropertySource; @@ -127,8 +129,8 @@ class ConfigReloadUtilTests { MockEnvironment environment = new MockEnvironment(); MutablePropertySources propertySources = environment.getPropertySources(); propertySources.addFirst(new OneComposite()); - propertySources.addFirst(new PlainPropertySource("plain")); - propertySources.addFirst(new OneBootstrap(new EnumerablePropertySource<>("enumerable") { + propertySources.addFirst(new PlainPropertySource<>("plain")); + propertySources.addFirst(new OneBootstrap<>(new EnumerablePropertySource<>("enumerable") { @Override public String[] getPropertyNames() { return new String[0]; @@ -143,11 +145,38 @@ class ConfigReloadUtilTests { List result = ConfigReloadUtil.findPropertySources(PlainPropertySource.class, environment); + Assertions.assertThat(result.size()).isEqualTo(4); Assertions.assertThat(result.get(0).getProperty("a")).isEqualTo("b"); Assertions.assertThat(result.get(1).getProperty("")).isEqualTo("plain"); Assertions.assertThat(result.get(2).getProperty("")).isEqualTo("from-bootstrap"); Assertions.assertThat(result.get(3).getProperty("")).isEqualTo("from-inner-two-composite"); + + } + + @Test + void testSecretsPropertySource() { + MockEnvironment environment = new MockEnvironment(); + MutablePropertySources propertySources = environment.getPropertySources(); + propertySources.addFirst(new SecretsPropertySource(new SourceData("secret", Map.of("a", "b")))); + + List result = ConfigReloadUtil.findPropertySources(PlainPropertySource.class, + environment); + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0).getProperty("a")).isEqualTo("b"); + } + + @Test + void testBootstrapSecretsPropertySource() { + MockEnvironment environment = new MockEnvironment(); + MutablePropertySources propertySources = environment.getPropertySources(); + propertySources + .addFirst(new OneBootstrap<>(new SecretsPropertySource(new SourceData("secret", Map.of("a", "b"))))); + + List result = ConfigReloadUtil.findPropertySources(PlainPropertySource.class, + environment); + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0).getProperty("a")).isEqualTo("b"); } private static final class OneComposite extends CompositePropertySource { @@ -171,12 +200,12 @@ class ConfigReloadUtilTests { @Override public Collection> getPropertySources() { - return List.of(new PlainPropertySource("from-inner-two-composite")); + return List.of(new PlainPropertySource<>("from-inner-two-composite")); } } - private static final class PlainPropertySource extends PropertySource { + private static final class PlainPropertySource extends PropertySource { private PlainPropertySource(String name) { super(name); @@ -189,15 +218,18 @@ class ConfigReloadUtilTests { } - private static final class OneBootstrap extends BootstrapPropertySource { + private static final class OneBootstrap extends BootstrapPropertySource { - private OneBootstrap(EnumerablePropertySource delegate) { + private final EnumerablePropertySource delegate; + + private OneBootstrap(EnumerablePropertySource delegate) { super(delegate); + this.delegate = delegate; } @Override - public PropertySource getDelegate() { - return new PlainPropertySource("from-bootstrap"); + public PropertySource getDelegate() { + return delegate; } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterIT.java index dfbe1ad2..2bdf0a24 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/client/discovery/Fabric8DiscoveryFilterIT.java @@ -51,16 +51,16 @@ class Fabric8DiscoveryFilterIT extends Fabric8DiscoveryBase { util.createNamespace(NAMESPACE_A_UAT); util.createNamespace(NAMESPACE_B_UAT); - util.wiremock(NAMESPACE_A_UAT, Phase.CREATE); - util.wiremock(NAMESPACE_B_UAT, Phase.CREATE); + util.wiremock(NAMESPACE_A_UAT, Phase.CREATE, false); + util.wiremock(NAMESPACE_B_UAT, Phase.CREATE, false); } @AfterEach void afterEach() { - util.wiremock(NAMESPACE_A_UAT, Phase.DELETE); - util.wiremock(NAMESPACE_B_UAT, Phase.DELETE); + util.wiremock(NAMESPACE_A_UAT, Phase.DELETE, false); + util.wiremock(NAMESPACE_B_UAT, Phase.DELETE, false); util.deleteNamespace(NAMESPACE_A_UAT); util.deleteNamespace(NAMESPACE_B_UAT); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshIT.java index da62ad2a..83c825dd 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshIT.java @@ -44,8 +44,6 @@ import static org.springframework.cloud.kubernetes.configuration.watcher.TestUti */ class ActuatorRefreshIT { - private static final String WIREMOCK_PATH = "/"; - private static final String NAMESPACE = "default"; private static final K3sContainer K3S = Commons.container(); @@ -72,12 +70,12 @@ class ActuatorRefreshIT { @BeforeEach void setup() { - util.wiremock(NAMESPACE, WIREMOCK_PATH, Phase.CREATE); + util.wiremock(NAMESPACE, Phase.CREATE, true); } @AfterEach void after() { - util.wiremock(NAMESPACE, WIREMOCK_PATH, Phase.DELETE); + util.wiremock(NAMESPACE, Phase.DELETE, true); } /* @@ -114,10 +112,10 @@ class ActuatorRefreshIT { deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, null, true); + util.createAndWait(NAMESPACE, null, deployment, service, true); } else { - util.deleteAndWait(NAMESPACE, deployment, service, null); + util.deleteAndWait(NAMESPACE, deployment, service); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshMultipleNamespacesIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshMultipleNamespacesIT.java index b74dbc03..54e602ee 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshMultipleNamespacesIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ActuatorRefreshMultipleNamespacesIT.java @@ -60,7 +60,7 @@ class ActuatorRefreshMultipleNamespacesIT { util = new Util(K3S); util.createNamespace(LEFT_NAMESPACE); util.createNamespace(RIGHT_NAMESPACE); - util.wiremock(DEFAULT_NAMESPACE, "/", Phase.CREATE); + util.wiremock(DEFAULT_NAMESPACE, Phase.CREATE, true); util.setUpClusterWide(DEFAULT_NAMESPACE, Set.of(DEFAULT_NAMESPACE, LEFT_NAMESPACE, RIGHT_NAMESPACE)); configWatcher(Phase.CREATE); } @@ -68,7 +68,7 @@ class ActuatorRefreshMultipleNamespacesIT { @AfterAll static void afterAll() { configWatcher(Phase.DELETE); - util.wiremock(DEFAULT_NAMESPACE, "/", Phase.DELETE); + util.wiremock(DEFAULT_NAMESPACE, Phase.DELETE, true); util.deleteClusterWide(DEFAULT_NAMESPACE, Set.of(DEFAULT_NAMESPACE, LEFT_NAMESPACE, RIGHT_NAMESPACE)); util.deleteNamespace(LEFT_NAMESPACE); util.deleteNamespace(RIGHT_NAMESPACE); @@ -128,10 +128,10 @@ class ActuatorRefreshMultipleNamespacesIT { .yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml"); if (phase.equals(Phase.CREATE)) { - util.createAndWait(DEFAULT_NAMESPACE, null, deployment, service, null, true); + util.createAndWait(DEFAULT_NAMESPACE, null, deployment, service, true); } else { - util.deleteAndWait(DEFAULT_NAMESPACE, deployment, service, null); + util.deleteAndWait(DEFAULT_NAMESPACE, deployment, service); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/TestUtil.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/TestUtil.java index 1eb5ad82..7a3a995b 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/TestUtil.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/TestUtil.java @@ -46,7 +46,7 @@ final class TestUtil { private static final String WIREMOCK_HOST = "localhost"; - private static final int WIREMOCK_PORT = 80; + private static final int WIREMOCK_PORT = 32321; static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME = "spring-cloud-kubernetes-configuration-watcher"; @@ -60,7 +60,7 @@ final class TestUtil { // is ready to take a request via 'Wiremock::stubFor' (because sometimes it fails) // As such, get the existing mappings and retrySpec() makes sure we retry until // we get a response back. - WebClient client = builder().baseUrl("http://localhost:80/__admin/mappings").build(); + WebClient client = builder().baseUrl("http://localhost:32321/__admin/mappings").build(); client.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()).block(); StubMapping stubMapping = WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh")) diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery-server/src/test/java/org/springframework/cloud/kubernetes/discoveryclient/it/DiscoveryServerClientBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery-server/src/test/java/org/springframework/cloud/kubernetes/discoveryclient/it/DiscoveryServerClientBase.java index 5506d4b3..cbbf11ea 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery-server/src/test/java/org/springframework/cloud/kubernetes/discoveryclient/it/DiscoveryServerClientBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery-server/src/test/java/org/springframework/cloud/kubernetes/discoveryclient/it/DiscoveryServerClientBase.java @@ -93,10 +93,10 @@ abstract class DiscoveryServerClientBase { V1Service service = (V1Service) util.yaml("manifests/discoveryserver-service.yaml"); if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, null, true); + util.createAndWait(NAMESPACE, null, deployment, service, true); } else { - util.deleteAndWait(NAMESPACE, deployment, service, null); + util.deleteAndWait(NAMESPACE, deployment, service); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery-server/src/test/java/org/springframework/cloud/kubernetes/discoveryclient/it/DiscoveryServerClientIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery-server/src/test/java/org/springframework/cloud/kubernetes/discoveryclient/it/DiscoveryServerClientIT.java index ab3de7a6..3dd8cab7 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery-server/src/test/java/org/springframework/cloud/kubernetes/discoveryclient/it/DiscoveryServerClientIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery-server/src/test/java/org/springframework/cloud/kubernetes/discoveryclient/it/DiscoveryServerClientIT.java @@ -78,8 +78,8 @@ class DiscoveryServerClientIT extends DiscoveryServerClientBase { discoveryServer(Phase.CREATE); Images.loadWiremock(K3S); - util.wiremock(NAMESPACE_LEFT, "/wiremock-" + NAMESPACE_LEFT, Phase.CREATE, false); - util.wiremock(NAMESPACE_RIGHT, "/wiremock-" + NAMESPACE_RIGHT, Phase.CREATE, false); + util.wiremock(NAMESPACE_LEFT, Phase.CREATE, false); + util.wiremock(NAMESPACE_RIGHT, Phase.CREATE, false); } @AfterAll @@ -87,8 +87,8 @@ class DiscoveryServerClientIT extends DiscoveryServerClientBase { serviceAccount(Phase.DELETE); discoveryServer(Phase.DELETE); - util.wiremock(NAMESPACE_LEFT, "/wiremock-" + NAMESPACE_LEFT, Phase.DELETE, false); - util.wiremock(NAMESPACE_RIGHT, "/wiremock-" + NAMESPACE_RIGHT, Phase.DELETE, false); + util.wiremock(NAMESPACE_LEFT, Phase.DELETE, false); + util.wiremock(NAMESPACE_RIGHT, Phase.DELETE, false); util.deleteNamespace(NAMESPACE_LEFT); util.deleteNamespace(NAMESPACE_RIGHT); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientBlockingIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientBlockingIT.java index 7ff34634..2ca22a60 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientBlockingIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientBlockingIT.java @@ -60,12 +60,12 @@ class KubernetesClientBlockingIT extends KubernetesClientDiscoveryBase { @BeforeEach void beforeEach() { Images.loadWiremock(K3S); - util.wiremock(NAMESPACE, "/", Phase.CREATE); + util.wiremock(NAMESPACE, Phase.CREATE, true); } @AfterEach void afterEach() { - util.wiremock(NAMESPACE, "/", Phase.DELETE); + util.wiremock(NAMESPACE, Phase.DELETE, true); } /** diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientDiscoveryFilterIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientDiscoveryFilterIT.java index 189dd3d7..31e61c80 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientDiscoveryFilterIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientDiscoveryFilterIT.java @@ -63,14 +63,14 @@ class KubernetesClientDiscoveryFilterIT extends KubernetesClientDiscoveryBase { util.createNamespace(NAMESPACE_B_UAT); Images.loadWiremock(K3S); - util.wiremock(NAMESPACE_A_UAT, "/", Phase.CREATE); - util.wiremock(NAMESPACE_B_UAT, "/", Phase.CREATE); + util.wiremock(NAMESPACE_A_UAT, Phase.CREATE, false); + util.wiremock(NAMESPACE_B_UAT, Phase.CREATE, false); } @AfterEach void afterEach() { - util.wiremock(NAMESPACE_A_UAT, "/", Phase.DELETE); - util.wiremock(NAMESPACE_B_UAT, "/", Phase.DELETE); + util.wiremock(NAMESPACE_A_UAT, Phase.DELETE, false); + util.wiremock(NAMESPACE_B_UAT, Phase.DELETE, false); util.deleteNamespace(NAMESPACE_A_UAT); util.deleteNamespace(NAMESPACE_B_UAT); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientDiscoverySimpleIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientDiscoverySimpleIT.java index e00c3b01..472fcd6b 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientDiscoverySimpleIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientDiscoverySimpleIT.java @@ -64,13 +64,13 @@ class KubernetesClientDiscoverySimpleIT extends KubernetesClientDiscoveryBase { util.busybox(NAMESPACE, Phase.CREATE); externalNameService = (V1Service) util.yaml("external-name-service.yaml"); - util.createAndWait(NAMESPACE, null, null, externalNameService, null, true); + util.createAndWait(NAMESPACE, null, null, externalNameService, true); } @AfterEach void afterEach() { util.busybox(NAMESPACE, Phase.DELETE); - util.deleteAndWait(NAMESPACE, null, externalNameService, null); + util.deleteAndWait(NAMESPACE, null, externalNameService); } @Test diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientReactiveIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientReactiveIT.java index 2f00f4f1..7fe1a897 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientReactiveIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/KubernetesClientReactiveIT.java @@ -60,12 +60,12 @@ class KubernetesClientReactiveIT extends KubernetesClientDiscoveryBase { @BeforeEach void beforeEach() { Images.loadWiremock(K3S); - util.wiremock(NAMESPACE, "/", Phase.CREATE); + util.wiremock(NAMESPACE, Phase.CREATE, true); } @AfterEach void afterEach() { - util.wiremock(NAMESPACE, "/", Phase.DELETE); + util.wiremock(NAMESPACE, Phase.DELETE, true); } /** diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/TestAssertions.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/TestAssertions.java index 8c0a8c62..14c71111 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/TestAssertions.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-discovery/src/test/java/org/springframework/cloud/kubernetes/k8s/client/discovery/TestAssertions.java @@ -133,8 +133,8 @@ final class TestAssertions { assertThat(wiremockInstance.getServiceId()).isEqualTo("service-wiremock"); assertThat(wiremockInstance.getInstanceId()).isNotNull(); assertThat(wiremockInstance.getHost()).isNotNull(); - assertThat(wiremockInstance.getMetadata()).isEqualTo(Map.of("k8s_namespace", "default", "type", "ClusterIP", - "port.http", "8080", "app", "service-wiremock")); + assertThat(wiremockInstance.getMetadata()).isEqualTo( + Map.of("k8s_namespace", "default", "type", "NodePort", "port.http", "8080", "app", "service-wiremock")); } @@ -147,8 +147,8 @@ final class TestAssertions { assertThat(wiremockInstance.getServiceId()).isEqualTo("service-wiremock"); assertThat(wiremockInstance.getInstanceId()).isNotNull(); assertThat(wiremockInstance.getHost()).isNotNull(); - assertThat(wiremockInstance.getMetadata()).isEqualTo(Map.of("k8s_namespace", "default", "type", "ClusterIP", - "port.http", "8080", "app", "service-wiremock")); + assertThat(wiremockInstance.getMetadata()).isEqualTo( + Map.of("k8s_namespace", "default", "type", "NodePort", "port.http", "8080", "app", "service-wiremock")); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/multiple/apps/ConfigurationWatcherBusKafkaIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/multiple/apps/ConfigurationWatcherBusKafkaIT.java index d5d4e539..36f3577d 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/multiple/apps/ConfigurationWatcherBusKafkaIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/multiple/apps/ConfigurationWatcherBusKafkaIT.java @@ -22,7 +22,6 @@ import java.util.Objects; 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.V1Ingress; import io.kubernetes.client.openapi.models.V1Service; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; @@ -153,7 +152,7 @@ class ConfigurationWatcherBusKafkaIT { util.createAndWait(NAMESPACE, configMap, null); WebClient.Builder builder = builder(); - WebClient serviceClient = builder.baseUrl("http://localhost:80/app").build(); + WebClient serviceClient = builder.baseUrl("http://localhost:32321/app").build(); Boolean[] value = new Boolean[1]; await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(240)).until(() -> { @@ -173,13 +172,12 @@ class ConfigurationWatcherBusKafkaIT { private void app(Phase phase) { V1Deployment deployment = (V1Deployment) util.yaml("app/app-deployment.yaml"); V1Service service = (V1Service) util.yaml("app/app-service.yaml"); - V1Ingress ingress = (V1Ingress) util.yaml("ingress/ingress.yaml"); if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); + util.createAndWait(NAMESPACE, null, deployment, service, true); } else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, ingress); + util.deleteAndWait(NAMESPACE, deployment, service); } } @@ -188,10 +186,10 @@ class ConfigurationWatcherBusKafkaIT { V1Service service = (V1Service) util.yaml("config-watcher/watcher-kus-kafka-service.yaml"); if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, null, true); + util.createAndWait(NAMESPACE, null, deployment, service, true); } else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, null); + util.deleteAndWait(NAMESPACE, deployment, service); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/resources/app/app-service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/resources/app/app-service.yaml index f9cb695f..3be7e66c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/resources/app/app-service.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/resources/app/app-service.yaml @@ -9,6 +9,7 @@ spec: - name: http port: 8081 targetPort: 8081 + nodePort: 32321 selector: app: app - type: ClusterIP + type: NodePort diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/resources/ingress/ingress.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/resources/ingress/ingress.yaml deleted file mode 100644 index faa02ba6..00000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-kafka-configmap-reload/kafka-configmap-test-app/src/test/resources/ingress/ingress.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: it-ingress-app - namespace: default -spec: - rules: - - http: - paths: - - path: /app - pathType: Prefix - backend: - service: - name: app - port: - number: 8081 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/multiple/apps/ConfigurationWatcherBusAmqpIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/multiple/apps/ConfigurationWatcherBusAmqpIT.java index 6989037f..7d4aaab9 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/multiple/apps/ConfigurationWatcherBusAmqpIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/multiple/apps/ConfigurationWatcherBusAmqpIT.java @@ -20,7 +20,6 @@ import java.time.Duration; import java.util.Objects; import io.kubernetes.client.openapi.models.V1Deployment; -import io.kubernetes.client.openapi.models.V1Ingress; import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1SecretBuilder; import io.kubernetes.client.openapi.models.V1Service; @@ -103,7 +102,7 @@ class ConfigurationWatcherBusAmqpIT { util.createAndWait(NAMESPACE, null, secret); WebClient.Builder builder = builder(); - WebClient serviceClient = builder.baseUrl("http://localhost:80/app").build(); + WebClient serviceClient = builder.baseUrl("http://localhost:32321/app").build(); Boolean[] value = new Boolean[1]; await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(240)).until(() -> { @@ -122,13 +121,12 @@ class ConfigurationWatcherBusAmqpIT { private void appA(Phase phase) { V1Deployment deployment = (V1Deployment) util.yaml("app/app-deployment.yaml"); V1Service service = (V1Service) util.yaml("app/app-service.yaml"); - V1Ingress ingress = (V1Ingress) util.yaml("ingress/ingress.yaml"); if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, ingress, true); + util.createAndWait(NAMESPACE, null, deployment, service, true); } else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, ingress); + util.deleteAndWait(NAMESPACE, deployment, service); } } @@ -137,10 +135,10 @@ class ConfigurationWatcherBusAmqpIT { V1Service service = (V1Service) util.yaml("config-watcher/watcher-service.yaml"); if (phase.equals(Phase.CREATE)) { - util.createAndWait(NAMESPACE, null, deployment, service, null, true); + util.createAndWait(NAMESPACE, null, deployment, service, true); } else if (phase.equals(Phase.DELETE)) { - util.deleteAndWait(NAMESPACE, deployment, service, null); + util.deleteAndWait(NAMESPACE, deployment, service); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/resources/app/app-service.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/resources/app/app-service.yaml index 292541f7..0f5318b7 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/resources/app/app-service.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/resources/app/app-service.yaml @@ -9,6 +9,7 @@ spec: - name: http port: 8080 targetPort: 8080 + nodePort: 32321 selector: app: app - type: ClusterIP + type: NodePort diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/resources/ingress/ingress.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/resources/ingress/ingress.yaml deleted file mode 100644 index 46ea6693..00000000 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload/rabbitmq-secret-test-app/src/test/resources/ingress/ingress.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: it-ingress - namespace: default -spec: - rules: - - http: - paths: - - path: /app - pathType: Prefix - backend: - service: - name: app - port: - number: 8080 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientReloadBase.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientReloadBase.java index cc91acce..6cbcefbf 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientReloadBase.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientReloadBase.java @@ -105,11 +105,11 @@ abstract class K8sClientReloadBase { if (phase.equals(Phase.CREATE)) { util.createAndWait(namespace, configMap, null); - util.createAndWait(namespace, imageName, deployment, service, null, true); + util.createAndWait(namespace, imageName, deployment, service, true); } else { util.deleteAndWait(namespace, configMap, null); - util.deleteAndWait(namespace, deployment, service, null); + util.deleteAndWait(namespace, deployment, service); } } @@ -122,11 +122,11 @@ abstract class K8sClientReloadBase { if (phase.equals(Phase.CREATE)) { util.createAndWait(namespace, null, secret); - util.createAndWait(namespace, imageName, deployment, service, null, true); + util.createAndWait(namespace, imageName, deployment, service, true); } else { util.deleteAndWait(namespace, null, secret); - util.deleteAndWait(namespace, deployment, service, null); + util.deleteAndWait(namespace, deployment, service); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientSecretMountBootstrapPollingIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientSecretMountBootstrapPollingIT.java new file mode 100644 index 00000000..c6b0f88f --- /dev/null +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientSecretMountBootstrapPollingIT.java @@ -0,0 +1,118 @@ +/* + * Copyright 2013-2025 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.k8s.client.reload.it; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Map; + +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1Secret; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.k3s.K3sContainer; + +import org.springframework.cloud.kubernetes.integration.tests.commons.Commons; +import org.springframework.cloud.kubernetes.integration.tests.commons.Phase; +import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util; +import org.springframework.http.HttpMethod; +import org.springframework.web.reactive.function.client.WebClient; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.builder; +import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.retrySpec; + +/** + * @author wind57 + */ +class K8sClientSecretMountBootstrapPollingIT extends K8sClientReloadBase { + + private static final String IMAGE_NAME = "spring-cloud-kubernetes-k8s-client-reload"; + + private static final String NAMESPACE = "default"; + + private static final K3sContainer K3S = Commons.container(); + + private static Util util; + + private static CoreV1Api coreV1Api; + + @BeforeAll + static void beforeAllLocal() throws Exception { + K3S.start(); + Commons.validateImage(IMAGE_NAME, K3S); + Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S); + + util = new Util(K3S); + coreV1Api = new CoreV1Api(); + util.setUp(NAMESPACE); + manifestsSecret(Phase.CREATE, util, NAMESPACE, IMAGE_NAME); + } + + @AfterAll + static void afterAll() { + manifestsSecret(Phase.DELETE, util, NAMESPACE, IMAGE_NAME); + } + + /** + *
+	 *     - we have bootstrap enabled
+	 *     - we will 'locate' property sources from secrets.
+	 *     - there are no explicit secrets to search for, but what we will also read,
+	 *     	 is 'spring.cloud.kubernetes.secret.paths', which we have set to
+	 *     	 '/tmp/application.properties'
+	 *       in this test. That is populated by the volumeMounts (see mount/deployment-with-secret.yaml)
+	 *     - we first assert that we are actually reading the path based source
+	 *
+	 *     - we then change the secret content, wait for k8s to pick it up and replace them
+	 *     - our polling will then detect that change, and trigger a reload.
+	 * 
+ */ + @Test + void test() throws Exception { + WebClient webClient = builder().baseUrl("http://localhost:32321/secret").build(); + String result = webClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block(); + + // we first read the initial value from the configmap + assertThat(result).isEqualTo("initial"); + + // replace data in secret and wait for k8s to pick it up + // our polling will detect that and restart the app + V1Secret secret = (V1Secret) util.yaml("mount/secret.yaml"); + secret.setData(Map.of("from.properties.secret.key", "as-mount-changed".getBytes(StandardCharsets.UTF_8))); + coreV1Api.replaceNamespacedSecret("secret-reload", NAMESPACE, secret, null, null, null, null); + + Commons.waitForLogStatement("Detected change in config maps/secrets, reload will be triggered", K3S, + IMAGE_NAME); + + await().atMost(Duration.ofSeconds(120)) + .pollInterval(Duration.ofSeconds(1)) + .until(() -> webClient.method(HttpMethod.GET) + .retrieve() + .bodyToMono(String.class) + .retryWhen(retrySpec()) + .block() + .equals("as-mount-changed")); + } + +} diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java index e47f04c6..d8e0a81f 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java @@ -295,7 +295,12 @@ public final class Util { }); } - public void wiremock(String namespace, Phase phase) { + /** + * 'withNodePort' specifies if we add the NodePort or not. It is needed because we + * sometimes deploy two instances of wiremock, and they can't have the same NodePort + * exposed + */ + public void wiremock(String namespace, Phase phase, boolean withNodePort) { InputStream deploymentStream = inputStream("wiremock/wiremock-deployment.yaml"); InputStream serviceStream = inputStream("wiremock/wiremock-service.yaml"); @@ -305,6 +310,11 @@ public final class Util { deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(imageWithVersion); Service service = client.services().load(serviceStream).item(); + if (!withNodePort) { + // we assume we only have one 'http' port + service.getSpec().getPorts().get(0).setNodePort(null); + service.getSpec().setType("ClusterIP"); + } if (phase.equals(Phase.CREATE)) { deployment.getMetadata().setNamespace(namespace); 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 9fc85e51..92619aaf 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java @@ -29,25 +29,19 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.openapi.ApiClient; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.Configuration; import io.kubernetes.client.openapi.apis.ApiregistrationV1Api; import io.kubernetes.client.openapi.apis.AppsV1Api; import io.kubernetes.client.openapi.apis.CoreV1Api; -import io.kubernetes.client.openapi.apis.NetworkingV1Api; import io.kubernetes.client.openapi.apis.RbacAuthorizationV1Api; import io.kubernetes.client.openapi.models.V1APIService; import io.kubernetes.client.openapi.models.V1ClusterRole; -import io.kubernetes.client.openapi.models.V1ClusterRoleBinding; import io.kubernetes.client.openapi.models.V1ConfigMap; import io.kubernetes.client.openapi.models.V1Deployment; import io.kubernetes.client.openapi.models.V1DeploymentCondition; import io.kubernetes.client.openapi.models.V1DeploymentList; -import io.kubernetes.client.openapi.models.V1Ingress; -import io.kubernetes.client.openapi.models.V1IngressLoadBalancerIngress; -import io.kubernetes.client.openapi.models.V1IngressLoadBalancerStatus; import io.kubernetes.client.openapi.models.V1NamespaceBuilder; import io.kubernetes.client.openapi.models.V1Role; import io.kubernetes.client.openapi.models.V1RoleBinding; @@ -55,7 +49,6 @@ import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.openapi.models.V1ServiceAccount; import io.kubernetes.client.util.Config; -import io.kubernetes.client.util.PatchUtils; import io.kubernetes.client.util.Yaml; import jakarta.annotation.Nullable; import org.apache.commons.logging.Log; @@ -82,8 +75,6 @@ public final class Util { private final AppsV1Api appsV1Api; - private final NetworkingV1Api networkingV1Api; - private final RbacAuthorizationV1Api rbacApi; private final K3sContainer container; @@ -104,7 +95,6 @@ public final class Util { this.container = container; this.coreV1Api = new CoreV1Api(); this.appsV1Api = new AppsV1Api(); - this.networkingV1Api = new NetworkingV1Api(); rbacApi = new RbacAuthorizationV1Api(); } @@ -116,7 +106,7 @@ public final class Util { * */ public void createAndWait(String namespace, String name, V1Deployment deployment, V1Service service, - @Nullable V1Ingress ingress, boolean changeVersion) { + boolean changeVersion) { try { coreV1Api.createNamespacedService(namespace, service, null, null, null, null); @@ -146,10 +136,6 @@ public final class Util { waitForDeployment(namespace, deployment); } - if (ingress != null) { - networkingV1Api.createNamespacedIngress(namespace, ingress, null, null, null, null); - waitForIngress(namespace, ingress); - } } catch (Exception e) { if (e instanceof ApiException apiException) { @@ -207,8 +193,7 @@ public final class Util { } } - public void deleteAndWait(String namespace, V1Deployment deployment, V1Service service, - @Nullable V1Ingress ingress) { + public void deleteAndWait(String namespace, V1Deployment deployment, V1Service service) { if (deployment != null) { try { @@ -223,26 +208,16 @@ public final class Util { labelSelector(podLabels), null, null, null, null, null, null, null, null); waitForDeploymentToBeDeleted(deploymentName, namespace); waitForDeploymentPodsToBeDeleted(podLabels, namespace); + + service.getMetadata().setNamespace(namespace); + coreV1Api.deleteNamespacedService(service.getMetadata().getName(), service.getMetadata().getNamespace(), + null, null, null, null, null, null); } catch (Exception e) { throw new RuntimeException(e); } } - - String serviceName = serviceName(service); - try { - coreV1Api.deleteNamespacedService(serviceName, namespace, null, null, null, null, null, null); - if (ingress != null) { - String ingressName = ingressName(ingress); - networkingV1Api.deleteNamespacedIngress(ingressName, namespace, null, null, null, null, null, null); - waitForIngressToBeDeleted(ingressName, namespace); - } - - } - catch (ApiException e) { - throw new RuntimeException(e); - } } public void busybox(String namespace, Phase phase) { @@ -254,10 +229,10 @@ public final class Util { V1Service service = (V1Service) yaml("busybox/service.yaml"); if (phase.equals(Phase.CREATE)) { - createAndWait(namespace, "busybox", deployment, service, null, false); + createAndWait(namespace, "busybox", deployment, service, false); } else if (phase.equals(Phase.DELETE)) { - deleteAndWait(namespace, deployment, service, null); + deleteAndWait(namespace, deployment, service); } } @@ -271,10 +246,10 @@ public final class Util { V1Service service = (V1Service) yaml("kafka/kafka-service.yaml"); if (phase.equals(Phase.CREATE)) { - createAndWait(namespace, "kafka", deployment, service, null, false); + createAndWait(namespace, "kafka", deployment, service, false); } else if (phase.equals(Phase.DELETE)) { - deleteAndWait(namespace, deployment, service, null); + deleteAndWait(namespace, deployment, service); } } @@ -288,10 +263,10 @@ public final class Util { V1Service service = (V1Service) yaml("rabbitmq/rabbitmq-service.yaml"); if (phase.equals(Phase.CREATE)) { - createAndWait(namespace, "rabbitmq", deployment, service, null, false); + createAndWait(namespace, "rabbitmq", deployment, service, false); } else if (phase.equals(Phase.DELETE)) { - deleteAndWait(namespace, deployment, service, null); + deleteAndWait(namespace, deployment, service); } } @@ -335,49 +310,6 @@ public final class Util { } - public void setUpClusterWideClusterRoleBinding(String serviceAccountNamespace) { - - try { - V1ServiceAccount serviceAccount = (V1ServiceAccount) yaml("cluster/service-account.yaml"); - CheckedSupplier accountSupplier = () -> coreV1Api - .readNamespacedServiceAccount(serviceAccount.getMetadata().getName(), serviceAccountNamespace, null); - CheckedSupplier accountDefaulter = () -> coreV1Api - .createNamespacedServiceAccount(serviceAccountNamespace, serviceAccount, null, null, null, null); - notExistsHandler(accountSupplier, accountDefaulter); - - V1ClusterRole clusterRole = (V1ClusterRole) yaml("cluster/cluster-role.yaml"); - notExistsHandler(() -> rbacApi.readClusterRole(clusterRole.getMetadata().getName(), null), - () -> rbacApi.createClusterRole(clusterRole, null, null, null, null)); - - V1ClusterRoleBinding clusterRoleBinding = (V1ClusterRoleBinding) yaml("cluster/cluster-role-binding.yaml"); - notExistsHandler(() -> rbacApi.readClusterRoleBinding(clusterRoleBinding.getMetadata().getName(), null), - () -> rbacApi.createClusterRoleBinding(clusterRoleBinding, null, null, null, null)); - } - catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - - } - - public void deleteClusterWideClusterRoleBinding(String serviceAccountNamespace) { - try { - V1ServiceAccount serviceAccount = (V1ServiceAccount) yaml("cluster/service-account.yaml"); - V1ClusterRole clusterRole = (V1ClusterRole) yaml("cluster/cluster-role.yaml"); - V1ClusterRoleBinding clusterRoleBinding = (V1ClusterRoleBinding) yaml("cluster/cluster-role-binding.yaml"); - - coreV1Api.deleteNamespacedServiceAccount(serviceAccount.getMetadata().getName(), serviceAccountNamespace, - null, null, null, null, null, null); - rbacApi.deleteClusterRole(clusterRole.getMetadata().getName(), null, null, null, null, null, null); - rbacApi.deleteClusterRoleBinding(clusterRoleBinding.getMetadata().getName(), null, null, null, null, null, - null); - } - catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - public void setUpClusterWide(String serviceAccountNamespace, Set namespaces) { try { @@ -481,11 +413,7 @@ public final class Util { .noneMatch(x -> x.getMetadata().getName().equals(name))); } - public void wiremock(String namespace, String path, Phase phase) { - wiremock(namespace, path, phase, true); - } - - public void wiremock(String namespace, String path, Phase phase, boolean withIngress) { + public void wiremock(String namespace, Phase phase, boolean withNodePort) { V1Deployment deployment = (V1Deployment) yaml("wiremock/wiremock-deployment.yaml"); String imageWithoutVersion = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage(); @@ -493,77 +421,28 @@ public final class Util { deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(imageWithVersion); V1Service service = (V1Service) yaml("wiremock/wiremock-service.yaml"); - - V1Ingress ingress = null; + service.getMetadata().setNamespace(namespace); + if (!withNodePort) { + // we assume we only have one 'http' port + service.getSpec().getPorts().get(0).setNodePort(null); + service.getSpec().setType("ClusterIP"); + } if (phase.equals(Phase.CREATE)) { - - if (withIngress) { - ingress = (V1Ingress) yaml("wiremock/wiremock-ingress.yaml"); - ingress.getMetadata().setNamespace(namespace); - ingress.getSpec().getRules().get(0).getHttp().getPaths().get(0).setPath(path); - } - deployment.getMetadata().setNamespace(namespace); service.getMetadata().setNamespace(namespace); - createAndWait(namespace, "wiremock", deployment, service, ingress, false); + createAndWait(namespace, "wiremock", deployment, service, false); } else { - if (withIngress) { - ingress = (V1Ingress) yaml("wiremock/wiremock-ingress.yaml"); - } - deleteAndWait(namespace, deployment, service, ingress); + deleteAndWait(namespace, deployment, service); } } - public static void patchWithMerge(String deploymentName, String namespace, String patchBody, - Map podLabels) { - try { - PatchUtils.patch(V1Deployment.class, - () -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, - new V1Patch(patchBody), null, null, null, null, null, null), - V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, new CoreV1Api().getApiClient()); - } - catch (ApiException e) { - LOG.error("error : " + e.getResponseBody()); - throw new RuntimeException(e); - } - - waitForDeploymentAfterPatch(deploymentName, namespace, podLabels); - } - - public static void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody, - Map podLabels) { - String body = patchBody.replace("image_name_here", imageName); - - try { - PatchUtils.patch(V1Deployment.class, - () -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, new V1Patch(body), - null, null, null, null, null, null), - V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, new CoreV1Api().getApiClient()); - } - catch (ApiException e) { - LOG.error("error : " + e.getResponseBody()); - throw new RuntimeException(e); - } - - waitForDeploymentAfterPatch(deploymentName, namespace, podLabels); - - } - private String deploymentName(V1Deployment deployment) { return deployment.getMetadata().getName(); } - private String serviceName(V1Service service) { - return service.getMetadata().getName(); - } - - private String ingressName(V1Ingress ingress) { - return ingress.getMetadata().getName(); - } - private String configMapName(V1ConfigMap configMap) { return configMap.getMetadata().getName(); } @@ -613,43 +492,6 @@ public final class Util { }); } - private void waitForIngress(String namespace, V1Ingress ingress) { - String ingressName = ingressName(ingress); - await().timeout(Duration.ofSeconds(90)).pollInterval(Duration.ofSeconds(3)).until(() -> { - try { - V1IngressLoadBalancerStatus status = networkingV1Api.readNamespacedIngress(ingressName, namespace, null) - .getStatus() - .getLoadBalancer(); - - if (status == null) { - LOG.info("ingress : " + ingressName + " not ready yet (loadbalancer not yet present)"); - return false; - } - - List loadBalancerIngress = status.getIngress(); - if (loadBalancerIngress == null) { - LOG.info("ingress : " + ingressName + " not ready yet (loadbalancer ingress not yet present)"); - return false; - } - - String ip = loadBalancerIngress.get(0).getIp(); - if (ip == null) { - LOG.info("ingress : " + ingressName + " not ready yet"); - return false; - } - - LOG.info("ingress : " + ingressName + " ready with ip : " + ip); - return true; - } - catch (ApiException e) { - if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) { - return false; - } - throw new RuntimeException(e); - } - }); - } - private void waitForDeploymentToBeDeleted(String deploymentName, String namespace) { await().timeout(Duration.ofSeconds(180)).until(() -> { try { @@ -684,21 +526,6 @@ public final class Util { }); } - private void waitForIngressToBeDeleted(String ingressName, String namespace) { - await().timeout(Duration.ofSeconds(90)).until(() -> { - try { - networkingV1Api.readNamespacedIngress(ingressName, namespace, null); - return false; - } - catch (ApiException e) { - if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) { - return true; - } - throw new RuntimeException(e); - } - }); - } - private boolean isDeploymentReady(String deploymentName, String namespace) throws ApiException { V1DeploymentList deployments = appsV1Api.listNamespacedDeployment(namespace, null, null, null, "metadata.name=" + deploymentName, null, null, null, null, null, null, null); @@ -729,24 +556,6 @@ public final class Util { } } - 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, podLabels)); - } - catch (Exception e) { - if (e instanceof ApiException apiException) { - LOG.error("Error: "); - LOG.error(apiException.getResponseBody()); - } - throw new RuntimeException(e); - } - - } - private static boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace, Map podLabels) throws ApiException { diff --git a/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-ingress.yaml b/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-ingress.yaml deleted file mode 100644 index 18d50519..00000000 --- a/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-ingress.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: wiremock-ingress - namespace: default -spec: - rules: - - http: - paths: - - path: /wiremock/ - pathType: Prefix - backend: - service: - name: service-wiremock - port: - number: 8080 - diff --git a/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-service.yaml b/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-service.yaml index af4d4e00..57b31e31 100644 --- a/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-service.yaml +++ b/spring-cloud-kubernetes-test-support/src/main/resources/wiremock/wiremock-service.yaml @@ -9,6 +9,7 @@ spec: - name: http port: 8080 targetPort: 8080 + nodePort: 32321 selector: app: service-wiremock - type: ClusterIP + type: NodePort