Skip to content

Commit

Permalink
feat: adding support for Terraform Variables (#103)
Browse files Browse the repository at this point in the history
* adding support for Terraform Variables
  • Loading branch information
away168 authored Mar 2, 2022
1 parent 7cf8250 commit b37ae48
Show file tree
Hide file tree
Showing 11 changed files with 11,194 additions and 4,060 deletions.
5 changes: 5 additions & 0 deletions node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ For sensitive environment variables, use:
- Usage: `--sensitiveEnvironmentVariables`
- Alias: `-q`

### Terraform Variables
> The terraform variables to set on the deployed environment - works only on deploy and can be multiple, the format is "terraformVariableName1=value"
- Usage: `--terraformVariables`
- Alias: `-u`

### Revision
> Your GIT revision, can be a branch, a tag or a commit hash. (For existing environments - if not specified, will use the previously defined revision)
- Usage: `--revision`
Expand Down
6,997 changes: 6,981 additions & 16 deletions node/npm-shrinkwrap.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions node/src/commands/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ const assertNoWorkspaceNameChanges = (options, environment) => {
throw new Error('You cannot change a workspace name once an environment has been deployed');
};

const getConfigurationChanges = environmentVariables =>
(environmentVariables || []).map(variable => ({
const getConfigurationChanges = variables =>
(variables || []).map(variable => ({
isSensitive: variable.sensitive,
name: variable.name,
value: variable.value,
type: 0 // supporting only environment variable type ATM
type: variable.type
}));

const deploy = async (options, environmentVariables) => {
const deploy = async (options, variables) => {
const deployUtils = new DeployUtils();
const configurationChanges = getConfigurationChanges(environmentVariables);
const configurationChanges = getConfigurationChanges(variables);

let deployment;
let environment = await deployUtils.getEnvironment(options[ENVIRONMENT_NAME], options[PROJECT_ID]);
Expand Down
4 changes: 2 additions & 2 deletions node/src/commands/run-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const assertRequiresApprovalOption = requiresApproval => {
}
};

const runCommand = async (command, options, environmentVariables) => {
const runCommand = async (command, options, variables) => {
options = configManager.read(options);
assertRequiredOptions(options);
assertRequiresApprovalOption(options[REQUIRES_APPROVAL]);
Expand All @@ -51,7 +51,7 @@ const runCommand = async (command, options, environmentVariables) => {

await DeployUtils.init(options);

await commands[command](options, environmentVariables);
await commands[command](options, variables);
logger.info(`Command ${command} has finished successfully.`);
};

Expand Down
11 changes: 11 additions & 0 deletions node/src/config/arguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
WORKSPACE_NAME,
ENVIRONMENT_NAME,
ENVIRONMENT_VARIABLES,
TERRAFORM_VARIABLES,
SENSITIVE_ENVIRONMENT_VARIABLES,
REVISION,
REQUIRES_APPROVAL,
Expand Down Expand Up @@ -91,6 +92,16 @@ const argumentsMap = {
description:
'The environment variables to set on the deployed environment - works only on deploy and can be multiple, the format is "environmentVariableName1=value"'
},
[TERRAFORM_VARIABLES]: {
name: TERRAFORM_VARIABLES,
alias: 'u',
type: String,
multiple: true,
defaultValue: [],
group: ['deploy'],
description:
'The terraform variables to set on the deployed environment - works only on deploy and can be multiple, the format is "terraformVariableName=value"'
},
[SENSITIVE_ENVIRONMENT_VARIABLES]: {
name: SENSITIVE_ENVIRONMENT_VARIABLES,
alias: 'q',
Expand Down
2 changes: 1 addition & 1 deletion node/src/config/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const commands = {
help: [
{
desc: 'Deploys an environment',
example: `$ env0 deploy -k <${API_KEY}> -s <${API_SECRET}> -o <${ORGANIZATION_ID}> -p <${PROJECT_ID}> -b <${BLUEPRINT_ID}> -e <${ENVIRONMENT_NAME}> -r [revision] -v [stage=dev]`
example: `$ env0 deploy -k <${API_KEY}> -s <${API_SECRET}> -o <${ORGANIZATION_ID}> -p <${PROJECT_ID}> -b <${BLUEPRINT_ID}> -e <${ENVIRONMENT_NAME}> -r [revision] -v [stage=dev] -u [tfvar=tfvalue]`
}
]
},
Expand Down
1 change: 1 addition & 0 deletions node/src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const options = {
ENVIRONMENT_NAME: 'environmentName',
WORKSPACE_NAME: 'workspaceName',
ENVIRONMENT_VARIABLES: 'environmentVariables',
TERRAFORM_VARIABLES: 'terraformVariables',
SENSITIVE_ENVIRONMENT_VARIABLES: 'sensitiveEnvironmentVariables',
REVISION: 'revision',
REQUIRES_APPROVAL: 'requiresApproval',
Expand Down
26 changes: 14 additions & 12 deletions node/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const logger = require('./lib/logger');

const mainDefinitions = [{ name: 'command', defaultOption: true }];

const { ENVIRONMENT_VARIABLES, SENSITIVE_ENVIRONMENT_VARIABLES } = options;
const { ENVIRONMENT_VARIABLES, SENSITIVE_ENVIRONMENT_VARIABLES, TERRAFORM_VARIABLES } = options;

const assertCommandExists = command => {
if (!commands[command]) {
Expand Down Expand Up @@ -75,12 +75,13 @@ const run = async () => {

const currentCommandOptions = getCommandOptions(command, argv);

const environmentVariables = getEnvironmentVariablesOptions(
const variables = getVariablesOptions(
currentCommandOptions[ENVIRONMENT_VARIABLES],
currentCommandOptions[SENSITIVE_ENVIRONMENT_VARIABLES]
currentCommandOptions[SENSITIVE_ENVIRONMENT_VARIABLES],
currentCommandOptions[TERRAFORM_VARIABLES]
);

await runCommand(command, currentCommandOptions, environmentVariables);
await runCommand(command, currentCommandOptions, variables);
} catch (error) {
logErrors(command, error);
process.exit(1);
Expand All @@ -94,26 +95,27 @@ const getCommandOptions = (command, argv) => {
return commandsOptions[command];
};

const parseEnvironmentVariables = (environmentVariables, sensitive) => {
const parseVariables = (variables, sensitive, type) => {
const result = [];

if (environmentVariables && environmentVariables.length > 0) {
logger.info(`Getting ${sensitive ? 'Sensitive ' : ''}Environment Variables from options:`, environmentVariables);
if (variables && variables.length > 0) {
logger.info(`Getting ${sensitive ? 'Sensitive ' : ''}${type ? 'Terraform ' : 'Environment '}variables from options.`);

environmentVariables.forEach(config => {
variables.forEach(config => {
let [name, ...value] = config.split('=');
value = value.join('=');
result.push({ name, value, sensitive });
result.push({ name, value, sensitive, type });
});
}

return result;
};

const getEnvironmentVariablesOptions = (environmentVariables, sensitiveEnvironmentVariables) => {
const getVariablesOptions = (environmentVariables, sensitiveEnvironmentVariables, terraformVariables) => {
return [
...parseEnvironmentVariables(environmentVariables, false),
...parseEnvironmentVariables(sensitiveEnvironmentVariables, true)
...parseVariables(environmentVariables, false, 0),
...parseVariables(sensitiveEnvironmentVariables, true, 0),
...parseVariables(terraformVariables, false, 1)
];
};

Expand Down
26 changes: 17 additions & 9 deletions node/tests/commands/deploy.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,38 +58,46 @@ describe('deploy', () => {
});

describe('proper set of configuration changes', () => {
const environmentVariables = [
const variables = [
{
name: 'foo',
value: 'bar',
sensitive: false
sensitive: false,
type: 0
},
{
name: 'baz',
value: 'waldo',
sensitive: true
sensitive: true,
type: 0
},
{
name: 'cal',
value: 'bears',
sensitive: false,
type: 1
}
];

const expectedConfigurationChanges = environmentVariables.map(variable => ({
const expectedConfigurationChanges = variables.map(variable => ({
name: variable.name,
value: variable.value,
isSensitive: variable.sensitive,
type: 0
type: variable.type
}));

it('on initial deploy', async () => {
mockGetEnvironment.mockResolvedValue(undefined);

await deploy(mockOptionsWithRequired, environmentVariables);
await deploy(mockOptionsWithRequired, variables);

expect(mockCreateAndDeployEnvironment).toBeCalledWith(mockOptionsWithRequired, expectedConfigurationChanges);
});

it('should redeploy with arguments', async () => {
mockGetEnvironment.mockResolvedValue(mockEnvironment);
const redeployOptions = { ...mockOptionsWithRequired, [WORKSPACE_NAME]: undefined };
await deploy(redeployOptions, environmentVariables);
await deploy(redeployOptions, variables);

expect(mockDeployEnvironment).toBeCalledWith(mockEnvironment, redeployOptions, expectedConfigurationChanges);
expect(mockPollDeploymentStatus).toBeCalledWith(mockDeployment);
Expand All @@ -98,7 +106,7 @@ describe('deploy', () => {
it('should not allow to redeploy with a different workspace name set', async () => {
mockGetEnvironment.mockResolvedValue(mockEnvironment);

await expect(deploy({ [WORKSPACE_NAME]: 'workspace0' }, environmentVariables)).rejects.toThrowError(
await expect(deploy({ [WORKSPACE_NAME]: 'workspace0' }, variables)).rejects.toThrowError(
'You cannot change a workspace name once an environment has been deployed'
);
});
Expand All @@ -107,7 +115,7 @@ describe('deploy', () => {
let existingEnvironmentWithWorkspace = { ...mockEnvironment, [WORKSPACE_NAME]: 'workspace0' };
mockGetEnvironment.mockResolvedValue(existingEnvironmentWithWorkspace);

await deploy(mockOptionsWithRequired, environmentVariables)
await deploy(mockOptionsWithRequired, variables)

const expectedOptions = { ...mockOptionsWithRequired }
delete expectedOptions[WORKSPACE_NAME]
Expand Down
19 changes: 11 additions & 8 deletions node/tests/main.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const getMockOptions = command => ({
blue: 'pill',
red: 'pill',
environmentVariables: ['key1=value1', 'key2=value2'],
sensitiveEnvironmentVariables: ['sensitiveKey1=sensitiveValue1', 'sensitiveKey2=sensitiveValue2']
sensitiveEnvironmentVariables: ['sensitiveKey1=sensitiveValue1', 'sensitiveKey2=sensitiveValue2'],
terraformVariables: ['tfkey1=tfvalue1', 'tfkey2=tfvalue2']
},
_unknown: ['test']
});
Expand Down Expand Up @@ -158,7 +159,7 @@ describe('main', () => {
});
});

describe('environment variables parsing', () => {
describe('variables parsing', () => {
describe.each`
command
${'deploy'}
Expand All @@ -170,15 +171,17 @@ describe('main', () => {
await mockOptionsAndRun({ command, args });
});

const expectedEnvVars = [
{ name: 'key1', value: 'value1', sensitive: false },
{ name: 'key2', value: 'value2', sensitive: false },
{ name: 'sensitiveKey1', value: 'sensitiveValue1', sensitive: true },
{ name: 'sensitiveKey2', value: 'sensitiveValue2', sensitive: true }
const expectedVars = [
{ name: 'key1', value: 'value1', sensitive: false, type: 0 },
{ name: 'key2', value: 'value2', sensitive: false, type: 0 },
{ name: 'sensitiveKey1', value: 'sensitiveValue1', sensitive: true, type: 0 },
{ name: 'sensitiveKey2', value: 'sensitiveValue2', sensitive: true, type: 0 },
{ name: 'tfkey1', value: 'tfvalue1', sensitive: false, type: 1 },
{ name: 'tfkey2', value: 'tfvalue2', sensitive: false, type: 1 }
];

it('should run deployment with proper params', () => {
expect(runCommand).toBeCalledWith(command, expect.objectContaining(args[command]), expectedEnvVars);
expect(runCommand).toBeCalledWith(command, expect.objectContaining(args[command]), expectedVars);
});
});
});
Expand Down
Loading

0 comments on commit b37ae48

Please sign in to comment.