Skip to content

Commit

Permalink
Merge branch '3.2.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanjbaxter committed Jan 31, 2025
2 parents 1c266c8 + 8fd8cb2 commit a33086e
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 373 deletions.
2 changes: 1 addition & 1 deletion spring-cloud-kubernetes-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<properties>
<kubernetes-fabric8-client.version>6.13.5</kubernetes-fabric8-client.version>
<kubernetes-native-client.version>19.0.2</kubernetes-native-client.version>
<wiremock.version>3.4.2</wiremock.version>
<wiremock.version>3.9.1</wiremock.version>
</properties>
<dependencyManagement>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.utils.Serialization;
import org.assertj.core.api.Assertions;
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;
Expand Down Expand Up @@ -106,7 +106,7 @@ void test() {
.block();

// istio profile is present
Assertions.assertTrue(result.contains("istio"));
Assertions.assertThat(result).contains("istio");
}

private static void appManifests(Phase phase) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-cloud-kubernetes-k8s-client-configuration-watcher</artifactId>
<packaging>jar</packaging>

<properties>
<wiremock.version>3.4.2</wiremock.version>
<spring-boot.repackage.skip>true</spring-boot.repackage.skip>
<spring-boot.build-image.skip>true</spring-boot.build-image.skip>
</properties>

<dependencies>
Expand All @@ -40,7 +40,6 @@
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>${wiremock.version}</version>
<scope>test</scope>
</dependency>

Expand All @@ -67,29 +66,5 @@
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>build-image</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
<execution>
<id>repackage</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* 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.
Expand All @@ -16,48 +16,38 @@

package org.springframework.cloud.kubernetes.configuration.watcher;

import java.net.SocketTimeoutException;
import java.time.Duration;
import java.util.List;

import com.github.tomakehurst.wiremock.client.WireMock;
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.V1Service;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Container;
import org.testcontainers.k3s.K3sContainer;

import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Images;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;

import static org.awaitility.Awaitility.await;
import static org.springframework.cloud.kubernetes.configuration.watcher.TestUtil.SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME;
import static org.springframework.cloud.kubernetes.configuration.watcher.TestUtil.configureWireMock;
import static org.springframework.cloud.kubernetes.configuration.watcher.TestUtil.createConfigMap;
import static org.springframework.cloud.kubernetes.configuration.watcher.TestUtil.deleteConfigMap;
import static org.springframework.cloud.kubernetes.configuration.watcher.TestUtil.verifyActuatorCalled;

/**
* @author Ryan Baxter
*/
class ActuatorRefreshIT {

private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME = "spring-cloud-kubernetes-configuration-watcher";

private static final String WIREMOCK_HOST = "localhost";

private static final String WIREMOCK_PATH = "/";

private static final int WIREMOCK_PORT = 80;

private static final String NAMESPACE = "default";

private static final String DOCKER_IMAGE = "docker.io/springcloud/" + SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME + ":"
+ Commons.pomVersion();

private static final K3sContainer K3S = Commons.container();

private static Util util;
Expand All @@ -67,7 +57,6 @@ static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);

Images.loadWiremock(K3S);

util = new Util(K3S);
Expand All @@ -92,155 +81,45 @@ void after() {
}

/*
* this test loads uses two services: wiremock on port 8080 and configuration-watcher
* on port 8888. we deploy configuration-watcher first and configure it via a
* configmap with the same name. then, we mock the call to actuator/refresh endpoint
* and deploy a new configmap: "service-wiremock", this in turn will trigger that
* This test loads two services: wiremock on port 8080 and configuration-watcher on
* port 8888. We deploy configuration-watcher first and configure its env variables
* that we need for this test. Then, we mock the call to actuator/refresh endpoint and
* deploy a new configmap: "service-wiremock". Because This in turn will trigger a
* refresh that we capture and assert for.
*/
// curl <WIREMOCK_POD_IP>:8080/__admin/mappings
@Test
void testActuatorRefresh() {

WireMock.configureFor(WIREMOCK_HOST, WIREMOCK_PORT);
await().timeout(Duration.ofSeconds(60))
.ignoreException(SocketTimeoutException.class)
.until(() -> WireMock
.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
.willReturn(WireMock.aResponse().withBody("{}").withStatus(200)))
.getResponse()
.wasConfigured());

