Skip to content

Commit

Permalink
feat(publish): Support for custom VCL logic (#893)
Browse files Browse the repository at this point in the history
  • Loading branch information
kptdobe authored May 20, 2019
1 parent 86f1b62 commit d773f2a
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 7 deletions.
8 changes: 8 additions & 0 deletions src/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ module.exports = function strain() {
describe: 'Don\'t publish strains with names following the specified pattern, use config from master branch instead',
type: 'string',
})
.option('custom-vcl', {
alias: 'customVCL',
describe: 'Path(s) to VCL file(s) to override the orginal one(s).',
type: 'string',
array: true,
default: [],
})
.conflicts('only', 'exclude')
.demandOption(
'fastly-auth',
Expand Down Expand Up @@ -106,6 +113,7 @@ module.exports = function strain() {
.withUpdateBotConfig(argv.updateBotConfig)
.withConfigPurgeAPI(argv.apiConfigPurge)
.withFilter(argv.only, argv.exclude)
.withCustomVCLs(argv.customVCL)
.run();
},
};
Expand Down
45 changes: 39 additions & 6 deletions src/remotepublish.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */

const request = require('request-promise-native');
const fs = require('fs-extra');
const path = require('path');
const fastly = require('@adobe/fastly-native-promises');
const chalk = require('chalk');
const ProgressBar = require('progress');
Expand All @@ -35,6 +37,7 @@ class RemotePublishCommand extends AbstractCommand {
this._githubToken = '';
this._updateBotConfig = false;
this._configPurgeAPI = 'https://app.project-helix.io/config/purge';
this._vcl = null;
}

