Skip to content

Commit 13906a8

Browse files
Add aws-secretsmanager: prefix config import (spring-attic/spring-cloud-aws#721)
In `spring-boot` 2.4, `Volume Mounted Config Directory Trees` was added. This commit introduces the prefix `aws-secretsmanager:` which will resolve the values given the configuration properties supported by secrets manager integration. Also, if keys are added after the prefix then just these will be resolved. Use: `aws-secretsmanager:` or `aws-secretsmanager:my-secret-key` or `aws-secretsmanager:my-secret-key;my-anoter-secret-key` Closes spring-attic/spring-cloud-aws#655 Closes spring-attic/spring-cloud-aws#515 Co-authored-by: Maciej Walkowiak <[email protected]>
1 parent 7de3073 commit 13906a8

File tree

11 files changed

+511
-49
lines changed

11 files changed

+511
-49
lines changed

docs/src/main/asciidoc/secrets-manager.adoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,19 @@ dots, dashes, forward slashes, backward slashes and underscores next to alphanum
6969
|`true`
7070
|Can be used to disable the Secrets Manager Configuration support even though the auto-configuration is on the classpath.
7171
|===
72+
73+
In `spring-cloud` `2020.0.0` (aka Ilford), the bootstrap phase is no longer enabled by default. In order
74+
enable it you need an additional dependency:
75+
76+
[source,xml,indent=0]
77+
----
78+
<dependency>
79+
<groupId>org.springframework.cloud</groupId>
80+
<artifactId>spring-cloud-starter-bootstrap</artifactId>
81+
<version>{spring-cloud-version}</version>
82+
</dependency>
83+
----
84+
85+
However, starting at `spring-cloud-aws` `2.3`, allows import default aws' secretsmanager keys
86+
(`spring.config.import=aws-secretsmanager:`) or individual keys
87+
(`spring.config.import=aws-secretsmanager:secret-key;other-secret-key`)

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<parent>
2323
<groupId>org.springframework.cloud</groupId>
2424
<artifactId>spring-cloud-build</artifactId>
25-
<version>3.0.0-M4</version>
25+
<version>3.0.0-SNAPSHOT</version>
2626
<relativePath/><!-- lookup parent from repository -->
2727
</parent>
2828

@@ -48,7 +48,7 @@
4848
<javax-mail.version>1.5.5</javax-mail.version>
4949
<maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
5050
<javax.activation.version>1.2.0</javax.activation.version>
51-
<spring-cloud-commons.version>3.0.0-M4</spring-cloud-commons.version>
51+
<spring-cloud-commons.version>3.0.0-SNAPSHOT</spring-cloud-commons.version>
5252
<spring-javaformat.version>0.0.25</spring-javaformat.version>
5353
</properties>
5454

spring-cloud-aws-dependencies/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<parent>
2323
<groupId>org.springframework.cloud</groupId>
2424
<artifactId>spring-cloud-dependencies-parent</artifactId>
25-
<version>2.3.2.BUILD-SNAPSHOT</version>
25+
<version>3.0.0-SNAPSHOT</version>
2626
<relativePath/>
2727
</parent>
2828
<artifactId>spring-cloud-aws-dependencies</artifactId>

spring-cloud-aws-secrets-manager-config/src/main/java/org/springframework/cloud/aws/secretsmanager/AwsSecretsManagerPropertySourceLocator.java

Lines changed: 10 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.springframework.core.env.ConfigurableEnvironment;
3232
import org.springframework.core.env.Environment;
3333
import org.springframework.core.env.PropertySource;
34-
import org.springframework.util.ReflectionUtils;
3534

3635
/**
3736
* Builds a {@link CompositePropertySource} with various
@@ -41,17 +40,18 @@
4140
*
4241
* @author Fabio Maia
4342
* @author Matej Nedic
43+
* @author Eddú Meléndez
4444
* @since 2.0.0
4545
*/
4646
public class AwsSecretsManagerPropertySourceLocator implements PropertySourceLocator {
4747

48-
private String propertySourceName;
48+
private final String propertySourceName;
4949

50-
private AWSSecretsManager smClient;
50+
private final AWSSecretsManager smClient;
5151

52-
private AwsSecretsManagerProperties properties;
52+
private final AwsSecretsManagerProperties properties;
5353

54-
private final Set<String> contexts = new LinkedHashSet();
54+
private final Set<String> contexts = new LinkedHashSet<>();
5555

5656
private Log logger = LogFactory.getLog(getClass());
5757

@@ -78,56 +78,20 @@ public PropertySource<?> locate(Environment environment) {
7878

7979
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
8080

81-
String appName = properties.getName();
82-
83-
if (appName == null) {
84-
appName = env.getProperty("spring.application.name");
85-
}
81+
AwsSecretsManagerPropertySources sources = new AwsSecretsManagerPropertySources(properties, logger);
8682

8783
List<String> profiles = Arrays.asList(env.getActiveProfiles());
88-
89-
String prefix = this.properties.getPrefix();
90-
91-
String appContext = prefix + "/" + appName;
92-
addProfiles(this.contexts, appContext, profiles);
93-
this.contexts.add(appContext);
94-
95-
String defaultContext = prefix + "/" + this.properties.getDefaultContext();
96-
addProfiles(this.contexts, defaultContext, profiles);
97-
this.contexts.add(defaultContext);
84+
this.contexts.addAll(sources.getAutomaticContexts(profiles));
9885

9986
CompositePropertySource composite = new CompositePropertySource(this.propertySourceName);
10087

10188
for (String propertySourceContext : this.contexts) {
102-
try {
103-
composite.addPropertySource(create(propertySourceContext));
104-
}
105-
catch (Exception e) {
106-
if (this.properties.isFailFast()) {
107-
logger.error(
108-
"Fail fast is set and there was an error reading configuration from AWS Secrets Manager:\n"
109-
+ e.getMessage());
110-
ReflectionUtils.rethrowRuntimeException(e);
111-
}
112-
else {
113-
logger.warn("Unable to load AWS secret from " + propertySourceContext, e);
114-
}
115-
}
89+
PropertySource<AWSSecretsManager> propertySource = sources.createPropertySource(propertySourceContext, true,
90+
this.smClient);
91+
composite.addPropertySource(propertySource);
11692
}
11793

11894
return composite;
11995
}
12096

121-
private AwsSecretsManagerPropertySource create(String context) {
122-
AwsSecretsManagerPropertySource propertySource = new AwsSecretsManagerPropertySource(context, this.smClient);
123-
propertySource.init();
124-
return propertySource;
125-
}
126-
127-
private void addProfiles(Set<String> contexts, String baseContext, List<String> profiles) {
128-
for (String profile : profiles) {
129-
contexts.add(baseContext + this.properties.getProfileSeparator() + profile);
130-
}
131-
}
132-
13397
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2013-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.aws.secretsmanager;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
23+
import org.apache.commons.logging.Log;
24+
25+
import org.springframework.util.StringUtils;
26+
27+
public class AwsSecretsManagerPropertySources {
28+
29+
private final AwsSecretsManagerProperties properties;
30+
31+
private final Log log;
32+
33+
public AwsSecretsManagerPropertySources(AwsSecretsManagerProperties properties, Log log) {
34+
this.properties = properties;
35+
this.log = log;
36+
}
37+
38+
public List<String> getAutomaticContexts(List<String> profiles) {
39+
List<String> contexts = new ArrayList<>();
40+
String prefix = this.properties.getPrefix();
41+
String defaultContext = getContext(prefix, this.properties.getDefaultContext());
42+
43+
String appName = this.properties.getName();
44+
45+
String appContext = prefix + "/" + appName;
46+
addProfiles(contexts, appContext, profiles);
47+
contexts.add(appContext);
48+
49+
addProfiles(contexts, defaultContext, profiles);
50+
contexts.add(defaultContext);
51+
return contexts;
52+
}
53+
54+
protected String getContext(String prefix, String context) {
55+
if (StringUtils.hasLength(prefix)) {
56+
return prefix + "/" + context;
57+
}
58+
return context;
59+
}
60+
61+
private void addProfiles(List<String> contexts, String baseContext, List<String> profiles) {
62+
for (String profile : profiles) {
63+
contexts.add(baseContext + this.properties.getProfileSeparator() + profile);
64+
}
65+
}
66+
67+
public AwsSecretsManagerPropertySource createPropertySource(String context, boolean optional,
68+
AWSSecretsManager client) {
69+
try {
70+
AwsSecretsManagerPropertySource propertySource = new AwsSecretsManagerPropertySource(context, client);
71+
propertySource.init();
72+
return propertySource;
73+
// TODO: howto call close when /refresh
74+
}
75+
catch (Exception e) {
76+
if (this.properties.isFailFast() || !optional) {
77+
throw new AwsSecretsManagerPropertySourceNotFoundException(e);
78+
}
79+
else {
80+
log.warn("Unable to load AWS secret from " + context, e);
81+
}
82+
}
83+
return null;
84+
}
85+
86+
static class AwsSecretsManagerPropertySourceNotFoundException extends RuntimeException {
87+
88+
AwsSecretsManagerPropertySourceNotFoundException(Exception source) {
89+
super(source);
90+
}
91+
92+
}
93+
94+
}

spring-cloud-starter-aws-secrets-manager-config/src/main/java/org/springframework/cloud/aws/autoconfigure/secretsmanager/AwsSecretsManagerBootstrapConfiguration.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.cloud.aws.secretsmanager.AwsSecretsManagerPropertySourceLocator;
3131
import org.springframework.context.annotation.Bean;
3232
import org.springframework.context.annotation.Configuration;
33+
import org.springframework.core.env.Environment;
3334

3435
/**
3536
* Spring Cloud Bootstrap Configuration for setting up an
@@ -46,15 +47,28 @@
4647
@ConditionalOnProperty(prefix = AwsSecretsManagerProperties.CONFIG_PREFIX, name = "enabled", matchIfMissing = true)
4748
public class AwsSecretsManagerBootstrapConfiguration {
4849

50+
private final Environment environment;
51+
52+
public AwsSecretsManagerBootstrapConfiguration(Environment environment) {
53+
this.environment = environment;
54+
}
55+
4956
@Bean
5057
AwsSecretsManagerPropertySourceLocator awsSecretsManagerPropertySourceLocator(AWSSecretsManager smClient,
5158
AwsSecretsManagerProperties properties) {
59+
if (StringUtils.isNullOrEmpty(properties.getName())) {
60+
properties.setName(this.environment.getProperty("spring.application.name"));
61+
}
5262
return new AwsSecretsManagerPropertySourceLocator(smClient, properties);
5363
}
5464

5565
@Bean
5666
@ConditionalOnMissingBean
5767
AWSSecretsManager smClient(AwsSecretsManagerProperties properties) {
68+
return createSecretsManagerClient(properties);
69+
}
70+
71+
public static AWSSecretsManager createSecretsManagerClient(AwsSecretsManagerProperties properties) {
5872
AWSSecretsManagerClientBuilder builder = AWSSecretsManagerClientBuilder.standard()
5973
.withClientConfiguration(SpringCloudClientConfiguration.getClientConfiguration());
6074
if (!StringUtils.isNullOrEmpty(properties.getRegion())) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2013-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.aws.autoconfigure.secretsmanager;
18+
19+
import java.util.Collections;
20+
21+
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
22+
23+
import org.springframework.boot.context.config.ConfigData;
24+
import org.springframework.boot.context.config.ConfigDataLoader;
25+
import org.springframework.boot.context.config.ConfigDataLoaderContext;
26+
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
27+
import org.springframework.cloud.aws.secretsmanager.AwsSecretsManagerPropertySource;
28+
29+
/**
30+
* @author Eddú Meléndez
31+
* @since 2.3.0
32+
*/
33+
public class AwsSecretsManagerConfigDataLoader implements ConfigDataLoader<AwsSecretsManagerConfigDataResource> {
34+
35+
@Override
36+
public ConfigData load(ConfigDataLoaderContext context, AwsSecretsManagerConfigDataResource resource) {
37+
try {
38+
AWSSecretsManager ssm = context.getBootstrapContext().get(AWSSecretsManager.class);
39+
AwsSecretsManagerPropertySource propertySource = resource.getPropertySources()
40+
.createPropertySource(resource.getContext(), resource.isOptional(), ssm);
41+
return new ConfigData(Collections.singletonList(propertySource));
42+
}
43+
catch (Exception e) {
44+
throw new ConfigDataResourceNotFoundException(resource, e);
45+
}
46+
}
47+
48+
}

0 commit comments

Comments
 (0)