diff --git a/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySource.java b/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySource.java
index 847eb2e7..4edb017e 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySource.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySource.java
@@ -69,6 +69,8 @@ import org.springframework.context.annotation.Import;
* MutablePropertySources} javadocs for details.
*
* @author Mark Paluch
+ * @see org.springframework.vault.core.env.VaultPropertySource
+ * @see org.springframework.vault.core.env.LeaseAwareVaultPropertySource
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -78,7 +80,7 @@ import org.springframework.context.annotation.Import;
public @interface VaultPropertySource {
/**
- * Indicate the Vault path(s) of the properties to be retrieved. For example,
+ * Indicate the Vault path(s) of the secret to be retrieved. For example,
* {@code "secret/myapp"} or {@code "secret/my-application/profile"}.
*
* Each location will be added to the enclosing {@code Environment} as its own
@@ -92,6 +94,15 @@ public @interface VaultPropertySource {
*/
String propertyNamePrefix() default "";
+ /**
+ * Indicate if failure to find the {@link #value() secrets} should be ignored.
+ *
+ * {@literal true} is appropriate if the secrets are completely optional. Default is
+ * {@literal true}.
+ * @since 2.2.
+ */
+ boolean ignoreSecretNotFound() default true;
+
/**
* Configure the name of the {@link org.springframework.vault.core.VaultTemplate} bean
* to be used with the property sources.
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySourceRegistrar.java b/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySourceRegistrar.java
index b5c8e2df..225965bf 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySourceRegistrar.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/annotation/VaultPropertySourceRegistrar.java
@@ -124,6 +124,8 @@ class VaultPropertySourceRegistrar implements ImportBeanDefinitionRegistrar,
String ref = propertySource.getString("vaultTemplateRef");
String propertyNamePrefix = propertySource.getString("propertyNamePrefix");
Renewal renewal = propertySource.getEnum("renewal");
+ boolean ignoreSecretNotFound = propertySource
+ .getBoolean("ignoreSecretNotFound");
Assert.isTrue(paths.length > 0,
"At least one @VaultPropertySource(value) location is required");
@@ -143,7 +145,7 @@ class VaultPropertySourceRegistrar implements ImportBeanDefinitionRegistrar,
}
AbstractBeanDefinition beanDefinition = createBeanDefinition(ref, renewal,
- propertyTransformer,
+ propertyTransformer, ignoreSecretNotFound,
potentiallyResolveRequiredPlaceholders(propertyPath));
do {
@@ -168,7 +170,8 @@ class VaultPropertySourceRegistrar implements ImportBeanDefinitionRegistrar,
}
private AbstractBeanDefinition createBeanDefinition(String ref, Renewal renewal,
- PropertyTransformer propertyTransformer, String propertyPath) {
+ PropertyTransformer propertyTransformer, boolean ignoreResourceNotFound,
+ String propertyPath) {
BeanDefinitionBuilder builder;
@@ -194,6 +197,7 @@ class VaultPropertySourceRegistrar implements ImportBeanDefinitionRegistrar,
}
builder.addConstructorArgValue(propertyTransformer);
+ builder.addConstructorArgValue(ignoreResourceNotFound);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
return builder.getBeanDefinition();
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/env/LeaseAwareVaultPropertySource.java b/spring-vault-core/src/main/java/org/springframework/vault/core/env/LeaseAwareVaultPropertySource.java
index 90f78a03..13467e80 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/env/LeaseAwareVaultPropertySource.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/env/LeaseAwareVaultPropertySource.java
@@ -24,16 +24,17 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
+import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.lease.SecretLeaseContainer;
import org.springframework.vault.core.lease.domain.RequestedSecret;
import org.springframework.vault.core.lease.event.BeforeSecretLeaseRevocationEvent;
-import org.springframework.vault.core.lease.event.LeaseListener;
import org.springframework.vault.core.lease.event.LeaseListenerAdapter;
import org.springframework.vault.core.lease.event.SecretLeaseCreatedEvent;
import org.springframework.vault.core.lease.event.SecretLeaseEvent;
import org.springframework.vault.core.lease.event.SecretLeaseExpiredEvent;
+import org.springframework.vault.core.lease.event.SecretNotFoundEvent;
import org.springframework.vault.core.util.PropertyTransformer;
import org.springframework.vault.core.util.PropertyTransformers;
import org.springframework.vault.support.JsonMapFlattener;
@@ -64,7 +65,14 @@ public class LeaseAwareVaultPropertySource
private final PropertyTransformer propertyTransformer;
- private final LeaseListener leaseListener;
+ private final boolean ignoreSecretNotFound;
+
+ private final LeaseListenerAdapter leaseListener;
+
+ private volatile boolean notFound = false;
+
+ @Nullable
+ private volatile Exception loadError;
/**
* Create a new {@link LeaseAwareVaultPropertySource} given a
@@ -111,6 +119,28 @@ public class LeaseAwareVaultPropertySource
SecretLeaseContainer secretLeaseContainer, RequestedSecret requestedSecret,
PropertyTransformer propertyTransformer) {
+ this(name, secretLeaseContainer, requestedSecret, propertyTransformer, true);
+ }
+
+ /**
+ * Create a new {@link LeaseAwareVaultPropertySource} given a {@code name},
+ * {@link SecretLeaseContainer} and {@link RequestedSecret}. This property source
+ * requests the secret upon initialization and receives secrets once they are emitted
+ * through events published by {@link SecretLeaseContainer}.
+ *
+ * @param name name of the property source, must not be {@literal null}.
+ * @param secretLeaseContainer must not be {@literal null}.
+ * @param requestedSecret must not be {@literal null}.
+ * @param propertyTransformer object to transform properties.
+ * @param ignoreSecretNotFound indicate if failure to find a secret at {@code path}
+ * should be ignored.
+ * @since 2.2
+ * @see PropertyTransformers
+ */
+ public LeaseAwareVaultPropertySource(String name,
+ SecretLeaseContainer secretLeaseContainer, RequestedSecret requestedSecret,
+ PropertyTransformer propertyTransformer, boolean ignoreSecretNotFound) {
+
super(name);
Assert.notNull(secretLeaseContainer,
@@ -122,12 +152,18 @@ public class LeaseAwareVaultPropertySource
this.requestedSecret = requestedSecret;
this.propertyTransformer = propertyTransformer
.andThen(PropertyTransformers.removeNullProperties());
+ this.ignoreSecretNotFound = ignoreSecretNotFound;
this.leaseListener = new LeaseListenerAdapter() {
@Override
public void onLeaseEvent(SecretLeaseEvent leaseEvent) {
handleLeaseEvent(leaseEvent,
LeaseAwareVaultPropertySource.this.properties);
}
+
+ @Override
+ public void onLeaseError(SecretLeaseEvent leaseEvent, Exception exception) {
+ handleLeaseErrorEvent(leaseEvent, exception);
+ }
};
loadProperties();
@@ -144,7 +180,28 @@ public class LeaseAwareVaultPropertySource
}
secretLeaseContainer.addLeaseListener(leaseListener);
+ secretLeaseContainer.addErrorListener(leaseListener);
secretLeaseContainer.addRequestedSecret(requestedSecret);
+
+ Exception loadError = this.loadError;
+ if (notFound || loadError != null) {
+
+ String msg = String.format("Vault location [%s] not resolvable",
+ requestedSecret.getPath());
+
+ if (ignoreSecretNotFound) {
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format("%s: %s", msg,
+ loadError != null ? loadError.getMessage() : "Not found"));
+ }
+ }
+ else {
+ if (loadError != null) {
+ throw new VaultPropertySourceNotFoundException(msg, loadError);
+ }
+ throw new VaultPropertySourceNotFoundException(msg);
+ }
+ }
}
public RequestedSecret getRequestedSecret() {
@@ -180,6 +237,10 @@ public class LeaseAwareVaultPropertySource
return;
}
+ if (leaseEvent instanceof SecretNotFoundEvent) {
+ this.notFound = true;
+ }
+
if (leaseEvent instanceof SecretLeaseExpiredEvent
|| leaseEvent instanceof BeforeSecretLeaseRevocationEvent
|| leaseEvent instanceof SecretLeaseCreatedEvent) {
@@ -193,6 +254,22 @@ public class LeaseAwareVaultPropertySource
}
}
+ /**
+ * Hook method to handle a {@link SecretLeaseEvent} errors.
+ *
+ * @param leaseEvent must not be {@literal null}.
+ * @param exception offending exception.
+ */
+ protected void handleLeaseErrorEvent(SecretLeaseEvent leaseEvent,
+ Exception exception) {
+
+ if (leaseEvent.getSource() != getRequestedSecret()) {
+ return;
+ }
+
+ this.loadError = exception;
+ }
+
/**
* Hook method to transform properties using {@link PropertyTransformer}.
*
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySource.java b/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySource.java
index 8aac6d75..01e2bfd6 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySource.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySource.java
@@ -57,6 +57,8 @@ public class VaultPropertySource extends EnumerablePropertySource properties = doGetProperties(path);
+ Map properties = null;
+ RuntimeException error = null;
- if (properties != null) {
+ try {
+ properties = doGetProperties(path);
+ }
+ catch (RuntimeException e) {
+ error = e;
+ }
+
+ if (properties == null) {
+
+ String msg = String.format("Vault location [%s] not resolvable", path);
+
+ if (ignoreSecretNotFound) {
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format("%s: %s", msg,
+ error != null ? error.getMessage() : "Not found"));
+ }
+ }
+ else {
+ if (error != null) {
+ throw new VaultPropertySourceNotFoundException(msg, error);
+ }
+ throw new VaultPropertySourceNotFoundException(msg);
+ }
+ }
+ else {
this.properties.putAll(doTransformProperties(properties));
}
}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySourceNotFoundException.java b/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySourceNotFoundException.java
new file mode 100644
index 00000000..2b4e83d2
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySourceNotFoundException.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 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.vault.core.env;
+
+import org.springframework.vault.VaultException;
+import org.springframework.vault.annotation.VaultPropertySource;
+
+/**
+ * Exception throws when a {@code VaultPropertySource} could not load its properties.
+ *
+ * @author Mark Paluch
+ * @since 2.2
+ * @see VaultPropertySource#ignoreSecretNotFound()
+ */
+public class VaultPropertySourceNotFoundException extends VaultException {
+
+ /**
+ * Create a {@code VaultPropertySourceNotFoundException} with the specified detail
+ * message.
+ *
+ * @param msg the detail message.
+ */
+ public VaultPropertySourceNotFoundException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Create a {@code VaultPropertySourceNotFoundException} with the specified detail
+ * message and nested exception.
+ *
+ * @param msg the detail message.
+ * @param cause the nested exception.
+ */
+ public VaultPropertySourceNotFoundException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/lease/SecretLeaseContainer.java b/spring-vault-core/src/main/java/org/springframework/vault/core/lease/SecretLeaseContainer.java
index 28346778..6ff85594 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/lease/SecretLeaseContainer.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/lease/SecretLeaseContainer.java
@@ -653,12 +653,20 @@ public class SecretLeaseContainer extends SecretLeaseEventPublisher
RequestedSecret requestedSecret) {
try {
+ VaultResponseSupport