Skip to content

Commit

Permalink
add support for ENCRYPTED Variables in pipeline run cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
eti-codefresh authored May 2, 2023
1 parent d630e59 commit 8d2666b
Show file tree
Hide file tree
Showing 14 changed files with 262 additions and 59 deletions.
9 changes: 4 additions & 5 deletions codefresh-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -445,14 +445,13 @@ steps:
update_documentation:
stage: documentation
title: "Update documentation http://cli.codefresh.io"
image: docker:18.01
image: codefresh/build-cli
commands:
- "apk update && apk add git nodejs"
- "npm install"
- "yarn"
- "echo cleaning previous public dir and recreating worktree"
- "rm -rf public && git worktree prune && git worktree add -B gh-pages public origin/gh-pages"
- "rm -rf public && git worktree prune && git worktree add -B gh-pages public origin/gh-pages"
- "echo Building public docs"
- "npm run build-public-docs"
- "yarn run build-public-docs"
- "echo Push new docs to gh-pages detached branch"
- 'git config --global user.email "[email protected]" && git config --global user.name "Automated CI"'
- 'cd public && git add --all && git commit -m "Publish new documentation for version ${{PACKAGE_VERSION}}" && git push https://${{GITHUB_TOKEN}}@github.com/codefresh-io/cli.git'
Expand Down
17 changes: 16 additions & 1 deletion codefresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ steps:
type: codefresh-run
arguments:
PIPELINE_ID: 'codefresh-io/cli/release'
DETACH: true
TRIGGER_ID: codefresh-io/cli_1
BRANCH: master
VARIABLE:
- PACKAGE_VERSION=${{PACKAGE_VERSION}}
Expand All @@ -342,3 +342,18 @@ steps:
- name: create_manifest_list
on:
- success

build_documentation:
stage: test
title: "build documentation http://cli.codefresh.io"
image: codefresh/build-cli
commands:
- "echo Building public docs"
- "npm run build-public-docs"
environment:
- HUGO_VERSION=0.32.0
when:
steps:
- name: install_dependencies
on:
- success
38 changes: 30 additions & 8 deletions docs/content/pipelines/Run Pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,44 @@ The pipeline will be triggered multiple times according to the array length.

#### Variable yaml file with 2 sets of variables
```yaml
- key: value
key2: key1
- key: value
key2: key2
- VARIABLE_A: value_a_for_the_first_build
VARIABLE_B: value_b_for_the_first_build
- VARIABLE_A: value_a_for_the_first_build
VARIABLE_B: value_b_for_the_first_build
```
#### Variable json file with 2 sets of variables
```json
[
{
"key": "value",
"key2": "key1"
"VARIABLE_A": "value_a_for_the_first_build",
"VARIABLE_B": "value_b_for_the_first_build"
},
{
"key": "value",
"key2": "key2"
"VARIABLE_A": "value_a_for_the_first_build",
"VARIABLE_B": "value_b_for_the_first_build"
}
]
```
### Use encrypted variables in Codefresh build runs; supported from CLI version: 0.82.8
#### Variable yaml file with single variable set with encrypted variables
```yaml
- key:
val: value
encrypted: true
key2: key1

```

