5
0
Fork 0
mirror of https://github.com/hashicorp/vault-action.git synced 2025-11-07 07:06:56 +00:00

Development flow enhancements (#430)

+ added a contribution section to the readme
+ added a local workflow to test changes
+ made the vault token configurable for tests
* bumped action/checkout
This commit is contained in:
Max Coulombe 2023-02-28 15:28:33 -05:00 committed by GitHub
parent 74bc2a617b
commit 3bbbc68bd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 146 additions and 40 deletions

View file

@ -10,7 +10,9 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
@ -37,7 +39,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- name: Run docker-compose - name: Run docker-compose
run: docker-compose up -d vault run: docker-compose up -d vault
@ -71,7 +75,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- name: Run docker-compose - name: Run docker-compose
run: docker-compose up -d vault-enterprise run: docker-compose up -d vault-enterprise
@ -107,7 +113,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- name: Run docker-compose - name: Run docker-compose
run: docker-compose up -d vault run: docker-compose up -d vault
@ -175,7 +183,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- name: Run docker-compose - name: Run docker-compose
run: docker-compose up -d vault-tls run: docker-compose up -d vault-tls

24
.github/workflows/local-test.yaml vendored Normal file
View file

@ -0,0 +1,24 @@
# This is a sample workflow to help test contributions
# Change the branch name, url and token to fit with your own environment
# Use 'on: push' instead of 'on: local-test' if you wish to run the test on github
# If running locally with act, run the workflow with 'act local-test'
# Don't forget to revert the file changes and invalidate any tokens that were committed before opening a pull-request
on: local-test
name: local-test
jobs:
build:
name: local-test
runs-on: ubuntu-latest
steps:
- name: Import Secrets
uses: hashicorp/vault-action@YOUR_BRANCH_NAME
with:
url: http://localhost:8200
method: token
token: testtoken
secrets: |
secret/data/test secret | SAMPLE_SECRET;

View file

@ -31,6 +31,7 @@ A helper action for easily pulling secrets from HashiCorp Vault™.
- [Reference](#reference) - [Reference](#reference)
- [Masking - Hiding Secrets from Logs](#masking---hiding-secrets-from-logs) - [Masking - Hiding Secrets from Logs](#masking---hiding-secrets-from-logs)
- [Normalization](#normalization) - [Normalization](#normalization)
- [Contributing](#contributing)
<!-- /TOC --> <!-- /TOC -->
@ -419,3 +420,70 @@ This action uses GitHub Action's built-in masking, so all variables will automat
## Normalization ## Normalization
To make it simpler to consume certain secrets as env vars, if no Env/Output Var Name is specified `vault-action` will replace and `.` chars with `__`, remove any other non-letter or number characters. If you're concerned about the result, it's recommended to provide an explicit Output Var Key. To make it simpler to consume certain secrets as env vars, if no Env/Output Var Name is specified `vault-action` will replace and `.` chars with `__`, remove any other non-letter or number characters. If you're concerned about the result, it's recommended to provide an explicit Output Var Key.
## Contributing
If you wish to contribute to this project, the following dependencies are recommended for local development:
- [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) to install dependencies, build project and run tests
- [docker](https://docs.docker.com/get-docker/) to run the pre-configured vault containers for acceptance tests
- [docker-compose](https://docs.docker.com/compose/) to spin up the pre-configured vault containers for acceptance tests
- [act](https://github.com/nektos/act) to run the vault-action locally
### Build
Use npm to install dependencies and build the project:
```sh
$ npm install && npm run build
```
### Vault test instance
The Github Action needs access to a working Vault instance to function.
Multiple docker configurations are available via the docker-compose.yml file to run containers compatible with the various acceptance test suites.
```sh
$ docker-compose up -d vault # Choose one of: vault, vault-enterprise, vault-tls depending on which tests you would like to run
```
Instead of using one of the dockerized instance, you can also use your own local or remote Vault instance by exporting these environment variables:
```sh
$ export VAULT_HOST=<YOUR VAULT CLUSTER LOCATION> # localhost if undefined
$ export VAULT_PORT=<YOUR VAULT PORT> # 8200 if undefined
$ export VAULT_TOKEN=<YOUR VAULT TOKEN> # testtoken if undefined
```
### Running unit tests
Unit tests can be executed at any time with no dependencies or prior setup.
```sh
$ npm test
```
### Running acceptance tests
With a succesful build to take your local changes into account and a working Vault instance configured, you can now run acceptance tests to validate if any regressions were introduced.
```sh
$ npm run test:integration:basic # Choose one of: basic, enterprise, e2e, e2e-tls
```
### Running the action locally
You can use the [act](https://github.com/nektos/act) command to test your changes locally if desired. Unfortunately it is not currently possible to use uncommitted local changes for a shared workfow. You will still need to push
the changes you would like to validate beforehand. Even if a commit is necessary, this is still a more detailed and faster feedback loop than waiting for the action to be executed by Github in a different repository.
Push your changes into a feature branch.
```sh
$ git checkout -b my-feature-branch
$ git commit -m "testing new changes"
$ git push
```
Edit the ./.github/workflows/local-test.yaml file to use your new feature branch. You may have to additionally edit the vault url, token and secret path if you are not using one of the provided containerized instance.
Run your feature branch locally.
```sh
$ act local-test
```

View file

@ -8,20 +8,21 @@ const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action'); const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`; const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
describe('integration', () => { describe('integration', () => {
beforeAll(async () => { beforeAll(async () => {
// Verify Connection // Verify Connection
await got(`${vaultUrl}/v1/secret/config`, { await got(`${vaultUrl}/v1/secret/config`, {
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
}); });
await got(`${vaultUrl}/v1/secret/data/test`, { await got(`${vaultUrl}/v1/secret/data/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
data: { data: {
@ -33,7 +34,7 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/secret/data/nested/test`, { await got(`${vaultUrl}/v1/secret/data/nested/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
data: { data: {
@ -45,7 +46,7 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/secret/data/foobar`, { await got(`${vaultUrl}/v1/secret/data/foobar`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
data: { data: {
@ -59,7 +60,7 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/sys/mounts/secret-kv1`, { await got(`${vaultUrl}/v1/sys/mounts/secret-kv1`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
type: 'kv' type: 'kv'
@ -77,7 +78,7 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/secret-kv1/test`, { await got(`${vaultUrl}/v1/secret-kv1/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
secret: 'CUSTOMSECRET', secret: 'CUSTOMSECRET',
@ -87,7 +88,7 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/secret-kv1/foobar`, { await got(`${vaultUrl}/v1/secret-kv1/foobar`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
fookv1: 'bar', fookv1: 'bar',
@ -97,7 +98,7 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/secret-kv1/nested/test`, { await got(`${vaultUrl}/v1/secret-kv1/nested/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
"other-Secret-dash": 'OTHERCUSTOMSECRET', "other-Secret-dash": 'OTHERCUSTOMSECRET',
@ -114,7 +115,7 @@ describe('integration', () => {
when(core.getInput) when(core.getInput)
.calledWith('token', expect.anything()) .calledWith('token', expect.anything())
.mockReturnValueOnce('testtoken'); .mockReturnValueOnce(vaultToken);
}); });
function mockInput(secrets) { function mockInput(secrets) {
@ -207,7 +208,7 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/cubbyhole/test`, { await got(`${vaultUrl}/v1/cubbyhole/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
foo: "bar", foo: "bar",

View file

@ -14,6 +14,7 @@ const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action'); const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`; const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
/** /**
* Returns Github OIDC response mock * Returns Github OIDC response mock
@ -59,7 +60,7 @@ describe('jwt auth', () => {
// Verify Connection // Verify Connection
await got(`${vaultUrl}/v1/secret/config`, { await got(`${vaultUrl}/v1/secret/config`, {
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
}); });
@ -67,7 +68,7 @@ describe('jwt auth', () => {
await got(`${vaultUrl}/v1/sys/auth/jwt`, { await got(`${vaultUrl}/v1/sys/auth/jwt`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
type: 'jwt' type: 'jwt'
@ -85,7 +86,7 @@ describe('jwt auth', () => {
await got(`${vaultUrl}/v1/sys/policy/reader`, { await got(`${vaultUrl}/v1/sys/policy/reader`, {
method: 'PUT', method: 'PUT',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
policy: ` policy: `
@ -99,7 +100,7 @@ describe('jwt auth', () => {
await got(`${vaultUrl}/v1/auth/jwt/config`, { await got(`${vaultUrl}/v1/auth/jwt/config`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
jwt_validation_pubkeys: publicRsaKey, jwt_validation_pubkeys: publicRsaKey,
@ -110,7 +111,7 @@ describe('jwt auth', () => {
await got(`${vaultUrl}/v1/auth/jwt/role/default`, { await got(`${vaultUrl}/v1/auth/jwt/role/default`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
role_type: 'jwt', role_type: 'jwt',
@ -126,7 +127,7 @@ describe('jwt auth', () => {
await got(`${vaultUrl}/v1/secret/data/test`, { await got(`${vaultUrl}/v1/secret/data/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
data: { data: {
@ -172,7 +173,7 @@ describe('jwt auth', () => {
await got(`${vaultUrl}/v1/auth/jwt/role/default-sigstore`, { await got(`${vaultUrl}/v1/auth/jwt/role/default-sigstore`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
role_type: 'jwt', role_type: 'jwt',

View file

@ -1,20 +1,21 @@
const got = require('got'); const got = require('got');
const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`; const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
(async () => { (async () => {
try { try {
// Verify Connection // Verify Connection
await got(`http://${vaultUrl}/v1/secret/config`, { await got(`http://${vaultUrl}/v1/secret/config`, {
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
}); });
await got(`http://${vaultUrl}/v1/secret/data/test`, { await got(`http://${vaultUrl}/v1/secret/data/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
data: { data: {
@ -26,7 +27,7 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/secret/data/nested/test`, { await got(`http://${vaultUrl}/v1/secret/data/nested/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
data: { data: {
@ -38,7 +39,7 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/sys/mounts/my-secret`, { await got(`http://${vaultUrl}/v1/sys/mounts/my-secret`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
type: 'kv' type: 'kv'
@ -48,7 +49,7 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/my-secret/test`, { await got(`http://${vaultUrl}/v1/my-secret/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
altSecret: 'CUSTOMSECRET', altSecret: 'CUSTOMSECRET',
@ -58,7 +59,7 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/my-secret/nested/test`, { await got(`http://${vaultUrl}/v1/my-secret/nested/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
otherAltSecret: 'OTHERCUSTOMSECRET', otherAltSecret: 'OTHERCUSTOMSECRET',
@ -68,7 +69,7 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/cubbyhole/test`, { await got(`http://${vaultUrl}/v1/cubbyhole/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
json: { json: {
foo: 'bar', foo: 'bar',

View file

@ -8,6 +8,7 @@ const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action'); const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8201'}`; const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8201'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
describe('integration', () => { describe('integration', () => {
beforeAll(async () => { beforeAll(async () => {
@ -15,7 +16,7 @@ describe('integration', () => {
// Verify Connection // Verify Connection
await got(`${vaultUrl}/v1/secret/config`, { await got(`${vaultUrl}/v1/secret/config`, {
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
}); });
@ -48,7 +49,7 @@ describe('integration', () => {
when(core.getInput) when(core.getInput)
.calledWith('token', expect.anything()) .calledWith('token', expect.anything())
.mockReturnValueOnce('testtoken'); .mockReturnValueOnce(vaultToken);
when(core.getInput) when(core.getInput)
.calledWith('namespace', expect.anything()) .calledWith('namespace', expect.anything())
@ -119,7 +120,7 @@ describe('authenticate with approle', () => {
// Verify Connection // Verify Connection
await got(`${vaultUrl}/v1/secret/config`, { await got(`${vaultUrl}/v1/secret/config`, {
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
}, },
}); });
@ -137,7 +138,7 @@ describe('authenticate with approle', () => {
await got(`${vaultUrl}/v1/sys/auth/approle`, { await got(`${vaultUrl}/v1/sys/auth/approle`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2', 'X-Vault-Namespace': 'ns2',
}, },
json: { json: {
@ -157,7 +158,7 @@ describe('authenticate with approle', () => {
await got(`${vaultUrl}/v1/sys/policies/acl/test`, { await got(`${vaultUrl}/v1/sys/policies/acl/test`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2', 'X-Vault-Namespace': 'ns2',
}, },
json: { json: {
@ -170,7 +171,7 @@ describe('authenticate with approle', () => {
await got(`${vaultUrl}/v1/auth/approle/role/my-role`, { await got(`${vaultUrl}/v1/auth/approle/role/my-role`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2', 'X-Vault-Namespace': 'ns2',
}, },
json: { json: {
@ -181,7 +182,7 @@ describe('authenticate with approle', () => {
// Get role-id // Get role-id
const roldIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/role-id`, { const roldIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/role-id`, {
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2', 'X-Vault-Namespace': 'ns2',
}, },
responseType: 'json', responseType: 'json',
@ -192,7 +193,7 @@ describe('authenticate with approle', () => {
const secretIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/secret-id`, { const secretIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/secret-id`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2', 'X-Vault-Namespace': 'ns2',
}, },
responseType: 'json', responseType: 'json',
@ -238,7 +239,7 @@ async function enableNamespace(name) {
await got(`${vaultUrl}/v1/sys/namespaces/${name}`, { await got(`${vaultUrl}/v1/sys/namespaces/${name}`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
} }
}); });
} catch (error) { } catch (error) {
@ -256,7 +257,7 @@ async function enableEngine(path, namespace, version) {
await got(`${vaultUrl}/v1/sys/mounts/${path}`, { await got(`${vaultUrl}/v1/sys/mounts/${path}`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
'X-Vault-Namespace': namespace, 'X-Vault-Namespace': namespace,
}, },
json: { type: 'kv', config: {}, options: { version }, generate_signing_key: true }, json: { type: 'kv', config: {}, options: { version }, generate_signing_key: true },
@ -277,7 +278,7 @@ async function writeSecret(engine, path, namespace, version, data) {
await got(`${vaultUrl}/v1/${secretPath}`, { await got(`${vaultUrl}/v1/${secretPath}`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-Vault-Token': 'testtoken', 'X-Vault-Token': vaultToken,
'X-Vault-Namespace': namespace, 'X-Vault-Namespace': namespace,
}, },
json: secretPayload json: secretPayload