createConfigMap();

// Wait a bit before we verify
await().atMost(Duration.ofSeconds(30))
.until(() -> !WireMock.findAll(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")))
.isEmpty());
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")));

deleteConfigMap();

// the other test
testActuatorRefreshReloadDisabled();

}

void testActuatorShutdown() {
TestUtil.patchForShutdownRefresh(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, DOCKER_IMAGE);
WireMock.configureFor(WIREMOCK_HOST, WIREMOCK_PORT);
await().timeout(Duration.ofSeconds(60))
.ignoreException(SocketTimeoutException.class)
.until(() -> WireMock
.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/shutdown"))
.willReturn(WireMock.aResponse().withBody("{}").withStatus(200)))
.getResponse()
.wasConfigured());

createConfigMap();

// Wait a bit before we verify
await().atMost(Duration.ofSeconds(30))
.until(() -> !WireMock.findAll(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/shutdown")))
.isEmpty());
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/shutdown")));

deleteConfigMap();

// the other test
testActuatorRefreshReloadDisabled();

}

/*
* same test as above, but reload is disabled.
*/
void testActuatorRefreshReloadDisabled() {

TestUtil.patchForDisabledReload(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, DOCKER_IMAGE);

WireMock.configureFor(WIREMOCK_HOST, WIREMOCK_PORT);
await().timeout(Duration.ofSeconds(60))
.until(() -> WireMock
.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
.willReturn(WireMock.aResponse().withBody("{}").withStatus(200)))
.getResponse()
.wasConfigured());

createConfigMap();

// Wait a bit before we verify
await().atMost(Duration.ofSeconds(30))
.until(() -> !WireMock.findAll(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")))
.isEmpty());
configureWireMock();
createConfigMap(util, NAMESPACE);
verifyActuatorCalled(1);

Commons.waitForLogStatement("creating NOOP strategy because reload is disabled", K3S,
SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME);

// nothing related to 'ConfigReloadUtil' is present in logs
// this proves that once we disable reload everything still works
Assertions.assertFalse(logs().contains("ConfigReloadUtil"));
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")));

deleteConfigMap();

deleteConfigMap(util, NAMESPACE);
}

private static void configWatcher(Phase phase) {
V1ConfigMap configMap = (V1ConfigMap) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-configmap.yaml");
V1Deployment deployment = (V1Deployment) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-deployment.yaml");
V1Service service = (V1Service) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");

List<V1EnvVar> envVars = List.of(
new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_CONFIGURATION_WATCHER_REFRESHDELAY").value("0"),
new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED").value("FALSE"),
new V1EnvVar().name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CONFIGURATION_WATCHER")
.value("DEBUG"));

deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);

if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, configMap, null);
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
}
else {
util.deleteAndWait(NAMESPACE, configMap, null);
util.deleteAndWait(NAMESPACE, deployment, service, null);
}

}

// Create new configmap to trigger controller to signal app to refresh
private void createConfigMap() {
V1ConfigMap configMap = new V1ConfigMapBuilder().editOrNewMetadata()
.withName("service-wiremock")
.addToLabels("spring.cloud.kubernetes.config", "true")
.endMetadata()
.addToData("foo", "bar")
.build();
util.createAndWait(NAMESPACE, configMap, null);
}

private void deleteConfigMap() {
V1ConfigMap configMap = new V1ConfigMapBuilder().editOrNewMetadata()
.withName("service-wiremock")
.addToLabels("spring.cloud.kubernetes.config", "true")
.endMetadata()
.addToData("foo", "bar")
.build();
util.deleteAndWait(NAMESPACE, configMap, null);
}

private String logs() {
try {
String appPodName = K3S
.execInContainer("sh", "-c",
"kubectl get pods -l app=" + SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME
+ " -o=name --no-headers | tr -d '\n'")
.getStdout();

Container.ExecResult execResult = K3S.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim());
return execResult.getStdout();
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

}
Loading

0 comments on commit a33086e

Please sign in to comment.