Fix 1323 part 1 (#1327)

This commit is contained in:
erabii
2023-04-28 16:56:19 +03:00
committed by GitHub
parent 0510e0eb5c
commit cd24fda5bc
5 changed files with 132 additions and 4 deletions

View File

@@ -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");

View File

@@ -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;
}
}

View File

@@ -1 +1,2 @@
org.springframework.cloud.kubernetes.configuration.watcher.ConfigurationWatcherAutoConfiguration
org.springframework.cloud.kubernetes.configuration.watcher.ConfigUpdateStrategyAutoConfiguration

View File

@@ -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);
}
}
}

View File

@@ -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];