Fix 1323 part 1 (#1327)
This commit is contained in:
@@ -26,6 +26,13 @@ import java.util.Objects;
|
||||
*/
|
||||
public record ConfigurationUpdateStrategy(String name, Runnable reloadProcedure) {
|
||||
|
||||
/**
|
||||
* Strategy that does nothing.
|
||||
*/
|
||||
public static final ConfigurationUpdateStrategy NOOP = new ConfigurationUpdateStrategy("no-op", () -> {
|
||||
|
||||
});
|
||||
|
||||
public ConfigurationUpdateStrategy(String name, Runnable reloadProcedure) {
|
||||
this.name = Objects.requireNonNull(name, "name cannot be null");
|
||||
this.reloadProcedure = Objects.requireNonNull(reloadProcedure, "reloadProcedure cannot be null");
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.configuration.watcher;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "spring.cloud.kubernetes.reload.enabled", havingValue = "false", matchIfMissing = true)
|
||||
class ConfigUpdateStrategyAutoConfiguration {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(
|
||||
LogFactory.getLog(ConfigUpdateStrategyAutoConfiguration.class));
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ConfigurationUpdateStrategy noopConfigurationUpdateStrategy() {
|
||||
LOG.debug(() -> "creating NOOP strategy because reload is disabled");
|
||||
return ConfigurationUpdateStrategy.NOOP;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherAutoConfiguration
|
||||
org.springframework.cloud.kubernetes.configuration.watcher.ConfigUpdateStrategyAutoConfiguration
|
||||
|
||||
@@ -17,17 +17,23 @@
|
||||
package org.springframework.cloud.kubernetes.configuration.watcher;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
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;
|
||||
@@ -71,13 +77,11 @@ class ActuatorRefreshIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
configWatcher(Phase.CREATE);
|
||||
util.wiremock(NAMESPACE, "/", Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void after() {
|
||||
configWatcher(Phase.DELETE);
|
||||
util.wiremock(NAMESPACE, "/", Phase.DELETE);
|
||||
}
|
||||
|
||||
@@ -91,6 +95,8 @@ class ActuatorRefreshIT {
|
||||
// curl <WIREMOCK_POD_IP>:8080/__admin/mappings
|
||||
@Test
|
||||
void testActuatorRefresh() {
|
||||
configWatcher(Phase.CREATE, false);
|
||||
|
||||
WireMock.configureFor(WIREMOCK_HOST, WIREMOCK_PORT, WIREMOCK_PATH);
|
||||
await().timeout(Duration.ofSeconds(60))
|
||||
.until(() -> WireMock
|
||||
@@ -109,13 +115,68 @@ class ActuatorRefreshIT {
|
||||
|
||||
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")));
|
||||
util.deleteAndWait(NAMESPACE, configMap, null);
|
||||
|
||||
configWatcher(Phase.DELETE, false);
|
||||
}
|
||||
|
||||
private void configWatcher(Phase phase) {
|
||||
/*
|
||||
* same test as above, but reload is disabled.
|
||||
*/
|
||||
@Test
|
||||
void testActuatorRefreshReloadDisabled() {
|
||||
configWatcher(Phase.CREATE, true);
|
||||
|
||||
WireMock.configureFor(WIREMOCK_HOST, WIREMOCK_PORT, WIREMOCK_PATH);
|
||||
await().timeout(Duration.ofSeconds(60))
|
||||
.until(() -> WireMock
|
||||
.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
|
||||
.willReturn(WireMock.aResponse().withBody("{}").withStatus(200)))
|
||||
.getResponse().wasConfigured());
|
||||
|
||||
// Create new configmap to trigger controller to signal app to refresh
|
||||
V1ConfigMap configMap = new V1ConfigMapBuilder().editOrNewMetadata().withName("service-wiremock")
|
||||
.addToLabels("spring.cloud.kubernetes.config", "true").endMetadata().addToData("foo", "bar").build();
|
||||
util.createAndWait(NAMESPACE, configMap, null);
|
||||
|
||||
// Wait a bit before we verify
|
||||
await().atMost(Duration.ofSeconds(30)).until(
|
||||
() -> !WireMock.findAll(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh"))).isEmpty());
|
||||
|
||||
Assertions.assertTrue(logs().contains("creating NOOP strategy because reload is disabled"));
|
||||
// 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")));
|
||||
util.deleteAndWait(NAMESPACE, configMap, null);
|
||||
|
||||
configWatcher(Phase.DELETE, true);
|
||||
}
|
||||
|
||||
private void configWatcher(Phase phase, boolean disableReload) {
|
||||
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-http-deployment.yaml");
|
||||
|
||||
List<V1EnvVar> envVars = new ArrayList<>(
|
||||
Optional.ofNullable(deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv())
|
||||
.orElse(new ArrayList<>()));
|
||||
|
||||
V1EnvVar commonsDebug = new V1EnvVar()
|
||||
.name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG_RELOAD").value("DEBUG");
|
||||
V1EnvVar watcherDebug = new V1EnvVar()
|
||||
.name("LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CONFIGURATION_WATCHER").value("DEBUG");
|
||||
|
||||
envVars.add(commonsDebug);
|
||||
envVars.add(watcherDebug);
|
||||
|
||||
if (disableReload) {
|
||||
V1EnvVar disableReloadEnvVar = new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED").value("FALSE");
|
||||
envVars.add(disableReloadEnvVar);
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
|
||||
}
|
||||
|
||||
V1Service service = (V1Service) util
|
||||
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
|
||||
|
||||
@@ -130,4 +191,18 @@ class ActuatorRefreshIT {
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class ActuatorRefreshRabbitMQIT {
|
||||
WebClient serviceClient = builder.baseUrl("http://localhost:80/it").build();
|
||||
|
||||
Boolean[] value = new Boolean[1];
|
||||
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> {
|
||||
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(180)).until(() -> {
|
||||
value[0] = serviceClient.method(HttpMethod.GET).retrieve().bodyToMono(Boolean.class).retryWhen(retrySpec())
|
||||
.block();
|
||||
return value[0];
|
||||
|
||||
Reference in New Issue
Block a user