Polishing.

Add author tags. Let VaultConfigLocation accept PropertyTransformer. Extract prefix computation into its own method. Tweak documentation wording.

See gh-488
Original pull request: gh-582.
This commit is contained in:
Mark Paluch
2021-06-18 10:35:38 +02:00
parent 6505bde94d
commit 80e3de4945
4 changed files with 55 additions and 31 deletions

View File

@@ -38,6 +38,22 @@ spring.config.import: vault://first/context/path, vault://other/path, vault://
----
====
Property names within a Spring `Environment` must be unique to avoid shadowing.
If you use the same secret names in different context paths and you want to expose these as individual properties you can distinguish them by adding a `prefix` query parameter to the location.
.application.yml
====
[source,yaml]
----
spring.config.import: vault://my/path?prefix=foo., vault://my/other/path?prefix=bar.
secret: ${foo.secret}
other.secret: ${bar.secret}
----
NOTE: Prefixes are added as-is to all property names returned by Vault. 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.
====
[[vault.configdata.location.optional]]
=== Conditionally enable/disable Vault Configuration
@@ -50,18 +66,6 @@ Optional locations are skipped during application startup if Vault support was d
NOTE: Vault context paths that cannot be found (HTTP Status 404) are skipped regardless of whether the config location is marked optional. <<vault.config.fail-fast>> allows failing on start if a Vault context path cannot be found because of HTTP Status 404.
If you have the same secret names in different paths, you can distinguish them by using a prefix on the path.
.application.yml
====
[source,yaml]
----
spring.config.import: vault://my/prefixed/path?prefix=prefix1, vault://my/other/path?prefix=prefix2
secret: ${prefix1.secret}
other.secret: ${prefix2.secret}
----
====
[[vault.configdata.customization]]
=== Infrastructure Customization

View File

@@ -77,6 +77,7 @@ import org.springframework.web.util.UriComponentsBuilder;
* required later on by {@link VaultConfigDataLoader}.
*
* @author Mark Paluch
* @author Jeffrey van der Laan
* @since 3.0
* @see VaultConfigurer
* @see BootstrapRegistry
@@ -118,17 +119,20 @@ public class VaultConfigDataLocationResolver implements ConfigDataLocationResolv
contextPath = contextPath.substring(1);
}
return Collections.singletonList(
new VaultConfigLocation(contextPath, getPropertyTransformer(contextPath), location.isOptional()));
}
private static PropertyTransformer getPropertyTransformer(String contextPath) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(contextPath).build();
String prefix = uriComponents.getQueryParams().getFirst("prefix");
String path = uriComponents.getPath();
if (StringUtils.hasLength(prefix) && StringUtils.hasLength(path)) {
PropertyTransformer keyPrefixPropertyTransformer = PropertyTransformers.propertyNamePrefix(prefix);
SecretBackendMetadata secretBackendMetadata = KeyValueSecretBackendMetadata.create(path, keyPrefixPropertyTransformer);
return Collections.singletonList(new VaultConfigLocation(secretBackendMetadata, location.isOptional()));
}
else {
return Collections.singletonList(new VaultConfigLocation(contextPath, location.isOptional()));
if (StringUtils.hasText(prefix) && StringUtils.hasText(uriComponents.getPath())) {
return PropertyTransformers.propertyNamePrefix(prefix);
}
return PropertyTransformers.noop();
}
private static void registerVaultProperties(ConfigDataLocationResolverContext context) {

View File

@@ -20,6 +20,8 @@ import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.config.ConfigDataResource;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.vault.core.util.PropertyTransformer;
import org.springframework.vault.core.util.PropertyTransformers;
/**
* Vault-specific implementation for a {@link ConfigDataLocation}. Consists of a
@@ -40,17 +42,38 @@ public class VaultConfigLocation extends ConfigDataResource {
private final boolean optional;
/**
* Create a new {@link VaultConfigLocation} instance.
* @param contextPath the context path
* @param optional if the resource is optional
*/
public VaultConfigLocation(String contextPath, boolean optional) {
this(contextPath, PropertyTransformers.noop(), optional);
}
/**
* Create a new {@link VaultConfigLocation} instance.
* @param contextPath the context path
* @param propertyTransformer the property transformer
* @param optional if the resource is optional
* @since 3.0.4
*/
public VaultConfigLocation(String contextPath, PropertyTransformer propertyTransformer, boolean optional) {
super(optional);
Assert.hasText(contextPath, "Location must not be empty");
validatePath(contextPath);
this.secretBackendMetadata = KeyValueSecretBackendMetadata.create(contextPath);
this.secretBackendMetadata = KeyValueSecretBackendMetadata.create(contextPath, propertyTransformer);
this.optional = optional;
}
/**
* Create a new {@link VaultConfigLocation} instance.
* @param secretBackendMetadata the backend descriptor.
* @param optional if the resource is optional
*/
public VaultConfigLocation(SecretBackendMetadata secretBackendMetadata, boolean optional) {
Assert.notNull(secretBackendMetadata, "SecretBackendMetadata must not be null");

View File

@@ -16,25 +16,19 @@
package org.springframework.cloud.vault.config;
import java.util.Arrays;
import java.util.List;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.context.properties.bind.Binder;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.Mockito.mock;
@@ -44,6 +38,7 @@ import static org.mockito.Mockito.when;
* Unit tests for {@link VaultConfigDataLocationResolver}.
*
* @author Mark Paluch
* @author Jeffrey van der Laan
*/
public class VaultConfigDataLocationResolverUnitTests {
@@ -106,10 +101,9 @@ public class VaultConfigDataLocationResolverUnitTests {
VaultConfigDataLocationResolver resolver = new VaultConfigDataLocationResolver();
List<VaultConfigLocation> locations = resolver.resolveProfileSpecific(this.contextMock,
ConfigDataLocation.of("vault://my/context/path?prefix=myPrefix"), this.profilesMock);
ConfigDataLocation.of("vault://my/context/path?prefix=myPrefix."), this.profilesMock);
assertThat(locations).hasSize(1);
assertThat(locations.get(0)).hasToString("VaultConfigLocation [path='my/context/path', optional=false]");
assertThat(locations.get(0).getSecretBackendMetadata().getPropertyTransformer()
.transformProperties(Collections.singletonMap("key", "value"))).containsEntry("myPrefix.key", "value");
}
@@ -123,7 +117,6 @@ public class VaultConfigDataLocationResolverUnitTests {
ConfigDataLocation.of("vault://my/context/path?prefix="), this.profilesMock);
assertThat(locations).hasSize(1);
assertThat(locations.get(0)).hasToString("VaultConfigLocation [path='my/context/path', optional=false]");
assertThat(locations.get(0).getSecretBackendMetadata().getPropertyTransformer()
.transformProperties(Collections.singletonMap("key", "value"))).containsEntry("key", "value");
}