diff --git a/functions/scheduleinstance/index.js b/functions/scheduleinstance/index.js index 462ce87605..3e732237f9 100644 --- a/functions/scheduleinstance/index.js +++ b/functions/scheduleinstance/index.js @@ -25,11 +25,12 @@ 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 { @@ -37,25 +38,32 @@ exports.startInstancePubSub = (event, callback) => { 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); @@ -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. @@ -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); @@ -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; } diff --git a/functions/scheduleinstance/package.json b/functions/scheduleinstance/package.json index 5a2d907fd5..cbdb92315f 100644 --- a/functions/scheduleinstance/package.json +++ b/functions/scheduleinstance/package.json @@ -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.", diff --git a/functions/scheduleinstance/test/index.test.js b/functions/scheduleinstance/test/index.test.js index a556288f59..82afd576ba 100644 --- a/functions/scheduleinstance/test/index.test.js +++ b/functions/scheduleinstance/test/index.test.js @@ -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'}; @@ -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(); @@ -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'}; @@ -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`) ); }); @@ -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'}; @@ -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(); @@ -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'}; @@ -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`) ); });