Skip to content

Commit a0c11fb

Browse files
djmailhotfhinkel
authored andcommitted
Add sample for managing GCE instances from GCF (#815)
NodeJS 6 code sample for Cloud Function to start/stop GCE instances.
1 parent f63088e commit a0c11fb

File tree

5 files changed

+524
-0
lines changed

5 files changed

+524
-0
lines changed
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Format: //devtools/kokoro/config/proto/build.proto
2+
3+
# Set the folder in which the tests are run
4+
env_vars: {
5+
key: "PROJECT"
6+
value: "functions/scheduleinstance"
7+
}
8+
9+
# Tell the trampoline which build file to use.
10+
env_vars: {
11+
key: "TRAMPOLINE_BUILD_FILE"
12+
value: "github/nodejs-docs-samples/.kokoro/build.sh"
13+
}

functions/scheduleinstance/README.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<img src="https://avatars2.githubusercontent.com/u/2810941?v=3&s=96" alt="Google Cloud Platform logo" title="Google Cloud Platform" align="right" height="96" width="96"/>
2+
3+
# Google Cloud Functions - Scheduling GCE Instances sample
4+
5+
## Deploy and run the sample
6+
7+
See the [Scheduling Instances with Cloud Scheduler tutorial][tutorial].
8+
9+
[tutorial]: https://cloud.google.com/scheduler/docs/scheduling-instances-with-cloud-scheduler
10+
11+
## Run the tests
12+
13+
1. Read and follow the [prerequisites](../../#how-to-run-the-tests).
14+
15+
1. Install dependencies:
16+
17+
npm install
18+
19+
1. Run the tests:
20+
21+
npm test
22+
23+
## Additional resources
24+
25+
* [Cloud Scheduler documentation][docs]
26+
* [HTTP Cloud Functions documentation][http_docs]
27+
* [HTTP Cloud Functions tutorial][http_tutorial]
28+
29+
[docs]: https://cloud.google.com/scheduler/docs/
30+
[http_docs]: https://cloud.google.com/functions/docs/writing/http
31+
[http_tutorial]: https://cloud.google.com/functions/docs/tutorials/http

functions/scheduleinstance/index.js

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/**
2+
* Copyright 2018, Google, Inc.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
// [START functions_start_instance_http]
17+
// [START functions_stop_instance_http]
18+
const Compute = require('@google-cloud/compute');
19+
const compute = new Compute();
20+
21+
// [END functions_stop_instance_http]
22+
/**
23+
* Starts a Compute Engine instance.
24+
*
25+
* Expects an HTTP POST request with a request body containing the following
26+
* attributes:
27+
* zone - the GCP zone the instance is located in.
28+
* instance - the name of the instance.
29+
*
30+
* @param {!object} req Cloud Function HTTP request data.
31+
* @param {!object} res Cloud Function HTTP response data.
32+
* @returns {!object} Cloud Function response data with status code and message.
33+
*/
34+
exports.startInstance = (req, res) => {
35+
try {
36+
const reqBody = _validateReqBody(_parseReqBody(_validateReq(req)));
37+
compute.zone(reqBody.zone)
38+
.vm(reqBody.instance)
39+
.start()
40+
.then(data => {
41+
// Operation pending.
42+
const operation = data[0];
43+
return operation.promise();
44+
})
45+
.then(() => {
46+
// Operation complete. Instance successfully started.
47+
const message = 'Successfully started instance ' + reqBody.instance;
48+
console.log(message);
49+
res.status(200).send(message);
50+
})
51+
.catch(err => {
52+
console.log(err);
53+
res.status(500).send({error: err.message});
54+
});
55+
} catch (err) {
56+
console.log(err);
57+
res.status(400).send({error: err.message});
58+
}
59+
return res;
60+
};
61+
// [END functions_start_instance_http]
62+
63+
// [START functions_stop_instance_http]
64+
/**
65+
* Stops a Compute Engine instance.
66+
*
67+
* Expects an HTTP POST request with a request body containing the following
68+
* attributes:
69+
* zone - the GCP zone the instance is located in.
70+
* instance - the name of the instance.
71+
*
72+
* @param {!object} req Cloud Function HTTP request data.
73+
* @param {!object} res Cloud Function HTTP response data.
74+
* @returns {!object} Cloud Function response data with status code and message.
75+
*/
76+
exports.stopInstance = (req, res) => {
77+
try {
78+
const reqBody = _validateReqBody(_parseReqBody(_validateReq(req)));
79+
compute.zone(reqBody.zone)
80+
.vm(reqBody.instance)
81+
.stop()
82+
.then(data => {
83+
// Operation pending.
84+
const operation = data[0];
85+
return operation.promise();
86+
})
87+
.then(() => {
88+
// Operation complete. Instance successfully stopped.
89+
const message = 'Successfully stopped instance ' + reqBody.instance;
90+
console.log(message);
91+
res.status(200).send(message);
92+
})
93+
.catch(err => {
94+
console.log(err);
95+
res.status(500).send({error: err.message});
96+
});
97+
} catch (err) {
98+
console.log(err);
99+
res.status(400).send({error: err.message});
100+
}
101+
return res;
102+
};
103+
// [START functions_start_instance_http]
104+
105+
/**
106+
* Parses the request body attributes of an HTTP request based on content-type.
107+
*
108+
* @param {!object} req a Cloud Functions HTTP request object.
109+
* @returns {!object} an object with attributes matching the HTTP request body.
110+
*/
111+
function _parseReqBody (req) {
112+
const contentType = req.get('content-type');
113+
if (contentType === 'application/json') {
114+
// Request.body automatically parsed as an object.
115+
return req.body;
116+
} else if (contentType === 'application/octet-stream') {
117+
// Convert buffer to a string and parse as JSON string.
118+
return JSON.parse(req.body.toString());
119+
} else {
120+
throw new Error('Unsupported HTTP content-type ' + req.get('content-type') +
121+
'; use application/json or application/octet-stream');
122+
}
123+
}
124+
125+
/**
126+
* Validates that a request body contains the expected fields.
127+
*
128+
* @param {!object} reqBody the request body to validate.
129+
* @returns {!object} the request body object.
130+
*/
131+
function _validateReqBody (reqBody) {
132+
if (!reqBody.zone) {
133+
throw new Error(`Attribute 'zone' missing from POST request`);
134+
} else if (!reqBody.instance) {
135+
throw new Error(`Attribute 'instance' missing from POST request`);
136+
}
137+
return reqBody;
138+
}
139+
140+
/**
141+
* Validates that a HTTP request contains the expected fields.
142+
*
143+
* @param {!object} req the request to validate.
144+
* @returns {!object} the request object.
145+
*/
146+
function _validateReq (req) {
147+
if (req.method !== 'POST') {
148+
throw new Error('Unsupported HTTP method ' + req.method +
149+
'; use method POST');
150+
} else if (typeof req.get('content-type') === 'undefined') {
151+
throw new Error('HTTP content-type missing');
152+
}
153+
return req;
154+
}
155+
// [END functions_start_instance_http]
156+
// [END functions_stop_instance_http]
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "schedule-instance",
3+
"version": "0.0.1",
4+
"private": true,
5+
"license": "Apache-2.0",
6+
"author": "Google Inc.",
7+
"repository": {
8+
"type": "git",
9+
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
10+
},
11+
"engines": {
12+
"node": ">=6.0"
13+
},
14+
"scripts": {
15+
"lint": "repo-tools lint",
16+
"pretest": "npm run lint",
17+
"test": "ava -T 20s --verbose test/*.test.js"
18+
},
19+
"devDependencies": {
20+
"@google-cloud/nodejs-repo-tools": "2.2.1",
21+
"ava": "0.25.0",
22+
"proxyquire": "2.0.0",
23+
"sinon": "4.4.2"
24+
},
25+
"dependencies": {
26+
"@google-cloud/compute": "^0.10.0",
27+
"safe-buffer": "5.1.1"
28+
}
29+
}

0 commit comments

Comments
 (0)