Adopt Spring Vault's LeaseAwareVaultPropertySource.
We now use Spring Vault's LeaseAwareVaultPropertySource instead of the own `LeaseAwareVaultPropertySource`. Closes gh-88.
This commit is contained in:
@@ -15,10 +15,16 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.core.util.PropertyTransformer;
|
||||
import org.springframework.vault.core.util.PropertyTransformers;
|
||||
|
||||
@@ -77,4 +83,58 @@ class GenericSecretBackendMetadata implements SecretBackendMetadata {
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of context paths from application name and the active profile names.
|
||||
* Application name and profiles support multiple (comma-separated) values.
|
||||
*
|
||||
* @param genericBackendProperties
|
||||
* @param environment
|
||||
* @return
|
||||
*/
|
||||
public static List<String> buildContexts(
|
||||
VaultGenericBackendProperties genericBackendProperties,
|
||||
Environment environment) {
|
||||
|
||||
String appName = genericBackendProperties.getApplicationName();
|
||||
List<String> profiles = Arrays.asList(environment.getActiveProfiles());
|
||||
List<String> contexts = new ArrayList<>();
|
||||
|
||||
String defaultContext = genericBackendProperties.getDefaultContext();
|
||||
addContext(contexts, defaultContext, profiles, genericBackendProperties);
|
||||
|
||||
for (String applicationName : StringUtils.commaDelimitedListToSet(appName)) {
|
||||
addContext(contexts, applicationName, profiles, genericBackendProperties);
|
||||
}
|
||||
|
||||
Collections.reverse(contexts);
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private static void addContext(List<String> contexts, String applicationName,
|
||||
List<String> profiles,
|
||||
VaultGenericBackendProperties genericBackendProperties) {
|
||||
|
||||
if (!StringUtils.hasText(applicationName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contexts.contains(applicationName)) {
|
||||
contexts.add(applicationName);
|
||||
}
|
||||
|
||||
for (String profile : profiles) {
|
||||
|
||||
if (!StringUtils.hasText(profile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String contextName = applicationName
|
||||
+ genericBackendProperties.getProfileSeparator() + profile.trim();
|
||||
|
||||
if (!contexts.contains(contextName)) {
|
||||
contexts.add(contextName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.cloud.vault.config;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
class Lease {
|
||||
|
||||
private final String leaseId;
|
||||
|
||||
private final long leaseDuration;
|
||||
|
||||
private final boolean renewable;
|
||||
|
||||
private Lease(String leaseId, long leaseDuration, boolean renewable) {
|
||||
|
||||
Assert.hasText(leaseId, "LeaseId must not be empty");
|
||||
|
||||
this.leaseId = leaseId;
|
||||
this.leaseDuration = leaseDuration;
|
||||
this.renewable = renewable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Lease}.
|
||||
*
|
||||
* @param leaseId must not be empty or {@literal null}.
|
||||
* @param leaseDuration the lease duration in seconds
|
||||
* @param renewable {@literal true} if this lease is renewable.
|
||||
* @return the created {@link Lease}
|
||||
*/
|
||||
public static Lease of(String leaseId, long leaseDuration, boolean renewable) {
|
||||
return new Lease(leaseId, leaseDuration, renewable);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the lease Id
|
||||
*/
|
||||
public String getLeaseId() {
|
||||
return leaseId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getLeaseDuration() {
|
||||
return leaseDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {@literal true} if the lease is renewable.
|
||||
*/
|
||||
public boolean isRenewable() {
|
||||
return renewable;
|
||||
}
|
||||
}
|
||||
@@ -1,400 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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
|
||||
*
|
||||
* http://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.cloud.vault.config;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.TriggerContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.client.VaultResponses;
|
||||
import org.springframework.vault.core.RestOperationsCallback;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
|
||||
/**
|
||||
* A {@link VaultPropertySource} that renews a {@link Lease} associated with
|
||||
* {@link Secrets}.
|
||||
*
|
||||
* <p>
|
||||
* {@link Lease} is scheduled right before its expiry. Expiry threshold can be set by
|
||||
* calling {@link #setExpiryThresholdSeconds(int)}. Leases that reached their maximum
|
||||
* lifetime are not re-read from Vault.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CommonsLog
|
||||
class LeasingVaultPropertySource extends VaultPropertySource implements DisposableBean {
|
||||
|
||||
private final LeaseRenewalScheduler leaseRenewal;
|
||||
|
||||
private int minRenewalSeconds = 10;
|
||||
|
||||
private int expiryThresholdSeconds = 60;
|
||||
|
||||
private volatile Lease lease;
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultPropertySource}.
|
||||
*
|
||||
* @param operations must not be {@literal null}.
|
||||
* @param failFast fail if properties could not be read because of access errors.
|
||||
* @param secretBackendMetadata must not be {@literal null}.
|
||||
* @param taskScheduler must not be {@literal null}.
|
||||
*/
|
||||
public LeasingVaultPropertySource(VaultConfigOperations operations, boolean failFast,
|
||||
SecretBackendMetadata secretBackendMetadata, TaskScheduler taskScheduler) {
|
||||
|
||||
super(operations, failFast, secretBackendMetadata);
|
||||
|
||||
Assert.notNull(taskScheduler, "TaskScheduler must not be null");
|
||||
|
||||
leaseRenewal = new LeaseRenewalScheduler(taskScheduler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expiry threshold. {@link Lease} is renewed the given seconds before it
|
||||
* expires.
|
||||
*
|
||||
* @param expiryThresholdSeconds number of seconds before {@link Lease} expiry.
|
||||
*/
|
||||
public void setExpiryThresholdSeconds(int expiryThresholdSeconds) {
|
||||
this.expiryThresholdSeconds = expiryThresholdSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount of seconds that is at least required before renewing a lease.
|
||||
* {@code minRenewalSeconds} prevents renewals to happen too often.
|
||||
*
|
||||
* @param minRenewalSeconds number of seconds that is at least required before
|
||||
* renewing a {@link Lease}.
|
||||
*/
|
||||
public void setMinRenewalSeconds(int minRenewalSeconds) {
|
||||
this.minRenewalSeconds = minRenewalSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
|
||||
super.init();
|
||||
|
||||
Secrets secrets = getSecrets();
|
||||
|
||||
this.lease = getLease(secrets);
|
||||
|
||||
potentiallyScheduleLeaseRenewal(this.lease);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown this {@link LeasingVaultPropertySource}
|
||||
*/
|
||||
public void destroy() {
|
||||
|
||||
if (this.lease != null) {
|
||||
try {
|
||||
leaseRenewal.disableScheduleRenewal();
|
||||
doRevokeLease(this.lease);
|
||||
}
|
||||
finally {
|
||||
this.lease = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Lease getLease(Secrets secrets) {
|
||||
|
||||
if (secrets == null || !StringUtils.hasText(secrets.getLeaseId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Lease.of(secrets.getLeaseId(), secrets.getLeaseDuration(),
|
||||
secrets.isRenewable());
|
||||
}
|
||||
|
||||
private void potentiallyScheduleLeaseRenewal(Lease lease) {
|
||||
|
||||
if (leaseRenewal.isLeaseRenewable(lease)) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Lease %s qualified for renewal",
|
||||
lease.getLeaseId()));
|
||||
}
|
||||
|
||||
leaseRenewal.scheduleRenewal(new RenewLease() {
|
||||
@Override
|
||||
public Lease renewLease(Lease lease) {
|
||||
|
||||
Lease newLease = doRenewLease(lease);
|
||||
LeasingVaultPropertySource.this.lease = newLease;
|
||||
potentiallyScheduleLeaseRenewal(newLease);
|
||||
|
||||
return newLease;
|
||||
}
|
||||
}, lease, minRenewalSeconds, expiryThresholdSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renews a {@link Lease}.
|
||||
*
|
||||
* @param lease the lease
|
||||
* @return the new lease.
|
||||
*/
|
||||
private Lease doRenewLease(final Lease lease) {
|
||||
|
||||
ResponseEntity<Map<String, Object>> entity = null;
|
||||
try {
|
||||
entity = getSource().getVaultOperations().doWithSession(
|
||||
new RestOperationsCallback<ResponseEntity<Map<String, Object>>>() {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ResponseEntity<Map<String, Object>> doWithRestOperations(
|
||||
RestOperations restOperations) {
|
||||
return (ResponseEntity) restOperations.exchange(
|
||||
"/sys/renew/{leaseId}", HttpMethod.PUT, null,
|
||||
Map.class, lease.getLeaseId());
|
||||
}
|
||||
});
|
||||
|
||||
Map<String, Object> body = entity.getBody();
|
||||
String leaseId = (String) body.get("lease_id");
|
||||
Number leaseDuration = (Number) body.get("lease_duration");
|
||||
boolean renewable = (Boolean) body.get("renewable");
|
||||
|
||||
if (!StringUtils.hasText(leaseId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Lease.of(leaseId,
|
||||
leaseDuration != null ? leaseDuration.longValue() : 0, renewable);
|
||||
}
|
||||
catch (HttpStatusCodeException e) {
|
||||
throw new VaultException(String.format("Cannot renew lease: %s",
|
||||
VaultResponses.getError(e.getResponseBodyAsString())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes the {@link Lease}.
|
||||
*
|
||||
* @param lease the lease.
|
||||
*/
|
||||
private void doRevokeLease(final Lease lease) {
|
||||
|
||||
try {
|
||||
getSource().getVaultOperations().doWithSession(
|
||||
new RestOperationsCallback<ResponseEntity<Map<String, Object>>>() {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ResponseEntity<Map<String, Object>> doWithRestOperations(
|
||||
RestOperations restOperations) {
|
||||
return (ResponseEntity) restOperations.exchange(
|
||||
"/sys/revoke/{leaseId}", HttpMethod.PUT, null,
|
||||
Map.class, lease.getLeaseId());
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (HttpStatusCodeException e) {
|
||||
throw new VaultException(String.format("Cannot revoke lease: %s",
|
||||
VaultResponses.getError(e.getResponseBodyAsString())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstracts scheduled lease renewal. A {@link LeaseRenewalScheduler} can be accessed
|
||||
* concurrently to schedule lease renewal. Each renewal run checks if the previously
|
||||
* attached {@link Lease} is still relevant to update. If any other process scheduled
|
||||
* a newer {@link Lease} for renewal, the previously registered renewal task will skip
|
||||
* renewal.
|
||||
*/
|
||||
@CommonsLog
|
||||
private static class LeaseRenewalScheduler {
|
||||
|
||||
private final TaskScheduler taskScheduler;
|
||||
|
||||
private final AtomicReference<Lease> currentLease = new AtomicReference<>();
|
||||
|
||||
private final Map<Lease, ScheduledFuture<?>> schedules = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param taskScheduler must not be {@literal null}.
|
||||
*/
|
||||
LeaseRenewalScheduler(TaskScheduler taskScheduler) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule {@link Lease} renewal. Previously registered renewal tasks are
|
||||
* canceled to prevent renewal of stale {@link Lease}s.
|
||||
* @param renewLease strategy to renew a {@link Lease}.
|
||||
* @param lease the current {@link Lease}.
|
||||
* @param minRenewalSeconds minimum number of seconds before renewing a
|
||||
* {@link Lease}. This is to prevent too many renewals in a very short timeframe.
|
||||
* @param expiryThresholdSeconds number of seconds to renew before {@link Lease}.
|
||||
* expires.
|
||||
*/
|
||||
void scheduleRenewal(final RenewLease renewLease, final Lease lease,
|
||||
final int minRenewalSeconds, final int expiryThresholdSeconds) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format(
|
||||
"Scheduling renewal for lease %s, lease duration %d",
|
||||
lease.getLeaseId(), lease.getLeaseDuration()));
|
||||
}
|
||||
|
||||
Lease currentLease = this.currentLease.get();
|
||||
this.currentLease.set(lease);
|
||||
|
||||
if (currentLease != null) {
|
||||
cancelSchedule(currentLease);
|
||||
}
|
||||
|
||||
ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
|
||||
schedules.remove(lease);
|
||||
|
||||
if (LeaseRenewalScheduler.this.currentLease.get() != lease) {
|
||||
log.debug("Current lease has changed. Skipping renewal");
|
||||
return;
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Renewing lease %s",
|
||||
lease.getLeaseId()));
|
||||
}
|
||||
|
||||
LeaseRenewalScheduler.this.currentLease.compareAndSet(lease,
|
||||
renewLease.renewLease(lease));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(String.format("Cannot renew lease %s",
|
||||
lease.getLeaseId()), e);
|
||||
}
|
||||
}
|
||||
}, new OneShotTrigger(
|
||||
getRenewalSeconds(lease, minRenewalSeconds, expiryThresholdSeconds)));
|
||||
|
||||
schedules.put(lease, scheduledFuture);
|
||||
}
|
||||
|
||||
private void cancelSchedule(Lease lease) {
|
||||
|
||||
ScheduledFuture<?> scheduledFuture = schedules.get(lease);
|
||||
if (scheduledFuture != null) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format(
|
||||
"Canceling previously registered schedule for lease %s",
|
||||
lease.getLeaseId()));
|
||||
}
|
||||
|
||||
scheduledFuture.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables schedule for already scheduled renewals.
|
||||
*/
|
||||
public void disableScheduleRenewal() {
|
||||
|
||||
currentLease.set(null);
|
||||
Set<Lease> leases = new HashSet<>(schedules.keySet());
|
||||
|
||||
for (Lease lease : leases) {
|
||||
cancelSchedule(lease);
|
||||
schedules.remove(lease);
|
||||
}
|
||||
}
|
||||
|
||||
private long getRenewalSeconds(Lease lease, int minRenewalSeconds,
|
||||
int expiryThresholdSeconds) {
|
||||
return Math.max(minRenewalSeconds,
|
||||
lease.getLeaseDuration() - expiryThresholdSeconds);
|
||||
}
|
||||
|
||||
private boolean isLeaseRenewable(Lease lease) {
|
||||
return lease != null && lease.isRenewable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This one-shot trigger creates only one execution time to trigger an execution only
|
||||
* once.
|
||||
*/
|
||||
private static class OneShotTrigger implements Trigger {
|
||||
|
||||
private final AtomicBoolean fired = new AtomicBoolean();
|
||||
|
||||
private final long seconds;
|
||||
|
||||
OneShotTrigger(long seconds) {
|
||||
this.seconds = seconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date nextExecutionTime(TriggerContext triggerContext) {
|
||||
|
||||
if (fired.compareAndSet(false, true)) {
|
||||
return new Date(
|
||||
System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(seconds));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy interface to renew a {@link Lease}.
|
||||
*/
|
||||
private interface RenewLease {
|
||||
|
||||
/**
|
||||
* Renew a lease.
|
||||
*
|
||||
* @param lease must not be {@literal null}.
|
||||
* @return the new lease
|
||||
* @throws VaultException if lease renewal runs into problems
|
||||
*/
|
||||
Lease renewLease(Lease lease) throws VaultException;
|
||||
}
|
||||
}
|
||||
@@ -15,92 +15,135 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.core.env.LeaseAwareVaultPropertySource;
|
||||
import org.springframework.vault.core.lease.SecretLeaseContainer;
|
||||
import org.springframework.vault.core.lease.domain.RequestedSecret;
|
||||
import org.springframework.vault.core.lease.event.LeaseErrorListener;
|
||||
import org.springframework.vault.core.lease.event.SecretLeaseEvent;
|
||||
import org.springframework.web.util.DefaultUriTemplateHandler;
|
||||
import org.springframework.web.util.UriTemplateHandler;
|
||||
|
||||
/**
|
||||
* Extension to {@link LeasingVaultPropertySourceLocator} that creates
|
||||
* {@link LeasingVaultPropertySource}s.
|
||||
* {@link LeaseAwareVaultPropertySource}s.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see LeasingVaultPropertySource
|
||||
* @see LeaseAwareVaultPropertySource
|
||||
*/
|
||||
@CommonsLog
|
||||
class LeasingVaultPropertySourceLocator extends VaultPropertySourceLocator
|
||||
implements DisposableBean {
|
||||
class LeasingVaultPropertySourceLocator extends VaultPropertySourceLocatorSupport
|
||||
implements PriorityOrdered {
|
||||
|
||||
private final VaultConfigOperations operations;
|
||||
private static final UriTemplateHandler TEMPLATE_HANDLER = new DefaultUriTemplateHandler();
|
||||
|
||||
private final SecretLeaseContainer secretLeaseContainer;
|
||||
|
||||
private final VaultProperties properties;
|
||||
|
||||
private final TaskScheduler taskScheduler;
|
||||
|
||||
private final Set<PropertySource<?>> locatedPropertySources = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link LeasingVaultPropertySourceLocator}.
|
||||
* @param operations must not be {@literal null}.
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param genericBackendProperties must not be {@literal null}.
|
||||
* @param backendAccessors must not be {@literal null}.
|
||||
* @param taskScheduler must not be {@literal null}.
|
||||
* @param secretLeaseContainer must not be {@literal null}.
|
||||
*/
|
||||
public LeasingVaultPropertySourceLocator(VaultConfigOperations operations,
|
||||
VaultProperties properties,
|
||||
public LeasingVaultPropertySourceLocator(VaultProperties properties,
|
||||
VaultGenericBackendProperties genericBackendProperties,
|
||||
Collection<SecretBackendMetadata> backendAccessors,
|
||||
TaskScheduler taskScheduler) {
|
||||
SecretLeaseContainer secretLeaseContainer) {
|
||||
|
||||
super(operations, properties, genericBackendProperties, backendAccessors);
|
||||
super("vault", genericBackendProperties, backendAccessors);
|
||||
|
||||
Assert.notNull(taskScheduler, "TaskScheduler must not be null");
|
||||
Assert.notNull(operations, "VaultConfigTemplate must not be null");
|
||||
Assert.notNull(secretLeaseContainer, "SecretLeaseContainer must not be null");
|
||||
Assert.notNull(properties, "VaultProperties must not be null");
|
||||
|
||||
this.operations = operations;
|
||||
this.secretLeaseContainer = secretLeaseContainer;
|
||||
this.properties = properties;
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VaultPropertySource createVaultPropertySource(
|
||||
public int getOrder() {
|
||||
return properties.getConfig().getOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link VaultPropertySource} initialized with a
|
||||
* {@link SecretBackendMetadata}.
|
||||
*
|
||||
* @param accessor the {@link SecretBackendMetadata}.
|
||||
* @return the {@link VaultPropertySource} to use.
|
||||
*/
|
||||
protected PropertySource<?> createVaultPropertySource(
|
||||
SecretBackendMetadata accessor) {
|
||||
|
||||
LeasingVaultPropertySource propertySource = new LeasingVaultPropertySource(
|
||||
this.operations, this.properties.isFailFast(), accessor, taskScheduler);
|
||||
URI expand = TEMPLATE_HANDLER.expand("{backend}/{key}", accessor.getVariables());
|
||||
|
||||
locatedPropertySources.add(propertySource);
|
||||
final RequestedSecret secret = RequestedSecret.renewable(expand.getPath());
|
||||
|
||||
return propertySource;
|
||||
if (properties.isFailFast()) {
|
||||
return createVaultPropertySourceFailFast(secret, accessor);
|
||||
}
|
||||
|
||||
return createVaultPropertySource(secret, accessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
/**
|
||||
* Decorated {@link PropertySource} creation to catch and throw the first error that
|
||||
* occurred durin initial secret retrieval.
|
||||
*
|
||||
* @param secret
|
||||
* @param accessor
|
||||
* @return
|
||||
*/
|
||||
private PropertySource<?> createVaultPropertySourceFailFast(
|
||||
final RequestedSecret secret, SecretBackendMetadata accessor) {
|
||||
|
||||
Set<PropertySource<?>> propertySources = new HashSet<>(locatedPropertySources);
|
||||
final AtomicReference<Exception> errorRef = new AtomicReference<>();
|
||||
|
||||
for (PropertySource<?> propertySource : propertySources) {
|
||||
LeaseErrorListener errorListener = new LeaseErrorListener() {
|
||||
@Override
|
||||
public void onLeaseError(SecretLeaseEvent leaseEvent, Exception exception) {
|
||||
|
||||
locatedPropertySources.remove(propertySource);
|
||||
|
||||
if (propertySource instanceof LeasingVaultPropertySource) {
|
||||
|
||||
try {
|
||||
((LeasingVaultPropertySource) propertySource).destroy();
|
||||
if (leaseEvent.getSource() == secret) {
|
||||
errorRef.compareAndSet(null, exception);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn(String.format("Cannot destroy property source %s",
|
||||
propertySource.getName()), e);
|
||||
}
|
||||
};
|
||||
|
||||
this.secretLeaseContainer.addErrorListener(errorListener);
|
||||
try {
|
||||
return createVaultPropertySource(secret, accessor);
|
||||
}
|
||||
finally {
|
||||
this.secretLeaseContainer.removeLeaseErrorListener(errorListener);
|
||||
|
||||
Exception exception = errorRef.get();
|
||||
if (exception != null) {
|
||||
if (exception instanceof VaultException) {
|
||||
throw (VaultException) exception;
|
||||
}
|
||||
throw new VaultException(
|
||||
String.format("Cannot initialize PropertySource for secret at %s",
|
||||
secret.getPath()),
|
||||
exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PropertySource<?> createVaultPropertySource(RequestedSecret secret,
|
||||
SecretBackendMetadata accessor) {
|
||||
|
||||
return new LeaseAwareVaultPropertySource(accessor.getName(),
|
||||
this.secretLeaseContainer, secret, accessor.getPropertyTransformer());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,11 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
@@ -56,10 +58,11 @@ import org.springframework.vault.authentication.StaticUserId;
|
||||
import org.springframework.vault.authentication.TokenAuthentication;
|
||||
import org.springframework.vault.client.VaultClients;
|
||||
import org.springframework.vault.client.VaultEndpoint;
|
||||
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
|
||||
import org.springframework.vault.config.AbstractVaultConfiguration.ClientFactoryWrapper;
|
||||
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
import org.springframework.vault.core.lease.SecretLeaseContainer;
|
||||
import org.springframework.vault.support.ClientOptions;
|
||||
import org.springframework.vault.support.SslConfiguration;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
@@ -111,11 +114,11 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
this.vaultSecretBackendDescriptors = applicationContext.getBeansOfType(
|
||||
VaultSecretBackendDescriptor.class).values();
|
||||
this.vaultSecretBackendDescriptors = applicationContext
|
||||
.getBeansOfType(VaultSecretBackendDescriptor.class).values();
|
||||
|
||||
this.factories = (Collection) applicationContext.getBeansOfType(
|
||||
SecretBackendMetadataFactory.class).values();
|
||||
this.factories = (Collection) applicationContext
|
||||
.getBeansOfType(SecretBackendMetadataFactory.class).values();
|
||||
|
||||
ClientHttpRequestFactory clientHttpRequestFactory = clientHttpRequestFactoryWrapper()
|
||||
.getClientHttpRequestFactory();
|
||||
@@ -125,11 +128,10 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VaultPropertySourceLocator vaultPropertySourceLocator(
|
||||
VaultOperations operations,
|
||||
public PropertySourceLocator vaultPropertySourceLocator(VaultOperations operations,
|
||||
VaultProperties vaultProperties,
|
||||
VaultGenericBackendProperties vaultGenericBackendProperties,
|
||||
ObjectFactory<TaskSchedulerWrapper<? extends TaskScheduler>> taskSchedulerFactory) {
|
||||
ObjectFactory<SecretLeaseContainer> secretLeaseContainerObjectFactory) {
|
||||
|
||||
Collection<SecretBackendMetadata> backendAccessors = SecretBackendFactories
|
||||
.createSecretBackendMetadata(vaultSecretBackendDescriptors, factories);
|
||||
@@ -142,9 +144,13 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
// otherwise, the bootstrap context is not shut down cleanly
|
||||
applicationContext.registerShutdownHook();
|
||||
|
||||
return new LeasingVaultPropertySourceLocator(vaultConfigTemplate,
|
||||
vaultProperties, vaultGenericBackendProperties, backendAccessors,
|
||||
taskSchedulerFactory.getObject().getTaskScheduler());
|
||||
SecretLeaseContainer secretLeaseContainer = secretLeaseContainerObjectFactory
|
||||
.getObject();
|
||||
secretLeaseContainer.start();
|
||||
|
||||
return new LeasingVaultPropertySourceLocator(vaultProperties,
|
||||
vaultGenericBackendProperties, backendAccessors,
|
||||
secretLeaseContainer);
|
||||
}
|
||||
|
||||
return new VaultPropertySourceLocator(vaultConfigTemplate, vaultProperties,
|
||||
@@ -179,8 +185,8 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
sslConfiguration = SslConfiguration.NONE;
|
||||
}
|
||||
|
||||
return new ClientFactoryWrapper(ClientHttpRequestFactoryFactory.create(
|
||||
clientOptions, sslConfiguration));
|
||||
return new ClientFactoryWrapper(
|
||||
ClientHttpRequestFactoryFactory.create(clientOptions, sslConfiguration));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,8 +198,9 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public VaultTemplate vaultTemplate(SessionManager sessionManager) {
|
||||
return new VaultTemplate(vaultEndpoint, clientHttpRequestFactoryWrapper()
|
||||
.getClientHttpRequestFactory(), sessionManager);
|
||||
return new VaultTemplate(vaultEndpoint,
|
||||
clientHttpRequestFactoryWrapper().getClientHttpRequestFactory(),
|
||||
sessionManager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,15 +211,20 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
* @see ThreadPoolTaskScheduler
|
||||
*/
|
||||
@Bean
|
||||
@Lazy
|
||||
@ConditionalOnMissingBean(TaskSchedulerWrapper.class)
|
||||
public TaskSchedulerWrapper<ThreadPoolTaskScheduler> vaultTaskScheduler() {
|
||||
public TaskSchedulerWrapper vaultTaskScheduler() {
|
||||
|
||||
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
threadPoolTaskScheduler.setPoolSize(2);
|
||||
threadPoolTaskScheduler.setDaemon(true);
|
||||
threadPoolTaskScheduler.setThreadNamePrefix("Spring-Cloud-Vault-");
|
||||
|
||||
return new TaskSchedulerWrapper<>(threadPoolTaskScheduler);
|
||||
// This is to destroy bootstrap resources
|
||||
// otherwise, the bootstrap context is not shut down cleanly
|
||||
applicationContext.registerShutdownHook();
|
||||
|
||||
return new TaskSchedulerWrapper(threadPoolTaskScheduler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,9 +234,8 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SessionManager sessionManager(
|
||||
ClientAuthentication clientAuthentication,
|
||||
ObjectFactory<TaskSchedulerWrapper<? extends AsyncTaskExecutor>> asyncTaskExecutorFactory) {
|
||||
public SessionManager sessionManager(ClientAuthentication clientAuthentication,
|
||||
ObjectFactory<TaskSchedulerWrapper> asyncTaskExecutorFactory) {
|
||||
|
||||
if (vaultProperties.getConfig().getLifecycle().isEnabled()) {
|
||||
return new LifecycleAwareSessionManager(clientAuthentication,
|
||||
@@ -235,6 +246,20 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
return new SimpleSessionManager(clientAuthentication);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SessionManager} for Vault session management.
|
||||
* @see SessionManager
|
||||
* @see LifecycleAwareSessionManager
|
||||
*/
|
||||
@Bean
|
||||
@Lazy
|
||||
@ConditionalOnMissingBean
|
||||
public SecretLeaseContainer secretLeaseContainer(VaultOperations vaultOperations,
|
||||
TaskSchedulerWrapper taskSchedulerWrapper) {
|
||||
return new SecretLeaseContainer(vaultOperations,
|
||||
taskSchedulerWrapper.getTaskScheduler());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClientAuthentication clientAuthentication() {
|
||||
@@ -263,9 +288,9 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"Client authentication %s not supported",
|
||||
vaultProperties.getAuthentication()));
|
||||
throw new UnsupportedOperationException(
|
||||
String.format("Client authentication %s not supported",
|
||||
vaultProperties.getAuthentication()));
|
||||
}
|
||||
|
||||
private ClientAuthentication appIdAuthentication(VaultProperties vaultProperties) {
|
||||
@@ -300,8 +325,8 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
|
||||
if (StringUtils.hasText(appId.getNetworkInterface())) {
|
||||
try {
|
||||
return new MacAddressUserId(Integer.parseInt(appId
|
||||
.getNetworkInterface()));
|
||||
return new MacAddressUserId(
|
||||
Integer.parseInt(appId.getNetworkInterface()));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return new MacAddressUserId(appId.getNetworkInterface());
|
||||
@@ -360,33 +385,27 @@ public class VaultBootstrapConfiguration implements InitializingBean {
|
||||
|
||||
/**
|
||||
* Wrapper to keep {@link TaskScheduler} local to Spring Cloud Vault.
|
||||
* @param <T>
|
||||
*/
|
||||
public static class TaskSchedulerWrapper<T extends AsyncTaskExecutor & TaskScheduler>
|
||||
implements InitializingBean, DisposableBean {
|
||||
public static class TaskSchedulerWrapper implements InitializingBean, DisposableBean {
|
||||
|
||||
private final T taskScheduler;
|
||||
private final ThreadPoolTaskScheduler taskScheduler;
|
||||
|
||||
public TaskSchedulerWrapper(T taskScheduler) {
|
||||
public TaskSchedulerWrapper(ThreadPoolTaskScheduler taskScheduler) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
T getTaskScheduler() {
|
||||
ThreadPoolTaskScheduler getTaskScheduler() {
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
if (taskScheduler instanceof DisposableBean) {
|
||||
((DisposableBean) taskScheduler).destroy();
|
||||
}
|
||||
taskScheduler.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (taskScheduler instanceof InitializingBean) {
|
||||
((InitializingBean) taskScheduler).afterPropertiesSet();
|
||||
}
|
||||
taskScheduler.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,10 +85,6 @@ class VaultPropertySource extends EnumerablePropertySource<VaultConfigOperations
|
||||
}
|
||||
}
|
||||
|
||||
Secrets getSecrets() {
|
||||
return secrets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
return this.properties.get(name);
|
||||
|
||||
@@ -15,22 +15,13 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import static org.springframework.cloud.vault.config.GenericSecretBackendMetadata.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link PropertySourceLocator} using {@link VaultConfigTemplate}.
|
||||
@@ -40,16 +31,15 @@ import org.springframework.util.StringUtils;
|
||||
* @author Jean-Philippe Bélanger
|
||||
* @author Ryan Hoegg
|
||||
*/
|
||||
class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrdered {
|
||||
class VaultPropertySourceLocator extends VaultPropertySourceLocatorSupport
|
||||
implements PriorityOrdered {
|
||||
|
||||
private final VaultConfigOperations operations;
|
||||
private final VaultProperties properties;
|
||||
private final VaultGenericBackendProperties genericBackendProperties;
|
||||
private final Collection<SecretBackendMetadata> backendAccessors;
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultPropertySourceLocator}.
|
||||
*
|
||||
*
|
||||
* @param operations must not be {@literal null}.
|
||||
* @param properties must not be {@literal null}.
|
||||
* @param genericBackendProperties must not be {@literal null}.
|
||||
@@ -60,30 +50,13 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder
|
||||
VaultGenericBackendProperties genericBackendProperties,
|
||||
Collection<SecretBackendMetadata> backendAccessors) {
|
||||
|
||||
super("vault", genericBackendProperties, backendAccessors);
|
||||
|
||||
Assert.notNull(operations, "VaultConfigOperations must not be null");
|
||||
Assert.notNull(properties, "VaultProperties must not be null");
|
||||
Assert.notNull(backendAccessors, "BackendAccessors must not be null");
|
||||
Assert.notNull(genericBackendProperties,
|
||||
"VaultGenericBackendProperties must not be null");
|
||||
|
||||
this.operations = operations;
|
||||
this.properties = properties;
|
||||
this.backendAccessors = backendAccessors;
|
||||
this.genericBackendProperties = genericBackendProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertySource<?> locate(Environment environment) {
|
||||
|
||||
if (environment instanceof ConfigurableEnvironment) {
|
||||
|
||||
CompositePropertySource propertySource = createCompositePropertySource(
|
||||
(ConfigurableEnvironment) environment);
|
||||
initialize(propertySource);
|
||||
|
||||
return propertySource;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,88 +64,6 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder
|
||||
return properties.getConfig().getOrder();
|
||||
}
|
||||
|
||||
private List<String> buildContexts(ConfigurableEnvironment env) {
|
||||
|
||||
String appName = genericBackendProperties.getApplicationName();
|
||||
List<String> profiles = Arrays.asList(env.getActiveProfiles());
|
||||
List<String> contexts = new ArrayList<>();
|
||||
|
||||
String defaultContext = genericBackendProperties.getDefaultContext();
|
||||
addContext(contexts, defaultContext, profiles);
|
||||
|
||||
for (String context : StringUtils.commaDelimitedListToSet(appName)) {
|
||||
addContext(contexts, context, profiles);
|
||||
}
|
||||
|
||||
Collections.reverse(contexts);
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private void addContext(List<String> contexts, String context, List<String> profiles) {
|
||||
if (StringUtils.hasText(context)) {
|
||||
if (!contexts.contains(context)) {
|
||||
contexts.add(context);
|
||||
}
|
||||
|
||||
addProfiles(contexts, context, profiles);
|
||||
}
|
||||
}
|
||||
|
||||
private CompositePropertySource createCompositePropertySource(
|
||||
ConfigurableEnvironment environment) {
|
||||
|
||||
List<PropertySource<?>> propertySources = new ArrayList<>();
|
||||
|
||||
if (genericBackendProperties.isEnabled()) {
|
||||
|
||||
List<String> contexts = buildContexts(environment);
|
||||
for (String propertySourceContext : contexts) {
|
||||
|
||||
if (StringUtils.hasText(propertySourceContext)) {
|
||||
|
||||
VaultPropertySource vaultPropertySource = createVaultPropertySource(
|
||||
create(genericBackendProperties.getBackend(),
|
||||
propertySourceContext));
|
||||
|
||||
propertySources.add(vaultPropertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (SecretBackendMetadata backendAccessor : backendAccessors) {
|
||||
|
||||
VaultPropertySource vaultPropertySource = createVaultPropertySource(
|
||||
backendAccessor);
|
||||
propertySources.add(vaultPropertySource);
|
||||
}
|
||||
|
||||
return doCreateCompositePropertySource(propertySources);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Implementation hooks and helper methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a {@link CompositePropertySource} given a {@link List} of
|
||||
* {@link PropertySource}s.
|
||||
*
|
||||
* @param propertySources the property sources.
|
||||
* @return the {@link CompositePropertySource} to use.
|
||||
*/
|
||||
protected CompositePropertySource doCreateCompositePropertySource(
|
||||
List<PropertySource<?>> propertySources) {
|
||||
|
||||
CompositePropertySource compositePropertySource = new CompositePropertySource(
|
||||
"vault");
|
||||
|
||||
for (PropertySource<?> propertySource : propertySources) {
|
||||
compositePropertySource.addPropertySource(propertySource);
|
||||
}
|
||||
|
||||
return compositePropertySource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize nested {@link PropertySource}s inside the
|
||||
* {@link CompositePropertySource}.
|
||||
@@ -192,22 +83,9 @@ class VaultPropertySourceLocator implements PropertySourceLocator, PriorityOrder
|
||||
* @param accessor the {@link SecretBackendMetadata}.
|
||||
* @return the {@link VaultPropertySource} to use.
|
||||
*/
|
||||
protected VaultPropertySource createVaultPropertySource(
|
||||
protected PropertySource<?> createVaultPropertySource(
|
||||
SecretBackendMetadata accessor) {
|
||||
return new VaultPropertySource(this.operations, this.properties.isFailFast(),
|
||||
accessor);
|
||||
}
|
||||
|
||||
private void addProfiles(List<String> contexts, String baseContext,
|
||||
List<String> profiles) {
|
||||
|
||||
for (String profile : profiles) {
|
||||
String context = baseContext
|
||||
+ this.genericBackendProperties.getProfileSeparator() + profile;
|
||||
|
||||
if (!contexts.contains(context)) {
|
||||
contexts.add(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
*
|
||||
* http://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.cloud.vault.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.cloud.vault.config.GenericSecretBackendMetadata.create;
|
||||
|
||||
/**
|
||||
* Abstract {@link PropertySourceLocator} to create {@link PropertySource}s based on
|
||||
* {@link VaultGenericBackendProperties} and {@link SecretBackendMetadata}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public abstract class VaultPropertySourceLocatorSupport implements PropertySourceLocator {
|
||||
|
||||
private final String propertySourceName;
|
||||
|
||||
private final VaultGenericBackendProperties genericBackendProperties;
|
||||
|
||||
private final Collection<SecretBackendMetadata> backendAccessors;
|
||||
|
||||
/**
|
||||
* Creates a new {@link VaultPropertySourceLocatorSupport}.
|
||||
*
|
||||
* @param propertySourceName must not be {@literal null} or empty.
|
||||
* @param genericBackendProperties must not be {@literal null}.
|
||||
* @param backendAccessors must not be {@literal null}.
|
||||
*/
|
||||
public VaultPropertySourceLocatorSupport(String propertySourceName,
|
||||
VaultGenericBackendProperties genericBackendProperties,
|
||||
Collection<SecretBackendMetadata> backendAccessors) {
|
||||
|
||||
Assert.hasText(propertySourceName, "PropertySource name must not be empty");
|
||||
Assert.notNull(backendAccessors, "BackendAccessors must not be null");
|
||||
Assert.notNull(genericBackendProperties,
|
||||
"VaultGenericBackendProperties must not be null");
|
||||
|
||||
this.propertySourceName = propertySourceName;
|
||||
this.backendAccessors = backendAccessors;
|
||||
this.genericBackendProperties = genericBackendProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertySource<?> locate(Environment environment) {
|
||||
|
||||
CompositePropertySource propertySource = createCompositePropertySource(
|
||||
environment);
|
||||
initialize(propertySource);
|
||||
|
||||
return propertySource;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Implementation hooks and helper methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Allows initialization the {@link PropertySource} before use. Implementations may
|
||||
* override this method to preload properties in the {@link PropertySource}.
|
||||
*
|
||||
* @param propertySource must not be {@literal null}.
|
||||
*/
|
||||
protected void initialize(CompositePropertySource propertySource) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link CompositePropertySource}.
|
||||
*
|
||||
* @param environment must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected CompositePropertySource createCompositePropertySource(
|
||||
Environment environment) {
|
||||
|
||||
List<PropertySource<?>> propertySources = doCreatePropertySources(environment);
|
||||
|
||||
return doCreateCompositePropertySource(propertySourceName, propertySources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link PropertySource}s given {@link Environment} from the property
|
||||
* configuration.
|
||||
*
|
||||
* @param environment must not be {@literal null}.
|
||||
* @return a {@link List} of ordered {@link PropertySource}s.
|
||||
*/
|
||||
protected List<PropertySource<?>> doCreatePropertySources(Environment environment) {
|
||||
|
||||
List<PropertySource<?>> propertySources = new ArrayList<>();
|
||||
|
||||
if (genericBackendProperties.isEnabled()) {
|
||||
propertySources.addAll(doCreateGenericPropertySources(environment));
|
||||
}
|
||||
|
||||
for (SecretBackendMetadata backendAccessor : backendAccessors) {
|
||||
|
||||
PropertySource<?> vaultPropertySource = createVaultPropertySource(
|
||||
backendAccessor);
|
||||
propertySources.add(vaultPropertySource);
|
||||
}
|
||||
|
||||
return propertySources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link PropertySource}s using the generic {@literal secret} backend.
|
||||
* Property sources for the generic secret backend derive from the application name
|
||||
* and active profiles to generate context paths.
|
||||
*
|
||||
* @param environment must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected List<PropertySource<?>> doCreateGenericPropertySources(
|
||||
Environment environment) {
|
||||
|
||||
List<PropertySource<?>> propertySources = new ArrayList<>();
|
||||
List<String> contexts = GenericSecretBackendMetadata
|
||||
.buildContexts(genericBackendProperties, environment);
|
||||
|
||||
for (String propertySourceContext : contexts) {
|
||||
|
||||
if (StringUtils.hasText(propertySourceContext)) {
|
||||
|
||||
PropertySource<?> vaultPropertySource = createVaultPropertySource(create(
|
||||
genericBackendProperties.getBackend(), propertySourceContext));
|
||||
|
||||
propertySources.add(vaultPropertySource);
|
||||
}
|
||||
}
|
||||
|
||||
return propertySources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link CompositePropertySource} given a {@link List} of
|
||||
* {@link PropertySource}s.
|
||||
*
|
||||
* @param propertySourceName the property source name.
|
||||
* @param propertySources the property sources.
|
||||
* @return the {@link CompositePropertySource} to use.
|
||||
*/
|
||||
protected CompositePropertySource doCreateCompositePropertySource(
|
||||
String propertySourceName, List<PropertySource<?>> propertySources) {
|
||||
|
||||
CompositePropertySource compositePropertySource = new CompositePropertySource(
|
||||
propertySourceName);
|
||||
|
||||
for (PropertySource<?> propertySource : propertySources) {
|
||||
compositePropertySource.addPropertySource(propertySource);
|
||||
}
|
||||
|
||||
return compositePropertySource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link VaultPropertySource} initialized with a
|
||||
* {@link SecretBackendMetadata}.
|
||||
*
|
||||
* @param accessor the {@link SecretBackendMetadata}.
|
||||
* @return the {@link VaultPropertySource} to use.
|
||||
*/
|
||||
protected abstract PropertySource<?> createVaultPropertySource(
|
||||
SecretBackendMetadata accessor);
|
||||
|
||||
}
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Tests for fail fast option.
|
||||
*
|
||||
@@ -32,10 +33,25 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
public class ApplicationFailFastTests {
|
||||
|
||||
@Test
|
||||
public void contextLoadsWithFailFast() {
|
||||
public void contextLoadsWithFailFastUsingLeasing() {
|
||||
try {
|
||||
new SpringApplicationBuilder().sources(ApplicationFailFastTests.class).run(
|
||||
"--server.port=0", "--spring.cloud.vault.failFast=true",
|
||||
"--spring.cloud.vault.config.lifecycle.enabled=true",
|
||||
"--spring.cloud.vault.port=9999");
|
||||
fail("failFast option did not produce an exception");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e.getMessage()).isNotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextLoadsWithFailFastWithoutLeasing() {
|
||||
try {
|
||||
new SpringApplicationBuilder().sources(ApplicationFailFastTests.class).run(
|
||||
"--server.port=0", "--spring.cloud.vault.failFast=true",
|
||||
"--spring.cloud.vault.config.lifecycle.enabled=false",
|
||||
"--spring.cloud.vault.port=9999");
|
||||
fail("failFast option did not produce an exception");
|
||||
}
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -30,8 +26,10 @@ import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.vault.core.lease.SecretLeaseContainer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LeasingVaultPropertySourceLocator}.
|
||||
@@ -43,24 +41,18 @@ public class LeasingVaultPropertySourceLocatorUnitTests {
|
||||
|
||||
private LeasingVaultPropertySourceLocator propertySourceLocator;
|
||||
|
||||
@Mock
|
||||
private VaultConfigTemplate operations;
|
||||
|
||||
@Mock
|
||||
private TaskScheduler taskScheduler;
|
||||
|
||||
@Mock
|
||||
private ConfigurableEnvironment configurableEnvironment;
|
||||
|
||||
@Mock
|
||||
private LeasingVaultPropertySource leasingVaultPropertySource;
|
||||
private SecretLeaseContainer secretLeaseContainer;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
propertySourceLocator = new LeasingVaultPropertySourceLocator(operations,
|
||||
propertySourceLocator = new LeasingVaultPropertySourceLocator(
|
||||
new VaultProperties(), new VaultGenericBackendProperties(),
|
||||
Collections.<SecretBackendMetadata> emptyList(), taskScheduler);
|
||||
Collections.<SecretBackendMetadata>emptyList(), secretLeaseContainer);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,9 +61,9 @@ public class LeasingVaultPropertySourceLocatorUnitTests {
|
||||
VaultProperties vaultProperties = new VaultProperties();
|
||||
vaultProperties.getConfig().setOrder(10);
|
||||
|
||||
propertySourceLocator = new LeasingVaultPropertySourceLocator(operations,
|
||||
vaultProperties, new VaultGenericBackendProperties(),
|
||||
Collections.<SecretBackendMetadata> emptyList(), taskScheduler);
|
||||
propertySourceLocator = new LeasingVaultPropertySourceLocator(vaultProperties,
|
||||
new VaultGenericBackendProperties(),
|
||||
Collections.<SecretBackendMetadata>emptyList(), secretLeaseContainer);
|
||||
|
||||
assertThat(propertySourceLocator.getOrder()).isEqualTo(10);
|
||||
}
|
||||
@@ -89,17 +81,4 @@ public class LeasingVaultPropertySourceLocatorUnitTests {
|
||||
CompositePropertySource composite = (CompositePropertySource) propertySource;
|
||||
assertThat(composite.getPropertySources()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldDispose() {
|
||||
|
||||
Set set = (Set) ReflectionTestUtils.getField(propertySourceLocator,
|
||||
"locatedPropertySources");
|
||||
set.add(leasingVaultPropertySource);
|
||||
|
||||
propertySourceLocator.destroy();
|
||||
|
||||
verify(leasingVaultPropertySource).destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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
|
||||
*
|
||||
* http://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.cloud.vault.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.vault.core.RestOperationsCallback;
|
||||
import org.springframework.vault.core.VaultOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LeasingVaultPropertySource}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LeasingVaultPropertySourceUnitTests {
|
||||
|
||||
@Mock
|
||||
private VaultConfigTemplate configOperations;
|
||||
|
||||
@Mock
|
||||
private VaultOperations vaultOperations;
|
||||
|
||||
@Mock
|
||||
private SecretBackendMetadata secretBackendMetadata;
|
||||
|
||||
@Mock
|
||||
private TaskScheduler taskScheduler;
|
||||
|
||||
@Mock
|
||||
private ScheduledFuture scheduledFuture;
|
||||
|
||||
private LeasingVaultPropertySource propertySource;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
|
||||
when(secretBackendMetadata.getName()).thenReturn("test");
|
||||
when(configOperations.getVaultOperations()).thenReturn(vaultOperations);
|
||||
|
||||
propertySource = new LeasingVaultPropertySource(configOperations, false,
|
||||
secretBackendMetadata, taskScheduler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldWorkIfSecretsNotFound() {
|
||||
|
||||
propertySource.init();
|
||||
|
||||
assertThat(propertySource.getPropertyNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptSecretsWithoutLease() {
|
||||
|
||||
Secrets secrets = new Secrets();
|
||||
secrets.setData(Collections.singletonMap("key", "value"));
|
||||
|
||||
when(configOperations.read(secretBackendMetadata)).thenReturn(secrets);
|
||||
|
||||
propertySource.init();
|
||||
|
||||
assertThat(propertySource.getPropertyNames()).contains("key");
|
||||
verifyZeroInteractions(taskScheduler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptSecretsWithStaticLease() {
|
||||
|
||||
Secrets secrets = new Secrets();
|
||||
secrets.setLeaseId("lease");
|
||||
secrets.setRenewable(false);
|
||||
secrets.setData(Collections.singletonMap("key", "value"));
|
||||
|
||||
when(configOperations.read(secretBackendMetadata)).thenReturn(secrets);
|
||||
|
||||
propertySource.init();
|
||||
|
||||
verifyZeroInteractions(taskScheduler);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldAcceptSecretsWithRenewableLease() {
|
||||
|
||||
when(taskScheduler.schedule(any(Runnable.class), any(Trigger.class))).thenReturn(
|
||||
scheduledFuture);
|
||||
when(configOperations.read(secretBackendMetadata)).thenReturn(createSecrets());
|
||||
|
||||
propertySource.init();
|
||||
|
||||
verify(taskScheduler).schedule(any(Runnable.class), any(Trigger.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldRenewLease() {
|
||||
|
||||
prepareRenewal();
|
||||
|
||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
||||
verify(taskScheduler).schedule(captor.capture(), any(Trigger.class));
|
||||
|
||||
captor.getValue().run();
|
||||
verifyZeroInteractions(scheduledFuture);
|
||||
verify(taskScheduler, times(2)).schedule(captor.capture(), any(Trigger.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void scheduleRenewalShouldApplyExpiryThreshold() {
|
||||
|
||||
prepareRenewal();
|
||||
|
||||
ArgumentCaptor<Trigger> captor = ArgumentCaptor.forClass(Trigger.class);
|
||||
verify(taskScheduler).schedule(any(Runnable.class), captor.capture());
|
||||
|
||||
Date nextExecutionTime = captor.getValue().nextExecutionTime(null);
|
||||
assertThat(nextExecutionTime).isBetween(
|
||||
new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(35)),
|
||||
new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(41)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void subsequentScheduleRenewalShouldApplyExpiryThreshold() {
|
||||
|
||||
prepareRenewal();
|
||||
|
||||
ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
|
||||
verify(taskScheduler).schedule(runnableCaptor.capture(), any(Trigger.class));
|
||||
|
||||
runnableCaptor.getValue().run();
|
||||
|
||||
ArgumentCaptor<Trigger> captor = ArgumentCaptor.forClass(Trigger.class);
|
||||
verify(taskScheduler, times(2)).schedule(any(Runnable.class), captor.capture());
|
||||
|
||||
assertThat(captor.getAllValues().get(0).nextExecutionTime(null)).isBetween(
|
||||
new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(35)),
|
||||
new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(41)));
|
||||
|
||||
assertThat(captor.getAllValues().get(1).nextExecutionTime(null)).isBetween(
|
||||
new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(9)),
|
||||
new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(11)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void scheduleRenewalShouldTriggerOnlyOnce() {
|
||||
|
||||
prepareRenewal();
|
||||
|
||||
ArgumentCaptor<Trigger> captor = ArgumentCaptor.forClass(Trigger.class);
|
||||
verify(taskScheduler).schedule(any(Runnable.class), captor.capture());
|
||||
|
||||
Trigger trigger = captor.getValue();
|
||||
|
||||
assertThat(trigger.nextExecutionTime(null)).isNotNull();
|
||||
assertThat(trigger.nextExecutionTime(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void subsequentInitShouldCancelExistingSchedule() {
|
||||
|
||||
prepareRenewal();
|
||||
|
||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
||||
verify(taskScheduler).schedule(captor.capture(), any(Trigger.class));
|
||||
|
||||
propertySource.init();
|
||||
|
||||
verify(scheduledFuture).cancel(false);
|
||||
verify(taskScheduler, times(2)).schedule(captor.capture(), any(Trigger.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void canceledRenewalShouldSkipRenewal() {
|
||||
|
||||
prepareRenewal();
|
||||
|
||||
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
|
||||
verify(taskScheduler).schedule(captor.capture(), any(Trigger.class));
|
||||
|
||||
propertySource.init();
|
||||
verify(scheduledFuture).cancel(false);
|
||||
|
||||
captor.getValue().run();
|
||||
|
||||
verifyZeroInteractions(vaultOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDisableRenewalOnDisposal() {
|
||||
|
||||
prepareRenewal();
|
||||
|
||||
propertySource.destroy();
|
||||
|
||||
verify(vaultOperations).doWithSession(any(RestOperationsCallback.class));
|
||||
verify(scheduledFuture).cancel(false);
|
||||
}
|
||||
|
||||
private void prepareRenewal() {
|
||||
|
||||
when(taskScheduler.schedule(any(Runnable.class), any(Trigger.class))).thenReturn(
|
||||
scheduledFuture);
|
||||
when(configOperations.read(secretBackendMetadata)).thenReturn(createSecrets());
|
||||
propertySource.init();
|
||||
when(vaultOperations.doWithSession(any(RestOperationsCallback.class)))
|
||||
.thenReturn(getResponseEntity("new_lease", true, 70, HttpStatus.OK));
|
||||
}
|
||||
|
||||
private ResponseEntity<Map<String, Object>> getResponseEntity(String leaseId,
|
||||
Boolean renewable, Integer leaseDuration, HttpStatus httpStatus) {
|
||||
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("lease_id", leaseId);
|
||||
body.put("renewable", renewable);
|
||||
body.put("lease_duration", leaseDuration);
|
||||
|
||||
return getEntity(body, httpStatus);
|
||||
}
|
||||
|
||||
private ResponseEntity<Map<String, Object>> getEntity(Map<String, Object> body,
|
||||
HttpStatus status) {
|
||||
|
||||
return new ResponseEntity<>(body, status);
|
||||
}
|
||||
|
||||
private Secrets createSecrets() {
|
||||
|
||||
Secrets secrets = new Secrets();
|
||||
|
||||
secrets.setLeaseId("lease");
|
||||
secrets.setRenewable(true);
|
||||
secrets.setLeaseDuration(100);
|
||||
secrets.setData(Collections.singletonMap("key", "value"));
|
||||
|
||||
return secrets;
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ public class VaultConfigTests {
|
||||
ApplicationContext parent = applicationContext.getParent();
|
||||
|
||||
assertThat(parent.getBeanNamesForType(VaultTemplate.class)).isNotEmpty();
|
||||
assertThat(parent.getBeanNamesForType(VaultPropertySourceLocator.class))
|
||||
assertThat(parent.getBeanNamesForType(LeasingVaultPropertySourceLocator.class))
|
||||
.isNotEmpty();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user