From f76704bc5d8f73c0da9bf0f8819e5e4884a3f5fe Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Fri, 21 Dec 2018 19:49:55 -0800 Subject: [PATCH] fix: modernize the samples (#240) --- package.json | 2 +- samples/createVM.js | 28 +++++++ samples/deleteVM.js | 28 +++++++ .../{system-test/vms.test.js => listVMs.js} | 24 +++--- samples/mailjet.js | 4 +- samples/package.json | 12 ++- samples/quickstart.js | 37 +++++---- samples/startup-script/README.md | 70 ----------------- samples/startup-script/apache.png | Bin 43485 -> 0 bytes samples/startup-script/package.json | 28 ------- .../startup-script/system-test/.eslintrc.yml | 3 - .../startup-script/system-test/index.test.js | 40 ---------- .../index.js => startupScript.js} | 65 +++------------- samples/system-test/.eslintrc.yml | 3 - samples/system-test/vms_api.test.js | 30 -------- samples/test/samples.test.js | 72 ++++++++++++++++++ samples/vms_api.js | 53 ------------- 17 files changed, 177 insertions(+), 322 deletions(-) create mode 100644 samples/createVM.js create mode 100644 samples/deleteVM.js rename samples/{system-test/vms.test.js => listVMs.js} (64%) delete mode 100644 samples/startup-script/README.md delete mode 100644 samples/startup-script/apache.png delete mode 100644 samples/startup-script/package.json delete mode 100644 samples/startup-script/system-test/.eslintrc.yml delete mode 100644 samples/startup-script/system-test/index.test.js rename samples/{startup-script/index.js => startupScript.js} (62%) delete mode 100644 samples/system-test/.eslintrc.yml delete mode 100644 samples/system-test/vms_api.test.js create mode 100644 samples/test/samples.test.js delete mode 100644 samples/vms_api.js diff --git a/package.json b/package.json index d10fe9a3..fdf2c80d 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "docs": "jsdoc -c .jsdoc.js", "generate-scaffolding": "repo-tools generate all && repo-tools generate lib_samples_readme -l samples/ --config ../.cloud-repo-tools.json", "lint": "eslint '**/*.js'", - "samples-test": "cd samples/ && npm link ../ && cd startup-script && npm link ../../ && cd ../ && npm test && cd ../", + "samples-test": "cd samples/ && npm link ../ && npm test && cd ../", "system-test": "mocha system-test/*.js --timeout 600000", "test": "nyc mocha", "fix": "eslint --fix '**/*.js'" diff --git a/samples/createVM.js b/samples/createVM.js new file mode 100644 index 00000000..788e301e --- /dev/null +++ b/samples/createVM.js @@ -0,0 +1,28 @@ +// Copyright 2017, Google, Inc. +// Licensed 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function createVM( + vmName = 'new_virtual_machine' // VM name of your choice +) { + const Compute = require('@google-cloud/compute'); + const compute = new Compute(); + const zone = compute.zone('us-central1-c'); + const [vm, operation] = await zone.createVM(vmName, {os: 'ubuntu'}); + console.log(vm); + await operation.promise(); + console.log('Virtual machine created!'); +} + +createVM(...process.argv.slice(2)).catch(console.error); diff --git a/samples/deleteVM.js b/samples/deleteVM.js new file mode 100644 index 00000000..fe88f0dc --- /dev/null +++ b/samples/deleteVM.js @@ -0,0 +1,28 @@ +// Copyright 2017, Google, Inc. +// Licensed 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function deleteVM( + name = 'virtual_machine_name' // VM name of your choice +) { + const Compute = require('@google-cloud/compute'); + const compute = new Compute(); + const zone = compute.zone('us-central1-c'); + const vm = zone.vm(name); + const [operation] = await vm.delete(); + await operation.promise(); + console.log(`VM deleted!`); +} + +deleteVM(...process.argv.slice(2)).catch(console.error); diff --git a/samples/system-test/vms.test.js b/samples/listVMs.js similarity index 64% rename from samples/system-test/vms.test.js rename to samples/listVMs.js index ff6a6d85..b2375a41 100644 --- a/samples/system-test/vms.test.js +++ b/samples/listVMs.js @@ -15,16 +15,16 @@ 'use strict'; -const execa = require('execa'); -const path = require(`path`); -const {assert} = require('chai'); - -const cmd = `node vms.js`; -const cwd = path.join(__dirname, `..`); - -describe('should retrieve list of vms', () => { - it('vms_inspect_string', async () => { - const {stdout} = await execa.shell(cmd, {cwd}); - assert.match(stdout, /^VMs:/); +// [START list] +async function listVMs() { + const Compute = require('@google-cloud/compute'); + const compute = new Compute(); + const vms = await compute.getVMs({ + maxResults: 10, }); -}); + console.log(`Found ${vms.length} VMs!`); + vms.forEach(vm => console.log(vm)); +} +// [END list] + +listVMs().catch(console.error); diff --git a/samples/mailjet.js b/samples/mailjet.js index 3adf5b61..f0e559f3 100644 --- a/samples/mailjet.js +++ b/samples/mailjet.js @@ -39,6 +39,6 @@ async function mailjet() { }); console.log(json); } -mailjet().catch(console.error); - // [END send] + +mailjet().catch(console.error); diff --git a/samples/package.json b/samples/package.json index b04cc1fd..59033459 100644 --- a/samples/package.json +++ b/samples/package.json @@ -11,22 +11,20 @@ "node": ">=8" }, "scripts": { - "system-test": "mocha system-test/*.js --timeout 600000", - "startup-test": "mocha startup-script/system-test/*.test.js --timeout 600000", - "test": "npm run system-test && npm run startup-test" + "test": "mocha --timeout 1200000" }, "dependencies": { + "node-fetch": "^2.3.0", "@google-cloud/compute": "^0.11.0", - "googleapis": "^36.0.0", "nodemailer": "^4.3.1", "nodemailer-smtp-transport": "^2.7.4", - "sendgrid": "^5.2.3", - "uuid": "^3.2.1" + "sendgrid": "^5.2.3" }, "devDependencies": { "chai": "^4.2.0", "execa": "^1.0.0", "mocha": "^5.0.0", - "proxyquire": "^2.0.1" + "proxyquire": "^2.0.1", + "uuid": "^3.2.1" } } diff --git a/samples/quickstart.js b/samples/quickstart.js index a65c414f..522d26c4 100644 --- a/samples/quickstart.js +++ b/samples/quickstart.js @@ -11,32 +11,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -/* eslint-disable no-unused-vars */ - 'use strict'; // [START compute_engine_quickstart] -// Imports the Google Cloud client library -const Compute = require('@google-cloud/compute'); -const uuid = require('uuid'); -// Creates a client -const compute = new Compute(); +async function createVM( + vmName = 'new_virtual_machine' // VM name of your choice +) { + // Imports the Google Cloud client library + const Compute = require('@google-cloud/compute'); -// Create a new VM using the latest OS image of your choice. -const zone = compute.zone('us-central1-a'); -const name = `ubuntu-http-${uuid().split('-')[0]}`; + // Creates a client + const compute = new Compute(); -async function createVM() { - const data = await zone.createVM(name, {os: 'ubuntu'}); + // Create a new VM using the latest OS image of your choice. + const zone = compute.zone('us-central1-c'); - // `operation` lets you check the status of long-running tasks. - const vm = data[0]; - const operation = data[1]; + // Start the VM create task + const [vm, operation] = await zone.createVM(vmName, {os: 'ubuntu'}); + console.log(vm); + // `operation` lets you check the status of long-running tasks. await operation.promise(); - // Virtual machine created! -} - -createVM().catch(console.error); + // Complete! + console.log('Virtual machine created!'); +} // [END compute_engine_quickstart] + +createVM(...process.argv.slice(2)).catch(console.error); diff --git a/samples/startup-script/README.md b/samples/startup-script/README.md deleted file mode 100644 index dbf327f5..00000000 --- a/samples/startup-script/README.md +++ /dev/null @@ -1,70 +0,0 @@ -Google Cloud Platform logo - -# Create VM with Apache and Custom Homepage - -[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-compute&page=editor&open_in_editor=samples/startup-script/index.js,samples/startup-script/README.md) - -This example shows how to create a VM with Apache and a custom homepage. On creation, the -VM runs the startup script. The startup script installs -Apache and a custom homepage. You can change to script to fit your needs, for example -run a Node.js server on the VM. - -### Before you begin - -Before running the samples, make sure you've followed the steps in the -[Before you begin section](../../README.md#before-you-begin) of the client -library's README and that your environment variable -`GOOGLE_APPLICATION_CREDENTIALS` is set. - -### Run the sample - -``` -git clone git@github.com:googleapis/nodejs-compute.git -cd nodejs-compute/samples/startup-script -npm install -npm start -``` - -On success, you should see output like this: - -``` -npm start - -> compute-sample-startup-script@1.0.0 start nodejs-compute/samples/startup-script -> node -e 'require("./index.js").create("vm-with-apache", console.log)' - -Booting new VM with IP http://35.202.127.163... -................................. Ready! -null '35.202.127.163' -``` - -You can test the new VM in your browser by navigating to its IP address. - -![Screenshot of homepage][homepage_img] - -### Cleanup - -Delete the VM you just created by running the following: - -``` -npm run delete -``` - -To see all the VMs currently running in your project, run `npm run list` or use the [Cloud Console](https://console.cloud.google.com/compute/instances). - - -### Troubleshooting - -It takes about two minutes to install Apache. If you cannot access the homepage, -connect to the VM and check the log files in `/var/log/syslog`. You can use the [Cloud Console](https://console.cloud.google.com/compute/instance) to connect to a VM or use -the following command: - -`gcloud compute --project "YOUR PROJECT ID" ssh --zone "us-central1-a" "vm-with-apache"` - -Keep in -mind that the startup script is run as root with a different -home directory than your default user. - -[shell_img]: https://gstatic.com/cloudssh/images/open-btn.png -[shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-compute&page=editor&open_in_editor=samples/startup-script/README.md -[homepage_img]: ./apache.png \ No newline at end of file diff --git a/samples/startup-script/apache.png b/samples/startup-script/apache.png deleted file mode 100644 index 2aebdd83525be53ef9df2ae584f3ae641437d90f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43485 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 GR^2Ty0ySVK15{eBv^RO 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

-d?vjno~>*P-HJX)umZp*N+w!yBr0 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 diff --git a/samples/startup-script/package.json b/samples/startup-script/package.json deleted file mode 100644 index 1f06ebaf..00000000 --- a/samples/startup-script/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "compute-sample-startup-script", - "version": "1.0.0", - "description": "Start a Google Compute Engine and run the startup script.", - "main": "index.js", - "files": [ - "*.js" - ], - "dependencies": { - "@google-cloud/compute": "0.10.0", - "node-fetch": "^2.2.0" - }, - "engines": { - "node": ">=8" - }, - "devDependencies": { - "mocha": "^5.0.0", - "uuid": "^3.2.1" - }, - "scripts": { - "test": "mocha system-test/*.js --timeout 600000", - "start": "node -e 'require(\"./index.js\").createVM(\"vm-with-apache\", console.log)'", - "delete": "node -e 'require(\"./index.js\").deleteVM(\"vm-with-apache\", console.log)'", - "list": "node -e 'require(\"./index.js\").listVMs(console.log)'" - }, - "author": "Google, LLC", - "license": "Apache-2.0" -} diff --git a/samples/startup-script/system-test/.eslintrc.yml b/samples/startup-script/system-test/.eslintrc.yml deleted file mode 100644 index 6db2a46c..00000000 --- a/samples/startup-script/system-test/.eslintrc.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -env: - mocha: true diff --git a/samples/startup-script/system-test/index.test.js b/samples/startup-script/system-test/index.test.js deleted file mode 100644 index e6eadacb..00000000 --- a/samples/startup-script/system-test/index.test.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2018, Google, Inc. - * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const uuid = require('uuid'); -const assert = require('assert'); - -const example = require('../index'); -const name = `gcloud-apache-${uuid.v4().split('-')[0]}`; - -describe('start-up script', async () => { - it('should create vm', async () => { - const ip = await example.createVM(name); - assert.ok(ip); - }); - - it('should list vms', async () => { - const vms = await example.listVMs(); - assert.ok(vms); - assert.ok(Array.isArray(vms)); - }); - - it('should delete vm', async () => { - const result = await example.deleteVM(name); - assert.strictEqual(result, name); - }); -}); diff --git a/samples/startup-script/index.js b/samples/startupScript.js similarity index 62% rename from samples/startup-script/index.js rename to samples/startupScript.js index 4f09b3d8..e09af659 100644 --- a/samples/startup-script/index.js +++ b/samples/startupScript.js @@ -19,16 +19,15 @@ const Compute = require('@google-cloud/compute'); const fetch = require('node-fetch'); const compute = new Compute(); -const zone = compute.zone('us-central1-a'); +const zone = compute.zone('us-central1-c'); /** * Create a new virtual machine with Ubuntu and Apache * @param {string} name Name of the virtual machine */ -async function createVM(name) { +async function createVMWithStartupScript(name) { // Create a new VM, using default ubuntu image. The startup script // installs apache and a custom homepage. - const config = { os: 'ubuntu', http: true, @@ -60,18 +59,16 @@ async function createVM(name) { console.log('Acquiring VM metadata...'); const [metadata] = await vm.getMetadata(); - console.log(metadata); // External IP of the VM. const ip = metadata.networkInterfaces[0].accessConfigs[0].natIP; console.log(`Booting new VM with IP http://${ip}...`); // Ping the VM to determine when the HTTP server is ready. - console.log(`Operation complete. Waiting for IP ...`); + console.log('Operation complete. Waiting for IP'); await pingVM(ip); - console.log(`${name} created succesfully`); - return ip; + console.log(`\n${name} created succesfully`); } /** @@ -79,60 +76,20 @@ async function createVM(name) { * @param {string} ip IP address to poll */ async function pingVM(ip) { - let waiting = true; - while (waiting) { + let exit = false; + while (!exit) { await new Promise(r => setTimeout(r, 2000)); try { const res = await fetch(`http://${ip}`); - const statusCode = res.status; - if (statusCode === 200) { - waiting = false; - console.log('Ready!'); - return; - } else { - process.stdout.write('.'); + if (res.status !== 200) { + throw new Error(res.status); } + exit = true; } catch (err) { process.stdout.write('.'); } } } -/** - * List all VMs and their external IPs in a given zone. - */ -async function listVMs() { - const [vms] = await zone.getVMs(); - const results = await Promise.all( - vms.map(async vm => { - const [metadata] = await vm.getMetadata(); - return { - ip: metadata['networkInterfaces'][0]['accessConfigs'] - ? metadata['networkInterfaces'][0]['accessConfigs'][0]['natIP'] - : 'no external ip', - name: metadata.name, - }; - }) - ); - console.log(results); - return results; -} - -/** - * Delete a VM with a given name. - * @param {string} name - */ -async function deleteVM(name) { - const vm = zone.vm(name); - console.log(`Deleting ${name} ...`); - const [operation] = await vm.delete(); - console.log(`Polling operation ${operation.id} ...`); - await operation.promise(); - console.log(`${name} deleted succesfully!`); - return name; -} -module.exports = { - createVM, - deleteVM, - listVMs, -}; +const args = process.argv.slice(2); +createVMWithStartupScript(...args).catch(console.error); diff --git a/samples/system-test/.eslintrc.yml b/samples/system-test/.eslintrc.yml deleted file mode 100644 index 6db2a46c..00000000 --- a/samples/system-test/.eslintrc.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -env: - mocha: true diff --git a/samples/system-test/vms_api.test.js b/samples/system-test/vms_api.test.js deleted file mode 100644 index b53e60af..00000000 --- a/samples/system-test/vms_api.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2017, Google, Inc. - * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const execa = require(`execa`); -const path = require(`path`); -const {assert} = require('chai'); - -const cmd = `node vms_api.js`; -const cwd = path.join(__dirname, `..`); - -describe('should retrieve list of vms via api', () => { - it('vms_api_inspect_string', async () => { - const {stdout} = await execa.shell(cmd, {cwd}); - assert.match(stdout, /^VMs:/); - }); -}); diff --git a/samples/test/samples.test.js b/samples/test/samples.test.js new file mode 100644 index 00000000..9c76232e --- /dev/null +++ b/samples/test/samples.test.js @@ -0,0 +1,72 @@ +/** + * Copyright 2018, Google, Inc. + * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const uuid = require('uuid'); +const execa = require('execa'); +const {assert} = require('chai'); +const Compute = require('@google-cloud/compute'); + +const exec = async cmd => (await execa.shell(cmd)).stdout; +const compute = new Compute(); + +describe('quickstart', () => { + const name = `gcloud-ubuntu-${uuid.v4().split('-')[0]}`; + after(async () => deleteVM(name)); + it('should run the quickstart', async () => { + const output = await exec(`node quickstart ${name}`); + assert.match(output, /Virtual machine created!/); + }); +}); + +describe('lifecycle', async () => { + const name = `gcloud-ubuntu-${uuid.v4().split('-')[0]}`; + + it('should create a VM', async () => { + const output = await exec(`node createVM ${name}`); + assert.match(output, /Virtual machine created!/); + }); + + it('should list the VMs', async () => { + const output = await exec('node listVMs'); + assert.match(output, /Found \d+ VMs!/); + }); + + it('should delete the VM', async () => { + const output = await exec(`node deleteVM ${name}`); + assert.match(output, /VM deleted!/); + }); +}); + +describe('start-up script', async () => { + const name = `gcloud-apache-${uuid.v4().split('-')[0]}`; + after(async () => deleteVM(name)); + it('should create vm with startup script', async () => { + const output = await exec(`node startupScript ${name}`); + assert.match(output, /created succesfully$/); + }); +}); + +/** + * Utility function to delete a VM. + * @param {string} name + */ +async function deleteVM(name) { + const zone = compute.zone('us-central1-c'); + const vm = zone.vm(name); + const [operation] = await vm.delete(); + await operation.promise(); +} diff --git a/samples/vms_api.js b/samples/vms_api.js deleted file mode 100644 index ea9ab7fe..00000000 --- a/samples/vms_api.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2017, Google, Inc. - * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -// [START complete] -// [START initialize] -const {google} = require('googleapis'); -const compute = google.compute('v1'); -// [END initialize] - -// [START list] -/** - * @param {Function} callback Callback function. - */ -async function getVmsExample() { - const authClient = await google.auth.getClient({ - scopes: [ - 'https://www.googleapis.com/auth/cloud-platform', - 'https://www.googleapis.com/auth/compute', - 'https://www.googleapis.com/auth/compute.readonly', - ], - }); - const projectId = await google.auth.getProjectId(); - - // Retrieve the vms - const result = await compute.instances.aggregatedList({ - auth: authClient, - project: projectId, - // In this example we only want one VM per page - maxResults: 1, - }); - const vms = result.data; - console.log('VMs:', vms); - return vms; -} -// [END list] -// [END complete] - -// Run the examples -getVmsExample().catch(console.error);