Skip to content

Commit 3f76eb2

Browse files
mbhavephilwebb
andcommitted
Add volume mount property source support
Add support for volume mounted directories where the filename becomes the property key and the file contents becomes the value. Support is provided via a dedicated `VolumeMountDirectoryPropertySource` class which can either be used directly, or via a "volumemount:/..." `spring.config.import` location. Closes gh-19990 Co-authored-by: Phillip Webb <[email protected]>
1 parent eee260f commit 3f76eb2

File tree

10 files changed

+900
-3
lines changed

10 files changed

+900
-3
lines changed

Diff for: spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc

+43-1
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ Locations will be processed in the order that they are defined, with later impor
670670
[TIP]
671671
====
672672
Spring Boot includes pluggable API that allows various different location addresses to be supported.
673-
By default you can import Java Properties and YAML.
673+
By default you can import Java Properties, YAML and volume mounts.
674674
675675
Third-party jars can offer support for additional technologies (there's no requirement for files to be local).
676676
For example, you can imagine config data being from external stores such as Consul, Apache ZooKeeper or Netflix Archaius.
@@ -680,6 +680,48 @@ If you want to support your own locations, see the `ConfigDataLocationResolver`
680680

681681

682682

683+
[[boot-features-external-config-files-voumemounts]]
684+
==== Using Volume Mount Properties
685+
When running applications on a cloud platform (such as Kubernetes) you often need to read config values that the platform supplies.
686+
It's not uncommon to use environment variables for such purposes, but this can have drawbacks, especially if the value is supposed to be kept secret.
687+
688+
As an alternative to environment variables, many cloud platforms now allow you to map configuration into mounted data volumes.
689+
For example, Kubernetes can volume mount both https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#populate-a-volume-with-data-stored-in-a-configmap[`ConfigMaps`] and https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-files-from-a-pod[`Secrets`].
690+
691+
There are two common volume mount patterns that can be use:
692+
693+
. A single file contains a complete set of properties (usually written as YAML).
694+
. Multiple files are written to a directory with the filename becoming the '`key`' and the contents becoming the '`value`'.
695+
696+
For the first case, you can import the YAML or Properties file directly using `spring.config.import` as described <<boot-features-external-config-files-importing,above>>.
697+
For the second case, you need to use the `volumemount:` prefix so that Spring Boot knows it needs to expose all the files as properties.
698+
699+
As an example, let's imagine that Kubernetes has mounted the following volume:
700+
701+
[source,indent=0]
702+
----
703+
etc/
704+
config/
705+
myapp/
706+
username
707+
password
708+
----
709+
710+
The contents of the `username` file would be a config value, and the contents of `password` would be a secret.
711+
712+
To import these properties, you can add the following to your `application.properties` file:
713+
714+
[source,properties,indent=0]
715+
----
716+
spring.config.import=volumemount:/etc/config
717+
----
718+
719+
You can then access or inject `myapp.username` and `myapp.password` properties from the `Environment` in the usual way.
720+
721+
TIP: Volume mounted values can be bound to both string `String` and `byte[]` types depending on the contents expected.
722+
723+
724+
683725
[[boot-features-external-config-placeholders-in-properties]]
684726
==== Property Placeholders
685727
The values in `application.properties` and `application.yml` are filtered through the existing `Environment` when they are used, so you can refer back to previously defined values (for example, from System properties).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2012-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.boot.context.config;
18+
19+
import java.io.IOException;
20+
import java.nio.file.Path;
21+
import java.util.Collections;
22+
23+
import org.springframework.boot.env.VolumeMountDirectoryPropertySource;
24+
25+
/**
26+
* {@link ConfigDataLoader} for directory locations mounted as volumes.
27+
*
28+
* @author Madhura Bhave
29+
* @author Phillip Webb
30+
*/
31+
class VolumeMountConfigDataLoader implements ConfigDataLoader<VolumeMountConfigDataLocation> {
32+
33+
@Override
34+
public ConfigData load(VolumeMountConfigDataLocation location) throws IOException {
35+
Path path = location.getPath();
36+
String name = "Volume mount config '" + path + "'";
37+
VolumeMountDirectoryPropertySource source = new VolumeMountDirectoryPropertySource(name, path);
38+
return new ConfigData(Collections.singletonList(source));
39+
}
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2012-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.boot.context.config;
18+
19+
import java.nio.file.Path;
20+
import java.nio.file.Paths;
21+
import java.util.Objects;
22+
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* {@link ConfigDataLocation} backed by a directory mounted as a volume.
27+
*
28+
* @author Madhura Bhave
29+
* @author Phillip Webb
30+
*/
31+
class VolumeMountConfigDataLocation extends ConfigDataLocation {
32+
33+
private final Path path;
34+
35+
VolumeMountConfigDataLocation(String path) {
36+
Assert.notNull(path, "Path must not be null");
37+
this.path = Paths.get(path).toAbsolutePath();
38+
}
39+
40+
Path getPath() {
41+
return this.path;
42+
}
43+
44+
@Override
45+
public boolean equals(Object obj) {
46+
if (this == obj) {
47+
return true;
48+
}
49+
if (obj == null || getClass() != obj.getClass()) {
50+
return false;
51+
}
52+
VolumeMountConfigDataLocation other = (VolumeMountConfigDataLocation) obj;
53+
return Objects.equals(this.path, other.path);
54+
}
55+
56+
@Override
57+
public int hashCode() {
58+
return this.path.hashCode();
59+
}
60+
61+
@Override
62+
public String toString() {
63+
return "volume mount [" + this.path + "]";
64+
}
65+
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-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.boot.context.config;
18+
19+
import java.util.Collections;
20+
import java.util.List;
21+
22+
/**
23+
* {@link ConfigDataLocationResolver} for volume mounted locations such as Kubernetes
24+
* ConfigMaps and Secrets.
25+
*
26+
* @author Madhura Bhave
27+
* @author Phillip Webb
28+
*/
29+
class VolumeMountConfigDataLocationResolver implements ConfigDataLocationResolver<VolumeMountConfigDataLocation> {
30+
31+
private static final String PREFIX = "volumemount:";
32+
33+
@Override
34+
public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
35+
return location.startsWith(PREFIX);
36+
}
37+
38+
@Override
39+
public List<VolumeMountConfigDataLocation> resolve(ConfigDataLocationResolverContext context, String location) {
40+
VolumeMountConfigDataLocation resolved = new VolumeMountConfigDataLocation(location.substring(PREFIX.length()));
41+
return Collections.singletonList(resolved);
42+
}
43+
44+
}

0 commit comments

Comments
 (0)