Implement config data loader to load from environment variables
The config data loader supports the env: prefix and also accepts extension hints. Example: env:VAR1[.properties] reads the environment variable 'VAR1' in properties format (using the PropertiesPropertySourceLoader). The PropertySourceLoaders are loaded via spring.factories. Also adds a smoke test to test it end to end. Closes gh-41609
This commit is contained in:
parent
910d57ed90
commit
61d7f3783e
@ -354,11 +354,39 @@ spring:
|
||||
|
||||
|
||||
|
||||
[[features.external-config.files.env-variables]]
|
||||
=== Using Environment Variables
|
||||
|
||||
When running applications on a cloud platform (such as Kubernetes) you often need to read config values that the platform supplies.
|
||||
You can either use environment variables for such purpose, or you can use xref:reference:features/external-config.adoc#features.external-config.files.configtree[configuration trees].
|
||||
|
||||
You can even store whole configurations in properties or yaml format in (multiline) environment variables and load them using the `env:` prefix.
|
||||
Assume there's an environment variable called `MY_CONFIGURATION` with this content:
|
||||
|
||||
[source,properties]
|
||||
----
|
||||
my.name=Service1
|
||||
my.cluster=Cluster1
|
||||
----
|
||||
|
||||
Using the `env:` prefix it is possible to import all properties from this variable:
|
||||
|
||||
[configprops,yaml]
|
||||
----
|
||||
spring:
|
||||
config:
|
||||
import: "env:MY_CONFIGURATION"
|
||||
----
|
||||
|
||||
TIP: This feature also supports xref:reference:features/external-config.adoc#features.external-config.files.importing-extensionless[specifying the extension].
|
||||
The default extension is `.properties`.
|
||||
|
||||
|
||||
|
||||
[[features.external-config.files.configtree]]
|
||||
=== Using Configuration Trees
|
||||
|
||||
When running applications on a cloud platform (such as Kubernetes) you often need to read config values that the platform supplies.
|
||||
It is not uncommon to use environment variables for such purposes, but this can have drawbacks, especially if the value is supposed to be kept secret.
|
||||
Storing config values in environment variables has drawbacks, especially if the value is supposed to be kept secret.
|
||||
|
||||
As an alternative to environment variables, many cloud platforms now allow you to map configuration into mounted data volumes.
|
||||
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`].
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 org.springframework.boot.context.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
|
||||
/**
|
||||
* {@link ConfigDataLoader} to load data from environment variables.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class EnvConfigDataLoader implements ConfigDataLoader<EnvConfigDataResource> {
|
||||
|
||||
private final Function<String, String> readEnvVariable;
|
||||
|
||||
EnvConfigDataLoader() {
|
||||
this.readEnvVariable = System::getenv;
|
||||
}
|
||||
|
||||
EnvConfigDataLoader(Function<String, String> readEnvVariable) {
|
||||
this.readEnvVariable = readEnvVariable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigData load(ConfigDataLoaderContext context, EnvConfigDataResource resource)
|
||||
throws IOException, ConfigDataResourceNotFoundException {
|
||||
String content = this.readEnvVariable.apply(resource.getVariableName());
|
||||
if (content == null) {
|
||||
throw new ConfigDataResourceNotFoundException(resource);
|
||||
}
|
||||
String name = String.format("Environment variable '%s' via location '%s'", resource.getVariableName(),
|
||||
resource.getLocation());
|
||||
return new ConfigData(resource.getLoader().load(name, createResource(content)));
|
||||
}
|
||||
|
||||
private ByteArrayResource createResource(String content) {
|
||||
return new ByteArrayResource(content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 org.springframework.boot.context.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.boot.env.PropertySourceLoader;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
||||
/**
|
||||
* {@link ConfigDataLocationResolver} to resolve {@code env:} locations.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class EnvConfigDataLocationResolver implements ConfigDataLocationResolver<EnvConfigDataResource> {
|
||||
|
||||
private static final String PREFIX = "env:";
|
||||
|
||||
private static final Pattern EXTENSION_HINT_PATTERN = Pattern.compile("^(.*)\\[(\\.\\w+)](?!\\[)$");
|
||||
|
||||
private static final String DEFAULT_EXTENSION = ".properties";
|
||||
|
||||
private final List<PropertySourceLoader> loaders;
|
||||
|
||||
private final Function<String, String> readEnvVariable;
|
||||
|
||||
EnvConfigDataLocationResolver() {
|
||||
this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
|
||||
this.readEnvVariable = System::getenv;
|
||||
}
|
||||
|
||||
EnvConfigDataLocationResolver(List<PropertySourceLoader> loaders, Function<String, String> readEnvVariable) {
|
||||
this.loaders = loaders;
|
||||
this.readEnvVariable = readEnvVariable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
|
||||
return location.hasPrefix(PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnvConfigDataResource> resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location)
|
||||
throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException {
|
||||
String value = location.getNonPrefixedValue(PREFIX);
|
||||
Matcher matcher = EXTENSION_HINT_PATTERN.matcher(value);
|
||||
String extension = getExtension(matcher);
|
||||
String variableName = getVariableName(matcher, value);
|
||||
PropertySourceLoader loader = getLoader(extension);
|
||||
if (hasEnvVariable(variableName)) {
|
||||
return List.of(new EnvConfigDataResource(location, variableName, loader));
|
||||
}
|
||||
if (location.isOptional()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
throw new ConfigDataLocationNotFoundException(location,
|
||||
"Environment variable '%s' is not set".formatted(variableName), null);
|
||||
}
|
||||
|
||||
private PropertySourceLoader getLoader(String extension) {
|
||||
if (extension == null) {
|
||||
extension = DEFAULT_EXTENSION;
|
||||
}
|
||||
if (extension.startsWith(".")) {
|
||||
extension = extension.substring(1);
|
||||
}
|
||||
for (PropertySourceLoader loader : this.loaders) {
|
||||
for (String supportedExtension : loader.getFileExtensions()) {
|
||||
if (supportedExtension.equalsIgnoreCase(extension)) {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"File extension '%s' is not known to any PropertySourceLoader".formatted(extension));
|
||||
}
|
||||
|
||||
private boolean hasEnvVariable(String variableName) {
|
||||
return this.readEnvVariable.apply(variableName) != null;
|
||||
}
|
||||
|
||||
private String getVariableName(Matcher matcher, String value) {
|
||||
if (matcher.matches()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String getExtension(Matcher matcher) {
|
||||
if (matcher.matches()) {
|
||||
return matcher.group(2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 org.springframework.boot.context.config;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.boot.env.PropertySourceLoader;
|
||||
|
||||
/**
|
||||
* {@link ConfigDataResource} used by {@link EnvConfigDataLoader}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class EnvConfigDataResource extends ConfigDataResource {
|
||||
|
||||
private final ConfigDataLocation location;
|
||||
|
||||
private final String variableName;
|
||||
|
||||
private final PropertySourceLoader loader;
|
||||
|
||||
EnvConfigDataResource(ConfigDataLocation location, String variableName, PropertySourceLoader loader) {
|
||||
super(location.isOptional());
|
||||
this.location = location;
|
||||
this.variableName = variableName;
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
ConfigDataLocation getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
String getVariableName() {
|
||||
return this.variableName;
|
||||
}
|
||||
|
||||
PropertySourceLoader getLoader() {
|
||||
return this.loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
EnvConfigDataResource that = (EnvConfigDataResource) o;
|
||||
return Objects.equals(this.location, that.location) && Objects.equals(this.variableName, that.variableName)
|
||||
&& Objects.equals(this.loader, that.loader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.location, this.variableName, this.loader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "env variable [" + this.variableName + "]";
|
||||
}
|
||||
|
||||
}
|
@ -12,11 +12,13 @@ org.springframework.boot.env.YamlPropertySourceLoader
|
||||
# ConfigData Location Resolvers
|
||||
org.springframework.boot.context.config.ConfigDataLocationResolver=\
|
||||
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
|
||||
org.springframework.boot.context.config.EnvConfigDataLocationResolver,\
|
||||
org.springframework.boot.context.config.StandardConfigDataLocationResolver
|
||||
|
||||
# ConfigData Loaders
|
||||
org.springframework.boot.context.config.ConfigDataLoader=\
|
||||
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
|
||||
org.springframework.boot.context.config.EnvConfigDataLoader,\
|
||||
org.springframework.boot.context.config.StandardConfigDataLoader
|
||||
|
||||
# Application Context Factories
|
||||
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 org.springframework.boot.context.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.env.PropertiesPropertySourceLoader;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link EnvConfigDataLoader}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class EnvConfigDataLoaderTests {
|
||||
|
||||
private ConfigDataLoaderContext context;
|
||||
|
||||
private Map<String, String> envVariables;
|
||||
|
||||
private EnvConfigDataLoader loader;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.context = mock(ConfigDataLoaderContext.class);
|
||||
this.envVariables = new HashMap<>();
|
||||
this.loader = new EnvConfigDataLoader(this.envVariables::get);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadFromVariable() throws IOException {
|
||||
this.envVariables.put("VAR1", "key1=value1");
|
||||
ConfigData data = this.loader.load(this.context, createResource("VAR1"));
|
||||
assertThat(data.getPropertySources()).hasSize(1);
|
||||
PropertySource<?> propertySource = data.getPropertySources().get(0);
|
||||
assertThat(propertySource.getProperty("key1")).isEqualTo("value1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailIfVariableIsNotSet() {
|
||||
assertThatExceptionOfType(ConfigDataResourceNotFoundException.class)
|
||||
.isThrownBy(() -> this.loader.load(this.context, createResource("VAR1")))
|
||||
.withMessage("Config data resource 'env variable [VAR1]' cannot be found");
|
||||
}
|
||||
|
||||
private static EnvConfigDataResource createResource(String variableName) {
|
||||
return new EnvConfigDataResource(ConfigDataLocation.of("env:" + variableName), variableName,
|
||||
new PropertiesPropertySourceLoader());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 org.springframework.boot.context.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.env.PropertiesPropertySourceLoader;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link EnvConfigDataLocationResolver}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class EnvConfigDataLocationResolverTests {
|
||||
|
||||
private EnvConfigDataLocationResolver resolver;
|
||||
|
||||
private Map<String, String> envVariables;
|
||||
|
||||
private ConfigDataLocationResolverContext context;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.context = mock(ConfigDataLocationResolverContext.class);
|
||||
this.envVariables = new HashMap<>();
|
||||
this.resolver = new EnvConfigDataLocationResolver(
|
||||
List.of(new PropertiesPropertySourceLoader(), new YamlPropertySourceLoader()), this.envVariables::get);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isResolvable() {
|
||||
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("env:VAR1"))).isTrue();
|
||||
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("dummy:VAR1"))).isFalse();
|
||||
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("VAR1"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolve() {
|
||||
this.envVariables.put("VAR1", "VALUE1");
|
||||
ConfigDataLocation location = ConfigDataLocation.of("env:VAR1");
|
||||
List<EnvConfigDataResource> resolved = this.resolver.resolve(this.context, location);
|
||||
assertThat(resolved).hasSize(1);
|
||||
EnvConfigDataResource resource = resolved.get(0);
|
||||
assertThat(resource.getLocation()).isEqualTo(location);
|
||||
assertThat(resource.getVariableName()).isEqualTo("VAR1");
|
||||
assertThat(resource.getLoader()).isInstanceOf(PropertiesPropertySourceLoader.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveOptional() {
|
||||
this.envVariables.put("VAR1", "VALUE1");
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:env:VAR1");
|
||||
List<EnvConfigDataResource> resolved = this.resolver.resolve(this.context, location);
|
||||
assertThat(resolved).hasSize(1);
|
||||
EnvConfigDataResource resource = resolved.get(0);
|
||||
assertThat(resource.getLocation()).isEqualTo(location);
|
||||
assertThat(resource.getVariableName()).isEqualTo("VAR1");
|
||||
assertThat(resource.getLoader()).isInstanceOf(PropertiesPropertySourceLoader.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveOptionalIfVariableIsNotSet() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:env:VAR1");
|
||||
List<EnvConfigDataResource> resolved = this.resolver.resolve(this.context, location);
|
||||
assertThat(resolved).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveWithPropertiesExtension() {
|
||||
this.envVariables.put("VAR1", "VALUE1");
|
||||
ConfigDataLocation location = ConfigDataLocation.of("env:VAR1[.properties]");
|
||||
List<EnvConfigDataResource> resolved = this.resolver.resolve(this.context, location);
|
||||
assertThat(resolved).hasSize(1);
|
||||
EnvConfigDataResource resource = resolved.get(0);
|
||||
assertThat(resource.getLocation()).isEqualTo(location);
|
||||
assertThat(resource.getVariableName()).isEqualTo("VAR1");
|
||||
assertThat(resource.getLoader()).isInstanceOf(PropertiesPropertySourceLoader.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveWithYamlExtension() {
|
||||
this.envVariables.put("VAR1", "VALUE1");
|
||||
ConfigDataLocation location = ConfigDataLocation.of("env:VAR1[.yaml]");
|
||||
List<EnvConfigDataResource> resolved = this.resolver.resolve(this.context, location);
|
||||
assertThat(resolved).hasSize(1);
|
||||
EnvConfigDataResource resource = resolved.get(0);
|
||||
assertThat(resource.getLocation()).isEqualTo(location);
|
||||
assertThat(resource.getVariableName()).isEqualTo("VAR1");
|
||||
assertThat(resource.getLoader()).isInstanceOf(YamlPropertySourceLoader.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailIfVariableIsNotSet() {
|
||||
assertThatExceptionOfType(ConfigDataLocationNotFoundException.class)
|
||||
.isThrownBy(() -> this.resolver.resolve(this.context, ConfigDataLocation.of("env:VAR1")))
|
||||
.withMessage("Environment variable 'VAR1' is not set");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailIfUnknownExtensionIsGiven() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.resolver.resolve(this.context, ConfigDataLocation.of("env:VAR1[.dummy]")))
|
||||
.withMessage("File extension 'dummy' is not known to any PropertySourceLoader");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 org.springframework.boot.context.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.env.PropertiesPropertySourceLoader;
|
||||
import org.springframework.boot.env.PropertySourceLoader;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link EnvConfigDataResource}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class EnvConfigDataResourceTests {
|
||||
|
||||
private final YamlPropertySourceLoader yamlPropertySourceLoader = new YamlPropertySourceLoader();
|
||||
|
||||
private final PropertiesPropertySourceLoader propertiesPropertySourceLoader = new PropertiesPropertySourceLoader();
|
||||
|
||||
@Test
|
||||
void shouldHaveEqualsAndHashcode() {
|
||||
EnvConfigDataResource var1 = createResource("VAR1");
|
||||
EnvConfigDataResource var2 = createResource("VAR2");
|
||||
EnvConfigDataResource var3 = createResource("VAR1", this.yamlPropertySourceLoader);
|
||||
EnvConfigDataResource var4 = createResource("VAR1");
|
||||
assertThat(var1).isNotEqualTo(var2);
|
||||
assertThat(var1).isNotEqualTo(var3);
|
||||
assertThat(var1).isEqualTo(var4);
|
||||
assertThat(var1).hasSameHashCodeAs(var4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveToString() {
|
||||
EnvConfigDataResource resource = createResource("VAR1");
|
||||
assertThat(resource).hasToString("env variable [VAR1]");
|
||||
}
|
||||
|
||||
private EnvConfigDataResource createResource(String variableName) {
|
||||
return createResource(variableName, this.propertiesPropertySourceLoader);
|
||||
}
|
||||
|
||||
private EnvConfigDataResource createResource(String variableName, PropertySourceLoader propertySourceLoader) {
|
||||
return new EnvConfigDataResource(ConfigDataLocation.of("env:" + variableName), variableName,
|
||||
propertySourceLoader);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
plugins {
|
||||
id "java"
|
||||
}
|
||||
|
||||
description = "Spring Boot Config smoke test"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))
|
||||
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
|
||||
}
|
||||
|
||||
tasks.named("test", Test) {
|
||||
environment "SMOKE_TEST_CONFIG_ENV", "from-env.key1=value1"
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 smoketest.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties("from-env")
|
||||
class FromEnvConfigurationProperties {
|
||||
|
||||
private String key1;
|
||||
|
||||
String getKey1() {
|
||||
return this.key1;
|
||||
}
|
||||
|
||||
void setKey1(String key1) {
|
||||
this.key1 = key1;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 smoketest.config;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableConfigurationProperties(FromEnvConfigurationProperties.class)
|
||||
public class SampleConfigApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleConfigApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
spring.config.import=optional:env:SMOKE_TEST_CONFIG_ENV
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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 smoketest.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link FromEnvConfigurationProperties}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
@SpringBootTest
|
||||
class FromEnvConfigurationPropertiesTests {
|
||||
|
||||
@Autowired
|
||||
private FromEnvConfigurationProperties properties;
|
||||
|
||||
@Test
|
||||
void shouldHaveImportedValues() {
|
||||
assertThat(this.properties.getKey1()).isEqualTo("value1");
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user