Skip to content

Commit 5bd8b94

Browse files
committed
Introduce loading parameters as key value store
1 parent aaedf69 commit 5bd8b94

File tree

3 files changed

+85
-7
lines changed

3 files changed

+85
-7
lines changed

docs/src/main/asciidoc/parameter-store.adoc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,30 @@ With such config, properties `spring.datasource.url` and `spring.datasource.user
8888
NOTE: Prefixes are added as-is to all property names returned by Parameter Store. If you want key names to be separated with a dot between the prefix and key name, make sure to add a trailing dot to the prefix.
8989

9090

91+
Sometimes it is useful to group multiple properties in a text-based format, similar to application.properties. With Spring Cloud AWS, you can load a Parameter Store parameter as a text-based key/value configuration by using the spring.config.import property with the ?properties suffix:
92+
93+
[source,properties]
94+
----
95+
spring.config.import=aws-parameterstore:/config/my-datasource/?properties
96+
----
97+
98+
All parameters stored under this path will be interpreted as key/value pairs. For example, if the value of a parameter is:
99+
100+
[source,properties]
101+
----
102+
spring.cloud.aws.region=eu-central-1
103+
spring.cloud.aws.endpoint=randomEndpoint
104+
----
105+
106+
Spring Cloud AWS will automatically load these as:
107+
Key: `spring.cloud.aws.region`, Value: `eu-central-1`
108+
Key: `spring.cloud.aws.endpoint`, Value: `randomEndpoint`
109+
110+
NOTE: Standard Parameter Store parameters are limited to 4 KB of data.
111+
112+
This approach allows you to maintain multiple related properties in a single parameter, making configuration management simpler and more organized.
113+
114+
91115
=== Using SsmClient
92116

93117
The starter automatically configures and registers a `SsmClient` bean in the Spring application context. The `SsmClient` bean can be used to create or retrieve parameters from Parameter Store.

spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/config/parameterstore/ParameterStoreConfigDataLoaderIntegrationTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,28 @@ void resolvesPropertiesWithPrefixes() {
123123
}
124124
}
125125

126+
@Test
127+
void resolvesPropertiesWithPrefixProperties() {
128+
SpringApplication application = new SpringApplication(App.class);
129+
application.setWebApplicationType(WebApplicationType.NONE);
130+
String applicationProperties = """
131+
first.message=value from tests
132+
first.another-parameter=another parameter value
133+
second.secondMessage=second value from tests
134+
""";
135+
putParameter(localstack, "/test/path/secondMessage", applicationProperties, REGION);
136+
137+
try (ConfigurableApplicationContext context = runApplication(application,
138+
"aws-parameterstore:/test/path/?properties")) {
139+
assertThat(context.getEnvironment().getProperty("first.message")).isEqualTo("value from tests");
140+
assertThat(context.getEnvironment().getProperty("first.another-parameter"))
141+
.isEqualTo("another parameter value");
142+
assertThat(context.getEnvironment().getProperty("second.secondMessage"))
143+
.isEqualTo("second value from tests");
144+
assertThat(context.getEnvironment().getProperty("non-existing-parameter")).isNull();
145+
}
146+
}
147+
126148
@Test
127149
void clientIsConfiguredWithCustomizerProvidedToBootstrapRegistry() {
128150
SpringApplication application = new SpringApplication(App.class);

spring-cloud-aws-parameter-store/src/main/java/io/awspring/cloud/parameterstore/ParameterStorePropertySource.java

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
package io.awspring.cloud.parameterstore;
1717

1818
import io.awspring.cloud.core.config.AwsPropertySource;
19+
import java.io.InputStream;
20+
import java.util.Arrays;
1921
import java.util.LinkedHashMap;
2022
import java.util.Map;
23+
import java.util.Properties;
2124
import org.apache.commons.logging.Log;
2225
import org.apache.commons.logging.LogFactory;
2326
import org.springframework.lang.Nullable;
@@ -41,6 +44,8 @@ public class ParameterStorePropertySource extends AwsPropertySource<ParameterSto
4144
// ParameterStoreConfigDataLoader
4245
private static Log LOG = LogFactory.getLog(ParameterStorePropertySource.class);
4346
private static final String PREFIX_PART = "?prefix=";
47+
48+
private static final String PREFIX_PROPERTIES_LOAD = "?properties";
4449
private final String context;
4550

4651
private final String parameterPath;
@@ -52,6 +57,8 @@ public class ParameterStorePropertySource extends AwsPropertySource<ParameterSto
5257
@Nullable
5358
private final String prefix;
5459

60+
private Boolean propertiesType = false;
61+
5562
private final Map<String, Object> properties = new LinkedHashMap<>();
5663

5764
public ParameterStorePropertySource(String context, SsmClient ssmClient) {
@@ -87,11 +94,20 @@ public Object getProperty(String name) {
8794
private void getParameters(GetParametersByPathRequest paramsRequest) {
8895
GetParametersByPathResponse paramsResult = this.source.getParametersByPath(paramsRequest);
8996
for (Parameter parameter : paramsResult.parameters()) {
90-
String key = parameter.name().replace(this.parameterPath, "").replace('/', '.').replaceAll("_(\\d)_",
91-
"[$1]");
92-
LOG.debug("Populating property retrieved from AWS Parameter Store: " + key);
93-
String propertyKey = prefix != null ? prefix + key : key;
94-
this.properties.put(propertyKey, parameter.value());
97+
if (propertiesType) {
98+
Arrays.stream(parameter.value().split("\\n")).map(line -> line.split("=", 2)).forEach(keyValue -> {
99+
if (keyValue.length == 2) {
100+
this.properties.put(keyValue[0].trim(), keyValue[1].trim());
101+
}
102+
});
103+
}
104+
else {
105+
String key = parameter.name().replace(this.parameterPath, "").replace('/', '.').replaceAll("_(\\d)_",
106+
"[$1]");
107+
LOG.debug("Populating property retrieved from AWS Parameter Store: " + key);
108+
String propertyKey = prefix != null ? prefix + key : key;
109+
this.properties.put(propertyKey, parameter.value());
110+
}
95111
}
96112
if (paramsResult.nextToken() != null) {
97113
getParameters(paramsRequest.toBuilder().nextToken(paramsResult.nextToken()).build());
@@ -112,20 +128,36 @@ String getParameterPath() {
112128
}
113129

114130
@Nullable
115-
private static String resolvePrefix(String context) {
131+
private String resolvePrefix(String context) {
116132
int prefixIndex = context.indexOf(PREFIX_PART);
117133
if (prefixIndex != -1) {
118134
return context.substring(prefixIndex + PREFIX_PART.length());
119135
}
120136
return null;
121137
}
122138

123-
private static String resolveParameterPath(String context) {
139+
private String resolveParameterPath(String context) {
124140
int prefixIndex = context.indexOf(PREFIX_PART);
125141
if (prefixIndex != -1) {
126142
return context.substring(0, prefixIndex);
127143
}
144+
prefixIndex = context.indexOf(PREFIX_PROPERTIES_LOAD);
145+
if (prefixIndex != -1) {
146+
this.propertiesType = true;
147+
return context.substring(0, prefixIndex);
148+
}
128149
return context;
129150
}
130151

152+
private Properties readProperties(InputStream inputStream) {
153+
Properties properties = new Properties();
154+
try (InputStream in = inputStream) {
155+
properties.load(in);
156+
}
157+
catch (Exception e) {
158+
throw new IllegalStateException("Cannot load environment", e);
159+
}
160+
return properties;
161+
}
162+
131163
}

0 commit comments

Comments
 (0)