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

Stop/start instances by label instead of name #1293

Merged
merged 6 commits into from
May 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 62 additions & 44 deletions functions/scheduleinstance/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,45 @@ const compute = new Compute();
*
* Expects a PubSub message with JSON-formatted event data containing the
* following attributes:
* zone - the GCP zone the instance is located in.
* instance - the name of the instance.
* zone - the GCP zone the instances are located in.
* label - the label of instances to start.
*
* @param {!object} event Cloud Function PubSub message event.
* @param {!object} callback Cloud Function PubSub callback indicating completion.
* @param {!object} callback Cloud Function PubSub callback indicating
* completion.
*/
exports.startInstancePubSub = (event, callback) => {
try {
const pubsubMessage = event.data;
const payload = _validatePayload(
JSON.parse(Buffer.from(pubsubMessage.data, 'base64').toString())
);
compute
.zone(payload.zone)
.vm(payload.instance)
.start()
.then(data => {
// Operation pending.
const operation = data[0];
return operation.promise();
})
.then(() => {
// Operation complete. Instance successfully started.
const message = 'Successfully started instance ' + payload.instance;
console.log(message);
callback(null, message);
})
.catch(err => {
console.log(err);
callback(err);
const options = {filter: `labels.${payload.label}`};
compute.getVMs(options).then(vms => {
vms[0].forEach(instance => {
if (payload.zone === instance.zone.id) {
compute
.zone(payload.zone)
.vm(instance.name)
.start()
.then(data => {
// Operation pending.
const operation = data[0];
return operation.promise();
})
.then(() => {
// Operation complete. Instance successfully started.
const message = 'Successfully started instance ' + instance.name;
console.log(message);
callback(null, message);
})
.catch(err => {
console.log(err);
callback(err);
});
}
});
});
} catch (err) {
console.log(err);
callback(err);
Expand All @@ -69,8 +77,11 @@ exports.startInstancePubSub = (event, callback) => {
*
* Expects a PubSub message with JSON-formatted event data containing the
* following attributes:
* zone - the GCP zone the instance is located in.
* instance - the name of the instance.
* zone - the GCP zone the instances are located in.
* instance - the name of a single instance.
* label - the label of instances to start.
*
* Exactly one of instance or label must be specified.
*
* @param {!object} event Cloud Function PubSub message event.
* @param {!object} callback Cloud Function PubSub callback indicating completion.
Expand All @@ -81,25 +92,32 @@ exports.stopInstancePubSub = (event, callback) => {
const payload = _validatePayload(
JSON.parse(Buffer.from(pubsubMessage.data, 'base64').toString())
);
compute
.zone(payload.zone)
.vm(payload.instance)
.stop()
.then(data => {
// Operation pending.
const operation = data[0];
return operation.promise();
})
.then(() => {
// Operation complete. Instance successfully stopped.
const message = 'Successfully stopped instance ' + payload.instance;
console.log(message);
callback(null, message);
})
.catch(err => {
console.log(err);
callback(err);
const options = {filter: `labels.${payload.label}`};
compute.getVMs(options).then(vms => {
vms[0].forEach(instance => {
if (payload.zone === instance.zone.id) {
compute
.zone(payload.zone)
.vm(instance.name)
.stop()
.then(data => {
// Operation pending.
const operation = data[0];
return operation.promise();
})
.then(() => {
// Operation complete. Instance successfully stopped.
const message = 'Successfully stopped instance ' + instance.name;
console.log(message);
callback(null, message);
})
.catch(err => {
console.log(err);
callback(err);
});
}
});
});
} catch (err) {
console.log(err);
callback(err);
Expand All @@ -111,13 +129,13 @@ exports.stopInstancePubSub = (event, callback) => {
* Validates that a request payload contains the expected fields.
*
* @param {!object} payload the request payload to validate.
* @returns {!object} the payload object.
* @return {!object} the payload object.
*/
function _validatePayload(payload) {
if (!payload.zone) {
throw new Error(`Attribute 'zone' missing from payload`);
} else if (!payload.instance) {
throw new Error(`Attribute 'instance' missing from payload`);
} else if (!payload.label) {
throw new Error(`Attribute 'label' missing from payload`);
}
return payload;
}
Expand Down
2 changes: 1 addition & 1 deletion functions/scheduleinstance/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cloud-functions-schedule-instance",
"version": "0.0.1",
"version": "0.0.2",
"private": true,
"license": "Apache-2.0",
"author": "Google Inc.",
Expand Down
40 changes: 34 additions & 6 deletions functions/scheduleinstance/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ afterEach(tools.restoreConsole);

/** Tests for startInstancePubSub */

it('startInstancePubSub: should accept JSON-formatted event payload', async () => {
it('startInstancePubSub: should accept JSON-formatted event payload with instance', async () => {
const mocks = getMocks();
const sample = getSample();
const pubsubData = {zone: 'test-zone', instance: 'test-instance'};
Expand All @@ -70,6 +70,20 @@ it('startInstancePubSub: should accept JSON-formatted event payload', async () =
assert.strictEqual(data, 'request sent');
});

it('startInstancePubSub: should accept JSON-formatted event payload with label', async () => {
const mocks = getMocks();
const sample = getSample();
const pubsubData = {zone: 'test-zone', label: 'testkey=value'};
mocks.event.data.data = Buffer.from(JSON.stringify(pubsubData)).toString(
'base64'
);
sample.program.startInstancePubSub(mocks.event, mocks.callback);

const data = await sample.mocks.requestPromise();
// The request was successfully sent.
assert.strictEqual(data, 'request sent');
});

it(`startInstancePubSub: should fail with missing 'zone' attribute`, () => {
const mocks = getMocks();
const sample = getSample();
Expand All @@ -85,7 +99,7 @@ it(`startInstancePubSub: should fail with missing 'zone' attribute`, () => {
);
});

it(`startInstancePubSub: should fail with missing 'instance' attribute`, () => {
it(`startInstancePubSub: should fail with missing 'label' attribute`, () => {
const mocks = getMocks();
const sample = getSample();
const pubsubData = {zone: 'test-zone'};
Expand All @@ -96,7 +110,7 @@ it(`startInstancePubSub: should fail with missing 'instance' attribute`, () => {

assert.deepStrictEqual(
mocks.callback.firstCall.args[0],
new Error(`Attribute 'instance' missing from payload`)
new Error(`Attribute 'label' missing from payload`)
);
});

Expand All @@ -117,7 +131,7 @@ it('startInstancePubSub: should fail with empty event payload', () => {

/** Tests for stopInstancePubSub */

it('stopInstancePubSub: should accept JSON-formatted event payload', async () => {
it('stopInstancePubSub: should accept JSON-formatted event payload with instance', async () => {
const mocks = getMocks();
const sample = getSample();
const pubsubData = {zone: 'test-zone', instance: 'test-instance'};
Expand All @@ -131,6 +145,20 @@ it('stopInstancePubSub: should accept JSON-formatted event payload', async () =>
assert.strictEqual(data, 'request sent');
});

it('startInstancePubSub: should accept JSON-formatted event payload with label', async () => {
const mocks = getMocks();
const sample = getSample();
const pubsubData = {zone: 'test-zone', label: 'testkey=value'};
mocks.event.data.data = Buffer.from(JSON.stringify(pubsubData)).toString(
'base64'
);
sample.program.stopInstancePubSub(mocks.event, mocks.callback);

const data = await sample.mocks.requestPromise();
// The request was successfully sent.
assert.strictEqual(data, 'request sent');
});

it(`stopInstancePubSub: should fail with missing 'zone' attribute`, () => {
const mocks = getMocks();
const sample = getSample();
Expand All @@ -146,7 +174,7 @@ it(`stopInstancePubSub: should fail with missing 'zone' attribute`, () => {
);
});

it(`stopInstancePubSub: should fail with missing 'instance' attribute`, () => {
it(`stopInstancePubSub: should fail with missing 'label' attribute`, () => {
const mocks = getMocks();
const sample = getSample();
const pubsubData = {zone: 'test-zone'};
Expand All @@ -157,7 +185,7 @@ it(`stopInstancePubSub: should fail with missing 'instance' attribute`, () => {

assert.deepStrictEqual(
mocks.callback.firstCall.args[0],
new Error(`Attribute 'instance' missing from payload`)
new Error(`Attribute 'label' missing from payload`)
);
});

Expand Down