Skip to content

Refactor ConfigurationChangeDetector to simplify conditionals. Issue 643 #644

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand All @@ -36,6 +35,8 @@
import org.springframework.cloud.context.restart.RestartEndpoint;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.condition.EventReloadDetectionMode;
import org.springframework.cloud.kubernetes.config.reload.condition.PollingReloadDetectionMode;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
Expand All @@ -49,14 +50,14 @@
* Definition of beans needed for the automatic reload of configuration.
*
* @author Nicolla Ferraro
* @author Kris Iyer
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
@ConditionalOnClass(EndpointAutoConfiguration.class)
@AutoConfigureAfter({ InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class,
RefreshAutoConfiguration.class })
@EnableConfigurationProperties(ConfigReloadProperties.class)

public class ConfigReloadAutoConfiguration {

/**
Expand All @@ -75,25 +76,71 @@ protected static class ConfigReloadAutoConfigurationBeans {
private KubernetesClient kubernetesClient;

/**
* Polling configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@Conditional(OnConfigEnabledOrSecretsEnabled.class)
public ConfigurationChangeDetector propertyChangeWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy,
@Autowired(required = false) ConfigMapPropertySourceLocator configMapPropertySourceLocator,
@Autowired(required = false) SecretsPropertySourceLocator secretsPropertySourceLocator) {
switch (properties.getMode()) {
case POLLING:
return new PollingConfigurationChangeDetector(this.environment, properties, this.kubernetesClient,
strategy, configMapPropertySourceLocator, secretsPropertySourceLocator);
case EVENT:
return new EventBasedConfigurationChangeDetector(this.environment, properties, this.kubernetesClient,
strategy, configMapPropertySourceLocator, secretsPropertySourceLocator);
}
throw new IllegalStateException("Unsupported configuration reload mode: " + properties.getMode());
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) {

return new PollingConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
configMapPropertySourceLocator);
}

/**
* Polling secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnBean(SecretsPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) {

return new PollingSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
secretsPropertySourceLocator);
}

/**
* Event Based configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configMap change events and fire a reload.
*/
@Bean
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) {

return new EventBasedConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
configMapPropertySourceLocator);
}

/**
* Event Based secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @return a bean that listen to secrets change events and fire a reload.
*/
@Bean
@ConditionalOnBean(SecretsPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) {

return new EventBasedSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
secretsPropertySourceLocator);
}

/**
Expand Down Expand Up @@ -135,24 +182,6 @@ private static void wait(ConfigReloadProperties properties) {
}
}

private static class OnConfigEnabledOrSecretsEnabled extends AnyNestedCondition {

OnConfigEnabledOrSecretsEnabled() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
static class configEnabled {

}

@ConditionalOnBean(SecretsPropertySourceLocator.class)
static class secretsEnabled {

}

}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,25 @@
import io.fabric8.kubernetes.client.KubernetesClient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.condition.EventReloadDetectionMode;
import org.springframework.cloud.kubernetes.config.reload.condition.PollingReloadDetectionMode;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
* @author Ryan Baxter
* @author Kris Iyer
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
Expand Down Expand Up @@ -63,30 +68,76 @@ protected static class ConfigReloadAutoConfigurationBeans {
private SecretsPropertySourceLocator secretsPropertySourceLocator;

/**
* Polling configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnMissingBean
public ConfigurationChangeDetector propertyChangeWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy) {
switch (properties.getMode()) {
case POLLING:
return new PollingConfigurationChangeDetector(this.environment, properties, this.kubernetesClient,
strategy, this.configMapPropertySourceLocator, this.secretsPropertySourceLocator);
case EVENT:
return new EventBasedConfigurationChangeDetector(this.environment, properties, this.kubernetesClient,
strategy, this.configMapPropertySourceLocator, this.secretsPropertySourceLocator);
}
throw new IllegalStateException("Unsupported configuration reload mode: " + properties.getMode());
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) {

return new PollingConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
configMapPropertySourceLocator);
}

/**
* Polling secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnBean(SecretsPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) {

return new PollingSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
secretsPropertySourceLocator);
}

/**
* Event Based configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configMap change events and fire a reload.
*/
@Bean
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) {

return new EventBasedConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
configMapPropertySourceLocator);
}

