From 421b80079732d420e0744b69cfc83f1caec5a0c5 Mon Sep 17 00:00:00 2001
From: Enrico Bianchi An immutable retry setting for API operations: the maximum number of times a failing
+ * operation is retried, and the time to wait between attempts. A policy with maxRetries set to 0 disables retries.null to use the retries currently set in
+ * {@link #config}.
+ */
+ protected final RetryPolicy retryPolicy;
+
protected OperationsBase(VaultConfig config) {
+ this(config, null);
+ }
+
+ /**
+ * @param config The configuration
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration, read on every
+ * call
+ */
+ protected OperationsBase(VaultConfig config, RetryPolicy retryPolicy) {
this.config = config;
+ this.retryPolicy = retryPolicy;
+ }
+
+ /**
+ * @return The maximum number of retries from the retry policy if set, otherwise the one
+ * currently set in the configuration
+ */
+ protected int getMaxRetries() {
+ return retryPolicy != null ? retryPolicy.getMaxRetries() : config.getMaxRetries();
+ }
+
+ /**
+ * @return The interval between retries from the retry policy if set, otherwise the one
+ * currently set in the configuration
+ */
+ protected int getRetryIntervalMilliseconds() {
+ return retryPolicy != null
+ ? retryPolicy.getRetryIntervalMilliseconds()
+ : config.getRetryIntervalMilliseconds();
}
protected Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Auth(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
@@ -1153,7 +1164,7 @@ public LookupResponse lookupSelf(final String tokenAuthMount) throws VaultExcept
*/
@Deprecated
public LogicalResponse lookupWrap() throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().lookupWrap(config.getToken(), false);
}
@@ -1163,7 +1174,7 @@ public LogicalResponse lookupWrap() throws VaultException {
*/
@Deprecated
public LogicalResponse lookupWrap(final char[] wrappedToken) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().lookupWrap(wrappedToken, true);
}
@@ -1174,7 +1185,7 @@ public LogicalResponse lookupWrap(final char[] wrappedToken) throws VaultExcepti
@Deprecated
public LogicalResponse lookupWrap(final char[] wrappedToken, boolean inBody)
throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().lookupWrap(wrappedToken, inBody);
}
@@ -1220,7 +1231,7 @@ public void revokeSelf(final String tokenAuthMount) throws VaultException {
*/
@Deprecated
public UnwrapResponse unwrap() throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().unwrap(config.getToken(), false);
}
@@ -1230,7 +1241,7 @@ public UnwrapResponse unwrap() throws VaultException {
*/
@Deprecated
public UnwrapResponse unwrap(final char[] wrappedToken) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().unwrap(wrappedToken, true);
}
@@ -1240,7 +1251,7 @@ public UnwrapResponse unwrap(final char[] wrappedToken) throws VaultException {
*/
@Deprecated
public UnwrapResponse unwrap(final char[] wrappedToken, boolean inBody) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().unwrap(wrappedToken, inBody);
}
@@ -1250,7 +1261,7 @@ public UnwrapResponse unwrap(final char[] wrappedToken, boolean inBody) throws V
*/
@Deprecated
public WrapResponse wrap(final JsonObject jsonObject, int ttlInSec) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().wrap(jsonObject, ttlInSec);
}
@@ -1260,7 +1271,7 @@ public WrapResponse wrap(final JsonObject jsonObject, int ttlInSec) throws Vault
*/
@Deprecated
public WrapResponse rewrap(final char[] wrappedToken) throws VaultException {
- final var sys = new Sys(this.config);
+ final var sys = new Sys(this.config, this.retryPolicy);
return sys.wrapping().rewrap(wrappedToken);
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/Debug.java b/src/main/java/io/github/jopenlibs/vault/api/Debug.java
index 52832f34..ffe4de32 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/Debug.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/Debug.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.response.HealthResponse;
@@ -21,7 +22,17 @@ public class Debug extends OperationsBase {
private String nameSpace;
public Debug(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Debug(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/Logical.java b/src/main/java/io/github/jopenlibs/vault/api/Logical.java
index a6710e20..9d3fbbcb 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/Logical.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/Logical.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.json.Json;
@@ -37,7 +38,17 @@ public class Logical extends OperationsBase {
public enum logicalOperations {authentication, deleteV1, deleteV2, destroy, listV1, listV2, listSubKeys, readV1, readV2, writeV1, writeV2, unDelete, mount}
public Logical(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Logical(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/database/Database.java b/src/main/java/io/github/jopenlibs/vault/api/database/Database.java
index b58fee17..4f51a72d 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/database/Database.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/database/Database.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.database;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -49,7 +50,23 @@ public Database(final VaultConfig config) {
* /v1/ prefix (e.g. "root-ca")
*/
public Database(final VaultConfig config, final String mountPath) {
- super(config);
+ this(config, mountPath, null);
+ }
+
+ /**
+ * Constructor for use when the Database backend is mounted on some non-default custom path, with
+ * a retry policy.
+ *
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param mountPath The path on which your Vault Database backend is mounted, without the
+ * /v1/ prefix (e.g. "db123")
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Database(final VaultConfig config, final String mountPath,
+ final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.mountPath = mountPath;
this.nameSpace = this.config.getNameSpace();
diff --git a/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java b/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
index 1a3148e8..9fb0463e 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/pki/Pki.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.pki;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -50,7 +51,23 @@ public Pki(final VaultConfig config) {
* /v1/ prefix (e.g. "root-ca")
*/
public Pki(final VaultConfig config, final String mountPath) {
- super(config);
+ this(config, mountPath, null);
+ }
+
+ /**
+ * Constructor for use when the PKI backend is mounted on some non-default custom path, with
+ * a retry policy.
+ *
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param mountPath The path on which your Vault PKI backend is mounted, without the
+ * /v1/ prefix (e.g. "root-ca")
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Pki(final VaultConfig config, final String mountPath,
+ final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.mountPath = mountPath;
this.nameSpace = this.config.getNameSpace();
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/Leases.java b/src/main/java/io/github/jopenlibs/vault/api/sys/Leases.java
index 10938922..8e06d2dd 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/Leases.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/Leases.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -23,7 +24,17 @@ public class Leases extends OperationsBase {
private String nameSpace;
public Leases(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Leases(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/Seal.java b/src/main/java/io/github/jopenlibs/vault/api/sys/Seal.java
index d4bd8e02..976305da 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/Seal.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/Seal.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -23,7 +24,17 @@ public class Seal extends OperationsBase {
private String nameSpace;
public Seal(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Seal(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/Sys.java b/src/main/java/io/github/jopenlibs/vault/api/sys/Sys.java
index 98011247..f5d3dc53 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/Sys.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/Sys.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.Vault;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -19,7 +20,17 @@ public class Sys extends OperationsBase {
private String nameSpace;
public Sys(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Sys(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
@@ -35,7 +46,7 @@ public Sys withNameSpace(final String nameSpace) {
* @return The implementing class for wrapping operations
*/
public Wrapping wrapping() {
- return new Wrapping(this.config);
+ return new Wrapping(this.config, this.retryPolicy);
}
/**
@@ -44,7 +55,7 @@ public Wrapping wrapping() {
* @return The implementing class for Vault's seal operations (e.g. seal, unseal, sealStatus).
*/
public Seal seal() {
- return new Seal(this.config);
+ return new Seal(this.config, this.retryPolicy);
}
/**
@@ -54,7 +65,7 @@ public Seal seal() {
* @return the implementing class for Vault's sys mounts operations
*/
public Mounts mounts() {
- return new Mounts(this.config);
+ return new Mounts(this.config, this.retryPolicy);
}
/**
@@ -64,6 +75,6 @@ public Mounts mounts() {
* @return The implementing class for Vault's lease operations
*/
public Leases leases() {
- return new Leases(this.config);
+ return new Leases(this.config, this.retryPolicy);
}
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/Wrapping.java b/src/main/java/io/github/jopenlibs/vault/api/sys/Wrapping.java
index b8823ff9..bb88489c 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/Wrapping.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/Wrapping.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.Logical;
@@ -27,7 +28,17 @@ public class Wrapping extends OperationsBase {
private String nameSpace;
public Wrapping(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Wrapping(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
this.nameSpace = this.config.getNameSpace();
}
diff --git a/src/main/java/io/github/jopenlibs/vault/api/sys/mounts/Mounts.java b/src/main/java/io/github/jopenlibs/vault/api/sys/mounts/Mounts.java
index 42b655bd..fa2862b6 100644
--- a/src/main/java/io/github/jopenlibs/vault/api/sys/mounts/Mounts.java
+++ b/src/main/java/io/github/jopenlibs/vault/api/sys/mounts/Mounts.java
@@ -1,5 +1,6 @@
package io.github.jopenlibs.vault.api.sys.mounts;
+import io.github.jopenlibs.vault.RetryPolicy;
import io.github.jopenlibs.vault.VaultConfig;
import io.github.jopenlibs.vault.VaultException;
import io.github.jopenlibs.vault.api.OperationsBase;
@@ -17,7 +18,17 @@
public class Mounts extends OperationsBase {
public Mounts(final VaultConfig config) {
- super(config);
+ this(config, null);
+ }
+
+ /**
+ * @param config A container for the configuration settings needed to initialize a
+ * Vault driver instance
+ * @param retryPolicy The retries for this instance, overriding those set in the configuration,
+ * or null to use the retries currently set in the configuration
+ */
+ public Mounts(final VaultConfig config, final RetryPolicy retryPolicy) {
+ super(config, retryPolicy);
}
/**
diff --git a/src/test/java/io/github/jopenlibs/vault/api/RetryPolicyPropagationTests.java b/src/test/java/io/github/jopenlibs/vault/api/RetryPolicyPropagationTests.java
new file mode 100644
index 00000000..e621cc03
--- /dev/null
+++ b/src/test/java/io/github/jopenlibs/vault/api/RetryPolicyPropagationTests.java
@@ -0,0 +1,105 @@
+package io.github.jopenlibs.vault.api;
+
+import io.github.jopenlibs.vault.RetryPolicy;
+import io.github.jopenlibs.vault.VaultConfig;
+import io.github.jopenlibs.vault.VaultException;
+import io.github.jopenlibs.vault.VaultTestUtils;
+import io.github.jopenlibs.vault.api.database.Database;
+import io.github.jopenlibs.vault.api.pki.Pki;
+import io.github.jopenlibs.vault.api.sys.Leases;
+import io.github.jopenlibs.vault.api.sys.Seal;
+import io.github.jopenlibs.vault.api.sys.Sys;
+import io.github.jopenlibs.vault.api.sys.Wrapping;
+import io.github.jopenlibs.vault.api.sys.mounts.Mounts;
+import io.github.jopenlibs.vault.mock.RetriesMockVault;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests that the operation classes keep the {@link RetryPolicy} they are given, and pass it to the
+ * operation classes they create.
+ */
+public class RetryPolicyPropagationTests {
+
+ private static final RetryPolicy POLICY = new RetryPolicy(3, 10);
+
+ @Test
+ public void testConstructorsKeepPolicy() throws VaultException {
+ final var config = config();
+ assertSame(POLICY, new Logical(config, POLICY).retryPolicy);
+ assertSame(POLICY, new Auth(config, POLICY).retryPolicy);
+ assertSame(POLICY, new Debug(config, POLICY).retryPolicy);
+ assertSame(POLICY, new Sys(config, POLICY).retryPolicy);
+ assertSame(POLICY, new Leases(config, POLICY).retryPolicy);
+ assertSame(POLICY, new Seal(config, POLICY).retryPolicy);
+ assertSame(POLICY, new Wrapping(config, POLICY).retryPolicy);
+ assertSame(POLICY, new Mounts(config, POLICY).retryPolicy);
+ assertSame(POLICY, new Pki(config, "pki", POLICY).retryPolicy);
+ assertSame(POLICY, new Database(config, "database", POLICY).retryPolicy);
+ }
+
+ @Test
+ public void testConfigOnlyConstructorsHaveNoPolicy() throws VaultException {
+ final var config = config();
+ assertNull(new Logical(config).retryPolicy);
+ assertNull(new Auth(config).retryPolicy);
+ assertNull(new Debug(config).retryPolicy);
+ assertNull(new Sys(config).retryPolicy);
+ assertNull(new Leases(config).retryPolicy);
+ assertNull(new Seal(config).retryPolicy);
+ assertNull(new Wrapping(config).retryPolicy);
+ assertNull(new Mounts(config).retryPolicy);
+ assertNull(new Pki(config).retryPolicy);
+ assertNull(new Pki(config, "root-ca").retryPolicy);
+ assertNull(new Database(config).retryPolicy);
+ assertNull(new Database(config, "db123").retryPolicy);
+ }
+
+ @Test
+ public void testPkiAndDatabaseKeepMountPathConstructorWithNull() throws VaultException {
+ final var config = config();
+ // Must still compile: a (VaultConfig, RetryPolicy) overload would make these ambiguous
+ assertNull(new Pki(config, null).retryPolicy);
+ assertNull(new Database(config, null).retryPolicy);
+ }
+
+ @Test
+ public void testSysPassesPolicy() throws VaultException {
+ final var sys = new Sys(config(), POLICY);
+ assertSame(POLICY, sys.wrapping().retryPolicy);
+ assertSame(POLICY, sys.seal().retryPolicy);
+ assertSame(POLICY, sys.mounts().retryPolicy);
+ assertSame(POLICY, sys.leases().retryPolicy);
+ }
+
+ @Test
+ public void testSysWithoutPolicyPassesNull() throws VaultException {
+ final var sys = new Sys(config());
+ assertNull(sys.wrapping().retryPolicy);
+ assertNull(sys.seal().retryPolicy);
+ assertNull(sys.mounts().retryPolicy);
+ assertNull(sys.leases().retryPolicy);
+ }
+
+ @Test
+ public void testAuthPassesPolicyToSys() throws Exception {
+ // The config allows no retries, so the call only succeeds if Auth passes its policy on
+ final var server = VaultTestUtils.initHttpMockVault(
+ new RetriesMockVault(2, 200, "{\"data\":{\"token\":\"mock\"}}"));
+ server.start();
+ try {
+ final var response = new Auth(config(), POLICY).lookupWrap();
+ assertEquals(2, response.getRetries());
+ } finally {
+ VaultTestUtils.shutdownMockVault(server);
+ }
+ }
+
+ private static VaultConfig config() throws VaultException {
+ return new VaultConfig().address("http://127.0.0.1:8999").token("mock_token")
+ .engineVersion(1).build();
+ }
+}
From b424b9b9314a651f47b3e305fec33a93e8055d6c Mon Sep 17 00:00:00 2001
From: Enrico Bianchi
A policy with maxRetries set to 0 disables retries.
Returns a new Vault whose API operations are retried with the given rules,
+ * overriding the retries set in the configuration. This instance, and its configuration, are
+ * left unchanged:
+ * {@code
+ * final LogicalResponse response = vault.retrying(5, 1000).logical().read("secret/hello");
+ * }
+ *
+ *
+ * The returned instance shares the configuration of this one, so later changes to it (e.g. + * a new token) apply to both.
+ * + *The default implementation throws UnsupportedOperationException, so that
+ * existing implementations of this interface keep compiling.
Vault using the given retries
+ * @throws IllegalArgumentException If either value is negative
+ * @throws UnsupportedOperationException If this implementation does not support it
+ * @see RetryPolicy
+ */
+ default Vault retrying(final int maxRetries, final int retryIntervalMilliseconds) {
+ throw new UnsupportedOperationException(
+ getClass().getName() + " does not support retrying()");
+ }
+
/**
* Returns the implementing class for Vault's core/logical operations (e.g. read, write).
*
diff --git a/src/main/java/io/github/jopenlibs/vault/VaultImpl.java b/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
index 04b81768..daa15a7d 100644
--- a/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
+++ b/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
@@ -58,6 +58,11 @@
public class VaultImpl implements Vault {
private final VaultConfig vaultConfig;
+ /**
+ * The retries for the operations of this instance, or null to use the retries
+ * currently set in {@link #vaultConfig}.
+ */
+ private final RetryPolicy retryPolicy;
private final Logger logger = Logger.getLogger(VaultImpl.class.getCanonicalName());
/**
@@ -69,6 +74,7 @@ public class VaultImpl implements Vault {
*/
public VaultImpl(final VaultConfig vaultConfig) {
this.vaultConfig = vaultConfig;
+ this.retryPolicy = null;
logNameSpace();
if (this.vaultConfig.getSecretsEnginePathMap().isEmpty()
&& this.vaultConfig.getGlobalEngineVersion() == null) {
@@ -95,6 +101,7 @@ public VaultImpl(final VaultConfig vaultConfig, final Integer engineVersion) {
}
vaultConfig.setEngineVersion(engineVersion);
this.vaultConfig = vaultConfig;
+ this.retryPolicy = null;
logNameSpace();
}
@@ -115,6 +122,7 @@ public VaultImpl(final VaultConfig vaultConfig, final Boolean useSecretsEnginePa
final Integer globalFallbackVersion)
throws VaultException {
this.vaultConfig = vaultConfig;
+ this.retryPolicy = null;
logNameSpace();
this.vaultConfig.setEngineVersion(globalFallbackVersion);
if (Boolean.TRUE.equals(useSecretsEnginePathMap) && this.vaultConfig.getSecretsEnginePathMap().isEmpty()) {
@@ -139,6 +147,15 @@ public VaultImpl(final VaultConfig vaultConfig, final Boolean useSecretsEnginePa
}
}
+ /**
+ * Used by {@link #retrying(int, int)}: shares the configuration of an existing instance, which
+ * has already been set up, so the engine version setup is not run again.
+ */
+ private VaultImpl(final VaultConfig vaultConfig, final RetryPolicy retryPolicy) {
+ this.vaultConfig = vaultConfig;
+ this.retryPolicy = retryPolicy;
+ }
+
private void logNameSpace() {
final String nameSpace = this.vaultConfig.getNameSpace();
if (nameSpace != null && !nameSpace.isEmpty()) {
@@ -165,6 +182,26 @@ public VaultImpl withRetries(final int maxRetries, final int retryIntervalMillis
return this;
}
+ /**
+ * Returns a new VaultImpl whose API operations are retried with the given
+ * rules, overriding the retries set in the configuration. This instance, and its configuration,
+ * are left unchanged.
The returned instance shares the configuration of this one, so later changes to it (e.g. + * a new token) apply to both.
+ * + * @param maxRetries The number of times that API operations will be retried when a failure + * occurs + * @param retryIntervalMilliseconds The number of milliseconds that the driver will wait in + * between retries + * @return A newVaultImpl using the given retries
+ * @throws IllegalArgumentException If either value is negative
+ */
+ @Override
+ public VaultImpl retrying(final int maxRetries, final int retryIntervalMilliseconds) {
+ return new VaultImpl(vaultConfig, new RetryPolicy(maxRetries, retryIntervalMilliseconds));
+ }
+
/**
* Returns the implementing class for Vault's core/logical operations (e.g. read, write).
*
@@ -172,7 +209,7 @@ public VaultImpl withRetries(final int maxRetries, final int retryIntervalMillis
*/
@Override
public Logical logical() {
- return new Logical(vaultConfig);
+ return new Logical(vaultConfig, retryPolicy);
}
/**
@@ -183,7 +220,7 @@ public Logical logical() {
*/
@Override
public Auth auth() {
- return new Auth(vaultConfig);
+ return new Auth(vaultConfig, retryPolicy);
}
/**
@@ -194,7 +231,7 @@ public Auth auth() {
*/
@Override
public Sys sys() {
- return new Sys(vaultConfig);
+ return new Sys(vaultConfig, retryPolicy);
}
/**
@@ -205,7 +242,7 @@ public Sys sys() {
*/
@Override
public Pki pki() {
- return new Pki(vaultConfig);
+ return new Pki(vaultConfig, "pki", retryPolicy);
}
/**
@@ -232,17 +269,17 @@ public Pki pki() {
*/
@Override
public Pki pki(final String mountPath) {
- return new Pki(vaultConfig, mountPath);
+ return new Pki(vaultConfig, mountPath, retryPolicy);
}
@Override
public Database database() {
- return new Database(vaultConfig);
+ return new Database(vaultConfig, "database", retryPolicy);
}
@Override
public Database database(final String mountPath) {
- return new Database(vaultConfig, mountPath);
+ return new Database(vaultConfig, mountPath, retryPolicy);
}
/**
@@ -255,7 +292,7 @@ public Database database(final String mountPath) {
@Override
@Deprecated
public Leases leases() {
- return new Leases(vaultConfig);
+ return new Leases(vaultConfig, retryPolicy);
}
/**
@@ -265,7 +302,7 @@ public Leases leases() {
*/
@Override
public Debug debug() {
- return new Debug(vaultConfig);
+ return new Debug(vaultConfig, retryPolicy);
}
/**
@@ -279,7 +316,7 @@ public Debug debug() {
@Override
@Deprecated
public Mounts mounts() {
- return new Mounts(vaultConfig);
+ return new Mounts(vaultConfig, retryPolicy);
}
/**
@@ -292,7 +329,7 @@ public Mounts mounts() {
@Override
@Deprecated
public Seal seal() {
- return new Seal(vaultConfig);
+ return new Seal(vaultConfig, retryPolicy);
}
/**
diff --git a/src/test/java/io/github/jopenlibs/vault/RetryTests.java b/src/test/java/io/github/jopenlibs/vault/RetryTests.java
index 2b021f9b..e98e5941 100644
--- a/src/test/java/io/github/jopenlibs/vault/RetryTests.java
+++ b/src/test/java/io/github/jopenlibs/vault/RetryTests.java
@@ -1,11 +1,18 @@
package io.github.jopenlibs.vault;
+import io.github.jopenlibs.vault.api.OperationsBase;
import io.github.jopenlibs.vault.mock.RetriesMockVault;
import io.github.jopenlibs.vault.response.LogicalResponse;
+import java.util.List;
import java.util.Map;
import org.junit.Test;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
/**
* Unit tests for the Vault driver, having no dependency on an actual Vault server instance
@@ -117,4 +124,136 @@ public void testNoRetries_Write_400() throws Exception {
VaultTestUtils.shutdownMockVault(server);
}
+ private static final String READ_RESPONSE =
+ "{\"lease_id\":\"12345\",\"renewable\":false,\"lease_duration\":10000,\"data\":{\"value\":\"mock\"}}";
+
+ @Test
+ public void testRetrying_Read() throws Exception {
+ final var server = VaultTestUtils.initHttpMockVault(
+ new RetriesMockVault(5, 200, READ_RESPONSE));
+ server.start();
+ try {
+ final var vault = Vault.create(mockConfig());
+ final LogicalResponse response = vault.retrying(5, 10).logical().read("secret/hello");
+ assertEquals(5, response.getRetries());
+ assertEquals("mock", response.getData().get("value"));
+ } finally {
+ VaultTestUtils.shutdownMockVault(server);
+ }
+ }
+
+ @Test
+ public void testRetrying_LeavesOriginalUnchanged() throws Exception {
+ final var server = VaultTestUtils.initHttpMockVault(
+ new RetriesMockVault(1, 200, READ_RESPONSE));
+ server.start();
+ try {
+ final var config = mockConfig();
+ final var vault = Vault.create(config);
+ assertNotSame(vault, vault.retrying(5, 10));
+ assertEquals(0, config.getMaxRetries());
+ assertEquals(0, config.getRetryIntervalMilliseconds());
+
+ final var e = assertThrows(VaultException.class,
+ () -> vault.logical().read("secret/hello"));
+ assertEquals(500, e.getHttpStatusCode());
+ } finally {
+ VaultTestUtils.shutdownMockVault(server);
+ }
+ }
+
+ @Test
+ public void testRetrying_OverridesConfigRetries() throws Exception {
+ final var server = VaultTestUtils.initHttpMockVault(
+ new RetriesMockVault(3, 200, READ_RESPONSE));
+ server.start();
+ try {
+ final var vault = Vault.create(mockConfig());
+ vault.withRetries(5, 10);
+ // 1 retry is fewer than the 3 failures, while the config retries would be enough
+ final var e = assertThrows(VaultException.class,
+ () -> vault.retrying(1, 10).logical().read("secret/hello"));
+ assertEquals(500, e.getHttpStatusCode());
+ } finally {
+ VaultTestUtils.shutdownMockVault(server);
+ }
+ }
+
+ @Test
+ public void testRetrying_SysLeasesGetsPolicy() throws Exception {
+ final var server = VaultTestUtils.initHttpMockVault(new RetriesMockVault(2, 204, null));
+ server.start();
+ try {
+ final var vault = Vault.create(mockConfig());
+ final var response = vault.retrying(2, 10).sys().leases().revoke("lease-id");
+ assertEquals(2, response.getRetries());
+ } finally {
+ VaultTestUtils.shutdownMockVault(server);
+ }
+ }
+
+ @Test
+ public void testRetrying_SharesConfig() throws Exception {
+ final var config = mockConfig();
+ final var retrying = Vault.create(config).retrying(5, 10);
+ assertSame(config, baseFieldOf(retrying.auth(), "config"));
+ config.token("new_token");
+ assertArrayEquals("new_token".toCharArray(),
+ ((VaultConfig) baseFieldOf(retrying.logical(), "config")).getToken());
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void testRetrying_EveryOperationGetsPolicy() throws Exception {
+ final var vault = Vault.create(mockConfig());
+ final var policy = new RetryPolicy(5, 10);
+ final var retrying = vault.retrying(5, 10);
+ for (final OperationsBase operations : List.of(retrying.logical(), retrying.auth(),
+ retrying.sys(), retrying.pki(), retrying.pki("root-ca"), retrying.database(),
+ retrying.database("db123"), retrying.leases(), retrying.debug(),
+ retrying.mounts(), retrying.seal())) {
+ assertEquals(operations.getClass().getName(), policy, retryPolicyOf(operations));
+ }
+ for (final OperationsBase operations : List.of(vault.logical(), vault.pki(),
+ vault.database())) {
+ assertNull(operations.getClass().getName(), retryPolicyOf(operations));
+ }
+ }
+
+ @Test
+ public void testRetrying_PkiAndDatabaseUseDefaultMountPaths() throws Exception {
+ final var retrying = Vault.create(mockConfig()).retrying(5, 10);
+ assertEquals("pki", fieldOf(retrying.pki(), "mountPath"));
+ assertEquals("database", fieldOf(retrying.database(), "mountPath"));
+ }
+
+ @Test
+ public void testRetrying_RejectsNegativeValues() throws Exception {
+ final var vault = Vault.create(mockConfig());
+ assertThrows(IllegalArgumentException.class, () -> vault.retrying(-1, 10));
+ assertThrows(IllegalArgumentException.class, () -> vault.retrying(5, -1));
+ }
+
+ private static VaultConfig mockConfig() throws VaultException {
+ return new VaultConfig().address("http://127.0.0.1:8999")
+ .token("mock_token").engineVersion(1).build();
+ }
+
+ private static Object retryPolicyOf(final OperationsBase operations) throws Exception {
+ return baseFieldOf(operations, "retryPolicy");
+ }
+
+ private static Object baseFieldOf(final OperationsBase operations, final String name)
+ throws Exception {
+ final var field = OperationsBase.class.getDeclaredField(name);
+ field.setAccessible(true);
+ return field.get(operations);
+ }
+
+ private static Object fieldOf(final Object target, final String name) throws Exception {
+ final var field = target.getClass().getDeclaredField(name);
+ field.setAccessible(true);
+ return field.get(target);
+ }
+
}
From 63b02ad381291665c18a77575800a7aa3e0effa5 Mon Sep 17 00:00:00 2001
From: Enrico Bianchi A policy with Sets the retry rules for every API operation using this configuration: the maximum number
+ * of times that a failing operation will be retried, and the time to wait between attempts.
+ * By default, operations are not retried. To retry only some calls, use Sets the maximum number of times that an API operation will retry upon failure. Sets the retry rules for API operations, by writing them to the configuration of this
+ * instance. Despite being chained ahead of endpoints (e.g. Sets the maximum number of times that an API operation will retry upon failure. This method is not meant to be called from application-level code outside of this package
- * (hence the maxRetries set to 0 disables retries.Vault.retrying() instead, which overrides
+ * these values.logical(), auth(),
- * etc... to specify retry rules for any API operations invoked on that endpoint.
+ * logical(),
+ * auth()), the retries are not limited to that call: they apply to every later
+ * call on this instance, and on every other Vault sharing the same configuration.
+ * A Vault returned by {@link #retrying(int, int)} ignores them.protected access level. It is meant to be invoked via
- * Vault.withRetries() in a builder pattern DSL-style.Vault.withRetries(): use {@link #retries(int, int)} instead.
This method is not meant to be called from application-level code outside of this package
- * (hence the protected access level. It is meant to be invoked via
- * Vault.withRetries() in a builder pattern DSL-style.
Vault.withRetries(): use {@link #retries(int, int)} instead.
*
* @param retryIntervalMilliseconds The number of milliseconds that the driver will wait in
* between retries.
diff --git a/src/main/java/io/github/jopenlibs/vault/VaultImpl.java b/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
index daa15a7d..64bce224 100644
--- a/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
+++ b/src/main/java/io/github/jopenlibs/vault/VaultImpl.java
@@ -43,7 +43,7 @@
* secrets.put("other_value", "You can store multiple name/value pairs under a given key");
*
* final LogicalResponse writeResponse = vault
- * .withRetries(5, 1000) // optional
+ * .retrying(5, 1000) // optional
* .logical()
* .write("secret/hello", secrets);
*
@@ -166,16 +166,25 @@ private void logNameSpace() {
}
/**
- * This method is chained ahead of endpoints (e.g. logical(), auth(),
- * etc... to specify retry rules for any API operations invoked on that endpoint.
+ * Sets the retry rules for API operations, by writing them to the configuration of this + * instance.
+ * + *Despite being chained ahead of endpoints (e.g. logical(),
+ * auth()), the retries are not limited to that call: they apply to every later
+ * call on this instance, and on every other Vault sharing the same configuration.
+ * A Vault returned by {@link #retrying(int, int)} ignores them.