-
-
Notifications
You must be signed in to change notification settings - Fork 294
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
Auto-Reload for SecretsManager
& ParameterStore
property sources
#536
Merged
Merged
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
710ffda
Reload properties in Secrets Manager integration.
maciejwalkowiak d3e98bc
Tests for context refresh strategy.
maciejwalkowiak 6e30a47
Tests for restart strategy.
maciejwalkowiak b4d9f6e
Generify `PollingSecretsManagerChangeDetector`.
maciejwalkowiak cacdf68
Polish & Javadocs
maciejwalkowiak 62e7eed
Polish.
maciejwalkowiak d336477
Reload parameters for ParameterStore.
maciejwalkowiak 9093dae
Spotless
maciejwalkowiak 48ed4e0
Polish.
maciejwalkowiak eef2054
simplify
maciejwalkowiak 1f3c322
simplify
maciejwalkowiak 5414ee9
simplify
maciejwalkowiak 19d570b
docs
maciejwalkowiak 8db82a9
Add tests for `SecretsManagerReloadAutoConfiguration`.
maciejwalkowiak 791f81b
Tests & polishing
maciejwalkowiak cff4e71
Polish.
maciejwalkowiak 3638c4d
Add reference docs.
maciejwalkowiak 464e6bc
Merge remote-tracking branch 'origin/main' into secretsmanager-reload
maciejwalkowiak 513b875
Merge remote-tracking branch 'origin/main' into secretsmanager-reload
maciejwalkowiak c305ca2
Fix tests.
maciejwalkowiak aa551e4
Polish
maciejwalkowiak 2bc079b
Merge remote-tracking branch 'origin/main' into secretsmanager-reload
maciejwalkowiak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
...ring/cloud/autoconfigure/config/parameterstore/ParameterStoreReloadAutoConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright 2013-2022 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 io.awspring.cloud.autoconfigure.config.parameterstore; | ||
|
||
import io.awspring.cloud.autoconfigure.config.reload.ConfigurationChangeDetector; | ||
import io.awspring.cloud.autoconfigure.config.reload.ConfigurationUpdateStrategy; | ||
import io.awspring.cloud.autoconfigure.config.reload.PollingAwsPropertySourceChangeDetector; | ||
import io.awspring.cloud.parameterstore.ParameterStorePropertySource; | ||
import java.util.Optional; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
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.ConditionalOnBean; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
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.autoconfigure.RefreshAutoConfiguration; | ||
import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; | ||
import org.springframework.cloud.commons.util.TaskSchedulerWrapper; | ||
import org.springframework.cloud.context.refresh.ContextRefresher; | ||
import org.springframework.cloud.context.restart.RestartEndpoint; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.env.ConfigurableEnvironment; | ||
import org.springframework.scheduling.TaskScheduler; | ||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; | ||
|
||
@Configuration(proxyBeanMethods = false) | ||
maciejwalkowiak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@EnableConfigurationProperties(ParameterStoreProperties.class) | ||
@ConditionalOnClass({ EndpointAutoConfiguration.class, RestartEndpoint.class, ContextRefresher.class }) | ||
@AutoConfigureAfter({ InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class, | ||
RefreshAutoConfiguration.class }) | ||
@ConditionalOnProperty(value = ParameterStoreProperties.CONFIG_PREFIX + ".monitored", havingValue = "true") | ||
public class ParameterStoreReloadAutoConfiguration { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
@Bean("parameterStoreTaskScheduler") | ||
@ConditionalOnMissingBean | ||
public TaskSchedulerWrapper<TaskScheduler> taskScheduler() { | ||
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); | ||
|
||
threadPoolTaskScheduler.setThreadNamePrefix("spring-cloud-aws-parameterstore-ThreadPoolTaskScheduler-"); | ||
threadPoolTaskScheduler.setDaemon(true); | ||
|
||
return new TaskSchedulerWrapper<>(threadPoolTaskScheduler); | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
public ConfigurationUpdateStrategy parameterStoreConfigurationUpdateStrategy(ParameterStoreProperties properties, | ||
Optional<RestartEndpoint> restarter, ContextRefresher refresher) { | ||
return ConfigurationUpdateStrategy.create(properties.getReload(), refresher, restarter); | ||
} | ||
|
||
@Bean | ||
@ConditionalOnBean(ConfigurationUpdateStrategy.class) | ||
public ConfigurationChangeDetector<ParameterStorePropertySource> parameterStoreDataPropertyChangePollingWatcher( | ||
ParameterStoreProperties properties, ConfigurationUpdateStrategy strategy, | ||
@Qualifier("parameterStoreTaskScheduler") TaskSchedulerWrapper<TaskScheduler> taskScheduler, | ||
ConfigurableEnvironment environment) { | ||
|
||
return new PollingAwsPropertySourceChangeDetector<>(properties, ParameterStorePropertySource.class, strategy, | ||
taskScheduler.getTaskScheduler(), environment); | ||
} | ||
} |
93 changes: 93 additions & 0 deletions
93
.../main/java/io/awspring/cloud/autoconfigure/config/reload/ConfigurationChangeDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* | ||
* Copyright 2013-2022 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 io.awspring.cloud.autoconfigure.config.reload; | ||
|
||
import io.awspring.cloud.autoconfigure.config.secretsmanager.ReloadableProperties; | ||
import io.awspring.cloud.core.config.AwsPropertySource; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.stream.Collectors; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.springframework.core.env.ConfigurableEnvironment; | ||
import org.springframework.core.env.EnumerablePropertySource; | ||
import org.springframework.core.log.LogAccessor; | ||
|
||
/** | ||
* This is the superclass of all beans that can listen to changes in the configuration and fire a reload. | ||
* | ||
* Heavily inspired by Spring Cloud Kubernetes. | ||
* | ||
* @author Nicola Ferraro | ||
* @author Matej Nedic | ||
* @author Maciej Walkowiak | ||
*/ | ||
public abstract class ConfigurationChangeDetector<T extends AwsPropertySource<?, ?>> { | ||
|
||
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(ConfigurationChangeDetector.class)); | ||
|
||
protected ReloadableProperties properties; | ||
|
||
protected ConfigurationUpdateStrategy strategy; | ||
|
||
protected ConfigurableEnvironment environment; | ||
|
||
private final Class<T> propertySourceClass; | ||
|
||
public ConfigurationChangeDetector(ReloadableProperties properties, ConfigurationUpdateStrategy strategy, | ||
ConfigurableEnvironment environment, Class<T> propertySourceClass) { | ||
this.properties = Objects.requireNonNull(properties); | ||
this.strategy = Objects.requireNonNull(strategy); | ||
this.environment = environment; | ||
this.propertySourceClass = propertySourceClass; | ||
} | ||
|
||
public void reloadProperties() { | ||
LOG.info(() -> "Reloading using strategy: " + this.strategy); | ||
strategy.getReloadProcedure().run(); | ||
} | ||
|
||
/** | ||
* Determines if two property sources are different. | ||
* @param left left map property sources | ||
* @param right right map property sources | ||
* @return {@code true} if source has changed | ||
*/ | ||
protected boolean changed(EnumerablePropertySource<?> left, EnumerablePropertySource<?> right) { | ||
if (left == right) { | ||
return false; | ||
} | ||
for (String property : left.getPropertyNames()) { | ||
if (!Objects.equals(left.getProperty(property), right.getProperty(property))) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* Returns a list of MapPropertySource that correspond to the current state of the system. This only handles the | ||
* PropertySource objects that are returned. | ||
* @param environment Spring environment | ||
* @return a list of MapPropertySource that correspond to the current state of the system | ||
*/ | ||
protected List<T> locateMapPropertySources(ConfigurableEnvironment environment) { | ||
|
||
return environment.getPropertySources().stream() | ||
.filter(it -> (it.getClass().isAssignableFrom(propertySourceClass))).map(it -> (T) it) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
} |
73 changes: 73 additions & 0 deletions
73
.../main/java/io/awspring/cloud/autoconfigure/config/reload/ConfigurationUpdateStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* Copyright 2013-2022 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 io.awspring.cloud.autoconfigure.config.reload; | ||
|
||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.concurrent.ThreadLocalRandom; | ||
import org.springframework.cloud.context.refresh.ContextRefresher; | ||
import org.springframework.cloud.context.restart.RestartEndpoint; | ||
|
||
/** | ||
* This is the superclass of all named strategies that can be fired when the configuration changes. | ||
* | ||
* Heavily inspired by Spring Cloud Kubernetes. | ||
* | ||
* @author Nicola Ferraro | ||
*/ | ||
public class ConfigurationUpdateStrategy { | ||
|
||
private final ReloadStrategy reloadStrategy; | ||
private final Runnable reloadProcedure; | ||
|
||
public static ConfigurationUpdateStrategy create(ReloadProperties reloadProperties, ContextRefresher refresher, | ||
Optional<RestartEndpoint> restarter) { | ||
switch (reloadProperties.getStrategy()) { | ||
case RESTART_CONTEXT: | ||
restarter.orElseThrow(() -> new AssertionError("Restart endpoint is not enabled")); | ||
return new ConfigurationUpdateStrategy(reloadProperties.getStrategy(), () -> { | ||
wait(reloadProperties); | ||
restarter.get().restart(); | ||
}); | ||
case REFRESH: | ||
return new ConfigurationUpdateStrategy(reloadProperties.getStrategy(), refresher::refresh); | ||
} | ||
throw new IllegalStateException("Unsupported configuration update strategy: " + reloadProperties.getStrategy()); | ||
} | ||
|
||
public ConfigurationUpdateStrategy(ReloadStrategy reloadStrategy, Runnable reloadProcedure) { | ||
this.reloadStrategy = Objects.requireNonNull(reloadStrategy, "reloadStrategy cannot be null"); | ||
this.reloadProcedure = Objects.requireNonNull(reloadProcedure, "reloadProcedure cannot be null"); | ||
} | ||
|
||
public ReloadStrategy getReloadStrategy() { | ||
return reloadStrategy; | ||
} | ||
|
||
public Runnable getReloadProcedure() { | ||
return reloadProcedure; | ||
} | ||
|
||
private static void wait(ReloadProperties properties) { | ||
long waitMillis = ThreadLocalRandom.current().nextLong(properties.getMaxWaitForRestart().toMillis()); | ||
try { | ||
Thread.sleep(waitMillis); | ||
} | ||
catch (InterruptedException ignored) { | ||
Thread.currentThread().interrupt(); | ||
} | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
...io/awspring/cloud/autoconfigure/config/reload/PollingAwsPropertySourceChangeDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
* Copyright 2013-2022 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 io.awspring.cloud.autoconfigure.config.reload; | ||
|
||
import io.awspring.cloud.autoconfigure.config.secretsmanager.ReloadableProperties; | ||
import io.awspring.cloud.core.config.AwsPropertySource; | ||
import java.util.List; | ||
import javax.annotation.PostConstruct; | ||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.springframework.core.env.ConfigurableEnvironment; | ||
import org.springframework.scheduling.TaskScheduler; | ||
import org.springframework.scheduling.support.PeriodicTrigger; | ||
|
||
/** | ||
* Configuration change detector that checks for changed configuration on a scheduled basis. | ||
* | ||
* Heavily inspired by Spring Cloud Kubernetes. | ||
* | ||
* @param <T> - property source class to check | ||
* @author Matej Nedic | ||
* @author Maciej Walkowiak | ||
*/ | ||
public class PollingAwsPropertySourceChangeDetector<T extends AwsPropertySource<?, ?>> | ||
extends ConfigurationChangeDetector<T> { | ||
|
||
protected Log log = LogFactory.getLog(getClass()); | ||
private final TaskScheduler taskExecutor; | ||
|
||
public PollingAwsPropertySourceChangeDetector(ReloadableProperties properties, Class<T> clazz, | ||
ConfigurationUpdateStrategy strategy, TaskScheduler taskExecutor, ConfigurableEnvironment environment) { | ||
super(properties, strategy, environment, clazz); | ||
this.taskExecutor = taskExecutor; | ||
|
||
} | ||
|
||
@PostConstruct | ||
private void init() { | ||
log.info("Polling configurations change detector activated"); | ||
long period = properties.getReload().getPeriod().toMillis(); | ||
PeriodicTrigger trigger = new PeriodicTrigger(period); | ||
trigger.setInitialDelay(period); | ||
taskExecutor.schedule(this::executeCycle, trigger); | ||
} | ||
|
||
public void executeCycle() { | ||
if (this.properties.isMonitored()) { | ||
if (log.isDebugEnabled()) { | ||
log.debug("Polling for changes in secrets"); | ||
} | ||
List<T> currentSecretSources = locateMapPropertySources(this.environment); | ||
if (!currentSecretSources.isEmpty()) { | ||
for (T propertySource : currentSecretSources) { | ||
AwsPropertySource<?, ?> clone = propertySource.copy(); | ||
clone.init(); | ||
if (changed(propertySource, clone)) { | ||
reloadProperties(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this should be moved to
ReloadProperties
.