Merge branch '3.1.x'
This commit is contained in:
@@ -20,9 +20,11 @@ import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.stubbing.Scenario;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.Configuration;
|
||||
import io.kubernetes.client.openapi.JSON;
|
||||
@@ -59,8 +61,10 @@ import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
|
||||
/**
|
||||
@@ -81,7 +85,11 @@ class EventReloadConfigMapTest {
|
||||
|
||||
private static final String NAMESPACE = "spring-k8s";
|
||||
|
||||
private static final boolean[] strategyCalled = new boolean[] { false };
|
||||
private static final String SCENARIO_NAME = "reload-test";
|
||||
|
||||
private static final String PATH = "/api/v1/namespaces/spring-k8s/configmaps";
|
||||
|
||||
private static final AtomicBoolean STRATEGY_CALLED = new AtomicBoolean(false);
|
||||
|
||||
private static CoreV1Api coreV1Api;
|
||||
|
||||
@@ -98,6 +106,12 @@ class EventReloadConfigMapTest {
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor("localhost", wireMockServer.port());
|
||||
|
||||
// something that the informer can work with. Since we do not care about this one
|
||||
// in the test, we mock it to return a 500 as it does not matter anyway.
|
||||
stubFor(get(urlPathMatching(PATH)).withQueryParam("resourceVersion", equalTo("0"))
|
||||
.withQueryParam("watch", equalTo("false"))
|
||||
.willReturn(aResponse().withStatus(500).withBody("Error From Informer")));
|
||||
|
||||
ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
|
||||
client.setDebugging(true);
|
||||
MOCK_STATIC.when(KubernetesClientUtils::createApiClientForInformerClient).thenReturn(client);
|
||||
@@ -107,30 +121,6 @@ class EventReloadConfigMapTest {
|
||||
.thenReturn(NAMESPACE);
|
||||
Configuration.setDefaultApiClient(client);
|
||||
coreV1Api = new CoreV1Api();
|
||||
|
||||
String path = "/api/v1/namespaces/spring-k8s/configmaps";
|
||||
V1ConfigMap configMapOne = configMap(CONFIG_MAP_NAME, Map.of());
|
||||
V1ConfigMapList listOne = new V1ConfigMapList().addItemsItem(configMapOne);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listOne)))
|
||||
.inScenario("mine-test")
|
||||
.willSetStateTo("go-to-fail"));
|
||||
|
||||
// first call will fail
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))
|
||||
.inScenario("mine-test")
|
||||
.whenScenarioStateIs("go-to-fail")
|
||||
.willSetStateTo("go-to-ok"));
|
||||
|
||||
// second call passes (change data so that reload is triggered)
|
||||
configMapOne = configMap(CONFIG_MAP_NAME, Map.of("a", "b"));
|
||||
listOne = new V1ConfigMapList().addItemsItem(configMapOne);
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listOne)))
|
||||
.inScenario("mine-test")
|
||||
.whenScenarioStateIs("go-to-ok")
|
||||
.willSetStateTo("done"));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@@ -149,6 +139,13 @@ class EventReloadConfigMapTest {
|
||||
*/
|
||||
@Test
|
||||
void test(CapturedOutput output) {
|
||||
|
||||
// first call will fail
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs("go-to-fail")
|
||||
.willSetStateTo("go-to-ok"));
|
||||
|
||||
V1ConfigMap configMapNotMine = configMap("not" + CONFIG_MAP_NAME, Map.of());
|
||||
kubernetesClientEventBasedConfigMapChangeDetector.onEvent(configMapNotMine);
|
||||
|
||||
@@ -158,17 +155,25 @@ class EventReloadConfigMapTest {
|
||||
boolean two = output.getOut().contains("Failed to load source");
|
||||
boolean three = output.getOut()
|
||||
.contains("Reloadable condition was not satisfied, reload will not be triggered");
|
||||
boolean updateStrategyNotCalled = !strategyCalled[0];
|
||||
boolean updateStrategyNotCalled = !STRATEGY_CALLED.get();
|
||||
return one && two && three && updateStrategyNotCalled;
|
||||
});
|
||||
|
||||
// second call passes (change data so that reload is triggered)
|
||||
V1ConfigMap configMap = configMap(CONFIG_MAP_NAME, Map.of("a", "b"));
|
||||
V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(configMap);
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMapList)))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs("go-to-ok")
|
||||
.willSetStateTo("done"));
|
||||
|
||||
// trigger the call again
|
||||
V1ConfigMap configMapMine = configMap(CONFIG_MAP_NAME, Map.of());
|
||||
kubernetesClientEventBasedConfigMapChangeDetector.onEvent(configMapMine);
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> strategyCalled[0]);
|
||||
.until(STRATEGY_CALLED::get);
|
||||
}
|
||||
|
||||
private static V1ConfigMap configMap(String name, Map<String, String> data) {
|
||||
@@ -193,6 +198,17 @@ class EventReloadConfigMapTest {
|
||||
@Bean
|
||||
@Primary
|
||||
AbstractEnvironment environment() {
|
||||
|
||||
V1ConfigMap configMap = configMap(CONFIG_MAP_NAME, Map.of());
|
||||
V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(configMap);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMapList)))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs(Scenario.STARTED)
|
||||
.willSetStateTo("go-to-fail"));
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty("spring.cloud.kubernetes.client.namespace", NAMESPACE);
|
||||
|
||||
@@ -216,7 +232,7 @@ class EventReloadConfigMapTest {
|
||||
@Primary
|
||||
ConfigReloadProperties configReloadProperties() {
|
||||
return new ConfigReloadProperties(true, true, false, ConfigReloadProperties.ReloadStrategy.REFRESH,
|
||||
ConfigReloadProperties.ReloadDetectionMode.POLLING, Duration.ofMillis(2000), Set.of("non-default"),
|
||||
ConfigReloadProperties.ReloadDetectionMode.POLLING, Duration.ofMillis(2000), Set.of("spring-k8s"),
|
||||
false, Duration.ofSeconds(2));
|
||||
}
|
||||
|
||||
@@ -237,7 +253,7 @@ class EventReloadConfigMapTest {
|
||||
@Primary
|
||||
ConfigurationUpdateStrategy configurationUpdateStrategy() {
|
||||
return new ConfigurationUpdateStrategy("to-console", () -> {
|
||||
strategyCalled[0] = true;
|
||||
STRATEGY_CALLED.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,12 @@ import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.stubbing.Scenario;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.Configuration;
|
||||
import io.kubernetes.client.openapi.JSON;
|
||||
@@ -61,8 +63,10 @@ import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
|
||||
/**
|
||||
@@ -83,7 +87,11 @@ class EventReloadSecretTest {
|
||||
|
||||
private static final String NAMESPACE = "spring-k8s";
|
||||
|
||||
private static final boolean[] strategyCalled = new boolean[] { false };
|
||||
private static final String PATH = "/api/v1/namespaces/spring-k8s/secrets";
|
||||
|
||||
private static final String SCENARIO_NAME = "reload-test";
|
||||
|
||||
private static final AtomicBoolean STRATEGY_CALLED = new AtomicBoolean(false);
|
||||
|
||||
private static CoreV1Api coreV1Api;
|
||||
|
||||
@@ -100,6 +108,12 @@ class EventReloadSecretTest {
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor("localhost", wireMockServer.port());
|
||||
|
||||
// something that the informer can work with. Since we do not care about this one
|
||||
// in the test, we mock it to return a 500 as it does not matter anyway.
|
||||
stubFor(get(urlPathMatching(PATH)).withQueryParam("resourceVersion", equalTo("0"))
|
||||
.withQueryParam("watch", equalTo("false"))
|
||||
.willReturn(aResponse().withStatus(500).withBody("Error From Informer")));
|
||||
|
||||
ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
|
||||
client.setDebugging(true);
|
||||
MOCK_STATIC.when(KubernetesClientUtils::createApiClientForInformerClient).thenReturn(client);
|
||||
@@ -109,30 +123,6 @@ class EventReloadSecretTest {
|
||||
.thenReturn(NAMESPACE);
|
||||
Configuration.setDefaultApiClient(client);
|
||||
coreV1Api = new CoreV1Api();
|
||||
|
||||
String path = "/api/v1/namespaces/spring-k8s/secrets";
|
||||
V1Secret secretOne = secret(SECRET_NAME, Map.of());
|
||||
V1SecretList listOne = new V1SecretList().addItemsItem(secretOne);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listOne)))
|
||||
.inScenario("mine-test")
|
||||
.willSetStateTo("go-to-fail"));
|
||||
|
||||
// first call will fail
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))
|
||||
.inScenario("mine-test")
|
||||
.whenScenarioStateIs("go-to-fail")
|
||||
.willSetStateTo("go-to-ok"));
|
||||
|
||||
// second call passes (change data so that reload is triggered)
|
||||
secretOne = secret(SECRET_NAME, Map.of("a", "b"));
|
||||
listOne = new V1SecretList().addItemsItem(secretOne);
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listOne)))
|
||||
.inScenario("mine-test")
|
||||
.whenScenarioStateIs("go-to-ok")
|
||||
.willSetStateTo("done"));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@@ -151,6 +141,12 @@ class EventReloadSecretTest {
|
||||
*/
|
||||
@Test
|
||||
void test(CapturedOutput output) {
|
||||
// first call will fail
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs("go-to-fail")
|
||||
.willSetStateTo("go-to-ok"));
|
||||
|
||||
V1Secret secretNotMine = secret("not" + SECRET_NAME, Map.of());
|
||||
kubernetesClientEventBasedSecretsChangeDetector.onEvent(secretNotMine);
|
||||
|
||||
@@ -160,17 +156,25 @@ class EventReloadSecretTest {
|
||||
boolean two = output.getOut().contains("Failed to load source");
|
||||
boolean three = output.getOut()
|
||||
.contains("Reloadable condition was not satisfied, reload will not be triggered");
|
||||
boolean updateStrategyNotCalled = !strategyCalled[0];
|
||||
boolean updateStrategyNotCalled = !STRATEGY_CALLED.get();
|
||||
return one && two && three && updateStrategyNotCalled;
|
||||
});
|
||||
|
||||
// second call passes (change data so that reload is triggered)
|
||||
V1Secret secret = secret(SECRET_NAME, Map.of("a", "b"));
|
||||
V1SecretList secretList = new V1SecretList().addItemsItem(secret);
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(secretList)))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs("go-to-ok")
|
||||
.willSetStateTo("done"));
|
||||
|
||||
// trigger the call again
|
||||
V1Secret secretMine = secret(SECRET_NAME, Map.of());
|
||||
kubernetesClientEventBasedSecretsChangeDetector.onEvent(secretMine);
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> strategyCalled[0]);
|
||||
.until(STRATEGY_CALLED::get);
|
||||
}
|
||||
|
||||
private static V1Secret secret(String name, Map<String, String> data) {
|
||||
@@ -199,6 +203,17 @@ class EventReloadSecretTest {
|
||||
@Bean
|
||||
@Primary
|
||||
AbstractEnvironment environment() {
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
V1Secret secret = secret(SECRET_NAME, Map.of());
|
||||
V1SecretList secretList = new V1SecretList().addItemsItem(secret);
|
||||
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(secretList)))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs(Scenario.STARTED)
|
||||
.willSetStateTo("go-to-fail"));
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty("spring.cloud.kubernetes.client.namespace", NAMESPACE);
|
||||
|
||||
@@ -221,7 +236,7 @@ class EventReloadSecretTest {
|
||||
@Primary
|
||||
ConfigReloadProperties configReloadProperties() {
|
||||
return new ConfigReloadProperties(true, true, false, ConfigReloadProperties.ReloadStrategy.REFRESH,
|
||||
ConfigReloadProperties.ReloadDetectionMode.POLLING, Duration.ofMillis(2000), Set.of("non-default"),
|
||||
ConfigReloadProperties.ReloadDetectionMode.POLLING, Duration.ofMillis(2000), Set.of("spring-k8s"),
|
||||
false, Duration.ofSeconds(2));
|
||||
}
|
||||
|
||||
@@ -242,7 +257,7 @@ class EventReloadSecretTest {
|
||||
@Primary
|
||||
ConfigurationUpdateStrategy configurationUpdateStrategy() {
|
||||
return new ConfigurationUpdateStrategy("to-console", () -> {
|
||||
strategyCalled[0] = true;
|
||||
STRATEGY_CALLED.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.stubbing.Scenario;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.Configuration;
|
||||
import io.kubernetes.client.openapi.JSON;
|
||||
@@ -72,18 +74,22 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class PollingReloadConfigMapTest {
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
private static final boolean FAIL_FAST = false;
|
||||
|
||||
private static final String CONFIG_MAP_NAME = "mine";
|
||||
|
||||
private static final String PATH = "/api/v1/namespaces/spring-k8s/configmaps";
|
||||
|
||||
private static final String NAMESPACE = "spring-k8s";
|
||||
|
||||
private static final boolean[] strategyCalled = new boolean[] { false };
|
||||
private static final String SCENARIO_NAME = "reload-test";
|
||||
|
||||
private static final AtomicBoolean STRATEGY_CALLED = new AtomicBoolean(false);
|
||||
|
||||
private static CoreV1Api coreV1Api;
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() {
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
@@ -95,30 +101,6 @@ class PollingReloadConfigMapTest {
|
||||
client.setDebugging(true);
|
||||
Configuration.setDefaultApiClient(client);
|
||||
coreV1Api = new CoreV1Api();
|
||||
|
||||
String path = "/api/v1/namespaces/spring-k8s/configmaps";
|
||||
V1ConfigMap configMapOne = configMap(CONFIG_MAP_NAME, Map.of());
|
||||
V1ConfigMapList listOne = new V1ConfigMapList().addItemsItem(configMapOne);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listOne)))
|
||||
.inScenario("my-test")
|
||||
.willSetStateTo("go-to-fail"));
|
||||
|
||||
// first reload call fails
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))
|
||||
.inScenario("my-test")
|
||||
.whenScenarioStateIs("go-to-fail")
|
||||
.willSetStateTo("go-to-ok"));
|
||||
|
||||
// second reload call passes
|
||||
V1ConfigMap configMapTwo = configMap(CONFIG_MAP_NAME, Map.of("a", "b"));
|
||||
V1ConfigMapList listTwo = new V1ConfigMapList().addItemsItem(configMapTwo);
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listTwo)))
|
||||
.inScenario("my-test")
|
||||
.whenScenarioStateIs("go-to-ok"));
|
||||
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@@ -137,21 +119,40 @@ class PollingReloadConfigMapTest {
|
||||
disabledReason = "failing on jenkins")
|
||||
@Test
|
||||
void test(CapturedOutput output) {
|
||||
|
||||
// first reload call fails
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs("go-to-fail")
|
||||
.willSetStateTo("go-to-ok"));
|
||||
|
||||
// we fail while reading 'configMapOne'
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).pollInterval(Duration.ofSeconds(1)).until(() -> {
|
||||
boolean one = output.getOut().contains("Failure in reading named sources");
|
||||
boolean two = output.getOut().contains("Failed to load source");
|
||||
boolean three = output.getOut()
|
||||
.contains("Reloadable condition was not satisfied, reload will not be triggered");
|
||||
boolean updateStrategyNotCalled = !strategyCalled[0];
|
||||
boolean updateStrategyNotCalled = !STRATEGY_CALLED.get();
|
||||
System.out.println("one: " + one + " two: " + two + " three: " + three + " updateStrategyNotCalled: "
|
||||
+ updateStrategyNotCalled);
|
||||
return one && two && three && updateStrategyNotCalled;
|
||||
});
|
||||
|
||||
// second reload call passes
|
||||
V1ConfigMap configMapTwo = configMap(CONFIG_MAP_NAME, Map.of("a", "b"));
|
||||
V1ConfigMapList listTwo = new V1ConfigMapList().addItemsItem(configMapTwo);
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listTwo)))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs("go-to-ok")
|
||||
.willSetStateTo("done"));
|
||||
|
||||
System.out.println("first assertion passed");
|
||||
|
||||
// it passes while reading 'configMapTwo'
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.atMost(Duration.ofSeconds(20))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> strategyCalled[0]);
|
||||
.until(STRATEGY_CALLED::get);
|
||||
}
|
||||
|
||||
private static V1ConfigMap configMap(String name, Map<String, String> data) {
|
||||
@@ -176,6 +177,17 @@ class PollingReloadConfigMapTest {
|
||||
@Bean
|
||||
@Primary
|
||||
AbstractEnvironment environment() {
|
||||
|
||||
V1ConfigMap configMapOne = configMap(CONFIG_MAP_NAME, Map.of());
|
||||
V1ConfigMapList listOne = new V1ConfigMapList().addItemsItem(configMapOne);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listOne)))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs(Scenario.STARTED)
|
||||
.willSetStateTo("go-to-fail"));
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty("spring.cloud.kubernetes.client.namespace", NAMESPACE);
|
||||
|
||||
@@ -219,7 +231,7 @@ class PollingReloadConfigMapTest {
|
||||
@Primary
|
||||
ConfigurationUpdateStrategy configurationUpdateStrategy() {
|
||||
return new ConfigurationUpdateStrategy("to-console", () -> {
|
||||
strategyCalled[0] = true;
|
||||
STRATEGY_CALLED.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,12 @@ import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.stubbing.Scenario;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.Configuration;
|
||||
import io.kubernetes.client.openapi.JSON;
|
||||
@@ -74,18 +76,22 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class PollingReloadSecretTest {
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
private static final boolean FAIL_FAST = false;
|
||||
|
||||
private static final String SECRET_NAME = "mine";
|
||||
|
||||
private static final String PATH = "/api/v1/namespaces/spring-k8s/secrets";
|
||||
|
||||
private static final String NAMESPACE = "spring-k8s";
|
||||
|
||||
private static final boolean[] strategyCalled = new boolean[] { false };
|
||||
private static final String SCENARIO_NAME = "reload-test";
|
||||
|
||||
private static final AtomicBoolean STRATEGY_CALLED = new AtomicBoolean(false);
|
||||
|
||||
private static CoreV1Api coreV1Api;
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() {
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
@@ -97,29 +103,6 @@ class PollingReloadSecretTest {
|
||||
client.setDebugging(true);
|
||||
Configuration.setDefaultApiClient(client);
|
||||
coreV1Api = new CoreV1Api();
|
||||
|
||||
String path = "/api/v1/namespaces/spring-k8s/secrets";
|
||||
V1Secret secretOne = secret(SECRET_NAME, Map.of());
|
||||
V1SecretList listOne = new V1SecretList().addItemsItem(secretOne);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listOne)))
|
||||
.inScenario("my-test")
|
||||
.willSetStateTo("go-to-fail"));
|
||||
|
||||
// first reload call fails
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))
|
||||
.inScenario("my-test")
|
||||
.whenScenarioStateIs("go-to-fail")
|
||||
.willSetStateTo("go-to-ok"));
|
||||
|
||||
V1Secret secretTwo = secret(SECRET_NAME, Map.of("a", "b"));
|
||||
V1SecretList listTwo = new V1SecretList().addItemsItem(secretTwo);
|
||||
stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listTwo)))
|
||||
.inScenario("my-test")
|
||||
.whenScenarioStateIs("go-to-ok"));
|
||||
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@@ -138,21 +121,35 @@ class PollingReloadSecretTest {
|
||||
disabledReason = "failing on jenkins")
|
||||
@Test
|
||||
void test(CapturedOutput output) {
|
||||
|
||||
// first reload call fails
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs("go-to-fail")
|
||||
.willSetStateTo("go-to-ok"));
|
||||
|
||||
// we fail while reading 'secretOne'
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).pollInterval(Duration.ofSeconds(1)).until(() -> {
|
||||
boolean one = output.getOut().contains("Failure in reading named sources");
|
||||
boolean two = output.getOut().contains("Failed to load source");
|
||||
boolean three = output.getOut()
|
||||
.contains("Reloadable condition was not satisfied, reload will not be triggered");
|
||||
boolean updateStrategyNotCalled = !strategyCalled[0];
|
||||
boolean updateStrategyNotCalled = !STRATEGY_CALLED.get();
|
||||
return one && two && three && updateStrategyNotCalled;
|
||||
});
|
||||
|
||||
V1Secret secretTwo = secret(SECRET_NAME, Map.of("a", "b"));
|
||||
V1SecretList listTwo = new V1SecretList().addItemsItem(secretTwo);
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listTwo)))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.willSetStateTo("done")
|
||||
.whenScenarioStateIs("go-to-ok"));
|
||||
|
||||
// it passes while reading 'secretTwo'
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.atMost(Duration.ofSeconds(20))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> strategyCalled[0]);
|
||||
.until(STRATEGY_CALLED::get);
|
||||
}
|
||||
|
||||
private static V1Secret secret(String name, Map<String, String> data) {
|
||||
@@ -182,6 +179,17 @@ class PollingReloadSecretTest {
|
||||
@Bean
|
||||
@Primary
|
||||
AbstractEnvironment environment() {
|
||||
|
||||
V1Secret secretOne = secret(SECRET_NAME, Map.of());
|
||||
V1SecretList listOne = new V1SecretList().addItemsItem(secretOne);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
stubFor(get(PATH).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(listOne)))
|
||||
.inScenario(SCENARIO_NAME)
|
||||
.whenScenarioStateIs(Scenario.STARTED)
|
||||
.willSetStateTo("go-to-fail"));
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty("spring.cloud.kubernetes.client.namespace", NAMESPACE);
|
||||
|
||||
@@ -225,7 +233,7 @@ class PollingReloadSecretTest {
|
||||
@Primary
|
||||
ConfigurationUpdateStrategy configurationUpdateStrategy() {
|
||||
return new ConfigurationUpdateStrategy("to-console", () -> {
|
||||
strategyCalled[0] = true;
|
||||
STRATEGY_CALLED.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ public final class ConfigReloadUtil {
|
||||
|
||||
boolean changed = changed(sourceFromK8s, existingSources);
|
||||
if (changed) {
|
||||
LOG.info("Detected change in config maps/secrets, reload will ne triggered");
|
||||
LOG.info("Detected change in config maps/secrets, reload will be triggered");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
||||
@@ -72,7 +73,7 @@ public class EventReloadConfigMapTest {
|
||||
|
||||
private static KubernetesClient kubernetesClient;
|
||||
|
||||
private static final boolean[] strategyCalled = new boolean[] { false };
|
||||
private static final AtomicBoolean STRATEGY_CALLED = new AtomicBoolean(false);
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
@@ -118,7 +119,7 @@ public class EventReloadConfigMapTest {
|
||||
boolean two = output.getOut().contains("Failed to load source");
|
||||
boolean three = output.getOut()
|
||||
.contains("Reloadable condition was not satisfied, reload will not be triggered");
|
||||
boolean updateStrategyNotCalled = !strategyCalled[0];
|
||||
boolean updateStrategyNotCalled = !STRATEGY_CALLED.get();
|
||||
return one && two && three && updateStrategyNotCalled;
|
||||
});
|
||||
|
||||
@@ -132,7 +133,7 @@ public class EventReloadConfigMapTest {
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> strategyCalled[0]);
|
||||
.until(STRATEGY_CALLED::get);
|
||||
}
|
||||
|
||||
private static ConfigMap configMap(String name, Map<String, String> data) {
|
||||
@@ -197,7 +198,7 @@ public class EventReloadConfigMapTest {
|
||||
@Primary
|
||||
ConfigurationUpdateStrategy configurationUpdateStrategy() {
|
||||
return new ConfigurationUpdateStrategy("to-console", () -> {
|
||||
strategyCalled[0] = true;
|
||||
STRATEGY_CALLED.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Secret;
|
||||
@@ -75,7 +76,7 @@ class EventReloadSecretTest {
|
||||
|
||||
private static KubernetesClient kubernetesClient;
|
||||
|
||||
private static final boolean[] strategyCalled = new boolean[] { false };
|
||||
private static final AtomicBoolean STRATEGY_CALLED = new AtomicBoolean(false);
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
@@ -120,7 +121,7 @@ class EventReloadSecretTest {
|
||||
boolean two = output.getOut().contains("Failed to load source");
|
||||
boolean three = output.getOut()
|
||||
.contains("Reloadable condition was not satisfied, reload will not be triggered");
|
||||
boolean updateStrategyNotCalled = !strategyCalled[0];
|
||||
boolean updateStrategyNotCalled = !STRATEGY_CALLED.get();
|
||||
return one && two && three && updateStrategyNotCalled;
|
||||
});
|
||||
|
||||
@@ -134,7 +135,7 @@ class EventReloadSecretTest {
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> strategyCalled[0]);
|
||||
.until(STRATEGY_CALLED::get);
|
||||
}
|
||||
|
||||
private static Secret secret(String name, Map<String, String> data) {
|
||||
@@ -204,7 +205,7 @@ class EventReloadSecretTest {
|
||||
@Primary
|
||||
ConfigurationUpdateStrategy configurationUpdateStrategy() {
|
||||
return new ConfigurationUpdateStrategy("to-console", () -> {
|
||||
strategyCalled[0] = true;
|
||||
STRATEGY_CALLED.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
||||
@@ -70,33 +71,17 @@ class PollingReloadConfigMapTest {
|
||||
|
||||
private static final String NAMESPACE = "spring-k8s";
|
||||
|
||||
private static final String PATH = "/api/v1/namespaces/spring-k8s/configmaps";
|
||||
|
||||
private static KubernetesMockServer kubernetesMockServer;
|
||||
|
||||
private static KubernetesClient kubernetesClient;
|
||||
|
||||
private static final boolean[] strategyCalled = new boolean[] { false };
|
||||
private static final AtomicBoolean STRATEGY_CALLED = new AtomicBoolean(false);
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
|
||||
kubernetesClient.getConfiguration().setRequestRetryBackoffLimit(0);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
ConfigMap configMapOne = configMap(CONFIG_MAP_NAME, Map.of());
|
||||
ConfigMap configMapTwo = configMap(CONFIG_MAP_NAME, Map.of("a", "b"));
|
||||
String path = "/api/v1/namespaces/spring-k8s/configmaps";
|
||||
kubernetesMockServer.expect()
|
||||
.withPath(path)
|
||||
.andReturn(200, new ConfigMapListBuilder().withItems(configMapOne).build())
|
||||
.once();
|
||||
|
||||
kubernetesMockServer.expect().withPath(path).andReturn(500, "Internal Server Error").once();
|
||||
|
||||
kubernetesMockServer.expect()
|
||||
.withPath(path)
|
||||
.andReturn(200, new ConfigMapListBuilder().withItems(configMapTwo).build())
|
||||
.once();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,20 +96,27 @@ class PollingReloadConfigMapTest {
|
||||
@Test
|
||||
void test(CapturedOutput output) {
|
||||
// we fail while reading 'configMapOne'
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).pollInterval(Duration.ofSeconds(1)).until(() -> {
|
||||
kubernetesMockServer.expect().withPath(PATH).andReturn(500, "Internal Server Error").once();
|
||||
Awaitility.await().atMost(Duration.ofSeconds(20)).pollInterval(Duration.ofSeconds(1)).until(() -> {
|
||||
boolean one = output.getOut().contains("Failure in reading named sources");
|
||||
boolean two = output.getOut().contains("Failed to load source");
|
||||
boolean three = output.getOut()
|
||||
.contains("Reloadable condition was not satisfied, reload will not be triggered");
|
||||
boolean updateStrategyNotCalled = !strategyCalled[0];
|
||||
boolean updateStrategyNotCalled = !STRATEGY_CALLED.get();
|
||||
return one && two && three && updateStrategyNotCalled;
|
||||
});
|
||||
|
||||
ConfigMap configMapTwo = configMap(CONFIG_MAP_NAME, Map.of("a", "b"));
|
||||
kubernetesMockServer.expect()
|
||||
.withPath(PATH)
|
||||
.andReturn(200, new ConfigMapListBuilder().withItems(configMapTwo).build())
|
||||
.once();
|
||||
|
||||
// it passes while reading 'configMapTwo'
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.atMost(Duration.ofSeconds(20))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> strategyCalled[0]);
|
||||
.until(STRATEGY_CALLED::get);
|
||||
}
|
||||
|
||||
private static ConfigMap configMap(String name, Map<String, String> data) {
|
||||
@@ -148,6 +140,15 @@ class PollingReloadConfigMapTest {
|
||||
@Bean
|
||||
@Primary
|
||||
AbstractEnvironment environment() {
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
ConfigMap configMapOne = configMap(CONFIG_MAP_NAME, Map.of());
|
||||
kubernetesMockServer.expect()
|
||||
.withPath(PATH)
|
||||
.andReturn(200, new ConfigMapListBuilder().withItems(configMapOne).build())
|
||||
.once();
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty("spring.cloud.kubernetes.client.namespace", NAMESPACE);
|
||||
|
||||
@@ -190,7 +191,7 @@ class PollingReloadConfigMapTest {
|
||||
@Primary
|
||||
ConfigurationUpdateStrategy configurationUpdateStrategy() {
|
||||
return new ConfigurationUpdateStrategy("to-console", () -> {
|
||||
strategyCalled[0] = true;
|
||||
STRATEGY_CALLED.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Secret;
|
||||
@@ -73,33 +74,17 @@ public class PollingReloadSecretTest {
|
||||
|
||||
private static final String NAMESPACE = "spring-k8s";
|
||||
|
||||
private static final String PATH = "/api/v1/namespaces/spring-k8s/secrets";
|
||||
|
||||
private static KubernetesMockServer kubernetesMockServer;
|
||||
|
||||
private static KubernetesClient kubernetesClient;
|
||||
|
||||
private static final boolean[] strategyCalled = new boolean[] { false };
|
||||
private static final AtomicBoolean STRATEGY_CALLED = new AtomicBoolean(false);
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
|
||||
kubernetesClient.getConfiguration().setRequestRetryBackoffLimit(0);
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
Secret secretOne = secret(SECRET_NAME, Map.of());
|
||||
Secret secretTwo = secret(SECRET_NAME, Map.of("a", "b"));
|
||||
String path = "/api/v1/namespaces/spring-k8s/secrets";
|
||||
kubernetesMockServer.expect()
|
||||
.withPath(path)
|
||||
.andReturn(200, new SecretListBuilder().withItems(secretOne).build())
|
||||
.once();
|
||||
|
||||
kubernetesMockServer.expect().withPath(path).andReturn(500, "Internal Server Error").once();
|
||||
|
||||
kubernetesMockServer.expect()
|
||||
.withPath(path)
|
||||
.andReturn(200, new SecretListBuilder().withItems(secretTwo).build())
|
||||
.once();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,20 +99,27 @@ public class PollingReloadSecretTest {
|
||||
@Test
|
||||
void test(CapturedOutput output) {
|
||||
// we fail while reading 'secretOne'
|
||||
Awaitility.await().atMost(Duration.ofSeconds(10)).pollInterval(Duration.ofSeconds(1)).until(() -> {
|
||||
kubernetesMockServer.expect().withPath(PATH).andReturn(500, "Internal Server Error").once();
|
||||
Awaitility.await().atMost(Duration.ofSeconds(20)).pollInterval(Duration.ofSeconds(1)).until(() -> {
|
||||
boolean one = output.getOut().contains("Failure in reading named sources");
|
||||
boolean two = output.getOut().contains("Failed to load source");
|
||||
boolean three = output.getOut()
|
||||
.contains("Reloadable condition was not satisfied, reload will not be triggered");
|
||||
boolean updateStrategyNotCalled = !strategyCalled[0];
|
||||
boolean updateStrategyNotCalled = !STRATEGY_CALLED.get();
|
||||
return one && two && three && updateStrategyNotCalled;
|
||||
});
|
||||
|
||||
Secret secretTwo = secret(SECRET_NAME, Map.of("a", "b"));
|
||||
kubernetesMockServer.expect()
|
||||
.withPath(PATH)
|
||||
.andReturn(200, new SecretListBuilder().withItems(secretTwo).build())
|
||||
.once();
|
||||
|
||||
// it passes while reading 'secretTwo'
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.atMost(Duration.ofSeconds(20))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> strategyCalled[0]);
|
||||
.until(STRATEGY_CALLED::get);
|
||||
}
|
||||
|
||||
private static Secret secret(String name, Map<String, String> data) {
|
||||
@@ -155,6 +147,15 @@ public class PollingReloadSecretTest {
|
||||
@Bean
|
||||
@Primary
|
||||
AbstractEnvironment environment() {
|
||||
|
||||
// needed so that our environment is populated with 'something'
|
||||
// this call is done in the method that returns the AbstractEnvironment
|
||||
Secret secretOne = secret(SECRET_NAME, Map.of());
|
||||
kubernetesMockServer.expect()
|
||||
.withPath(PATH)
|
||||
.andReturn(200, new SecretListBuilder().withItems(secretOne).build())
|
||||
.once();
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setProperty("spring.cloud.kubernetes.client.namespace", NAMESPACE);
|
||||
|
||||
@@ -197,7 +198,7 @@ public class PollingReloadSecretTest {
|
||||
@Primary
|
||||
ConfigurationUpdateStrategy configurationUpdateStrategy() {
|
||||
return new ConfigurationUpdateStrategy("to-console", () -> {
|
||||
strategyCalled[0] = true;
|
||||
STRATEGY_CALLED.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user