Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(publish): Support for custom VCL logic #893

Merged
merged 14 commits into from
May 20, 2019
Merged
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(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async, please

});
});

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