From 39a0a55f5f8c16b29170856c27b1824b699a5e1f Mon Sep 17 00:00:00 2001 From: JM Faircloth Date: Tue, 25 Jul 2023 09:31:36 -0500 Subject: [PATCH] add ignoreNotFound option --- CHANGELOG.md | 4 ++++ action.yml | 4 ++++ integrationTests/basic/integration.test.js | 24 +++++++++++++++++++++- src/secrets.js | 13 ++++++++++-- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 296a612d..72f49eda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +Features: + +* Add `ignoreNotFound` input (default: false) to prevent the action from failing when a secret does not exist [GH-518](https://github.com/hashicorp/vault-action/pull/518) + ## 2.7.5 (January 30, 2024) Improvements: diff --git a/action.yml b/action.yml index 60bb1a1e..e3d28550 100644 --- a/action.yml +++ b/action.yml @@ -89,6 +89,10 @@ inputs: secretEncodingType: description: 'The encoding type of the secret to decode. If not specified, the secret will not be decoded. Supported values: base64, hex, utf8' required: false + ignoreNotFound: + description: 'Whether or not the action should exit successfully if some requested secrets were not found.' + required: false + default: 'false' runs: using: 'node20' main: 'dist/index.js' diff --git a/integrationTests/basic/integration.test.js b/integrationTests/basic/integration.test.js index 167f9ea9..39a874e9 100644 --- a/integrationTests/basic/integration.test.js +++ b/integrationTests/basic/integration.test.js @@ -124,12 +124,34 @@ describe('integration', () => { .mockReturnValueOnce(secrets); } + function mockIgnoreNotFound(shouldIgnore) { + when(core.getInput) + .calledWith('ignoreNotFound', expect.anything()) + .mockReturnValueOnce(shouldIgnore); + } + + it('prints a nice error message when secret not found', async () => { mockInput(`secret/data/test secret ; secret/data/test secret | NAMED_SECRET ; secret/data/notFound kehe | NO_SIR ;`); - expect(exportSecrets()).rejects.toEqual(Error(`Unable to retrieve result for "secret/data/notFound" because it was not found: {"errors":[]}`)); + await expect(exportSecrets()).rejects.toEqual(Error(`Unable to retrieve result for "secret/data/notFound" because it was not found: {"errors":[]}`)); + }) + + it('does not error when secret not found and ignoreNotFound is true', async () => { + mockInput(`secret/data/test secret ; + secret/data/test secret | NAMED_SECRET ; + secret/data/notFound kehe | NO_SIR ;`); + + mockIgnoreNotFound("true"); + + await exportSecrets(); + + expect(core.exportVariable).toBeCalledTimes(2); + + expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET'); + expect(core.exportVariable).toBeCalledWith('NAMED_SECRET', 'SUPERSECRET'); }) it('get simple secret', async () => { diff --git a/src/secrets.js b/src/secrets.js index fe6a5e4c..10677da9 100644 --- a/src/secrets.js +++ b/src/secrets.js @@ -1,6 +1,8 @@ const jsonata = require("jsonata"); const { WILDCARD } = require("./constants"); const { normalizeOutputKey } = require("./utils"); +const core = require('@actions/core'); + /** * @typedef {Object} SecretRequest * @property {string} path @@ -21,7 +23,7 @@ const { normalizeOutputKey } = require("./utils"); * @param {import('got').Got} client * @return {Promise[]>} */ -async function getSecrets(secretRequests, client) { +async function getSecrets(secretRequests, client, ignoreNotFound) { const responseCache = new Map(); let results = []; @@ -42,7 +44,14 @@ async function getSecrets(secretRequests, client) { } catch (error) { const {response} = error; if (response?.statusCode === 404) { - throw Error(`Unable to retrieve result for "${path}" because it was not found: ${response.body.trim()}`) + notFoundMsg = `Unable to retrieve result for "${path}" because it was not found: ${response.body.trim()}`; + const ignoreNotFound = (core.getInput('ignoreNotFound', { required: false }) || 'false').toLowerCase() != 'false'; + if (ignoreNotFound) { + core.error(`✘ ${notFoundMsg}`); + continue; + } else { + throw Error(notFoundMsg) + } } throw error }