Skip to content

Commit fe3700d

Browse files
committed
Version 1.0.0
1 parent 302c702 commit fe3700d

13 files changed

+992
-23
lines changed

.gitignore

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,4 @@
1-
# Compiled class file
2-
*.class
3-
4-
# Log file
5-
*.log
6-
7-
# BlueJ files
8-
*.ctxt
9-
10-
# Mobile Tools for Java (J2ME)
11-
.mtj.tmp/
12-
13-
# Package Files #
14-
*.jar
15-
*.war
16-
*.nar
17-
*.ear
18-
*.zip
19-
*.tar.gz
20-
*.rar
21-
22-
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23-
hs_err_pid*
1+
.settings
2+
target
3+
*.classpath
4+
*.project

pom.xml

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.coveo</groupId>
7+
<artifactId>spring-boot-parameter-store-integration</artifactId>
8+
<version>1.0.0</version>
9+
10+
<name>Spring Boot Parameter Store Integration</name>
11+
<description>An integration of Amazon Web Services' Systems Manager Parameter Store for Spring Boot's properties injection.</description>
12+
<url>https://github.com/coveo/spring-boot-parameter-store-integration</url>
13+
14+
<properties>
15+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
16+
</properties>
17+
18+
<licenses>
19+
<license>
20+
<name>MIT</name>
21+
<url>https://opensource.org/licenses/MIT</url>
22+
</license>
23+
</licenses>
24+
25+
<developers>
26+
<developer>
27+
<name>Frederic Boutin</name>
28+
<organization>Coveo</organization>
29+
<organizationUrl>https://github.com/coveo</organizationUrl>
30+
</developer>
31+
</developers>
32+
33+
<scm>
34+
<connection>scm:git:git@github.com:coveo/spring-boot-parameter-store-integration.git</connection>
35+
<developerConnection>scm:git:git@github.com:coveo/spring-boot-parameter-store-integration.git</developerConnection>
36+
<url>http://github.com/coveo/spring-boot-parameter-store-integration</url>
37+
</scm>
38+
39+
<!-- <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId>
40+
<artifactId>spring-boot-dependencies</artifactId> <version>1.5.14.RELEASE</version>
41+
<type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> -->
42+
43+
<dependencies>
44+
<dependency>
45+
<groupId>org.springframework.boot</groupId>
46+
<artifactId>spring-boot</artifactId>
47+
<version>1.5.14.RELEASE</version>
48+
</dependency>
49+
<dependency>
50+
<groupId>com.amazonaws</groupId>
51+
<artifactId>aws-java-sdk-ssm</artifactId>
52+
<version>1.11.351</version>
53+
</dependency>
54+
55+
<!-- Test libraries -->
56+
<dependency>
57+
<groupId>org.springframework.boot</groupId>
58+
<artifactId>spring-boot-starter-test</artifactId>
59+
<version>1.5.14.RELEASE</version>
60+
<scope>test</scope>
61+
</dependency>
62+
</dependencies>
63+
64+
<build>
65+
<plugins>
66+
<plugin>
67+
<groupId>net.revelc.code.formatter</groupId>
68+
<artifactId>formatter-maven-plugin</artifactId>
69+
<version>2.7.3</version>
70+
<configuration>
71+
<configFile>${project.basedir}/src/main/resources/code-formatter.xml</configFile>
72+
<lineEnding>LF</lineEnding>
73+
</configuration>
74+
<executions>
75+
<execution>
76+
<goals>
77+
<goal>format</goal>
78+
</goals>
79+
</execution>
80+
</executions>
81+
</plugin>
82+
<plugin>
83+
<groupId>org.apache.maven.plugins</groupId>
84+
<artifactId>maven-source-plugin</artifactId>
85+
<version>3.0.1</version>
86+
<configuration>
87+
</configuration>
88+
<executions>
89+
<execution>
90+
<id>attach-sources</id>
91+
<goals>
92+
<goal>jar</goal>
93+
</goals>
94+
</execution>
95+
</executions>
96+
</plugin>
97+
<plugin>
98+
<groupId>org.apache.maven.plugins</groupId>
99+
<artifactId>maven-compiler-plugin</artifactId>
100+
<version>3.7.0</version>
101+
<configuration>
102+
<source>1.8</source>
103+
<target>1.8</target>
104+
</configuration>
105+
</plugin>
106+
</plugins>
107+
108+
<pluginManagement>
109+
<plugins>
110+
<plugin>
111+
<groupId>org.eclipse.m2e</groupId>
112+
<artifactId>lifecycle-mapping</artifactId>
113+
<version>1.0.0</version>
114+
<configuration>
115+
<lifecycleMappingMetadata>
116+
<pluginExecutions>
117+
<pluginExecution>
118+
<pluginExecutionFilter>
119+
<groupId>
120+
net.revelc.code.formatter
121+
</groupId>
122+
<artifactId>
123+
formatter-maven-plugin
124+
</artifactId>
125+
<versionRange>
126+
[2.7.2,)
127+
</versionRange>
128+
<goals>
129+
<goal>format</goal>
130+
</goals>
131+
</pluginExecutionFilter>
132+
<action>
133+
<ignore></ignore>
134+
</action>
135+
</pluginExecution>
136+
</pluginExecutions>
137+
</lifecycleMappingMetadata>
138+
</configuration>
139+
</plugin>
140+
</plugins>
141+
</pluginManagement>
142+
</build>
143+
144+
<profiles>
145+
<profile>
146+
<id>release</id>
147+
<build>
148+
<plugins>
149+
<plugin>
150+
<groupId>org.apache.maven.plugins</groupId>
151+
<artifactId>maven-javadoc-plugin</artifactId>
152+
<version>3.0.1</version>
153+
<executions>
154+
<execution>
155+
<id>attach-javadocs</id>
156+
<goals>
157+
<goal>jar</goal>
158+
</goals>
159+
</execution>
160+
</executions>
161+
</plugin>
162+
<plugin>
163+
<groupId>org.sonatype.plugins</groupId>
164+
<artifactId>nexus-staging-maven-plugin</artifactId>
165+
<version>1.6.8</version>
166+
<extensions>true</extensions>
167+
<configuration>
168+
<serverId>ossrh</serverId>
169+
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
170+
<autoReleaseAfterClose>true</autoReleaseAfterClose>
171+
</configuration>
172+
</plugin>
173+
<plugin>
174+
<groupId>org.apache.maven.plugins</groupId>
175+
<artifactId>maven-gpg-plugin</artifactId>
176+
<version>1.6</version>
177+
<executions>
178+
<execution>
179+
<id>sign-artifacts</id>
180+
<phase>verify</phase>
181+
<goals>
182+
<goal>sign</goal>
183+
</goals>
184+
</execution>
185+
</executions>
186+
</plugin>
187+
</plugins>
188+
</build>
189+
</profile>
190+
</profiles>
191+
192+
<distributionManagement>
193+
<snapshotRepository>
194+
<id>ossrh</id>
195+
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
196+
</snapshotRepository>
197+
<repository>
198+
<id>ossrh</id>
199+
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
200+
</repository>
201+
</distributionManagement>
202+
</project>