tick(ticks = 1, message, name) {
Expand Down Expand Up @@ -155,6 +158,31 @@ class RemotePublishCommand extends AbstractCommand {
return this;
}

/**
* Adds a list of VCL files to the publish command. Each VCL file is represented
* by a path relative to the current command (e.g. ['vcl/extensions.vcl']).
* @param {array} value List of vcl files to add as vcl extensions
* @returns {RemotePublishCommand} the current instance
*/
withCustomVCLs(vcls) {
if (vcls && vcls.length > 0) {
const vcl = {};
vcls.forEach((file) => {
try {
const fullPath = path.resolve(this.directory, file);
const name = path.basename(fullPath, '.vcl');
const content = fs.readFileSync(fullPath).toString();
vcl[name] = content;
} catch (error) {
this.log.error(`Cannot read provided custom vcl file ${file}`);
throw error;
}
});
this._vcl = vcl;
}
return this;
}

showNextStep(dryrun) {
this.progressBar().terminate();
if (dryrun) {
Expand Down Expand Up @@ -235,15 +263,20 @@ ${e}`);
throw new Error('Unable to merge configurations for selective publishing');
}
}
const body = {
configuration: this.config.toJSON(),
service: this._fastly_namespace,
token: this._fastly_auth,
version: this._version,
};

if (this._vcl) {
body.vcl = this._vcl;
}

return request.post(this._publishAPI, {
json: true,
body: {
configuration: this.config.toJSON(),
service: this._fastly_namespace,
token: this._fastly_auth,
version: this._version,
},
body,
}).then(() => {
this.tick(9, 'set service config up for Helix', true);
return true;
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/vcl/another.vcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
# Copyright 2019 Adobe. All rights reserved.
# This file is licensed to you 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 REPRESENTATIONS
# OF ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.
#

sub hlx_type_pipeline_before {
# THIS WILL DO SOME CUSTOM CODE
}

sub hlx_type_pipeline_after {
# THIS WILL DO SOME CUSTOM CODE
}
19 changes: 19 additions & 0 deletions test/fixtures/vcl/extensions.vcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
# Copyright 2019 Adobe. All rights reserved.
# This file is licensed to you 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 REPRESENTATIONS
# OF ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.
#

sub hlx_type_pipeline_before {
# THIS WILL DO SOME CUSTOM CODE
}

sub hlx_type_pipeline_after {
# THIS WILL DO SOME CUSTOM CODE
}
5 changes: 4 additions & 1 deletion test/testPublishCli.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe('hlx publish', () => {
mockPublish.withUpdateBotConfig.returnsThis();
mockPublish.withGithubToken.returnsThis();
mockPublish.withFilter.returnsThis();
mockPublish.withCustomVCLs.returnsThis();
mockPublish.run.returnsThis();
});

Expand All @@ -51,7 +52,7 @@ describe('hlx publish', () => {
new CLI()
.withCommandExecutor('publish', mockPublish)
.onFail((err) => {
assert.ok(err.indexOf('required'));
assert.ok(!err || err.indexOf('required'));
done();
})
.run(['publish']);
Expand All @@ -71,6 +72,7 @@ describe('hlx publish', () => {
sinon.assert.calledWith(mockPublish.withFastlyAuth, 'foobar');
sinon.assert.calledWith(mockPublish.withPublishAPI, 'foobar.api');
sinon.assert.calledWith(mockPublish.withDryRun, true);
sinon.assert.calledWith(mockPublish.withCustomVCLs, []);
});

it('hlx publish works with minimal arguments', () => {
Expand All @@ -88,6 +90,7 @@ describe('hlx publish', () => {
sinon.assert.calledWith(mockPublish.withWskNamespace, 'hlx');
sinon.assert.calledWith(mockPublish.withFastlyNamespace, 'hlx'); // TODO !!
sinon.assert.calledWith(mockPublish.withFastlyAuth, 'secret-key');
sinon.assert.calledWith(mockPublish.withCustomVCLs, []);
sinon.assert.calledOnce(mockPublish.run);
});

Expand Down
169 changes: 169 additions & 0 deletions test/testRemotePublishCmd.customvcl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright 2018 Adobe. All rights reserved.
* This file is licensed to you 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 REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

/* eslint-env mocha */

const assert = require('assert');
const fs = require('fs-extra');
const nock = require('nock');
const path = require('path');
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const shell = require('shelljs');
const { Logger } = require('@adobe/helix-shared');
const { createTestRoot } = require('./utils.js');

const RemotePublishCommand = require('../src/remotepublish.cmd');

describe('hlx publish --custom-vcl (check params)', () => {
it('hlx publish (no custom-vcl)', () => {
const remote = new RemotePublishCommand()
.withCustomVCLs();

// eslint-disable-next-line no-underscore-dangle
assert.equal(remote._vcl, null);
});

it('hlx publish --custom-vcl fixtures/vcl/extensions.vcl', async () => {
const e = path.resolve(__dirname, 'fixtures/vcl/extensions.vcl');
const remote = new RemotePublishCommand()
.withCustomVCLs([e]);

// eslint-disable-next-line no-underscore-dangle
assert.deepEqual(remote._vcl, {
extensions: (await fs.readFile(e, 'utf8')).toString(),
});
});

it('hlx publish --custom-vcl fixtures/vcl/extensions.vcl --custom-vcl fixtures/vcl/another.vcl', async () => {
const e = path.resolve(__dirname, 'fixtures/vcl/extensions.vcl');
const a = path.resolve(__dirname, 'fixtures/vcl/another.vcl');
const remote = new RemotePublishCommand()
.withCustomVCLs([e, a]);

// eslint-disable-next-line no-underscore-dangle
assert.deepEqual(remote._vcl, {
extensions: (await fs.readFile(e, 'utf8')).toString(),
another: (await fs.readFile(e, 'utf8')).toString(),
});
});

it('hlx publish --custom-vcl fixtures/vcl/unexisting.vcl', async () => {
const e = path.resolve(__dirname, 'fixtures/vcl/unexisting.vcl');
function throwsError() {
new RemotePublishCommand().withCustomVCLs([e]);
}
assert.throws(throwsError);
});
});

describe('hlx publish --custom-vcl (check requests)', () => {
let ProxiedRemotePublishCommand;
let writeDictItem;
let purgeAll;
let testRoot;
let pwd;
let vcl;
let scope;
let remote;

beforeEach('Setting up Fake Server', async function bef() {
this.timeout(5000);
writeDictItem = sinon.fake.resolves(true);
purgeAll = sinon.fake.resolves(true);

ProxiedRemotePublishCommand = proxyquire('../src/remotepublish.cmd', {
'@adobe/fastly-native-promises': () => ({
transact: fn => fn(3),
writeDictItem,
purgeAll,
}),
});


// ensure to reset nock to avoid conflicts with PollyJS
nock.restore();
nock.cleanAll();
nock.activate();

scope = nock('https://adobeioruntime.net')
.post('/api/v1/web/helix/default/publish', (body) => {
({ vcl } = body);
return true;
})
.reply(200, {})
.post('/api/v1/web/helix/default/addlogger')
.reply(200, {});

// set up a fake git repo.
testRoot = await createTestRoot();
await fs.copy(path.resolve(__dirname, 'fixtures/filtered-master.yaml'), path.resolve(testRoot, 'helix-config.yaml'));

// throw a Javascript error when any shell.js command encounters an error
shell.config.fatal = true;

// init git repo
pwd = shell.pwd();
shell.cd(testRoot);
shell.exec('git init');
shell.exec('git add -A');
shell.exec('git commit -m"initial commit."');

// set up command
const logger = Logger.getTestLogger();
remote = await new ProxiedRemotePublishCommand(logger)
.withWskAuth('fakeauth')
.withWskNamespace('fakename')
.withFastlyAuth('fake_auth')
.withFastlyNamespace('fake_name')
.withWskHost('doesn.t.matter')
.withPublishAPI('https://adobeioruntime.net/api/v1/web/helix/default/publish')
.withConfigFile(path.resolve(__dirname, 'fixtures/filtered.yaml'))
.withDryRun(false);
});

it('hlx publish (no custom-vcl)', async () => {
await remote.run();
assert.equal(vcl, null);
});

it('hlx publish --custom-vcl fixtures/vcl/extensions.vcl', async () => {
const e = path.resolve(__dirname, 'fixtures/vcl/extensions.vcl');
remote.withCustomVCLs([e]);
await remote.run();

assert.ok(vcl);
assert.deepEqual(vcl, {
extensions: fs.readFileSync(e).toString(),
});
});

it('hlx publish --custom-vcl fixtures/vcl/extensions.vcl --custom-vcl fixtures/vcl/another.vcl', async () => {
const e = path.resolve(__dirname, 'fixtures/vcl/extensions.vcl');
const a = path.resolve(__dirname, 'fixtures/vcl/another.vcl');
remote.withCustomVCLs([e, a]);
await remote.run();

assert.ok(vcl);
assert.deepEqual(vcl, {
extensions: fs.readFileSync(e).toString(),
another: fs.readFileSync(a).toString(),
});
});

afterEach(async () => {
scope.done();
nock.restore();
shell.cd(pwd);
await fs.remove(testRoot);
});
});
35 changes: 35 additions & 0 deletions test/testRemotePublishCmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

/* eslint-env mocha */

const assert = require('assert');
const nock = require('nock');
const path = require('path');
const proxyquire = require('proxyquire');
Expand Down Expand Up @@ -66,6 +67,40 @@ describe('hlx publish --remote (default)', () => {
scope.done();
});

it('publishing sends expected parameters', async () => {
let publishBody;
const scope = nock('https://adobeioruntime.net')
.post('/api/v1/web/helix/default/publish', (body) => {
publishBody = body;
return true;
})
.reply(200, {})
.post('/api/v1/web/helix/default/addlogger')
.reply(200, {});

const remote = await new RemotePublishCommand()
.withWskAuth('fakeauth')
.withWskNamespace('fakename')
.withFastlyAuth('fake_auth')
.withFastlyNamespace('fake_name')
.withWskHost('doesn.t.matter')
.withPublishAPI('https://adobeioruntime.net/api/v1/web/helix/default/publish')
.withConfigFile(path.resolve(__dirname, 'fixtures/deployed.yaml'))
.withFilter()
.withDryRun(false);
await remote.run();

assert.ok(publishBody);
assert.equal(publishBody.service, 'fake_name');
assert.equal(publishBody.token, 'fake_auth');
assert.equal(typeof (publishBody.configuration), 'object');
assert.ok(Array.isArray(publishBody.configuration.strains));
assert.equal(publishBody.configuration.strains.length, 4);
assert.equal(publishBody.vcl, undefined);

scope.done();
});

it('publishing stops of no strains with package info', async () => {
const remote = await new RemotePublishCommand()
.withWskAuth('fakeauth')
Expand Down

0 comments on commit d773f2a

Please sign in to comment.