diff --git a/README.md b/README.md index 5a1cf02..34107d5 100644 --- a/README.md +++ b/README.md @@ -385,31 +385,32 @@ steps: Here are all the inputs available through `with`: -| Input | Description | Default | Required | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------- | -| `url` | The URL for the vault endpoint | | ✔ | -| `secrets` | A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details | | | -| `namespace` | The Vault namespace from which to query secrets. Vault Enterprise only, unset by default | | | -| `method` | The method to use to authenticate with Vault. | `token` | | -| `role` | Vault role for specified auth method | | | -| `path` | Custom vault path, if the auth method was enabled at a different path | | | -| `token` | The Vault Token to be used to authenticate with Vault | | | -| `roleId` | The Role Id for App Role authentication | | | -| `secretId` | The Secret Id for App Role authentication | | | -| `githubToken` | The Github Token to be used to authenticate with Vault | | | -| `jwtPrivateKey` | Base64 encoded Private key to sign JWT | | | -| `jwtKeyPassword` | Password for key stored in jwtPrivateKey (if needed) | | | -| `jwtGithubAudience` | Identifies the recipient ("aud" claim) that the JWT is intended for |`sigstore`| | -| `jwtTtl` | Time in seconds, after which token expires | | 3600 | -| `kubernetesTokenPath` | The path to the service-account secret with the jwt token for kubernetes based authentication |`/var/run/secrets/kubernetes.io/serviceaccount/token` | | -| `authPayload` | The JSON payload to be sent to Vault when using a custom authentication method. | | | -| `extraHeaders` | A string of newline separated extra headers to include on every request. | | | -| `exportEnv` | Whether or not export secrets as environment variables. | `true` | | -| `exportToken` | Whether or not export Vault token as environment variables (i.e VAULT_TOKEN). | `false` | | -| `caCertificate` | Base64 encoded CA certificate the server certificate was signed with. | | | -| `clientCertificate` | Base64 encoded client certificate the action uses to authenticate with Vault when mTLS is enabled. | | | -| `clientKey` | Base64 encoded client key the action uses to authenticate with Vault when mTLS is enabled. | | | -| `tlsSkipVerify` | When set to true, disables verification of server certificates when testing the action. | `false` | | +| Input | Description | Default | Required | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -------- | +| `url` | The URL for the vault endpoint | | ✔ | +| `secrets` | A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details | | | +| `namespace` | The Vault namespace from which to query secrets. Vault Enterprise only, unset by default | | | +| `method` | The method to use to authenticate with Vault. | `token` | | +| `role` | Vault role for specified auth method | | | +| `path` | Custom vault path, if the auth method was enabled at a different path | | | +| `token` | The Vault Token to be used to authenticate with Vault | | | +| `roleId` | The Role Id for App Role authentication | | | +| `secretId` | The Secret Id for App Role authentication | | | +| `githubToken` | The Github Token to be used to authenticate with Vault | | | +| `jwtPrivateKey` | Base64 encoded Private key to sign JWT | | | +| `jwtKeyPassword` | Password for key stored in jwtPrivateKey (if needed) | | | +| `jwtGithubAudience` | Identifies the recipient ("aud" claim) that the JWT is intended for | `sigstore` | | +| `jwtTtl` | Time in seconds, after which token expires | | 3600 | +| `kubernetesTokenPath` | The path to the service-account secret with the jwt token for kubernetes based authentication | `/var/run/secrets/kubernetes.io/serviceaccount/token` | | +| `authPayload` | The JSON payload to be sent to Vault when using a custom authentication method. | | | +| `extraHeaders` | A string of newline separated extra headers to include on every request. | | | +| `exportEnv` | Whether or not export secrets as environment variables. | `true` | | +| `exportToken` | Whether or not export Vault token as environment variables (i.e VAULT_TOKEN). | `false` | | +| `caCertificate` | Base64 encoded CA certificate the server certificate was signed with. | | | +| `clientCertificate` | Base64 encoded client certificate the action uses to authenticate with Vault when mTLS is enabled. | | | +| `clientKey` | Base64 encoded client key the action uses to authenticate with Vault when mTLS is enabled. | | | +| `tlsSkipVerify` | When set to true, disables verification of server certificates when testing the action. | `false` | | +| `retries` | Amount of times we'll retry the request to get the secrets from Vault | `1` | | ## Masking - Hiding Secrets from Logs diff --git a/package-lock.json b/package-lock.json index aaa8467..de53563 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { + "async-retry": "^1.3.3", "got": "^11.5.1", "jsonata": "^1.8.6", "jsrsasign": "^10.5.24" @@ -3723,6 +3724,22 @@ "node": ">=0.10.0" } }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/async-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -22197,6 +22214,21 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "requires": { + "retry": "0.13.1" + }, + "dependencies": { + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + } + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", diff --git a/package.json b/package.json index 1c68f35..a04ecf9 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ }, "homepage": "https://github.com/hashicorp/vault-action#readme", "dependencies": { + "async-retry": "^1.3.3", "got": "^11.5.1", "jsonata": "^1.8.6", "jsrsasign": "^10.5.24" diff --git a/src/action.js b/src/action.js index 435f86e..b758fdf 100644 --- a/src/action.js +++ b/src/action.js @@ -71,7 +71,9 @@ async function exportSecrets() { return request; }); - const results = await getSecrets(requests, client); + const results = await getSecrets(requests, client, { + retries: core.getInput('retries') || 1 + }); for (const result of results) { const { value, request, cachedResponse } = result; diff --git a/src/secrets.js b/src/secrets.js index 3834f6d..f59c024 100644 --- a/src/secrets.js +++ b/src/secrets.js @@ -1,4 +1,5 @@ const jsonata = require("jsonata"); +const asyncRetry = require('async-retry'); /** @@ -21,7 +22,7 @@ const jsonata = require("jsonata"); * @param {import('got').Got} client * @return {Promise[]>} */ -async function getSecrets(secretRequests, client) { +async function getSecrets(secretRequests, client, { retries }) { const responseCache = new Map(); const results = []; for (const secretRequest of secretRequests) { @@ -35,7 +36,9 @@ async function getSecrets(secretRequests, client) { cachedResponse = true; } else { try { - const result = await client.get(requestPath); + const result = await asyncRetry(() => client.get(requestPath), { + retries + }); body = result.body; responseCache.set(requestPath, body); } catch (error) {