readme.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Spring Boot Parameter Store Integration
2+
3+
The Spring Boot Parameter Store Integration is a tiny library used to integrate AWS Parameter Store in Spring Boot's powerful property injection. For example, it allows you to fetch a property directly using the `@Value` annotation. In fact, it simply adds a PropertySource with highest precedence to the existing ones (see [Spring Boot's External Configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html)).
4+
5+
## Requirements
6+
The library uses:
7+
8+
- [Spring Boot](https://spring.io/projects/spring-boot) 1.5.14.RELEASE
9+
- [AWS Java SDK](https://aws.amazon.com/sdk-for-java/) 1.11.351
10+
11+
Those can be overriden in your `pom.xml`.
12+
13+
The library was tested and worked properly with:
14+
15+
- [Spring Boot](https://spring.io/projects/spring-boot) 1.4.x, 1.5.x and 2.0.x
16+
- [AWS Java SDK](https://aws.amazon.com/sdk-for-java/) >= 1.11.164
17+
18+
## Unleashing the Magic
19+
20+
#### For your pom.xml:
21+
```
22+
<dependency>
23+
<groupId>com.coveo</groupId>
24+
<artifactId>spring-boot-parameter-store-integration</artifactId>
25+
<version>1.0.0</version>
26+
</dependency>
27+
```
28+
29+
#### There are 3 ways to enable this lib after importing it in your pom.xml, pick yours:
30+
- Set `awsParameterStorePropertySource.enabled` to `true` (yml, properties, or anything [here](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html))
31+
- Add the profile `awsParameterStorePropertySourceEnabled`
32+
- Add some custom profiles that should integrate the AWS Parameter Store with a comma-separated list such as `MyProductionProfile,MyTestProfile`.
33+
**Important**: using other list injecting methods like a yaml list won't work because this property gets loaded too early in the boot process.
34+
35+
#### Using the lib:
36+
Use a property that is prefixed with `/` somewhere such as
37+
```
38+
@Value("${/my/parameter/store/property}")
39+
String value;
40+
```
41+
42+
#### You might be wondering why use slashes (`/`)?
43+
The AWS Parameter Store already uses this naming pattern to classify your properties as you would do with folders. Using this prefix to limit the number of calls to AWS at boot seemed natural. This means properties not prefixed with `/` can't yet be fetched in the AWS Parameter Store using this lib.
44+
45+
## AWS Credentials
46+
47+
The lib uses the [DefaultAWSCredentialProviderChain](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html). This means if your code is running on an EC2 instance that has access to a Parameter Store property and its associated KMS key, the library should be able to fetch it without any configuration.
48+
49+
## Using Spring Boot's Placeholder Properties
50+
51+
Since naming properties with some `/` everywhere seems a bit awkward and not coherent with actual property keys, we suggest using [placeholder properties](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-placeholders-in-properties). This way you can use AWS Parameter Store without modifying your current property naming scheme.
52+
Using nested properties makes things easier for multiple environments and simplifies property name changes in the Parameter Store without editing the code (using an environment variable).
53+
54+
So your yml could look like this:
55+
```
56+
my.super.duper.secret: defaultValue
57+
```
58+
And you would inject the Parameter Store key through an environment variable using a placeholder like this:
59+
```
60+
my.super.duper.secret: ${/my/parameter/store/secret}
61+
```
62+
When Spring Boot encounters your environment variable, it doesn't inject `${/my/parameter/store/secret}` in your property `my.super.duper.secret`, but instead tries to load the property `/my/parameter/store/secret` from its property sources, and then hits the Parameter Store source because of the prefix `/`.
63+
64+
## Halting the Boot to Prevent Production Incidents
65+
66+
The default behaviour of a PropertySource when it can't find a property is to return `null`, and then the PropertyResolver iterates on every other PropertySource to find a matching value. This is the default behaviour for this lib.
67+
68+
If you want to halt the boot when a property prefixed with `/` isn't found in the Parameter Store, just set `awsParameterStorePropertySource.haltBoot` to `true` in your properties. We personally use this to prevent injecting default properties in a production environment.
69+
70+
## Spring Cloud
71+
72+
TL;DR: Define the enabling properties in the bootstrap properties (`bootstrap.yml`, `bootstrap.properties`, [etc.](https://cloud.spring.io/spring-cloud-static/spring-cloud.html#_the_bootstrap_application_context))(see [Unleashing the Magic](#there-are-3-ways-to-enable-this-lib-after-importing-it-in-your-pomxml-pick-yours)).
73+
74+
Spring Cloud has a second application context named bootstrap that gets initialized before Spring Boot's normal application context. Since this lib uses an EnvironmentPostPrecessor to add the Parameter Store PropertySource, it gets triggered twice. Since you probably want it to be added on the bootstrap context, we highly recommend using the bootstrap properties to enable this lib. More details on this [Stack Overflow Thread](https://stackoverflow.com/questions/50935915/adding-a-conditional-external-propertysource-in-a-spring-boot-application).
75+
76+
## Contributing
77+
Open an issue to report bugs or to request additional features. Pull requests are always welcome.
78+
79+
# Enjoy 🍻
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.coveo.configuration.parameterstore;
2+
3+
import org.springframework.core.env.PropertySource;
4+
5+
public class ParameterStorePropertySource extends PropertySource<ParameterStoreSource>
6+
{
7+
public static final String PARAMETER_STORE_HIERARCHY_SPLIT_CHARACTER = "/";
8+
9+
public ParameterStorePropertySource(String name, ParameterStoreSource source)
10+
{
11+
super(name, source);
12+
}
13+
14+
@Override
15+
public Object getProperty(String name)
16+
{
17+
if (name.startsWith(PARAMETER_STORE_HIERARCHY_SPLIT_CHARACTER)) {
18+
return source.getProperty(name);
19+
}
20+
return null;
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.coveo.configuration.parameterstore;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.env.EnvironmentPostProcessor;
5+
import org.springframework.core.env.ConfigurableEnvironment;
6+
import org.springframework.util.ObjectUtils;
7+
8+
import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagementClientBuilder;
9+
10+
public class ParameterStorePropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor
11+
{
12+
static final String PARAMETER_STORE_ACCEPTED_PROFILE = "awsParameterStorePropertySourceEnabled";
13+
14+
static final String PARAMETER_STORE_ACCEPTED_PROFILES_CONFIGURATION_PROPERTY = "awsParameterStorePropertySource.enabledProfiles";
15+
static final String PARAMETER_STORE_ENABLED_CONFIGURATION_PROPERTY = "awsParameterStorePropertySource.enabled";
16+
static final String PARAMETER_STORE_HALT_BOOT_CONFIGURATION_PROPERTY = "awsParameterStorePropertySource.haltBoot";
17+
18+
private static final String PARAMETER_STORE_PROPERTY_SOURCE_NAME = "AWSParameterStorePropertySource";
19+
20+
static boolean initialized;
21+
22+
@Override
23+
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application)
24+
{
25+
if (!initialized && isParameterStorePropertySourceEnabled(environment)) {
26+
environment.getPropertySources()
27+
.addFirst(new ParameterStorePropertySource(PARAMETER_STORE_PROPERTY_SOURCE_NAME,
28+
new ParameterStoreSource(AWSSimpleSystemsManagementClientBuilder.defaultClient(),
29+
environment.getProperty(PARAMETER_STORE_HALT_BOOT_CONFIGURATION_PROPERTY,
30+
Boolean.class,
31+
Boolean.FALSE))));
32+
initialized = true;
33+
}
34+
}
35+
36+
private boolean isParameterStorePropertySourceEnabled(ConfigurableEnvironment environment)
37+
{
38+
String[] userDefinedEnabledProfiles = environment.getProperty(PARAMETER_STORE_ACCEPTED_PROFILES_CONFIGURATION_PROPERTY,
39+
String[].class);
40+
return environment.getProperty(PARAMETER_STORE_ENABLED_CONFIGURATION_PROPERTY, Boolean.class, Boolean.FALSE)
41+
|| environment.acceptsProfiles(PARAMETER_STORE_ACCEPTED_PROFILE)
42+
|| (!ObjectUtils.isEmpty(userDefinedEnabledProfiles)
43+
&& environment.acceptsProfiles(userDefinedEnabledProfiles));
44+
}
45+
}

0 commit comments

Comments
 (0)