diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e3ac7e..9e49f71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,39 @@ jobs: VAULT_PORT: ${{ job.services.vault.ports[8200] }} CI: true + test-ent: + runs-on: ubuntu-latest + + services: + vault: + image: hashicorp/vault-enterprise:1.3.0_ent + ports: + - 8200/tcp + env: + VAULT_DEV_ROOT_TOKEN_ID: testtoken + options: --cap-add=IPC_LOCK + + steps: + - uses: actions/checkout@v1 + - name: Use Node.js 10.x + uses: actions/setup-node@v1 + with: + node-version: 10.x + - name: npm install + run: npm ci + - name: npm build + run: npm run build + - name: npm run test + run: npm run test + env: + CI: true + - name: npm run test:integration-ent + run: npm run test:integration-ent + env: + VAULT_HOST: localhost + VAULT_PORT: ${{ job.services.vault.ports[8200] }} + CI: true + e2e: runs-on: ubuntu-latest diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..041702d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +# Start vault server locally +# You can run integration tests against server by running +# `VAULT_HOST=localhost VAULT_PORT=8200 CI=true npm run test:integration-ent` +version: "3.0" +services: + vault: + image: hashicorp/vault-enterprise:1.3.0_ent + environment: + VAULT_DEV_ROOT_TOKEN_ID: testtoken + ports: + - 8200:8200 + privileged: true \ No newline at end of file diff --git a/integration-ent/integration.test.js b/integration-ent/integration.test.js new file mode 100644 index 0000000..ede9c59 --- /dev/null +++ b/integration-ent/integration.test.js @@ -0,0 +1,131 @@ +jest.mock('@actions/core'); +jest.mock('@actions/core/lib/command'); +const core = require('@actions/core'); + +const got = require('got'); +const { when } = require('jest-when'); + +const { exportSecrets } = require('../action'); + +describe('integration', () => { + + beforeAll(async () => { + // Verify Connection + await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/secret/config`, { + headers: { + 'X-Vault-Token': 'testtoken', + }, + }); + + // Create namespace + await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/sys/namespaces/ns1`, { + method: 'POST', + headers: { + 'X-Vault-Token': 'testtoken', + }, + json: true, + }); + + // Enable secret engine + await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/sys/mounts/secret`, { + method: 'POST', + headers: { + 'X-Vault-Token': 'testtoken', + 'X-Vault-Namespace': 'ns1', + }, + body: {"path":"secret","type":"kv","config":{},"options":{"version":2},"generate_signing_key":true}, + json: true, + }); + + await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/secret/data/test`, { + method: 'POST', + headers: { + 'X-Vault-Token': 'testtoken', + 'X-Vault-Namespace': 'ns1', + }, + body: { + data: { + secret: "SUPERSECRET_IN_NAMESPACE", + }, + }, + json: true, + }); + + await got(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}/v1/secret/data/nested/test`, { + method: 'POST', + headers: { + 'X-Vault-Token': 'testtoken', + 'X-Vault-Namespace': 'ns1', + }, + body: { + data: { + otherSecret: "OTHERSUPERSECRET_IN_NAMESPACE", + }, + }, + json: true, + }); + + + + }) + beforeEach(() => { + jest.resetAllMocks(); + + when(core.getInput) + .calledWith('url') + .mockReturnValue(`http://${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`); + + when(core.getInput) + .calledWith('token') + .mockReturnValue('testtoken'); + + when(core.getInput) + .calledWith('namespace') + .mockReturnValue('ns1'); + }); + + function mockInput(secrets) { + when(core.getInput) + .calledWith('secrets') + .mockReturnValue(secrets); + } + + it('get simple secret', async () => { + mockInput('test secret') + + await exportSecrets(); + + expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_IN_NAMESPACE'); + }); + + it('re-map secret', async () => { + mockInput('test secret | TEST_KEY') + + await exportSecrets(); + + expect(core.exportVariable).toBeCalledWith('TEST_KEY', 'SUPERSECRET_IN_NAMESPACE'); + }); + + it('get nested secret', async () => { + mockInput('nested/test otherSecret') + + await exportSecrets(); + + expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET_IN_NAMESPACE'); + }); + + it('get multiple secrets', async () => { + mockInput(` + test secret ; + test secret | NAMED_SECRET ; + nested/test otherSecret ;`); + + await exportSecrets(); + + expect(core.exportVariable).toBeCalledTimes(3); + + expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_IN_NAMESPACE'); + expect(core.exportVariable).toBeCalledWith('NAMED_SECRET', 'SUPERSECRET_IN_NAMESPACE'); + expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET_IN_NAMESPACE'); + }); +}); \ No newline at end of file diff --git a/integration-ent/jest.config.js b/integration-ent/jest.config.js new file mode 100644 index 0000000..03d15be --- /dev/null +++ b/integration-ent/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + verbose: true +}; diff --git a/package.json b/package.json index bcfc78b..c294fa6 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "build": "ncc build index.js -o dist", "test": "jest", "test:integration": "jest -c integration/jest.config.js", + "test:integration-ent": "jest -c integration-ent/jest.config.js", "test:e2e": "jest -c e2e/jest.config.js" }, "release": {