/**
* Event Based secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @return a bean that listen to secrets change events and fire a reload.
*/
@Bean
@ConditionalOnBean(SecretsPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) {

return new EventBasedSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
secretsPropertySourceLocator);
}

/**
* @param properties config reload properties
* @param ctx application context
* @param restarter restart endpoint
* @param refresher context refresher
* @return provides the action to execute when the configuration changes.
*/
@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,51 +23,45 @@
import javax.annotation.PreDestroy;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;

import org.springframework.cloud.kubernetes.config.ConfigMapPropertySource;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.SecretsPropertySource;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.core.env.AbstractEnvironment;

/**
* A change detector that subscribes to changes in secrets and configmaps and fire a
* An Event Based change detector that subscribes to changes in configMaps and fire a
* reload when something changes.
*
* @author Nicola Ferraro
* @author Haytham Mohamed
* @author Kris Iyer
*/
public class EventBasedConfigurationChangeDetector extends ConfigurationChangeDetector {
public class EventBasedConfigMapChangeDetector extends ConfigurationChangeDetector {

private final ConfigMapPropertySourceLocator configMapPropertySourceLocator;

private final SecretsPropertySourceLocator secretsPropertySourceLocator;

private final Map<String, Watch> watches;

public EventBasedConfigurationChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
public EventBasedConfigMapChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
ConfigMapPropertySourceLocator configMapPropertySourceLocator,
SecretsPropertySourceLocator secretsPropertySourceLocator) {
ConfigMapPropertySourceLocator configMapPropertySourceLocator) {
super(environment, properties, kubernetesClient, strategy);

this.configMapPropertySourceLocator = configMapPropertySourceLocator;
this.secretsPropertySourceLocator = secretsPropertySourceLocator;
this.watches = new HashMap<>();
}

@PostConstruct
public void watch() {
boolean activated = false;

if (this.properties.isMonitoringConfigMaps() && this.configMapPropertySourceLocator != null) {
if (this.properties.isMonitoringConfigMaps()) {
try {
String name = "config-maps-watch";
String name = "config-maps-watch-event";
this.watches.put(name, this.kubernetesClient.configMaps().watch(new Watcher<ConfigMap>() {
@Override
public void eventReceived(Action action, ConfigMap configMap) {
Expand All @@ -91,34 +85,8 @@ public void onClose(KubernetesClientException e) {
}
}

if (this.properties.isMonitoringSecrets() && this.secretsPropertySourceLocator != null) {
try {
activated = false;
String name = "secrets-watch";
this.watches.put(name, this.kubernetesClient.secrets().watch(new Watcher<Secret>() {
@Override
public void eventReceived(Action action, Secret secret) {
if (log.isDebugEnabled()) {
log.debug(name + " received and event for Secret " + secret.getMetadata().getName());
}
onEvent(secret);
}

@Override
public void onClose(KubernetesClientException e) {
}
}));
activated = true;
this.log.info("Added new Kubernetes watch: " + name);
}
catch (Exception e) {
this.log.error("Error while establishing a connection to watch secrets: configuration may remain stale",
e);
}
}

if (activated) {
this.log.info("Kubernetes event-based configuration change detector activated");
this.log.info("Kubernetes event-based configMap change detector activated");
}
}

Expand Down Expand Up @@ -147,13 +115,4 @@ protected void onEvent(ConfigMap configMap) {
}
}

protected void onEvent(Secret secret) {
boolean changed = changed(locateMapPropertySources(this.secretsPropertySourceLocator, this.environment),
findPropertySources(SecretsPropertySource.class));
if (changed) {
this.log.info("Detected change in secrets");
reloadProperties();
}
}

}
Loading