Skip to content

Commit

Permalink
feat(cli): improve deployer selection
Browse files Browse the repository at this point in the history
fixes #18
  • Loading branch information
tripodsan committed Dec 16, 2020
1 parent 82cdfff commit 7ff6d76
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 67 deletions.
111 changes: 66 additions & 45 deletions src/action_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ module.exports = class ActionBuilder {
_gitRef: '',
_updatedAt: null,
_updatedBy: null,
_target: [],
_deployers: {
wsk: new OpenWhiskDeployer(this),
aws: new AWSDeployer(this),
Expand Down Expand Up @@ -231,6 +232,16 @@ module.exports = class ActionBuilder {
return this;
}

withTarget(value) {
this._target = [];
value.forEach((v) => {
v.split(',').forEach((t) => {
this._target.push(t.trim());
});
});
return this;
}

withDeploy(enable) {
this._deploy = enable;
return this;
Expand Down Expand Up @@ -597,7 +608,7 @@ module.exports = class ActionBuilder {
// create dist dir
await fse.ensureDir(this._distDir);

// init openwhisk props
// init deployers
await Promise.all(Object.values(this._deployers)
.filter((deployer) => !deployer.ready())
.filter((deployer) => typeof deployer.init === 'function')
Expand All @@ -621,6 +632,33 @@ module.exports = class ActionBuilder {
this._showHints = false;
this._links = [];
}

// init deployers
const targets = { };
this._target.forEach((t) => {
if (t === 'auto') {
// get all deployers that are ready();
Object.entries(this._deployers).forEach(([name, deployer]) => {
if (deployer.ready()) {
targets[name] = deployer;
}
});
} else {
// deployer must be ready
const deployer = this._deployers[t];
if (!deployer) {
throw Error(`'No such target: ${t}`);
}
deployer.validate();
targets[t] = deployer;
}
});
this._deployers = targets;
if (Object.keys(targets).length === 0) {
if (this._deploy || this._test || this._delete || this._updatePackage) {
throw new Error('No applicable deployers found');
}
}
}

async createArchive() {
Expand Down Expand Up @@ -831,67 +869,46 @@ module.exports = class ActionBuilder {
this.log.info(chalk`{green ok:} bundle can be loaded and has a {gray main()} function.`);
}

async deploy() {
const results = await Promise.all(Object.entries(this._deployers)
.filter(([name]) => this._deploy.length === 0
|| this._deploy.indexOf('all') >= 0
|| this._deploy.indexOf(name) >= 0)
.filter(([, deployer]) => deployer.ready())
.map(async ([, deployer]) => {
try {
return deployer.deploy();
} catch (e) {
return e;
}
}));

const errors = results.filter((result) => result instanceof Error);
if (errors.length) {
throw errors[0];
}
if (!results.length) {
throw Error('No applicable deployers found');
async execute(fnName, msg) {
const deps = Object.values(this._deployers)
.filter((deployer) => typeof deployer[fnName] === 'function');
// eslint-disable-next-line no-restricted-syntax
for (const dep of deps) {
this.log.info(chalk`--: ${msg}{yellow ${dep.name}} ...`);
// eslint-disable-next-line no-await-in-loop
await dep[fnName]();
}
}

async deploy() {
return this.execute('deploy', 'deploying action to ');
}

async updatePackage() {
console.log('updating all packages');
await Promise.all(await Object.values(this._deployers)
.filter((deployer) => deployer.ready())
.filter((deployer) => typeof deployer.updatePackage === 'function')
.map(async (deployer) => deployer.updatePackage()));
return this.execute('updatePackage', 'updating package on ');
}

async showDeployHints() {
await Promise.all(await Object.values(this._deployers)
.filter((deployer) => typeof deployer.showDeployHints === 'function')
.map(async (deployer) => deployer.showDeployHints()));
return this.execute('showDeployHints', 'hints for ');
}

async delete() {
await Promise.all(await Object.values(this._deployers)
.filter((deployer) => deployer.ready())
.filter((deployer) => typeof deployer.delete === 'function')
.map(async (deployer) => deployer.delete()));
return this.execute('delete', 'deleting action on ');
}

async test() {
await Promise.all(Object.values(this._deployers)
.filter((deployer) => deployer.ready())
.filter((deployer) => typeof deployer.test === 'function')
.map(async (deployer) => deployer.test()));
return this.execute('test', 'testing action on ');
}

async updateLinks() {
await Promise.all(await Object.values(this._deployers)
.filter((deployer) => deployer.ready())
.filter((deployer) => typeof deployer.updateLinks === 'function')
.map(async (deployer) => deployer.updateLinks()));
return this.execute('updateLinks', 'updating links on ');
}

async run() {
this.log.info(chalk`{grey openwhisk-action-builder v${version}}`);
await this.validate();
this.log.info(chalk`selected targets: {yellow ${Object.values(this._deployers).map((d) => d.name).join(', ')}}`);

if (this._build) {
await this.createPackage();
await this.createArchive();
Expand Down Expand Up @@ -919,9 +936,13 @@ module.exports = class ActionBuilder {

await this.updateLinks();

return {
name: `openwhisk;host=${this._deployers.wsk.host}`,
url: this._deployers.wsk.fullFunctionName,
};
return Object.entries(this._deployers).reduce((p, [name, dep]) => {
// eslint-disable-next-line no-param-reassign
p[name] = {
name: `${dep.name.toLowerCase()};host=${dep.host}`,
url: dep.fullFunctionName,
};
return p;
}, {});
}
};
11 changes: 9 additions & 2 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,17 @@ class CLI {
type: 'string',
default: '.',
})
.option('deploy', {
description: 'Automatically deploy to specified targets (wsk,aws,azure,google,all)',
.option('target', {
description: 'Select target(s) for test, deploy, update-package actions (wsk,aws,azure,google,auto)',
type: 'string',
default: ['auto'],
array: true,
})
.option('deploy', {
description: 'Automatically deploy to specified targets',
type: 'boolean',
default: false,
})
.option('test', {
description: 'Invoke action after deployment. Can be relative url.',
type: 'string',
Expand Down Expand Up @@ -233,6 +239,7 @@ class CLI {
return this.createBuilder()
.verbose(argv.verbose)
.withDirectory(argv.directory)
.withTarget(argv.target)
.withBuild(argv.build)
.withDelete(argv.delete)
.withDeploy(argv.deploy)
Expand Down
15 changes: 12 additions & 3 deletions src/deploy/AWSDeployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class AWSDeployer extends BaseDeployer {
super(builder);

Object.assign(this, {
name: 'AWS',
_region: '',
_role: '',
_functionARN: '',
Expand Down Expand Up @@ -85,6 +86,12 @@ class AWSDeployer extends BaseDeployer {
return res;
}

validate() {
if (!this._role || !this._region) {
throw Error('AWS target needs --aws-region and --aws-role');
}
}

async init() {
this._bucket = `poly-func-maker-temp-${crypto.randomBytes(16).toString('hex')}`;
if (this._region) {
Expand Down Expand Up @@ -260,7 +267,7 @@ class AWSDeployer extends BaseDeployer {

const { ApiId, ApiEndpoint } = res;
this._apiId = ApiId;
this._functionURL = ApiEndpoint;
this._functionURL = `${ApiEndpoint}/${this._builder.actionName.replace('@', '_')}`;

// check for stage
res = await this._api.send(new GetStagesCommand({
Expand Down Expand Up @@ -315,8 +322,6 @@ class AWSDeployer extends BaseDeployer {
StatementId: crypto.randomBytes(16).toString('hex'),
}));

this._functionURL += `/${this._builder.actionName.replace('@', '_')}`;

if (this._builder.showHints) {
const opts = '';
this.log.info('\nYou can verify the action with:');
Expand All @@ -334,6 +339,10 @@ class AWSDeployer extends BaseDeployer {
});
}

get fullFunctionName() {
return this._functionURL;
}

async updatePackage() {
this.log.info('--: updating app (package) parameters ...');
const commands = Object
Expand Down
7 changes: 7 additions & 0 deletions src/deploy/AzureDeployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class AzureDeployer extends BaseDeployer {
super(builder);

Object.assign(this, {
name: 'Azure',
_appName: '',
_auth: null,
_pubcreds: null,
Expand All @@ -32,6 +33,12 @@ class AzureDeployer extends BaseDeployer {
return !!this._appName && !!this._auth;
}

validate() {
if (!this.ready()) {
throw Error('Azure target needs --azure-app');
}
}

withAzureApp(value) {
this._appName = value;
return this;
Expand Down
6 changes: 6 additions & 0 deletions src/deploy/BaseDeployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ class BaseDeployer {
return this._builder && false;
}

validate() {
if (!this.ready()) {
throw Error(`${this.name} target not valid.`);
}
}

get relZip() {
return path.relative(process.cwd(), this._builder.zipFile);
}
Expand Down
7 changes: 7 additions & 0 deletions src/deploy/OpenWhiskDeployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class OpenWhiskDeployer extends BaseDeployer {
super(builder);

Object.assign(this, {
name: 'Openwhisk',
_packageName: '',
_namespace: '',
_packageShared: false,
Expand Down Expand Up @@ -68,6 +69,12 @@ class OpenWhiskDeployer extends BaseDeployer {
return !!this._wskApiHost && !!this._wskAuth && !!this._wskNamespace;
}

validate() {
if (!this.ready()) {
throw Error('Openwhisk target needs --wsk-host, --wsk-auth and --wsk-namespace');
}
}

getOpenwhiskClient() {
if (!this._wskApiHost || !this._wskAuth || !this._wskNamespace) {
throw Error(chalk`\nMissing OpenWhisk credentials. Make sure you have a {grey .wskprops} in your home directory.\nYou can also set {grey WSK_NAMESPACE}, {gray WSK_AUTH} and {gray WSK_API_HOST} environment variables.`);
Expand Down
6 changes: 4 additions & 2 deletions test/build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ describe('Build Test', () => {

const res = await builder.run();
assert.deepEqual(res, {
name: 'openwhisk;host=https://example.com',
url: '/foobar/simple-package/[email protected]',
wsk: {
name: 'openwhisk;host=https://example.com',
url: '/foobar/simple-package/[email protected]',
},
});

await assertZipEntries(path.resolve(testRoot, 'dist', 'simple-package', '[email protected]'), [
Expand Down
17 changes: 12 additions & 5 deletions test/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ describe('CLI Test', () => {
it('has correct defaults with no arguments', () => {
const builder = new CLI().prepare();
assert.equal(builder._verbose, false);
assert.equal(builder._deploy, undefined);
assert.equal(builder._deploy, false);
assert.deepEqual(builder._target, ['auto']);
assert.equal(builder._build, true);
assert.equal(builder._test, undefined);
assert.equal(builder._showHints, true);
Expand Down Expand Up @@ -50,13 +51,19 @@ describe('CLI Test', () => {
it('sets deploy flag', () => {
const builder = new CLI()
.prepare(['--deploy']);
assert.deepEqual(builder._deploy, []);
assert.deepEqual(builder._deploy, true);
});

it('sets deploy targets', () => {
it('sets targets', () => {
const builder = new CLI()
.prepare(['--deploy=aws', '--deploy=wsk']);
assert.deepEqual(builder._deploy, ['aws', 'wsk']);
.prepare(['--target=aws', '--target=wsk']);
assert.deepEqual(builder._target, ['aws', 'wsk']);
});

it('sets targets with csv', () => {
const builder = new CLI()
.prepare(['--target=aws,wsk']);
assert.deepEqual(builder._target, ['aws', 'wsk']);
});

it('clears build flag', () => {
Expand Down
Loading

0 comments on commit 7ff6d76

Please sign in to comment.