#### Variable json file single variable set with encrypted variables
```json
[
{
"key": {
"val": "value",
"encrypted": true
},
"key2": "key1"
}
]
```
Expand Down
2 changes: 1 addition & 1 deletion lib/interface/cli/commands/annotation/create.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const command = new Command({
.example('codefresh create annotation image 2dfacdaad466 coverage=75%', 'Annotate entity with a single label')
.example('codefresh create annotation image 2dfacdaad466 coverage=75% tests_passed=true', 'Annotate entity with multiple labels')
// eslint-disable-next-line max-len
.example('codefresh create annotation image 2dfacdaad466 coverage=75% tests_passed=true --display coverage', 'Annotate entity with multiple labels and display selection'),
.example('codefresh create annotation workflow 643d807b85bbe35931ae2282 ENV=prod tests_passed=true --display ENV', 'Annotate entity with multiple labels and display selection'),
handler: async (argv) => {
const { entityType, entityId, labels, display } = argv;

Expand Down
82 changes: 76 additions & 6 deletions lib/interface/cli/commands/pipeline/pipeline.sdk.spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const yaml = require('js-yaml');
const request = require('requestretry');
const fs = require('fs');
const _ = require('lodash');
const DEFAULTS = require('../../defaults');
const getCmd = require('./get.cmd').toCommand();
const deleteCmd = require('./delete.cmd').toCommand();
Expand All @@ -10,18 +14,21 @@ jest.mock('../../helpers/validation'); // eslint-disable-line
jest.mock('../../../../../check-version');
jest.mock('../../completion/helpers', () => { // eslint-disable-line
return {
authContextWrapper: func => func,
authContextWrapper: (func) => func,
};
});

jest.mock('../../helpers/general', () => ({
...jest.requireActual('../../helpers/general'),
isCompatibleApiVersion: () => true,
}));

jest.mock('../../../../logic/entities/Pipeline', () => { // eslint-disable-line
return {
fromResponse: res => res,
fromResponse: (res) => res,
};
});

const request = require('requestretry');

const DEFAULT_RESPONSE = request.__defaultResponse();

describe('pipeline', () => {
Expand Down Expand Up @@ -57,11 +64,11 @@ describe('pipeline', () => {
});

it('should return default limit', async () => {
expect(_getLimit(undefined,false)).toEqual(DEFAULTS.GET_LIMIT_RESULTS);
expect(_getLimit(undefined, false)).toEqual(DEFAULTS.GET_LIMIT_RESULTS);
});

it('should return `unlimited` value', async () => {
expect(_getLimit(undefined,true)).toEqual(DEFAULTS.GET_ALL_PIPELINES_LIMIT);
expect(_getLimit(undefined, true)).toEqual(DEFAULTS.GET_ALL_PIPELINES_LIMIT);
});
});

Expand All @@ -84,6 +91,69 @@ describe('pipeline', () => {
});
});

describe('run', () => {
it('should handle running pipeline with encrypted variables', async () => {
const argv = { name: 'some name',
detach: true,
annotation: [],
variable: [
'secret=secret',
'VAR1=VAL1',
],
encrypted: ['secret'],
};
const pip = new CfPipeline(argv);
await pip.run();
expect(pip.executionRequests[0].options.variables).toEqual([
{
key: 'secret',
value: 'secret',
encrypted: true,
},
{
key: 'VAR1',
value: 'VAL1',
},
]);
await verifyResponsesReturned([DEFAULT_RESPONSE]); // eslint-disable-line
});

it('should handle running pipeline with encrypted variables passing inside json file', async () => {
const rawFile = fs.readFileSync('lib/interface/cli/commands/pipeline/var.json', 'utf8');

const argv = { name: 'some name',
detach: true,
annotation: [],
'var-file': JSON.parse(rawFile),
};
const pip = new CfPipeline(argv);
await pip.run();
expect(pip.executionRequests[0].options.variables).toEqual(
[{ key: 'help6', value: '85858' },
{ key: 'should_be_encrepted', value: '0000' },
{ encrypted: true, key: 'help7', value: 'test' }],
);
await verifyResponsesReturned([DEFAULT_RESPONSE]); // eslint-disable-line
});

it('should handle running pipeline with encrypted variables passing inside yaml file', async () => {
const rawFile = fs.readFileSync('lib/interface/cli/commands/pipeline/var.yml', 'utf8');

const argv = { name: 'some name',
detach: true,
annotation: [],
'var-file': yaml.safeLoad(rawFile),
};
const pip = new CfPipeline(argv);
await pip.run();
expect(pip.executionRequests[0].options.variables).toEqual(
[{ key: 'VAR1', value: 'VAL1' },
{ encrypted: true, key: 'VAR2', value: 'VAL2' }],
);
await verifyResponsesReturned([DEFAULT_RESPONSE]); // eslint-disable-line
});
});

describe('runImpl', () => {
it('should handle running pipeline', async () => {
const argv = { name: 'some name', detach: true };
Expand Down
30 changes: 24 additions & 6 deletions lib/interface/cli/commands/pipeline/run.base.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
const _ = require('lodash');
const Promise = require('bluebird');
const { prepareKeyValueFromCLIEnvOption } = require('../../helpers/general');
const CFError = require('cf-errors');
const { prepareKeyValueFromCLIEnvOption,
markEncryptedFlagOnRequestedVariables,
prepareKeyValueObjectsFromEnvFileOption,
prepareKeyValueObjectsFromCLIEnvOption,
isCompatibleApiVersion,
} = require('../../helpers/general');
const { validatePipelineYaml } = require('../../helpers/validation');
const { printResult } = require('../root/validate.cmd');
const CFError = require('cf-errors');
const { sdk } = require('../../../../logic');
const defaults = require('../../defaults');

class RunBaseCommand {
constructor(argv) {
Expand Down Expand Up @@ -55,22 +61,34 @@ class RunBaseCommand {
packName,
},
};

const encryptedVarsSupported = await isCompatibleApiVersion({
supportedVersion: defaults.MIN_API_VERSION_FOR_ENCRYPTED_VARS_SUPPORT_IN_RUN_CMD,
});
if (variablesFromFile) {
_.forEach(variablesFromFile, (variables) => {
const request = _.cloneDeep(executionRequestTemplate);
request.options.variables = variables;
if (encryptedVarsSupported) {
request.options.variables = prepareKeyValueObjectsFromEnvFileOption(variables);
} else {
request.options.variables = variables;
}
this.executionRequests.push(request);
});
} else {
const variables = prepareKeyValueFromCLIEnvOption(this.argv.variable);
let variables;
if (encryptedVarsSupported) {
const varsArr = prepareKeyValueObjectsFromCLIEnvOption(this.argv.variable);
variables = markEncryptedFlagOnRequestedVariables(varsArr, this.argv.encrypted);
} else {
variables = prepareKeyValueFromCLIEnvOption(this.argv.variable);
}
const request = _.cloneDeep(executionRequestTemplate);
request.options.variables = variables;
request.options.contexts = contexts;
this.executionRequests.push(request);
}

const results = await Promise.all(this.executionRequests.map(request => this.runImpl(request)));
const results = await Promise.all(this.executionRequests.map((request) => this.runImpl(request)));
const findMaxReducer = (accumulator, currentValue) => (currentValue > accumulator ? currentValue : accumulator);
const exitCode = results.reduce(findMaxReducer);
await this.postRunRequest();
Expand Down
7 changes: 7 additions & 0 deletions lib/interface/cli/commands/pipeline/run.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ const run = new Command({
default: [],
alias: 'v',
})
.option('encrypted', {
array: true,
alias: 'e',
describe: 'Variable names to encrypt',
default: [],
})
.option('detach', {
alias: 'd',
describe: 'Run pipeline and print build ID',
Expand Down Expand Up @@ -126,6 +132,7 @@ const run = new Command({
.example('codefresh run PIPELINE_ID | PIPELINE_NAME -b=master', 'Defining the source control context using a branch')
.example('codefresh run PIPELINE_ID | PIPELINE_NAME -s=52b992e783d2f84dd0123c70ac8623b4f0f938d1', 'Defining the source control context using a commit')
.example('codefresh run PIPELINE_ID | PIPELINE_NAME -b=master -v key1=value1 -v key2=value2', 'Setting variables through the command')
.example('codefresh run PIPELINE_ID | PIPELINE_NAME -b=master -v key1=value1 -v key2=value2 -e key1', 'Setting variables through the command with encrypted option')
.example('codefresh run PIPELINE_ID | PIPELINE_NAME -b=master --var-file ./var_file.yml', 'Settings variables through a yml file')
.example('codefresh run PIPELINE_ID | PIPELINE_NAME -b=master --context context', 'Inject contexts to the pipeline execution')
.example('codefresh run PIPELINE_ID | PIPELINE_NAME --skip step1 step2 step3', 'Skip specific steps');
Expand Down
12 changes: 12 additions & 0 deletions lib/interface/cli/commands/pipeline/var.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"build1": {
"help6": "85858",
"should_be_encrepted": "0000",
"help7": {
"value": "test",
"encrypted": true
}
}
}


5 changes: 5 additions & 0 deletions lib/interface/cli/commands/pipeline/var.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build1:
VAR1: 'VAL1'
VAR2:
value: VAL2
encrypted: true
13 changes: 3 additions & 10 deletions lib/interface/cli/commands/project/apply.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const _ = require('lodash');
const { sdk } = require('../../../../logic');

const applyRoot = require('../root/apply.cmd');
const { prepareKeyValueObjectsFromCLIEnvOption, ignoreHttpError } = require('../../helpers/general');
const { prepareKeyValueObjectsFromCLIEnvOption, ignoreHttpError, markEncryptedFlagOnRequestedVariables } = require('../../helpers/general');

const command = new Command({
command: 'project [id|name]',
Expand Down Expand Up @@ -61,14 +61,7 @@ const command = new Command({
encrypted,
} = argv;

const variableMap = _.reduce(variables, (acc, v) => _.assign(acc, { [v.key]: v }), {});
_.forEach(encrypted, (varName) => {
const variable = variableMap[varName];
if (!variable) {
throw new CFError(`Variable is not provided: "${varName}"`);
}
variable.encrypted = true;
});
const requestedProjectVariables = markEncryptedFlagOnRequestedVariables(variables, encrypted);

let project = await sdk.projects.get({ id }).catch(ignoreHttpError);
project = project || await sdk.projects.getByName({ name }).catch(ignoreHttpError);
Expand All @@ -81,7 +74,7 @@ const command = new Command({
const updatePayload = _.pickBy({
projectName,
tags: tags || existingTags,
variables: variables || existingVariables,
variables: requestedProjectVariables || existingVariables,
}, _.identity);

await sdk.projects.patch({ id: project.id }, updatePayload);
Expand Down
15 changes: 3 additions & 12 deletions lib/interface/cli/commands/project/create.cmd.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
const Command = require('../../Command');
const CFError = require('cf-errors');
const _ = require('lodash');
const { sdk } = require('../../../../logic');
const createRoot = require('../root/create.cmd');
const { checkOrProjectExists } = require('../../helpers/validation');
const { prepareKeyValueObjectsFromCLIEnvOption } = require('../../helpers/general');
const { prepareKeyValueObjectsFromCLIEnvOption, markEncryptedFlagOnRequestedVariables } = require('../../helpers/general');

const command = new Command({
command: 'project <name>',
Expand Down Expand Up @@ -52,17 +50,10 @@ const command = new Command({
encrypted,
} = argv;

const variableMap = _.reduce(variables, (acc, v) => _.assign(acc, { [v.key]: v }), {});
_.forEach(encrypted, (varName) => {
const variable = variableMap[varName];
if (!variable) {
throw new CFError(`Variable is not provided: "${varName}"`);
}
variable.encrypted = true;
});
const requestedProjectVariables = markEncryptedFlagOnRequestedVariables(variables, encrypted);

await checkOrProjectExists(projectName);
await sdk.projects.create({ projectName, tags, variables });
await sdk.projects.create({ projectName, tags, variables: requestedProjectVariables });
console.log(`Project: "${projectName}" created`);
},
});
Expand Down
1 change: 1 addition & 0 deletions lib/interface/cli/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const DEFAULTS = {
MAX_CONSECUTIVE_ERRORS_LIMIT: 10,
CODEFRESH_PATH: path.resolve(homedir(), '.Codefresh'),
ENGINE_IMAGE: process.env.ENGINE_IMAGE || 'codefresh/engine:master',
MIN_API_VERSION_FOR_ENCRYPTED_VARS_SUPPORT_IN_RUN_CMD: '21.221.14',
};

module.exports = DEFAULTS;
Loading

0 comments on commit 8d2666b

Please sign in to comment.