Skip to content

Commit

Permalink
feat: add the --deploy.env flag (#226)
Browse files Browse the repository at this point in the history
This will allow users to pass in environment variables that should be added to a deployment config.

ex: nodeshift --deploy.env NODE_ENV=development

If a deployment.yaml in the .nodeshift directory already exists with environment variables, they will be merged together
If there are duplicate vars, the ones passed in from the cli/api will take precedence.

fixes #223
  • Loading branch information
lholmquist committed May 22, 2018
1 parent 98576e6 commit 74c482c
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 1 deletion.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ Enrichers will add things to the resource fragments, like missing metadata and l

Default Enrichers will also create a default Service and DeploymentConfig when none are provided.

DeploymentConfigs will also be enriched with a PORT environment variable.

```
env: [
{
name: 'PORT',
value: '8080'
}
]
```

The default port value is 8080, but that can be overriden with the `--deploy.port` flag.

You can also override this value by provideding a .nodeshift/deployment.yaml resource file


#### Resource Fragment Parameters

Expand Down Expand Up @@ -168,6 +183,9 @@ option to remove builds, buildConfigs and Imagestreams. Defaults to false - **O
#### deploy.port
Flag to update the default ports on the resource files. Defaults to 8080

#### deploy.env
Flag to pass deployment config environment variables as NAME=Value. Can be used multiple times. ex: `nodeshift --deploy.env NODE_ENV=development --deploy.env YARN_ENABLED=true`

#### build.recreate
Flag to recreate a BuildConfig or Imagestream. Defaults to false. Choices are "buildConfig", "imageStream", false, true. If true, both are re-created

Expand Down
8 changes: 8 additions & 0 deletions bin/nodeshift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ yargs
describe: 'flag to update the default ports on the resource files. Defaults to 8080',
default: 8080
})
.array('deploy.env')
.option('build.recreate', {
describe: 'flag to recreate a buildConfig or Imagestream',
choices: ['buildConfig', 'imageStream', false, true],
Expand Down Expand Up @@ -151,6 +152,13 @@ function createOptions (argv) {
options.build.env = helpers.parseMultiOption(argv.build.env);
}

// Check for the --deploy.env array
// If it is there, we need to parse it
// The values should be in the format KEY=value
if (argv.deploy.env) {
options.deploy.env = helpers.parseMultiOption(argv.deploy.env);
}

// Check for the -d array
// If it is there, we need to parse it.
// The values should be in the format KEY=value
Expand Down
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const cli = require('./bin/cli');
@param {boolean} [options.quiet] - supress INFO and TRACE lines from output logs
@param {object} [options.deploy] -
@param {number} [options.deploy.port] - flag to update the default ports on the resource files. Defaults to 8080
@param {Array} [options.deploy.env] - an array of objects to pass deployment config environment variables. [{name: NAME_PROP, value: VALUE}]
@param {object} [options.build] -
@param {string/boolean} [options.build.recreate] - flag to recreate a buildConfig or Imagestream. values are "buildConfig", "imageStream", true, false. Defaults to false
@param {boolean} [options.build.forcePull] - flag to make your BuildConfig always pull a new image from dockerhub or not. Defaults to false
Expand Down Expand Up @@ -67,6 +68,7 @@ function resource (options = {}) {
@param {boolean} [options.quiet] - supress INFO and TRACE lines from output logs
@param {object} [options.deploy] -
@param {number} [options.deploy.port] - flag to update the default ports on the resource files. Defaults to 8080
@param {Array} [options.deploy.env] - an array of objects to pass deployment config environment variables. [{name: NAME_PROP, value: VALUE}]
@param {object} [options.build] -
@param {string/boolean} [options.build.recreate] - flag to recreate a buildConfig or Imagestream. values are "buildConfig", "imageStream", true, false. Defaults to false
@param {boolean} [options.build.forcePull] - flag to make your BuildConfig always pull a new image from dockerhub or not. Defaults to false
Expand All @@ -90,6 +92,7 @@ function applyResource (options = {}) {
@param {boolean} [options.removeAll] - option to remove builds, buildConfigs and Imagestreams. Defaults to false
@param {object} [options.deploy] -
@param {number} [options.deploy.port] - flag to update the default ports on the resource files. Defaults to 8080
@param {Array} [options.deploy.env] - an array of objects to pass deployment config environment variables. [{name: NAME_PROP, value: VALUE}]
@param {object} [options.build] -
@param {string/boolean} [options.build.recreate] - flag to recreate a buildConfig or Imagestream. values are "buildConfig", "imageStream", true, false. Defaults to false
@param {boolean} [options.build.forcePull] - flag to make your BuildConfig always pull a new image from dockerhub or not. Defaults to false
Expand Down
2 changes: 1 addition & 1 deletion lib/resource-enrichers/default-enrichers.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[
"deployment-config", "service", "route", "labels", "git-info", "health-check"
"deployment-config", "deployment-env", "service", "route", "labels", "git-info", "health-check"
]
44 changes: 44 additions & 0 deletions lib/resource-enrichers/deployment-env-enricher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
*
* Copyright 2016-2017 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

'use strict';

const _ = require('lodash');

async function addDeploymentEnvVars (config, resourceList) {
return resourceList.map((resource) => {
if (resource.kind === 'DeploymentConfig') {
// Check for the env array in the spec.template.spec.containers array of the deployment config
// If there is one, we need to see if it already has values and merge if necessarry
const env = resource.spec.template.spec.containers[0].env;
if (!env) {
// If there isn't one, create it and add the --deploy.env stuff
resource.spec.template.spec.containers[0].env = config.deploy && config.deploy.env ? config.deploy.env : [];
return resource;
}
resource.spec.template.spec.containers[0].env = _.unionBy(config.deploy && config.deploy.env ? config.deploy.env : [], resource.spec.template.spec.containers[0].env, 'name');
return resource;
}
return resource;
});
}

module.exports = {
enrich: addDeploymentEnvVars,
name: 'deployment-env'
};
220 changes: 220 additions & 0 deletions test/enricher-tests/deployment-env-enricher-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
'use strict';

const test = require('tape');
const deploymentenEnvEnricher = require('../../lib/resource-enrichers/deployment-env-enricher');

test('deployment env enricher - no deployment config', (t) => {
const resourceList = [
{
kind: 'Service',
metadata: {
name: 'service meta'
}
}
];

const config = {
projectName: 'Project Name',
version: '1.0.0',
context: {
namespace: 'namespace'
}
};

t.ok(deploymentenEnvEnricher.enrich, 'has an enrich property');
t.equal(typeof deploymentenEnvEnricher.enrich, 'function', 'is a function');

t.ok(deploymentenEnvEnricher.name, 'has an name property');
t.equal(deploymentenEnvEnricher.name, 'deployment-env');

const p = deploymentenEnvEnricher.enrich(config, resourceList);
t.ok(p instanceof Promise, 'enricher should return a promise');

p.then((dce) => {
t.equal(Array.isArray(dce), true, 'should return an array');
t.equal(dce.length, 1, 'array should have 1 thing');
t.equal(dce[0].kind, 'Service', 'should have just the Service type');
t.end();
});
});

test('deployment env enricher - deployment config has no env', t => {
const resourceList = [
{
kind: 'DeploymentConfig',
spec: {
template: {
spec: {
containers: [
{
name: 'project name'
}
]
}
}
}
}
];

const config = {
projectName: 'Project Name',
version: '1.0.0',
context: {
namespace: 'namespace'
}
};

deploymentenEnvEnricher.enrich(config, resourceList).then(dce => {
t.equal(Array.isArray(dce), true, 'should return an array');
t.equal(dce.length, 1, 'array should have 1 thing');
t.equal(dce[0].kind, 'DeploymentConfig', 'should have just the Service type');
t.ok(dce[0].spec.template.spec.containers[0].env, 'env prop should be added');
t.equal(Array.isArray(dce[0].spec.template.spec.containers[0].env), true, 'env prop is an array');
t.end();
});
});

test('deployment env enricher - deployment config has a env, but none passed in', t => {
const resourceList = [
{
kind: 'DeploymentConfig',
spec: {
template: {
spec: {
containers: [
{
name: 'project name',
env: [
{
name: 'NOT_PORT',
value: 'NOT_8080'
},
{
name: 'ENV2',
value: 'VALUE2'
}
]
}
]
}
}
}
}
];

const config = {
projectName: 'Project Name',
version: '1.0.0',
context: {
namespace: 'namespace'
}
};
deploymentenEnvEnricher.enrich(config, resourceList).then(dce => {
t.equal(Array.isArray(dce), true, 'should return an array');
t.equal(dce.length, 1, 'array should have 1 thing');
t.equal(dce[0].kind, 'DeploymentConfig', 'should have just the Service type');
t.ok(dce[0].spec.template.spec.containers[0].env, 'env prop should be added');
t.equal(Array.isArray(dce[0].spec.template.spec.containers[0].env), true, 'env prop is an array');
t.equal(dce[0].spec.template.spec.containers[0].env.length, 2, 'should have 2 entries now');

t.end();
});
});

test('deployment env enricher - deployment config has a env, but none passed in', t => {
const resourceList = [
{
kind: 'DeploymentConfig',
spec: {
template: {
spec: {
containers: [
{
name: 'project name'
}
]
}
}
}
}
];

const config = {
projectName: 'Project Name',
version: '1.0.0',
context: {
namespace: 'namespace'
},
deploy: {
env: [
{name: 'PORT', value: '4000'}
]
}
};
deploymentenEnvEnricher.enrich(config, resourceList).then(dce => {
t.equal(Array.isArray(dce), true, 'should return an array');
t.equal(dce.length, 1, 'array should have 1 thing');
t.equal(dce[0].kind, 'DeploymentConfig', 'should have just the Service type');
t.ok(dce[0].spec.template.spec.containers[0].env, 'env prop should be added');
t.end();
});
});

test('deployment env enricher - deployment config has a env, passed in envs also, some overlapping', t => {
const resourceList = [
{
kind: 'DeploymentConfig',
spec: {
template: {
spec: {
containers: [
{
name: 'project name',
env: [
{
name: 'NOT_PORT',
value: 'NOT_8080'
},
{
name: 'ENV2',
value: 'VALUE2'
},
{
name: 'PORT',
value: '3000'
}
]
}
]
}
}
}
}
];

const config = {
projectName: 'Project Name',
version: '1.0.0',
context: {
namespace: 'namespace'
},
deploy: {
env: [
{name: 'PORT', value: '4000'}
]
}
};
deploymentenEnvEnricher.enrich(config, resourceList).then(dce => {
t.equal(Array.isArray(dce), true, 'should return an array');
t.equal(dce.length, 1, 'array should have 1 thing');
t.equal(dce[0].kind, 'DeploymentConfig', 'should have just the Service type');
t.ok(dce[0].spec.template.spec.containers[0].env, 'env prop should be added');
t.equal(Array.isArray(dce[0].spec.template.spec.containers[0].env), true, 'env prop is an array');
t.equal(dce[0].spec.template.spec.containers[0].env.length, 3, 'should have 3 entries now');
const envs = dce[0].spec.template.spec.containers[0].env.filter(e => e.name === 'PORT');
t.equal(envs.length, 1, 'only has 1 env with name PORT');
t.equal(envs[0].value, '4000', 'the value should be 4000');

t.end();
});
});

0 comments on commit 74c482c

Please sign in to comment.