diff --git a/.github/workflows/compute.yaml b/.github/workflows/compute.yaml new file mode 100644 index 0000000000..2e5e136470 --- /dev/null +++ b/.github/workflows/compute.yaml @@ -0,0 +1,69 @@ +name: compute +on: + push: + branches: + - main + paths: + - 'compute/**' + pull_request: + paths: + - 'compute/**' + pull_request_target: + types: [labeled] + paths: + - 'compute/**' + schedule: + - cron: '0 0 * * 0' +jobs: + test: + if: ${{ github.event.action != 'labeled' || github.event.label.name == 'actions:force-run' }} + runs-on: ubuntu-latest + timeout-minutes: 120 + permissions: + contents: 'write' + pull-requests: 'write' + id-token: 'write' + steps: + - uses: actions/checkout@v3 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + - uses: google-github-actions/auth@v1.0.0 + with: + workload_identity_provider: 'projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider' + service_account: 'kokoro-system-test@long-door-651.iam.gserviceaccount.com' + create_credentials_file: 'true' + access_token_lifetime: 600s + - uses: actions/setup-node@v3 + with: + node-version: 14 + - run: npm install + working-directory: compute + - run: npm test + working-directory: compute + env: + MOCHA_REPORTER_SUITENAME: compute + MOCHA_REPORTER_OUTPUT: compute_sponge_log.xml + MOCHA_REPORTER: xunit + - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + await github.rest.issues.removeLabel({ + name: 'actions:force-run', + owner: 'GoogleCloudPlatform', + repo: 'nodejs-docs-samples', + issue_number: context.payload.pull_request.number + }); + } catch (e) { + if (!e.message.includes('Label does not exist')) { + throw e; + } + } + - if: ${{ github.event_name == 'schedule' && always() }} + run: | + curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L + chmod +x ./flakybot + ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/workflows.json b/.github/workflows/workflows.json index 7db436808f..0894cd4a93 100644 --- a/.github/workflows/workflows.json +++ b/.github/workflows/workflows.json @@ -24,6 +24,7 @@ "cloud-tasks/tutorial-gcf/function", "composer", "composer/functions/composer-storage-trigger", + "compute", "container-analysis/snippets", "containerengine/hello-world", "container/snippets", diff --git a/compute/create-instance-templates/createTemplate.js b/compute/create-instance-templates/createTemplate.js new file mode 100644 index 0000000000..cb1bb077c3 --- /dev/null +++ b/compute/create-instance-templates/createTemplate.js @@ -0,0 +1,96 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new instance template with the provided name and a specific instance configuration. + * + * @param {string} projectId - Project ID or project number of the Cloud project you use. + * @param {string} templateName - Name of the new template to create. + */ +function main(projectId, templateName) { + // [START compute_template_create] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const templateName = 'your_template_name'; + + const compute = require('@google-cloud/compute'); + + // Create a new instance template with the provided name and a specific instance configuration. + async function createTemplate() { + const instanceTemplatesClient = new compute.InstanceTemplatesClient(); + + const [response] = await instanceTemplatesClient.insert({ + project: projectId, + instanceTemplateResource: { + name: templateName, + properties: { + disks: [ + { + // The template describes the size and source image of the boot disk + // to attach to the instance. + initializeParams: { + diskSizeGb: '250', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-11', + }, + autoDelete: true, + boot: true, + }, + ], + machineType: 'e2-standard-4', + // The template connects the instance to the `default` network, + // without specifying a subnetwork. + networkInterfaces: [ + { + // Use the network interface provided in the networkName argument. + name: 'global/networks/default', + // The template lets the instance use an external IP address. + accessConfigs: [ + { + name: 'External NAT', + type: 'ONE_TO_ONE_NAT', + networkTier: 'PREMIUM', + }, + ], + }, + ], + }, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Instance template created.'); + } + + createTemplate(); + // [END compute_template_create] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/create-instance-templates/createTemplateFromInstance.js b/compute/create-instance-templates/createTemplateFromInstance.js new file mode 100644 index 0000000000..ac98d14cd3 --- /dev/null +++ b/compute/create-instance-templates/createTemplateFromInstance.js @@ -0,0 +1,84 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new instance template based on an existing instance. + * + * @param {string} projectId - Project ID or project number of the Cloud project you use. + * @param {string} instance - The instance to base the new template on. This value uses + * the following format: "projects/{project}/zones/{zone}/instances/{instance_name}" + * @param {string} templateName - Name of the new template to create. + */ +function main(projectId, instance, templateName) { + // [START compute_template_create_from_instance] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const instance = 'projects/project/zones/zone/instances/instance'; + // const templateName = 'your_template_name'; + + const compute = require('@google-cloud/compute'); + + // Create a new instance template based on an existing instance. + // This new template specifies a different boot disk. + async function createTemplateFromInstance() { + const instanceTemplatesClient = new compute.InstanceTemplatesClient(); + + const [response] = await instanceTemplatesClient.insert({ + project: projectId, + instanceTemplateResource: { + name: templateName, + sourceInstance: instance, + sourceInstanceParams: { + diskConfigs: [ + { + // Device name must match the name of a disk attached to the instance + // your template is based on. + deviceName: 'disk-1', + // Replace the original boot disk image used in your instance with a Rocky Linux image. + instantiateFrom: 'CUSTOM_IMAGE', + customImage: + 'projects/rocky-linux-cloud/global/images/family/rocky-linux-8', + // Override the auto_delete setting. + autoDelete: true, + }, + ], + }, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Instance template created.'); + } + + createTemplateFromInstance(); + // [END compute_template_create_from_instance] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/create-instance-templates/createTemplateWithSubnet.js b/compute/create-instance-templates/createTemplateWithSubnet.js new file mode 100644 index 0000000000..9fa674ef4d --- /dev/null +++ b/compute/create-instance-templates/createTemplateWithSubnet.js @@ -0,0 +1,95 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates an instance template that uses a provided subnet. + * + * @param {string} projectId - Project ID or project number of the Cloud project you use. + * @param {string} network - The network to be used in the new template. This value uses + * the following format: "projects/{project}/global/networks/{network}" + * @param {string} subnetwork - The subnetwork to be used in the new template. This value + * uses the following format: "projects/{project}/regions/{region}/subnetworks/{subnetwork}" + * @param {string} templateName - Name of the new template to create. + */ +function main(projectId, network, subnetwork, templateName) { + // [START compute_template_create_with_subnet] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const network = 'projects/project/global/networks/network'; + // const subnetwork = 'projects/project/regions/region/subnetworks/subnetwork'; + // const templateName = 'your_template_name'; + + const compute = require('@google-cloud/compute'); + + // Create an instance template that uses a provided subnet. + async function createTemplateWithSubnet() { + const instanceTemplatesClient = new compute.InstanceTemplatesClient(); + + const [response] = await instanceTemplatesClient.insert({ + project: projectId, + instanceTemplateResource: { + name: templateName, + properties: { + // The template describes the size and source image of the boot disk + // to attach to the instance. + disks: [ + { + // The template describes the size and source image of the boot disk + // to attach to the instance. + initializeParams: { + diskSizeGb: '250', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-11', + }, + autoDelete: true, + boot: true, + }, + ], + machineType: 'e2-standard-4', + // The template connects the instance to the specified network and subnetwork. + networkInterfaces: [ + { + network, + subnetwork, + }, + ], + }, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Instance template created.'); + } + + createTemplateWithSubnet(); + // [END compute_template_create_with_subnet] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/create-instance-templates/deleteInstanceTemplate.js b/compute/create-instance-templates/deleteInstanceTemplate.js new file mode 100644 index 0000000000..781141daa3 --- /dev/null +++ b/compute/create-instance-templates/deleteInstanceTemplate.js @@ -0,0 +1,62 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Deletes an instance template. + * + * @param {string} projectId - Project ID or project number of the Cloud project you use. + * @param {string} templateName - Name of the template to delete. + */ +function main(projectId, templateName) { + // [START compute_template_delete] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const templateName = 'your_template_name'; + + const compute = require('@google-cloud/compute'); + + // Delete an instance template. + async function deleteInstanceTemplate() { + const instanceTemplatesClient = new compute.InstanceTemplatesClient(); + + const [response] = await instanceTemplatesClient.delete({ + project: projectId, + instanceTemplate: templateName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Instance template deleted.'); + } + + deleteInstanceTemplate(); + // [END compute_template_delete] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/create-instance-templates/getInstanceTemplate.js b/compute/create-instance-templates/getInstanceTemplate.js new file mode 100644 index 0000000000..fcfd934f30 --- /dev/null +++ b/compute/create-instance-templates/getInstanceTemplate.js @@ -0,0 +1,54 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Retrieves an instance template, which you can use to create virtual machine + * (VM) instances and managed instance groups (MIGs). + * + * @param {string} projectId - Project ID or project number of the Cloud project you use. + * @param {string} templateName - Name of the template to retrieve. + */ +function main(projectId, templateName) { + // [START compute_template_get] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const templateName = 'your_template_name'; + + const compute = require('@google-cloud/compute'); + + // Retrieve an instance template, which you can use to create + // virtual machine (VM) instances and managed instance groups (MIGs). + async function getInstanceTemplate() { + const instanceTemplatesClient = new compute.InstanceTemplatesClient(); + + const [instance] = await instanceTemplatesClient.get({ + project: projectId, + instanceTemplate: templateName, + }); + + console.log('Instance template:', instance); + } + + getInstanceTemplate(); + // [END compute_template_get] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/create-instance-templates/listInstanceTemplates.js b/compute/create-instance-templates/listInstanceTemplates.js new file mode 100644 index 0000000000..d05490ca14 --- /dev/null +++ b/compute/create-instance-templates/listInstanceTemplates.js @@ -0,0 +1,51 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Prints a list of instance template objects available in a project. + * + * @param {string} projectId - Project ID or project number of the Cloud project you use. + */ +function main(projectId) { + // [START compute_template_list] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + + const compute = require('@google-cloud/compute'); + + // Print a list of instance template objects available in a project. + async function listInstanceTemplates() { + const instanceTemplatesClient = new compute.InstanceTemplatesClient(); + + const instanceTemplates = instanceTemplatesClient.listAsync({ + project: projectId, + }); + + for await (const instanceTemplate of instanceTemplates) { + console.log(` - ${instanceTemplate.name}`); + } + } + + listInstanceTemplates(); + // [END compute_template_list] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/createInstance.js b/compute/createInstance.js new file mode 100644 index 0000000000..a2c4841eb3 --- /dev/null +++ b/compute/createInstance.js @@ -0,0 +1,107 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Sends an instance creation request to GCP and waits for it to complete. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b + * @param {string} instanceName - Name of the new machine. + * @param {string} machineType - Machine type you want to create in following format: + * "zones/{zone}/machineTypes/{type_name}". For example: + * "zones/europe-west3-c/machineTypes/f1-micro" + * You can find the list of available machine types using: + * https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list + * @param {string} sourceImage - Path the the disk image you want to use for your boot + * disk. This can be one of the public images + * (e.g. "projects/debian-cloud/global/images/family/debian-10") + * or a private image you have access to. + * You can check the list of available public images using: + * $ gcloud compute images list + * @param {string} networkName - Name of the network you want the new instance to use. + * For example: global/networks/default - if you want to use the default network. + */ +function main( + projectId, + zone, + instanceName, + machineType = 'n1-standard-1', + sourceImage = 'projects/debian-cloud/global/images/family/debian-10', + networkName = 'global/networks/default' +) { + // [START compute_instances_create] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + // const machineType = 'n1-standard-1'; + // const sourceImage = 'projects/debian-cloud/global/images/family/debian-10'; + // const networkName = 'global/networks/default'; + + const compute = require('@google-cloud/compute'); + + // Create a new instance with the values provided above in the specified project and zone. + async function createInstance() { + const instancesClient = new compute.InstancesClient(); + + console.log(`Creating the ${instanceName} instance in ${zone}...`); + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + // Describe the size and source image of the boot disk to attach to the instance. + initializeParams: { + diskSizeGb: '10', + sourceImage, + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/${machineType}`, + networkInterfaces: [ + { + // Use the network interface provided in the networkName argument. + name: networkName, + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstance(); + // [END compute_instances_create] +} + +main(...process.argv.slice(2)); diff --git a/compute/createInstanceFromTemplate.js b/compute/createInstanceFromTemplate.js new file mode 100644 index 0000000000..915aaa8717 --- /dev/null +++ b/compute/createInstanceFromTemplate.js @@ -0,0 +1,74 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Creates a Compute Engine VM instance from an instance template. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b + * @param {string} instanceName - Name of the new instance. + * @param {string} instanceTemplateUrl - URL of the instance template using for creating the new instance. It can be a full or partial URL. + * Examples: + * - https://www.googleapis.com/compute/v1/projects/project/global/instanceTemplates/example-instance-template + * - projects/project/global/instanceTemplates/example-instance-template + * - global/instanceTemplates/example-instance-template + */ +function main(projectId, zone, instanceName, instanceTemplateUrl) { + // [START compute_instances_create_from_template] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + // const instanceTemplateUrl = 'YOUR_INSTANCE_TEMPLATE_URL' + + const compute = require('@google-cloud/compute'); + + // Create a new instance from template in the specified project and zone. + async function createInstanceFromTemplate() { + const instancesClient = new compute.InstancesClient(); + + console.log( + `Creating the ${instanceName} instance in ${zone} from template ${instanceTemplateUrl}...` + ); + + const [response] = await instancesClient.insert({ + project: projectId, + zone, + instanceResource: { + name: instanceName, + }, + sourceInstanceTemplate: instanceTemplateUrl, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstanceFromTemplate(); + // [END compute_instances_create_from_template] +} + +main(...process.argv.slice(2)); diff --git a/compute/createInstanceFromTemplateWithOverrides.js b/compute/createInstanceFromTemplateWithOverrides.js new file mode 100644 index 0000000000..f4003c7717 --- /dev/null +++ b/compute/createInstanceFromTemplateWithOverrides.js @@ -0,0 +1,124 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Creates a Compute Engine VM instance from an instance template, but overrides the disk and machine type options in the template. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b + * @param {string} instanceName - Name of the new instance. + * @param {string} instanceTemplateName - Name of the instance template to use when creating the new instance. + * @param {string} machineType - Machine type you want to set in following format: + * "zones/{zone}/machineTypes/{type_name}". For example: + * "zones/europe-west3-c/machineTypes/f1-micro" + * You can find the list of available machine types using: + * https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list + * @param {string} newDiskSourceImage - Path the the disk image you want to use for your new + * disk. This can be one of the public images + * (like "projects/debian-cloud/global/images/family/debian-10") + * or a private image you have access to. + * For a list of available public images, see the documentation: + * http://cloud.google.com/compute/docs/images + */ +function main( + projectId, + zone, + instanceName, + instanceTemplateName, + machineType = 'n1-standard-2', + newDiskSourceImage = 'projects/debian-cloud/global/images/family/debian-10' +) { + // [START compute_instances_create_from_template_with_overrides] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const instanceTemplateName = 'YOUR_INSTANCE_TEMPLATE_NAME'; + // const machineType = 'n1-standard-1'; + // const newDiskSourceImage = 'projects/debian-cloud/global/images/family/debian-10'; + + const compute = require('@google-cloud/compute'); + + // Creates a new instance in the specified project and zone using a selected template, + // but overrides the disk and machine type options in the template. + async function createInstanceFromTemplateWithOverrides() { + const instancesClient = new compute.InstancesClient(); + const instanceTemplatesClient = new compute.InstanceTemplatesClient(); + + console.log( + `Creating the ${instanceName} instance in ${zone} from template ${instanceTemplateName}...` + ); + + // Retrieve an instance template by name. + const [instanceTemplate] = await instanceTemplatesClient.get({ + project: projectId, + instanceTemplate: instanceTemplateName, + }); + + // Adjust diskType field of the instance template to use the URL formatting required by instances.insert.diskType + // For instance template, there is only a name, not URL. + for (const disk of instanceTemplate.properties.disks) { + if (disk.initializeParams.diskType) { + disk.initializeParams.diskType = `zones/${zone}/diskTypes/${disk.initializeParams.diskType}`; + } + } + + const [response] = await instancesClient.insert({ + project: projectId, + zone, + instanceResource: { + name: instanceName, + machineType: `zones/${zone}/machineTypes/${machineType}`, + disks: [ + // If you override a repeated field, all repeated values + // for that property are replaced with the + // corresponding values provided in the request. + // When adding a new disk to existing disks, + // insert all existing disks as well. + ...instanceTemplate.properties.disks, + { + initializeParams: { + diskSizeGb: '10', + sourceImage: newDiskSourceImage, + }, + autoDelete: true, + boot: false, + type: 'PERSISTENT', + }, + ], + }, + sourceInstanceTemplate: instanceTemplate.selfLink, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstanceFromTemplateWithOverrides(); + // [END compute_instances_create_from_template_with_overrides] +} + +main(...process.argv.slice(2)); diff --git a/compute/custom-hostname-instance/createInstanceWithCustomHostname.js b/compute/custom-hostname-instance/createInstanceWithCustomHostname.js new file mode 100644 index 0000000000..35342660ba --- /dev/null +++ b/compute/custom-hostname-instance/createInstanceWithCustomHostname.js @@ -0,0 +1,122 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Creates an instance with custom hostname. + * + * @param {string} projectId - ID of the project in which you want to create the VM instance. + * @param {string} zone - Name of the zone where you want to create the VM in, for example: us-west3-b. + * @param {string} instanceName - Name of the new VM instance. + * @param {string} hostname - Custom hostname of the new VM instance. + * Custom hostnames must conform to RFC 1035 requirements for valid hostnames. + * @param {string} machineType - Machine type for the VM instance specified in the following format: + * "zones/{zone}/machineTypes/{type_name}". For example: + * "zones/europe-west3-c/machineTypes/f1-micro" + * You can find the list of available machine types by using this gcloud command: + * $ gcloud compute machine-types list + * @param {string} sourceImage - Path of the disk image you want to use for your boot + * disk. This image can be one of the public images + * (for example, "projects/...) + * or a private image you have access to. + * You can check the list of available public images using: + * $ gcloud compute images list + * @param {string} networkName - Name of the network you want the new instance to use. + * For example: global/networks/default - if you want to use the default network. + */ +function main( + projectId, + zone, + instanceName, + hostname, + machineType = 'n1-standard-1', + sourceImage = 'projects/debian-cloud/global/images/family/debian-10', + networkName = 'global/networks/default' +) { + // [START compute_instances_create_custom_hostname] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + // const hostname = 'host.example.com' + // const machineType = 'n1-standard-1'; + // const sourceImage = 'projects/debian-cloud/global/images/family/debian-10'; + // const networkName = 'global/networks/default'; + + const compute = require('@google-cloud/compute'); + + // Create a new instance with the values provided above in the specified project and zone. + async function createInstanceWithCustomHostname() { + const instancesClient = new compute.InstancesClient(); + + console.log( + `Creating the ${instanceName} instance in ${zone} with hostname ${hostname}...` + ); + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + // Custom hostnames are not resolved by the automatically created records + // provided by Compute Engine internal DNS. + // You must manually configure the DNS record for your custom hostname. + hostname, + disks: [ + { + // Describe the size and source image of the boot disk to attach to the instance. + initializeParams: { + diskSizeGb: '10', + sourceImage, + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/${machineType}`, + networkInterfaces: [ + { + // Use the network interface provided in the networkName argument. + name: networkName, + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstanceWithCustomHostname(); + // [END compute_instances_create_custom_hostname] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/custom-hostname-instance/getInstanceHostname.js b/compute/custom-hostname-instance/getInstanceHostname.js new file mode 100644 index 0000000000..561d8b2518 --- /dev/null +++ b/compute/custom-hostname-instance/getInstanceHostname.js @@ -0,0 +1,54 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Prints the hostname of the Google Cloud VM instance. + * + * @param {string} projectId - ID of the project in which you want to get the VM instance hostname. + * @param {string} zone - Name of the zone your instance belongs to, for example: us-west3-b. + * @param {string} instanceName - Name of the new VM instance. + */ +function main(projectId, zone, instanceName) { + // [START compute_instances_get_hostname] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + + const compute = require('@google-cloud/compute'); + + async function getInstanceHostname() { + const instancesClient = new compute.InstancesClient(); + + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + // If a custom hostname is not set, the output for instance.hostname will be undefined + console.log(`Instance ${instanceName} has hostname: ${instance.hostname}`); + } + + getInstanceHostname(); + // [END compute_instances_get_hostname] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/deleteInstance.js b/compute/deleteInstance.js new file mode 100644 index 0000000000..2c604dd3a1 --- /dev/null +++ b/compute/deleteInstance.js @@ -0,0 +1,63 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Sends a delete request to GCP and waits for it to complete. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b + * @param {string} instanceName - Name of the instance you want to delete. + */ +function main(projectId, zone, instanceName) { + // [START compute_instances_delete] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME'; + + const compute = require('@google-cloud/compute'); + + // Delete the instance specified by `instanceName` if it's present in the given project and zone. + async function deleteInstance() { + const instancesClient = new compute.InstancesClient(); + + console.log(`Deleting ${instanceName} from ${zone}...`); + + const [response] = await instancesClient.delete({ + project: projectId, + zone, + instance: instanceName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the delete operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance deleted.'); + } + + deleteInstance(); + // [END compute_instances_delete] +} + +main(...process.argv.slice(2)); diff --git a/compute/disks/createDiskFromSnapshot.js b/compute/disks/createDiskFromSnapshot.js new file mode 100644 index 0000000000..7ebd365b20 --- /dev/null +++ b/compute/disks/createDiskFromSnapshot.js @@ -0,0 +1,83 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new disk in a project in given zone. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone to create the instance in. For example: "us-west3-b". + * @param {string} diskName - Name of the disk you want to create. + * @param {string} diskType - The type of disk you want to create. This value uses the following format: + * "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)". + * For example: "zones/us-west3-b/diskTypes/pd-ssd". + * @param {int} diskSizeGb - Size of the new disk in gigabytes. + * @param {string} snapshotLink - A link to the snapshot you want to use as a source for the new disk. + * This value uses the following format: "projects/{project_name}/global/snapshots/{snapshot_name}" + */ + +function main(projectId, zone, diskName, diskType, diskSizeGb, snapshotLink) { + // [START compute_disk_create_from_snapshot] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const diskName = 'YOUR_DISK_NAME'; + // const diskType = 'zones/us-west3-b/diskTypes/pd-ssd'; + // const diskSizeGb = 10; + // const snapshotLink = 'projects/project_name/global/snapshots/snapshot_name'; + + const compute = require('@google-cloud/compute'); + + async function createDiskFromSnapshot() { + const disksClient = new compute.DisksClient(); + + const [response] = await disksClient.insert({ + project: projectId, + zone, + diskResource: { + sizeGb: diskSizeGb, + name: diskName, + zone, + type: diskType, + sourceSnapshot: snapshotLink, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create disk operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Disk created.'); + } + + createDiskFromSnapshot(); + // [END compute_disk_create_from_snapshot] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[4] = parseInt(args[4]); +main(...args); diff --git a/compute/disks/deleteDisk.js b/compute/disks/deleteDisk.js new file mode 100644 index 0000000000..1233f142f6 --- /dev/null +++ b/compute/disks/deleteDisk.js @@ -0,0 +1,65 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Deletes a disk from a project. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone in which is the disk you want to delete. + * @param {string} diskName - Name of the disk you want to delete. + */ +function main(projectId, zone, diskName) { + // [START compute_disk_delete] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const diskName = 'YOUR_DISK_NAME'; + + const compute = require('@google-cloud/compute'); + + async function deleteDisk() { + const disksClient = new compute.DisksClient(); + + const [response] = await disksClient.delete({ + project: projectId, + zone, + disk: diskName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create disk operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Disk deleted.'); + } + + deleteDisk(); + // [END compute_disk_delete] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/disks/setDiskAutodelete.js b/compute/disks/setDiskAutodelete.js new file mode 100644 index 0000000000..b7b331e0ed --- /dev/null +++ b/compute/disks/setDiskAutodelete.js @@ -0,0 +1,85 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Sets the autodelete flag of a disk to given value. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone in which is the disk you want to modify. + * @param {string} instanceName - Name of the instance the disk is attached to. + * @param {string} diskName - The name of the disk which flag you want to modify. + * @param {boolean} autoDelete - The new value of the autodelete flag. + */ +function main(projectId, zone, instanceName, diskName, autoDelete) { + // [START compute_disk_autodelete_change] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const diskName = 'YOUR_DISK_NAME'; + // const autoDelete = true; + + const compute = require('@google-cloud/compute'); + + async function setDiskAutodelete() { + const instancesClient = new compute.InstancesClient(); + + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + if (!instance.disks.some(disk => disk.deviceName === diskName)) { + throw new Error( + `Instance ${instanceName} doesn't have a disk named ${diskName} attached.` + ); + } + + const [response] = await instancesClient.setDiskAutoDelete({ + project: projectId, + zone, + instance: instanceName, + deviceName: diskName, + autoDelete, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the update instance operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Disk autoDelete field updated.'); + } + + setDiskAutodelete(); + // [END compute_disk_autodelete_change] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[4] = args[4] === 'true'; +main(...args); diff --git a/compute/firewall/createFirewallRule.js b/compute/firewall/createFirewallRule.js new file mode 100644 index 0000000000..ec4ed2481b --- /dev/null +++ b/compute/firewall/createFirewallRule.js @@ -0,0 +1,92 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Creates a simple firewall rule allowing for incoming HTTP and HTTPS access from the entire Internet. + * + * @param {string} projectId - project ID or project number of the Cloud project you want to use. + * @param {string} firewallRuleName - name of the rule that is created. + * @param {string} network - name of the network the rule will be applied to. Available name formats: + * https://www.googleapis.com/compute/v1/projects/{project_id}/global/networks/{network} + * projects/{project_id}/global/networks/{network} + * global/networks/{network} + */ +function main( + projectId, + firewallRuleName, + networkName = 'global/networks/default' +) { + // [START compute_firewall_create] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const firewallRuleName = 'YOUR_FIREWALL_RULE_NAME' + // const networkName = 'global/networks/default' + + const compute = require('@google-cloud/compute'); + const computeProtos = compute.protos.google.cloud.compute.v1; + + async function createFirewallRule() { + const firewallsClient = new compute.FirewallsClient(); + const operationsClient = new compute.GlobalOperationsClient(); + + const firewallRule = new computeProtos.Firewall(); + firewallRule.name = firewallRuleName; + firewallRule.direction = 'INGRESS'; + firewallRule.allowed = [ + { + IPProtocol: 'tcp', + ports: ['80', '443'], + }, + ]; + firewallRule.targetTags = ['web']; + firewallRule.network = networkName; + firewallRule.description = + 'Allowing TCP traffic on port 80 and 443 from Internet.'; + + // Note that the default value of priority for the firewall API is 1000. + // If you check the value of `firewallRule.priority` at this point it + // will be equal to null, however it is not treated as "set" by the library and thus + // the default will be applied to the new rule. If you want to create a rule that + // has priority == 0, you need to explicitly set it so: + + // firewallRule.priority = 0 + + const [response] = await firewallsClient.insert({ + project: projectId, + firewallResource: firewallRule, + }); + let operation = response.latestResponse; + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Firewall rule created'); + } + + createFirewallRule(); + // [END compute_firewall_create] +} + +main(...process.argv.slice(2)); +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); diff --git a/compute/firewall/deleteFirewallRule.js b/compute/firewall/deleteFirewallRule.js new file mode 100644 index 0000000000..91e3fbdd66 --- /dev/null +++ b/compute/firewall/deleteFirewallRule.js @@ -0,0 +1,60 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Deletes a firewall rule from the project. + * + * @param {string} projectId - project ID or project number of the Cloud project you want to use. + * @param {string} firewallRuleName - name of the rule you want to modify. + */ +function main(projectId, firewallRuleName) { + // [START compute_firewall_delete] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const firewallRuleName = 'FIREWALL_RULE_NAME'; + + const compute = require('@google-cloud/compute'); + + async function deleteFirewallRule() { + const firewallsClient = new compute.FirewallsClient(); + const operationsClient = new compute.GlobalOperationsClient(); + + const [response] = await firewallsClient.delete({ + project: projectId, + firewall: firewallRuleName, + }); + let operation = response.latestResponse; + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Firewall rule deleted'); + } + + deleteFirewallRule(); + // [END compute_firewall_delete] +} + +main(...process.argv.slice(2)); +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); diff --git a/compute/firewall/listFirewallRules.js b/compute/firewall/listFirewallRules.js new file mode 100644 index 0000000000..ae78d10777 --- /dev/null +++ b/compute/firewall/listFirewallRules.js @@ -0,0 +1,49 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Prints a list of all firewall rules in specified project. + * + * @param {string} projectId - project ID or project number of the Cloud project you want to use. + */ +function main(projectId) { + // [START compute_firewall_list] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + + const compute = require('@google-cloud/compute'); + + async function listFirewallRules() { + const firewallsClient = new compute.FirewallsClient(); + + const [firewallRules] = await firewallsClient.list({ + project: projectId, + }); + + for (const rule of firewallRules) { + console.log(` - ${rule.name}: ${rule.description}`); + } + } + + listFirewallRules(); + // [END compute_firewall_list] +} + +main(...process.argv.slice(2)); +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); diff --git a/compute/firewall/patchFirewallPriority.js b/compute/firewall/patchFirewallPriority.js new file mode 100644 index 0000000000..f2afa089e3 --- /dev/null +++ b/compute/firewall/patchFirewallPriority.js @@ -0,0 +1,69 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Modifies the priority of a given firewall rule. + * + * @param {string} projectId - project ID or project number of the Cloud project you want to use. + * @param {string} firewallRuleName - name of the rule you want to modify. + * @param {number} priority - the new priority to be set for the rule. + */ +function main(projectId, firewallRuleName, priority = 10) { + // [START compute_firewall_patch] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const firewallRuleName = 'FIREWALL_RULE_NAME'; + // const priority = 10; + + const compute = require('@google-cloud/compute'); + const computeProtos = compute.protos.google.cloud.compute.v1; + + async function patchFirewallPriority() { + const firewallsClient = new compute.FirewallsClient(); + const operationsClient = new compute.GlobalOperationsClient(); + + const firewallRule = new computeProtos.Firewall(); + firewallRule.priority = priority; + + // The patch operation doesn't require the full definition of a Firewall object. It will only update + // the values that were set in it, in this case it will only change the priority. + const [response] = await firewallsClient.patch({ + project: projectId, + firewall: firewallRuleName, + firewallResource: firewallRule, + }); + let operation = response.latestResponse; + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Firewall rule updated'); + } + + patchFirewallPriority(); + // [END compute_firewall_patch] +} + +main(...process.argv.slice(2)); +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); diff --git a/compute/getInstance.js b/compute/getInstance.js new file mode 100644 index 0000000000..5cb86db507 --- /dev/null +++ b/compute/getInstance.js @@ -0,0 +1,55 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Prints information about a VM instance in the given zone in the specified project. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone you want to use. For example: 'us-west3-b'. + * @param {string} instanceName - Name of the VM instance you want to query. + */ +function main(projectId, zone, instanceName) { + // [START compute_instances_get] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + + const compute = require('@google-cloud/compute'); + + async function getInstance() { + const instancesClient = new compute.InstancesClient(); + + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + console.log( + `Instance ${instanceName} data:\n${JSON.stringify(instance, null, 4)}` + ); + } + getInstance(); + // [END compute_instances_get] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/getUsageExportBucket.js b/compute/getUsageExportBucket.js new file mode 100644 index 0000000000..2dac7a7782 --- /dev/null +++ b/compute/getUsageExportBucket.js @@ -0,0 +1,64 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Retrieve Compute Engine usage export bucket for the Cloud project. + * Replaces the empty value returned by the API with the default value used to generate report file names. + * + * @param {string} projectId - ID or number of the project you want to use. + */ +function main(projectId) { + // [START compute_usage_report_get] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + + const compute = require('@google-cloud/compute'); + + async function getUsageExportBucket() { + // Get the usage export location for the project from the server. + const projectsClient = new compute.ProjectsClient(); + const [project] = await projectsClient.get({ + project: projectId, + }); + + const usageExportLocation = project.usageExportLocation; + + if (!usageExportLocation || !usageExportLocation.bucketName) { + // The usage reports are disabled. + return; + } + + if (!usageExportLocation.reportNamePrefix) { + // Although the server explicitly sent the empty string value, + // the next usage report generated with these settings still has the default prefix value `usage_gce`. + // (see https://cloud.google.com/compute/docs/reference/rest/v1/projects/get) + console.log( + 'Report name prefix not set, replacing with default value of `usage_gce`.' + ); + usageExportLocation.reportNamePrefix = 'usage_gce'; + } + + console.log( + 'Returned reportNamePrefix:', + usageExportLocation.reportNamePrefix + ); + } + + getUsageExportBucket(); + // [END compute_usage_report_get] +} + +main(...process.argv.slice(2)); diff --git a/compute/instances/create-start-instance/createInstanceFromCustomImage.js b/compute/instances/create-start-instance/createInstanceFromCustomImage.js new file mode 100644 index 0000000000..777ebd73f1 --- /dev/null +++ b/compute/instances/create-start-instance/createInstanceFromCustomImage.js @@ -0,0 +1,89 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new VM instance with custom image used as its boot disk. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone to create the instance in. For example: "us-west3-b" + * @param {string} instanceName - Name of the new virtual machine (VM) instance. + * @param {string} customImageLink - Link to the custom image you want to use in the form of: + * "projects/{project_name}/global/images/{image_name}" + */ +function main(projectId, zone, instanceName, customImageLink) { + // [START compute_instances_create_from_custom_image] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const customImageLink = 'projects/YOUR_PROJECT/global/images/YOUR_IMAGE_NAME'; + + const compute = require('@google-cloud/compute'); + + // Creates a new VM instance with custom image used as its boot disk. + async function createInstanceFromCustomImage() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.insert({ + project: projectId, + zone, + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '10', + sourceImage: customImageLink, + diskType: `zones/${zone}/diskTypes/pd-standard`, + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstanceFromCustomImage(); + // [END compute_instances_create_from_custom_image] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/create-start-instance/createInstanceFromSnapshot.js b/compute/instances/create-start-instance/createInstanceFromSnapshot.js new file mode 100644 index 0000000000..c22a917b2a --- /dev/null +++ b/compute/instances/create-start-instance/createInstanceFromSnapshot.js @@ -0,0 +1,89 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new VM instance with boot disk created from a snapshot. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone to create the instance in. For example: "us-west3-b" + * @param {string} instanceName - Name of the new virtual machine (VM) instance. + * @param {string} snapshotLink - link to the snapshot you want to use as the source of your + * data disk in the form of: "projects/{project_name}/global/snapshots/{snapshot_name}" + */ +function main(projectId, zone, instanceName, snapshotLink) { + // [START compute_instances_create_from_snapshot] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const snapshotLink = 'projects/YOUR_PROJECT/global/snapshots/YOUR_SNAPSHOT_NAME'; + + const compute = require('@google-cloud/compute'); + + // Creates a new VM instance with boot disk created from a snapshot. + async function createInstanceFromSnapshot() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.insert({ + project: projectId, + zone, + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '11', + sourceSnapshot: snapshotLink, + diskType: `zones/${zone}/diskTypes/pd-standard`, + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstanceFromSnapshot(); + // [END compute_instances_create_from_snapshot] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/create-start-instance/createInstanceWithAdditionalDisk.js b/compute/instances/create-start-instance/createInstanceWithAdditionalDisk.js new file mode 100644 index 0000000000..abbc540421 --- /dev/null +++ b/compute/instances/create-start-instance/createInstanceWithAdditionalDisk.js @@ -0,0 +1,102 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Create a new VM instance with Debian 10 operating system and a 11 GB additional empty disk. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone to create the instance in. For example: "us-west3-b" + * @param {string} instanceName - Name of the new virtual machine (VM) instance. + */ +function main(projectId, zone, instanceName) { + // [START compute_instances_create_from_image_plus_empty_disk] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + + const compute = require('@google-cloud/compute'); + + // Create a new VM instance with Debian 10 operating system and a 11 GB additional empty disk. + async function createWithAdditionalDisk() { + const instancesClient = new compute.InstancesClient(); + const imagesClient = new compute.ImagesClient(); + + // List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details. + const [newestDebian] = await imagesClient.getFromFamily({ + project: 'debian-cloud', + family: 'debian-10', + }); + + const [response] = await instancesClient.insert({ + project: projectId, + zone, + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '10', + sourceImage: newestDebian.selfLink, + diskType: `zones/${zone}/diskTypes/pd-standard`, + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + { + initializeParams: { + diskSizeGb: '11', + diskType: `zones/${zone}/diskTypes/pd-standard`, + }, + autoDelete: true, + boot: false, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createWithAdditionalDisk(); + // [END compute_instances_create_from_image_plus_empty_disk] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/create-start-instance/createInstanceWithExistingDisks.js b/compute/instances/create-start-instance/createInstanceWithExistingDisks.js new file mode 100644 index 0000000000..e0daeded1b --- /dev/null +++ b/compute/instances/create-start-instance/createInstanceWithExistingDisks.js @@ -0,0 +1,104 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Create a new VM instance using selected disks. The first disk in diskNames will be used as boot disk. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone to create the instance in. For example: "us-west3-b" + * @param {string} instanceName - Name of the new virtual machine (VM) instance. + * @param {Array} diskNames - Array of disk names to be attached to the new virtual machine. + * First disk in this list will be used as the boot device. + */ +function main(projectId, zone, instanceName, diskNames) { + // [START compute_instances_create_with_existing_disks] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const diskNames = ['boot_disk', 'disk1', 'disk2']; + + const compute = require('@google-cloud/compute'); + + async function createWithExistingDisks() { + const instancesClient = new compute.InstancesClient(); + const disksClient = new compute.DisksClient(); + + if (diskNames.length < 1) { + throw new Error('At least one disk should be provided'); + } + + const disks = []; + for (const diskName of diskNames) { + const [disk] = await disksClient.get({ + project: projectId, + zone, + disk: diskName, + }); + disks.push(disk); + } + + const attachedDisks = []; + + for (const disk of disks) { + attachedDisks.push({ + source: disk.selfLink, + }); + } + + attachedDisks[0].boot = true; + + const [response] = await instancesClient.insert({ + project: projectId, + zone, + instanceResource: { + name: instanceName, + disks: attachedDisks, + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createWithExistingDisks(); + // [END compute_instances_create_with_existing_disks] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[3] = args[3].split(','); +main(...args); diff --git a/compute/instances/create-start-instance/createInstanceWithSubnet.js b/compute/instances/create-start-instance/createInstanceWithSubnet.js new file mode 100644 index 0000000000..2c16e86813 --- /dev/null +++ b/compute/instances/create-start-instance/createInstanceWithSubnet.js @@ -0,0 +1,108 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new VM instance with Debian 10 operating system in specified network and subnetwork. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone to create the instance in. For example: "us-west3-b". + * @param {string} instanceName - Name of the new virtual machine (VM) instance. + * @param {string} networkLink - Name of the network you want the new instance to use. + * For example: "global/networks/default" represents the network + * named "default", which is created automatically for each project. + * @param {string} subnetworkLink - Name of the subnetwork you want the new instance to use. + * This value uses the following format: + * "regions/{region}/subnetworks/{subnetwork_name}" + */ +function main( + projectId, + zone, + instanceName, + networkLink = 'global/networks/default', + subnetworkLink = 'regions/europe-central2/subnetworks/default' +) { + // [START compute_instances_create_with_subnet] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const networkLink = 'global/networks/default'; + // const subnetworkLink = 'regions/europe-central2/subnetworks/default'; + + const compute = require('@google-cloud/compute'); + + // Creates a new VM instance with Debian 10 operating system in specified network and subnetwork. + async function createInstanceWithSubnet() { + const instancesClient = new compute.InstancesClient(); + const imagesClient = new compute.ImagesClient(); + + // List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details. + const [newestDebian] = await imagesClient.getFromFamily({ + project: 'debian-cloud', + family: 'debian-10', + }); + + const [response] = await instancesClient.insert({ + project: projectId, + zone, + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '10', + sourceImage: newestDebian.selfLink, + diskType: `zones/${zone}/diskTypes/pd-standard`, + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: networkLink, + subnetwork: subnetworkLink, + }, + ], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstanceWithSubnet(); + // [END compute_instances_create_with_subnet] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/custom-machine-type/createWithHelper.js b/compute/instances/custom-machine-type/createWithHelper.js new file mode 100644 index 0000000000..8bd49b6bd9 --- /dev/null +++ b/compute/instances/custom-machine-type/createWithHelper.js @@ -0,0 +1,256 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new VM instance with a custom machine type. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to use, for example: us-west3-b + * @param {string} instanceName - Name of the new machine. + * @param {string} cpuSeries - Type of CPU you want to use. + * @param {int} coreCount - Number of CPU cores you want to use. + * @param {int} memory - The amount of memory for the VM instance, in megabytes. + */ +function main(projectId, zone, instanceName, cpuSeries, coreCount, memory) { + // [START compute_custom_machine_type_create_with_helper] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const cpuSeries = 'N1'; + // const coreCount = 2 + // const memory = 256 + + const compute = require('@google-cloud/compute'); + + function range(from, to, step) { + return [...Array(Math.floor((to - from) / step) + 1)].map( + (_, i) => from + i * step + ); + } + + class CustomMachineType { + constructor(zone, cpuSeries, coreCount, memory) { + this.zone = zone; + this.cpuSeries = cpuSeries; + this.coreCount = coreCount; + this.memory = memory; + + this.N1 = 'custom'; + this.N2 = 'n2-custom'; + this.N2D = 'n2d-custom'; + this.E2 = 'e2-custom'; + this.E2Micro = 'e2-custom-micro'; + this.E2Small = 'e2-custom-small'; + this.E2Medium = 'e2-custom-medium'; + + this.CpuSeriesE2Limit = { + allowedCores: range(2, 33, 2), + minMemPerCore: 512, + maxMemPerCore: 8192, + allowExtraMemory: false, + extraMemoryLimit: 0, + }; + + this.CpuSeriesE2MicroLimit = { + allowedCores: [], + minMemPerCore: 1024, + maxMemPerCore: 2048, + allowExtraMemory: false, + extraMemoryLimit: 0, + }; + + this.CpuSeriesE2SmallLimit = { + allowedCores: [], + minMemPerCore: 2048, + maxMemPerCore: 4096, + allowExtraMemory: false, + extraMemoryLimit: 0, + }; + + this.CpuSeriesE2MediumLimit = { + allowedCores: [], + minMemPerCore: 4096, + maxMemPerCore: 8192, + allowExtraMemory: false, + extraMemoryLimit: 0, + }; + + this.CpuSeriesN2Limit = { + allowedCores: [...range(2, 33, 2), ...range(36, 129, 4)], + minMemPerCore: 512, + maxMemPerCore: 8192, + allowExtraMemory: true, + extraMemoryLimit: 624 << 10, + }; + + this.CpuSeriesN2DLimit = { + allowedCores: [2, 4, 8, 16, 32, 48, 64, 80, 96], + minMemPerCore: 512, + maxMemPerCore: 8192, + allowExtraMemory: true, + extraMemoryLimit: 768 << 10, + }; + + this.CpuSeriesN1Limit = { + allowedCores: [1, range(2, 97, 2)], + minMemPerCore: 922, + maxMemPerCore: 6656, + allowExtraMemory: true, + extraMemoryLimit: 624 << 10, + }; + + this.TYPE_LIMITS = { + [this.N1]: this.CpuSeriesN1Limit, + [this.N2]: this.CpuSeriesN2Limit, + [this.N2D]: this.CpuSeriesN2DLimit, + [this.E2]: this.CpuSeriesE2Limit, + [this.E2Micro]: this.CpuSeriesE2MicroLimit, + [this.E2Small]: this.CpuSeriesE2SmallLimit, + [this.E2Medium]: this.CpuSeriesE2MediumLimit, + }; + + if (![this.E2, this.N1, this.N2, this.N2D].includes(cpuSeries)) { + throw new Error(`Incorrect CPU type: ${this.cpuSeries}`); + } + + this.typeLimit = this.TYPE_LIMITS[this.cpuSeries]; + + // Check whether the requested parameters are allowed. + // Find more information about limitations of custom machine types at: + // https://cloud.google.com/compute/docs/general-purpose-machines#custom_machine_types + + // Check the number of cores + if ( + this.typeLimit.allowedCores.length > 0 && + !this.typeLimit.allowedCores.includes(coreCount) + ) { + throw new Error( + `Invalid number of cores requested. Allowed number of cores for ${this.cpuSeries} is: ${this.typeLimit.allowedCores}` + ); + } + + // Memory must be a multiple of 256 MB + if (this.memory % 256 !== 0) { + throw new Error('Requested memory must be a multiple of 256 MB'); + } + + // Check if the requested memory isn't too little + if (this.memory < this.coreCount * this.typeLimit.minMemPerCore) { + throw new Error( + `Requested memory is too low. Minimal memory for ${this.cpuSeries} is ${this.typeLimit.minMemPerCore} MB per core` + ); + } + + // Check if the requested memory isn't too much + if ( + this.memory > this.coreCount * this.typeLimit.maxMemPerCore && + !this.typeLimit.allowExtraMemory + ) { + throw new Error( + `Requested memory is too large.. Maximum memory allowed for ${this.cpuSeries} is ${this.typeLimit.maxMemPerCore} MB per core` + ); + } + + if ( + this.memory > this.typeLimit.extraMemoryLimit && + this.typeLimit.allowExtraMemory + ) { + throw new Error( + `Requested memory is too large.. Maximum memory allowed for ${this.cpuSeries} is ${this.typeLimit.extraMemoryLimit} MB` + ); + } + } + + // Returns the custom machine type in form of a string acceptable by Compute Engine API. + getMachineTypeURI() { + if ( + [this.E2Small, this.E2Micro, this.E2Medium].includes(this.cpuSeries) + ) { + return `zones/${this.zone}/machineTypes/${this.cpuSeries}-${this.memory}`; + } + + if (this.memory > this.coreCount * this.typeLimit.maxMemPerCore) { + return `zones/${this.zone}/machineTypes/${this.cpuSeries}-${this.coreCount}-${this.memory}-ext`; + } + + return `zones/${zone}/machineTypes/${this.cpuSeries}-${this.coreCount}-${this.memory}`; + } + } + + async function createInstanceWithCustomMachineTypeWithHelper() { + const instancesClient = new compute.InstancesClient(); + + const machineType = new CustomMachineType( + zone, + cpuSeries, + coreCount, + memory + ).getMachineTypeURI(); + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '64', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-11/', + }, + autoDelete: true, + boot: true, + }, + ], + machineType, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstanceWithCustomMachineTypeWithHelper(); + // [END compute_custom_machine_type_create_with_helper] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[4] = parseInt(args[4]); +args[5] = parseInt(args[5]); + +main(...args); diff --git a/compute/instances/custom-machine-type/createWithoutHelper.js b/compute/instances/custom-machine-type/createWithoutHelper.js new file mode 100644 index 0000000000..804b673230 --- /dev/null +++ b/compute/instances/custom-machine-type/createWithoutHelper.js @@ -0,0 +1,96 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates new VM instances without using a CustomMachineType class. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to use, for example: us-west3-b + * @param {string} instanceName - Name of the new machine. + * @param {string} cpuSeries - Type of CPU you want to use. + * @param {int} coreCount - Number of CPU cores you want to use. + * @param {int} memory - The amount of memory for the VM instance, in megabytes. + */ +function main(projectId, zone, instanceName, cpuSeries, coreCount, memory) { + // [START compute_custom_machine_type_create_without_helper] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const cpuSeries = 'N1'; + // const coreCount = 2 + // const memory = 256 + + const compute = require('@google-cloud/compute'); + + async function createWithoutHelper() { + const instancesClient = new compute.InstancesClient(); + + const machineType = `zones/${zone}/machineTypes/${cpuSeries}-${coreCount}-${memory}`; + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '64', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-11/', + }, + autoDelete: true, + boot: true, + }, + ], + machineType, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createWithoutHelper(); + // [END compute_custom_machine_type_create_without_helper] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[4] = parseInt(args[4]); +args[5] = parseInt(args[5]); + +main(...args); diff --git a/compute/instances/custom-machine-type/extraMemWithoutHelper.js b/compute/instances/custom-machine-type/extraMemWithoutHelper.js new file mode 100644 index 0000000000..2173b36cd6 --- /dev/null +++ b/compute/instances/custom-machine-type/extraMemWithoutHelper.js @@ -0,0 +1,98 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates new VM instances with extra memory without using a CustomMachineType class. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to use, for example: us-west3-b + * @param {string} instanceName - Name of the new machine. + * @param {string} cpuSeries - Type of CPU you want to use. + * @param {int} coreCount - Number of CPU cores you want to use. + * @param {int} memory - The amount of memory for the VM instance, in megabytes. + */ +function main(projectId, zone, instanceName, cpuSeries, coreCount, memory) { + // [START compute_custom_machine_type_extra_mem_no_helper] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const cpuSeries = 'N1'; + // const coreCount = 2 + // const memory = 256 + + // The coreCount and memory values are not validated anywhere and can be rejected by the API. + + const compute = require('@google-cloud/compute'); + + async function createInstanceWithExtraMemWithoutHelper() { + const instancesClient = new compute.InstancesClient(); + + const machineType = `zones/${zone}/machineTypes/${cpuSeries}-${coreCount}-${memory}-ext`; + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '64', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-11/', + }, + autoDelete: true, + boot: true, + }, + ], + machineType, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstanceWithExtraMemWithoutHelper(); + // [END compute_custom_machine_type_extra_mem_no_helper] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[4] = parseInt(args[4]); +args[5] = parseInt(args[5]); + +main(...args); diff --git a/compute/instances/custom-machine-type/helperClass.js b/compute/instances/custom-machine-type/helperClass.js new file mode 100644 index 0000000000..c483215407 --- /dev/null +++ b/compute/instances/custom-machine-type/helperClass.js @@ -0,0 +1,224 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates an instance of CustoMachineTypeClass. + * + * @param {string} zone - Name of the zone you want to use, for example: us-west3-b + * @param {string} cpuSeries - Type of CPU you want to use. + * @param {int} coreCount - Number of CPU cores you want to use. + * @param {int} memory - The amount of memory for the VM instance, in megabytes. + */ +function main(zone, cpuSeries, coreCount, memory) { + // [START compute_custom_machine_type_helper_class] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const cpuSeries = 'N1'; + // const coreCount = 2 + // const memory = 256 + + function range(from, to, step) { + return [...Array(Math.floor((to - from) / step) + 1)].map( + (_, i) => from + i * step + ); + } + + class CustomMachineType { + constructor(zone, cpuSeries, coreCount, memory) { + this.zone = zone; + this.cpuSeries = cpuSeries; + this.coreCount = coreCount; + this.memory = memory; + + this.N1 = 'custom'; + this.N2 = 'n2-custom'; + this.N2D = 'n2d-custom'; + this.E2 = 'e2-custom'; + this.E2Micro = 'e2-custom-micro'; + this.E2Small = 'e2-custom-small'; + this.E2Medium = 'e2-custom-medium'; + + this.CpuSeriesE2Limit = { + allowedCores: range(2, 33, 2), + minMemPerCore: 512, + maxMemPerCore: 8192, + allowExtraMemory: false, + extraMemoryLimit: 0, + }; + + this.CpuSeriesE2MicroLimit = { + allowedCores: [], + minMemPerCore: 1024, + maxMemPerCore: 2048, + allowExtraMemory: false, + extraMemoryLimit: 0, + }; + + this.CpuSeriesE2SmallLimit = { + allowedCores: [], + minMemPerCore: 2048, + maxMemPerCore: 4096, + allowExtraMemory: false, + extraMemoryLimit: 0, + }; + + this.CpuSeriesE2MediumLimit = { + allowedCores: [], + minMemPerCore: 4096, + maxMemPerCore: 8192, + allowExtraMemory: false, + extraMemoryLimit: 0, + }; + + this.CpuSeriesN2Limit = { + allowedCores: [...range(2, 33, 2), ...range(36, 129, 4)], + minMemPerCore: 512, + maxMemPerCore: 8192, + allowExtraMemory: true, + extraMemoryLimit: 624 << 10, + }; + + this.CpuSeriesN2DLimit = { + allowedCores: [2, 4, 8, 16, 32, 48, 64, 80, 96], + minMemPerCore: 512, + maxMemPerCore: 8192, + allowExtraMemory: true, + extraMemoryLimit: 768 << 10, + }; + + this.CpuSeriesN1Limit = { + allowedCores: [1, range(2, 97, 2)], + minMemPerCore: 922, + maxMemPerCore: 6656, + allowExtraMemory: true, + extraMemoryLimit: 624 << 10, + }; + + this.TYPE_LIMITS = { + [this.N1]: this.CpuSeriesN1Limit, + [this.N2]: this.CpuSeriesN2Limit, + [this.N2D]: this.CpuSeriesN2DLimit, + [this.E2]: this.CpuSeriesE2Limit, + [this.E2Micro]: this.CpuSeriesE2MicroLimit, + [this.E2Small]: this.CpuSeriesE2SmallLimit, + [this.E2Medium]: this.CpuSeriesE2MediumLimit, + }; + + this.typeLimit = this.TYPE_LIMITS[this.cpuSeries]; + } + + validate() { + // Check the number of cores + if ( + this.typeLimit.allowedCores.length > 0 && + !this.typeLimit.allowedCores.includes(this.coreCount) + ) { + throw new Error( + `Invalid number of cores requested. Allowed number of cores for ${this.cpuSeries} is: ${this.typeLimit.allowedCores}` + ); + } + + // Memory must be a multiple of 256 MB + if (this.memory % 256 !== 0) { + throw new Error('Requested memory must be a multiple of 256 MB'); + } + + // Check if the requested memory isn't too little + if (this.memory < this.coreCount * this.typeLimit.minMemPerCore) { + throw new Error( + `Requested memory is too low. Minimal memory for ${this.cpuSeries} is ${this.typeLimit.minMemPerCore} MB per core` + ); + } + + // Check if the requested memory isn't too much + if ( + this.memory > this.coreCount * this.typeLimit.maxMemPerCore && + !this.typeLimit.allowExtraMemory + ) { + throw new Error( + `Requested memory is too large.. Maximum memory allowed for ${this.cpuSeries} is ${this.typeLimit.maxMemPerCore} MB per core` + ); + } + + if ( + this.memory > this.typeLimit.extraMemoryLimit && + this.typeLimit.allowExtraMemory + ) { + throw new Error( + `Requested memory is too large.. Maximum memory allowed for ${this.cpuSeries} is ${this.typeLimit.extraMemoryLimit} MB` + ); + } + } + + // Returns the custom machine type in form of a string acceptable by Compute Engine API. + getMachineTypeURI() { + if ( + [this.E2Small, this.E2Micro, this.E2Medium].includes(this.cpuSeries) + ) { + return `zones/${this.zone}/machineTypes/${this.cpuSeries}-${this.memory}`; + } + + if (this.memory > this.coreCount * this.typeLimit.maxMemPerCore) { + return `zones/${this.zone}/machineTypes/${this.cpuSeries}-${coreCount}-${this.memory}-ext`; + } + + return `zones/${zone}/machineTypes/${this.cpuSeries}-${this.coreCount}-${this.memory}`; + } + + // Returns machine type in a format without the zone. For example, n2-custom-0-10240. + // This format is used to create instance templates. + getMachineType() { + return this.getMachineTypeURI().split('/').pop(); + } + } + + async function createCustomMachineType() { + if ( + [ + CustomMachineType.E2Small, + CustomMachineType.E2Micro, + CustomMachineType.E2Medium, + ].includes(cpuSeries) + ) { + coreCount = 2; + } + + const machineType = new CustomMachineType( + zone, + cpuSeries, + coreCount, + memory + ); + + console.log(`URI: ${machineType.getMachineTypeURI()}`); + console.log(`MachineType: ${machineType.getMachineType()}`); + } + + createCustomMachineType(); + // [END compute_custom_machine_type_helper_class] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[2] = parseInt(args[2]); +args[3] = parseInt(args[3]); + +main(...args); diff --git a/compute/instances/custom-machine-type/updateMemory.js b/compute/instances/custom-machine-type/updateMemory.js new file mode 100644 index 0000000000..1d680ef549 --- /dev/null +++ b/compute/instances/custom-machine-type/updateMemory.js @@ -0,0 +1,116 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Sends an update instance machine type request to the Compute Engine API and waits for it to complete. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to use, for example: us-west3-b + * @param {string} instanceName - Name of the new machine. + * @param {int} newMemory - The new amount of memory for the VM instance, in megabytes. + */ +function main(projectId, zone, instanceName, newMemory) { + // [START compute_custom_machine_type_update_memory] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const newMemory = 256; + + const compute = require('@google-cloud/compute'); + + async function modifyInstanceWithExtendedMemory() { + const instancesClient = new compute.InstancesClient(); + + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + if ( + !['machineTypes/n1-', 'machineTypes/n2-', 'machineTypes/n2d-'].some( + type => instance.machineType.includes(type) + ) + ) { + throw new Error('extra memory is available only for N1, N2 and N2D CPUs'); + } + + // Make sure that the machine is turned off + if (!['TERMINATED', 'STOPPED'].some(status => instance.status === status)) { + const [response] = await instancesClient.stop({ + project: projectId, + zone, + instance: instanceName, + }); + + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the stop operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + } + + // Modify the machine definition, remember that extended memory + // is available only for N1, N2 and N2D CPUs + + const start = instance.machineType.substring( + 0, + instance.machineType.lastIndexOf('-') + ); + + const [response] = await instancesClient.setMachineType({ + project: projectId, + zone, + instance: instanceName, + instancesSetMachineTypeRequestResource: { + machineType: `${start}-${newMemory}-ext`, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the update operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance updated.'); + } + + modifyInstanceWithExtendedMemory(); + // [END compute_custom_machine_type_update_memory] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[3] = parseInt(args[3]); + +main(...args); diff --git a/compute/instances/preemptible/createPreemptible.js b/compute/instances/preemptible/createPreemptible.js new file mode 100644 index 0000000000..31cb7fd0fe --- /dev/null +++ b/compute/instances/preemptible/createPreemptible.js @@ -0,0 +1,88 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new preemptible VM instance with Debian 11 operating system. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to use, for example: us-west3-b + * @param {string} instanceName - Name of the new machine. + */ +function main(projectId, zone, instanceName) { + // [START compute_preemptible_create] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + + const compute = require('@google-cloud/compute'); + + async function createPreemptible() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '64', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-11/', + }, + autoDelete: true, + boot: true, + }, + ], + scheduling: { + // Set the preemptible setting + preemptible: true, + }, + machineType: `zones/${zone}/machineTypes/e2-small`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createPreemptible(); + // [END compute_preemptible_create] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/preemptible/preemptionHistory.js b/compute/instances/preemptible/preemptionHistory.js new file mode 100644 index 0000000000..325b8bc4ec --- /dev/null +++ b/compute/instances/preemptible/preemptionHistory.js @@ -0,0 +1,76 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Gets a list of preemption operations from given zone in a project. + * Optionally limit the results to instance name. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b. + * @param {string} instanceName - Name of the virtual machine to look for. + * @param {string} customFilter - Filter string to be used for this listing operation. + */ +function main(projectId, zone, instanceName = '', customFilter = '') { + // [START compute_preemptible_history] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const customFilter = 'operationType="compute.instances.preempted"'; + + const compute = require('@google-cloud/compute'); + + async function preemptionHistory() { + const zoneOperationsClient = new compute.ZoneOperationsClient(); + + let filter; + + if (customFilter !== '') { + filter = customFilter; + } else { + filter = 'operationType="compute.instances.preempted"'; + + if (instanceName !== '') { + filter += ` AND targetLink="https://www.googleapis.com/compute/v1/projects/${projectId}/zones/${zone}/instances/${instanceName}"`; + } + } + + const [operationsList] = await zoneOperationsClient.list({ + project: projectId, + zone, + filter, + }); + + for (const operation of operationsList) { + const thisInstanceName = operation.targetLink.split('/').pop(); + if (thisInstanceName === instanceName) { + // The filter used is not 100% accurate, it's `contains` not `equals` + // So we need to check the name to make sure it's the one we want. + console.log(`- ${instanceName} ${operation.insertTime}`); + } + } + } + + preemptionHistory(); + // [END compute_preemptible_history] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/preemptible/printPreemptible.js b/compute/instances/preemptible/printPreemptible.js new file mode 100644 index 0000000000..510c65875a --- /dev/null +++ b/compute/instances/preemptible/printPreemptible.js @@ -0,0 +1,54 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Prints if a given instance is preemptible or not. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to use, for example: us-west3-b + * @param {string} instanceName - name of the virtual machine to check. + */ +function main(projectId, zone, instanceName) { + // [START compute_preemptible_check] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + + const compute = require('@google-cloud/compute'); + + async function printPreemptible() { + const instancesClient = new compute.InstancesClient(); + + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + console.log(`Is instance preemptible: ${instance.scheduling.preemptible}`); + } + + printPreemptible(); + // [END compute_preemptible_check] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/preventing-accidental-vm-deletion/createInstance.js b/compute/instances/preventing-accidental-vm-deletion/createInstance.js new file mode 100644 index 0000000000..3d293f0c39 --- /dev/null +++ b/compute/instances/preventing-accidental-vm-deletion/createInstance.js @@ -0,0 +1,97 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Sends an instance creation request to the Compute Engine API and wait for it to complete. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b + * @param {string} instanceName - Name of the new virtual machine. + * @param {string} deleteProtection - boolean value indicating if the new virtual machine should be + * protected against deletion or not. + */ + +function main(projectId, zone, instanceName, deleteProtection) { + // [START compute_delete_protection_create] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const deleteProtection = true; + + const compute = require('@google-cloud/compute'); + + // Send an instance creation request to the Compute Engine API and wait for it to complete. + async function createInstance() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.insert({ + project: projectId, + zone, + instanceResource: { + name: instanceName, + // Set the delete protection bit. + deletionProtection: deleteProtection, + disks: [ + { + // Describe the size and source image of the boot disk to attach to the instance. + initializeParams: { + diskSizeGb: '10', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-10', + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/e2-small`, + networkInterfaces: [ + { + // Use the default VPC network. + name: 'default', + }, + ], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createInstance(); + // [END compute_delete_protection_create] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[3] = args[3] === 'true'; + +main(...args); diff --git a/compute/instances/preventing-accidental-vm-deletion/getDeleteProtection.js b/compute/instances/preventing-accidental-vm-deletion/getDeleteProtection.js new file mode 100644 index 0000000000..6d8b493ce9 --- /dev/null +++ b/compute/instances/preventing-accidental-vm-deletion/getDeleteProtection.js @@ -0,0 +1,58 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Prints the state of delete protection flag of given instance. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b + * @param {string} instanceName - Name of the new virtual machine. + */ + +function main(projectId, zone, instanceName) { + // [START compute_delete_protection_get] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + + const compute = require('@google-cloud/compute'); + + // Print the state of delete protection flag of given instance. + async function getDeleteProtection() { + const instancesClient = new compute.InstancesClient(); + + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + console.log( + `Instance ${instanceName} has deletionProtection value: ${instance.deletionProtection}` + ); + } + + getDeleteProtection(); + // [END compute_delete_protection_get] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/preventing-accidental-vm-deletion/setDeleteProtection.js b/compute/instances/preventing-accidental-vm-deletion/setDeleteProtection.js new file mode 100644 index 0000000000..b012185a49 --- /dev/null +++ b/compute/instances/preventing-accidental-vm-deletion/setDeleteProtection.js @@ -0,0 +1,75 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Updates the delete protection setting of given instance. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b + * @param {string} instanceName - Name of the new virtual machine. + * @param {string} deleteProtection - boolean value indicating if the new virtual machine should be + * protected against deletion or not. + */ + +function main(projectId, zone, instanceName, deleteProtection) { + // [START compute_delete_protection_set] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const deleteProtection = True; + + const compute = require('@google-cloud/compute'); + + // Update the delete protection setting of given instance. + async function setDeleteProtection() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.setDeletionProtection({ + project: projectId, + zone, + // Set the delete protection bit. + deletionProtection: deleteProtection, + resource: instanceName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance updated.'); + } + + setDeleteProtection(); + // [END compute_delete_protection_set] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +args[3] = args[3] === 'true'; + +main(...args); diff --git a/compute/instances/suspend-resume/resume.js b/compute/instances/suspend-resume/resume.js new file mode 100644 index 0000000000..8b811190b9 --- /dev/null +++ b/compute/instances/suspend-resume/resume.js @@ -0,0 +1,79 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Resumes a suspended Google Compute Engine instance (with unencrypted disks). + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone to create the instance in. For example: "us-west3-b" + * @param {string} instanceName - Name of the new virtual machine (VM) instance. + */ +function main(projectId, zone, instanceName) { + // [START compute_resume_instance] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + + const compute = require('@google-cloud/compute'); + + // Resumes a suspended Google Compute Engine instance (with unencrypted disks). + async function resumeInstance() { + const instancesClient = new compute.InstancesClient(); + + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + if (instance.status !== 'SUSPENDED') { + throw new Error( + 'Only suspended instances can be resumed.' + + `Instance ${instanceName} is in ${instance.status} state.` + ); + } + + const [response] = await instancesClient.resume({ + project: projectId, + zone, + instance: instanceName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance resumed.'); + } + + resumeInstance(); + // [END compute_resume_instance] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/suspend-resume/suspend.js b/compute/instances/suspend-resume/suspend.js new file mode 100644 index 0000000000..aa435e8436 --- /dev/null +++ b/compute/instances/suspend-resume/suspend.js @@ -0,0 +1,66 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Suspends a running Google Compute Engine instance. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} zone - Name of the zone to create the instance in. For example: "us-west3-b" + * @param {string} instanceName - Name of the virtual machine (VM) instance that you want to suspend. + */ +function main(projectId, zone, instanceName) { + // [START compute_suspend_instance] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + + const compute = require('@google-cloud/compute'); + + // Suspends a running Google Compute Engine instance. + async function suspendInstance() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.suspend({ + project: projectId, + zone, + instance: instanceName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance suspended.'); + } + + suspendInstance(); + // [END compute_suspend_instance] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/windows/creating-managing-windows-instances/createWindowsServerInstanceExternalIP.js b/compute/instances/windows/creating-managing-windows-instances/createWindowsServerInstanceExternalIP.js new file mode 100644 index 0000000000..a76fc0f61d --- /dev/null +++ b/compute/instances/windows/creating-managing-windows-instances/createWindowsServerInstanceExternalIP.js @@ -0,0 +1,114 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new Windows Server instance that has an external IP address. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to use, for example: us-west3-b + * @param {string} instanceName - Name of the new machine. + * @param {string} machineType - Machine type you want to create in following format: + * "zones/{zone}/machineTypes/{type_name}". For example: + * "zones/europe-west3-c/machineTypes/f1-micro" + * You can find the list of available machine types using: + * https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list + * @param {string} sourceImageFamily - Name of the public image family for Windows Server or SQL Server images. + * https://cloud.google.com/compute/docs/images#os-compute-support + */ +function main( + projectId, + zone, + instanceName, + machineType = 'n1-standard-1', + sourceImageFamily = 'windows-2012-r2' +) { + // [START compute_create_windows_instance_external_ip] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const machineType = 'n1-standard-1'; + // const sourceImageFamily = 'windows-2012-r2'; + + const compute = require('@google-cloud/compute'); + + async function createWindowsServerInstanceExpernalIP() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + // Describe the size and source image of the boot disk to attach to the instance. + initializeParams: { + diskSizeGb: '64', + sourceImage: `projects/windows-cloud/global/images/family/${sourceImageFamily}/`, + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/${machineType}`, + networkInterfaces: [ + { + accessConfigs: [ + { + type: 'ONE_TO_ONE_NAT', + name: 'External NAT', + }, + ], + // If you are using a custom VPC network it must be configured to allow access to kms.windows.googlecloud.com. + // https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server. + name: 'global/networks/default', + }, + ], + // If you chose an image that supports Shielded VM, you can optionally change the instance's Shielded VM settings. + // "shieldedInstanceConfig": { + // "enableSecureBoot": true, + // "enableVtpm": true, + // "enableIntegrityMonitoring": true + // }, + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createWindowsServerInstanceExpernalIP(); + // [END compute_create_windows_instance_external_ip] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/windows/creating-managing-windows-instances/createWindowsServerInstanceInternalIP.js b/compute/instances/windows/creating-managing-windows-instances/createWindowsServerInstanceInternalIP.js new file mode 100644 index 0000000000..54aa921e78 --- /dev/null +++ b/compute/instances/windows/creating-managing-windows-instances/createWindowsServerInstanceInternalIP.js @@ -0,0 +1,124 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new Windows Server instance that has only an internal IP address. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone in which your instance is located, for example: us-west3-b + * @param {string} instanceName - Name of the new machine. + * @param {string} machineType - Machine type you want to create in following format: + * "zones/{zone}/machineTypes/{type_name}". For example: + * "zones/europe-west3-c/machineTypes/f1-micro" + * You can find the list of available machine types using: + * https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list + * @param {string} sourceImageFamily - Name of the public image family for Windows Server or SQL Server images. + * https://cloud.google.com/compute/docs/images#os-compute-support + * @param {string} networkLink - Name of the network you want the new instance to use. + * For example: "global/networks/default" represents the network + * named "default", which is created automatically for each project. + * @param {string} subnetworkLink - Name of the subnetwork you want the new instance to use. + * This value uses the following format: + * "regions/{region}/subnetworks/{subnetwork_name}" + */ +function main( + projectId, + zone, + instanceName, + machineType = 'n1-standard-1', + sourceImageFamily = 'windows-2012-r2', + networkLink = 'global/networks/default', + subnetworkLink = 'regions/europe-central2/subnetworks/default' +) { + // [START compute_create_windows_instance_internal_ip] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const instanceName = 'YOUR_INSTANCE_NAME'; + // const machineType = 'n1-standard-1'; + // const sourceImageFamily = 'windows-2012-r2'; + // const networkLink = 'global/networks/default'; + // const subnetworkLink = 'regions/europe-central2/subnetworks/default'; + + const compute = require('@google-cloud/compute'); + + async function createWindowsServerInstanceInternalIP() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + // Describe the size and source image of the boot disk to attach to the instance. + initializeParams: { + diskSizeGb: '64', + sourceImage: `projects/windows-cloud/global/images/family/${sourceImageFamily}/`, + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/${machineType}`, + networkInterfaces: [ + { + // You must verify or configure routes and firewall rules in your VPC network + // to allow access to kms.windows.googlecloud.com. + // More information about access to kms.windows.googlecloud.com: https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server + + // Additionally, you must enable Private Google Access for subnets in your VPC network + // that contain Windows instances with only internal IP addresses. + // More information about Private Google Access: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling + name: networkLink, + subnetwork: subnetworkLink, + }, + ], + // If you chose an image that supports Shielded VM, you can optionally change the instance's Shielded VM settings. + // "shieldedInstanceConfig": { + // "enableSecureBoot": true, + // "enableVtpm": true, + // "enableIntegrityMonitoring": true + // }, + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance created.'); + } + + createWindowsServerInstanceInternalIP(); + // [END compute_create_windows_instance_internal_ip] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/instances/windows/creatingWindowsOSImage.js b/compute/instances/windows/creatingWindowsOSImage.js new file mode 100644 index 0000000000..1a08a2a610 --- /dev/null +++ b/compute/instances/windows/creatingWindowsOSImage.js @@ -0,0 +1,134 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a new Windows image from the specified source disk. + * + * @param {string} projectId - Project ID or project number of the Cloud project you use. + * @param {string} zone - Zone of the disk you copy from. + * @param {string} sourceDiskName - Name of the source disk you copy from. + * @param {string} imageName - Name of the image you want to create. + * @param {string} storageLocation - Storage location for the image. If the value is undefined, function will store the image in the multi-region closest to your image's source location. + * @param {boolean} forceCreate - Create the image even if the source disk is attached to a running instance. + */ +function main( + projectId, + zone, + sourceDiskName, + imageName, + storageLocation, + forceCreate = false +) { + // [START compute_windows_image_create] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b'; + // const sourceDiskName = 'YOUR_SOURCE_DISK_NAME'; + // const imageName = 'YOUR_IMAGE_NAME'; + // const storageLocation = 'eu'; + // const forceCreate = false; + + const compute = require('@google-cloud/compute'); + + function parseInstanceName(name) { + const parsedName = name.split('/'); + const l = parsedName.length; + + if (parsedName.legth < 5) { + throw new Error( + 'Provide correct instance name in the following format: https://www.googleapis.com/compute/v1/projects/PROJECT/zones/ZONE/instances/INSTANCE_NAME' + ); + } + + return [parsedName[l - 1], parsedName[l - 3], parsedName[l - 5]]; + } + + async function createWindowsOSImage() { + const imagesClient = new compute.ImagesClient(); + const instancesClient = new compute.InstancesClient(); + const disksClient = new compute.DisksClient(); + + // Getting instances where source disk is attached + const [sourceDisk] = await disksClient.get({ + project: projectId, + zone, + disk: sourceDiskName, + }); + + // Сhecking whether the instances is stopped + for (const fullInstanceName of sourceDisk.users) { + const [instanceName, instanceZone, instanceProjectId] = + parseInstanceName(fullInstanceName); + const [instance] = await instancesClient.get({ + project: instanceProjectId, + zone: instanceZone, + instance: instanceName, + }); + + if ( + !['TERMINATED', 'STOPPED'].includes(instance.status) && + !forceCreate + ) { + throw new Error( + `Instance ${instanceName} should be stopped. Please stop the instance using GCESysprep command or set forceCreate parameter to true (not recommended). More information here: https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image#api.` + ); + } + } + + if (forceCreate) { + console.warn( + 'Warning: forceCreate option compromise the integrity of your image. Stop the instance before you create the image if possible.' + ); + } + + const [response] = await imagesClient.insert({ + project: projectId, + forceCreate, + imageResource: { + name: imageName, + sourceDisk: `/zones/${zone}/disks/${sourceDiskName}`, + storageLocations: storageLocation ? [storageLocation] : [], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Image created.'); + } + + createWindowsOSImage(); + // [END compute_windows_image_create] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +const args = process.argv.slice(2); +if (args.length >= 6) { + args[7] = args[7] === 'true'; +} + +main(...args); diff --git a/compute/listAllInstances.js b/compute/listAllInstances.js new file mode 100644 index 0000000000..65464db0a7 --- /dev/null +++ b/compute/listAllInstances.js @@ -0,0 +1,60 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Gets all instances present in a project, grouped by their zone. + * + * @param {string} projectId - ID or number of the project you want to use. + */ +function main(projectId) { + // [START compute_instances_list_all] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + + const compute = require('@google-cloud/compute'); + + // List all instances in the specified project. + async function listAllInstances() { + const instancesClient = new compute.InstancesClient(); + + //Use the `maxResults` parameter to limit the number of results that the API returns per response page. + const aggListRequest = instancesClient.aggregatedListAsync({ + project: projectId, + maxResults: 5, + }); + + console.log('Instances found:'); + + // Despite using the `maxResults` parameter, you don't need to handle the pagination + // yourself. The returned object handles pagination automatically, + // requesting next pages as you iterate over the results. + for await (const [zone, instancesObject] of aggListRequest) { + const instances = instancesObject.instances; + + if (instances && instances.length > 0) { + console.log(` ${zone}`); + for (const instance of instances) { + console.log(` - ${instance.name} (${instance.machineType})`); + } + } + } + } + + listAllInstances(); + // [END compute_instances_list_all] +} + +main(...process.argv.slice(2)); diff --git a/compute/listImages.js b/compute/listImages.js new file mode 100644 index 0000000000..99ecfce0ff --- /dev/null +++ b/compute/listImages.js @@ -0,0 +1,51 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Prints a list of all non-deprecated image names available in given project. + * + * @param {string} projectId - ID or number of the project you want to list images from + */ +function main(projectId) { + // [START compute_images_list] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + + const compute = require('@google-cloud/compute'); + + async function listImages() { + const imagesClient = new compute.ImagesClient(); + + // Listing only non-deprecated images to reduce the size of the reply. + const images = imagesClient.listAsync({ + project: projectId, + maxResults: 3, + filter: 'deprecated.state != DEPRECATED', + }); + + // Although the `maxResults` parameter is specified in the request, the iterable returned + // by the `listAsync()` method hides the pagination mechanic. The library makes multiple + // requests to the API for you, so you can simply iterate over all the images. + for await (const image of images) { + console.log(` - ${image.name}`); + } + } + + listImages(); + // [END compute_images_list] +} + +main(...process.argv.slice(2)); diff --git a/compute/listImagesByPage.js b/compute/listImagesByPage.js new file mode 100644 index 0000000000..c3337c287d --- /dev/null +++ b/compute/listImagesByPage.js @@ -0,0 +1,75 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Prints a list of all non-deprecated image names available in a given project, divided into pages as returned by the Compute Engine API. + * + * @param {string} projectId - ID or number of the project you want to list images from + * @param {number} pageSize - size of the pages you want the API to return on each call. + */ +function main(projectId, pageSize = 10) { + // [START compute_images_list_page] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const pageSize = 10; + + const compute = require('@google-cloud/compute'); + + async function listImagesByPage() { + const imagesClient = new compute.ImagesClient(); + + // Listing only non-deprecated images to reduce the size of the reply. + const listRequest = { + project: projectId, + maxResults: pageSize, + filter: 'deprecated.state != DEPRECATED', + }; + + const options = { + autoPaginate: false, + }; + + let pageNum = 1; + + // Set autoPaginate option to `false` to have more granular control of + // iteration over paginated results from the API. Each time you want to access the + // next page, the library retrieves that page from the API. + const listCallback = (err, resources, nextPageRequest, response) => { + if (err) { + console.error(err); + return; + } + + console.log(`Page ${pageNum}:`); + pageNum += 1; + + for (let i = 0; i < resources.length; i++) { + console.log(resources[i].name); + } + + if (response.nextPageToken) { + imagesClient.list(nextPageRequest, options, listCallback); + } + }; + + imagesClient.list(listRequest, options, listCallback); + } + + listImagesByPage(); + // [END compute_images_list_page] +} + +main(...process.argv.slice(2)); diff --git a/compute/listInstances.js b/compute/listInstances.js new file mode 100644 index 0000000000..8e83a7a943 --- /dev/null +++ b/compute/listInstances.js @@ -0,0 +1,51 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Gets a list of instances created in given project in given zone. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} zone - Name of the zone you want to check, for example: us-west3-b + */ +function main(projectId, zone) { + // [START compute_instances_list] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + + const compute = require('@google-cloud/compute'); + + // List all instances in the given zone in the specified project. + async function listInstances() { + const instancesClient = new compute.InstancesClient(); + + const [instanceList] = await instancesClient.list({ + project: projectId, + zone, + }); + + console.log(`Instances found in zone ${zone}:`); + + for (const instance of instanceList) { + console.log(` - ${instance.name} (${instance.machineType})`); + } + } + + listInstances(); + // [END compute_instances_list] +} + +main(...process.argv.slice(2)); diff --git a/compute/mailjet.js b/compute/mailjet.js new file mode 100644 index 0000000000..5ad072be17 --- /dev/null +++ b/compute/mailjet.js @@ -0,0 +1,46 @@ +// Copyright 2021 Google LLC +// +// 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. + +// sample-metadata: +// title: Mailjet +// usage: node mailjet + +'use strict'; + +// [START compute_send] +const mailer = require('nodemailer'); +const smtp = require('nodemailer-smtp-transport'); + +async function mailjet() { + const transport = mailer.createTransport( + smtp({ + host: 'in.mailjet.com', + port: 2525, + auth: { + user: process.env.MAILJET_API_KEY || '', + }, + }) + ); + + const json = await transport.sendMail({ + from: 'ANOTHER_EMAIL@ANOTHER_EXAMPLE.COM', // From address + to: 'EMAIL@EXAMPLE.COM', // To address + subject: 'test email from Node.js on Google Cloud Platform', // Subject + text: 'Hello!\n\nThis a test email from Node.js.', // Content + }); + console.log(json); +} +mailjet(); +// [END compute_send] diff --git a/compute/package.json b/compute/package.json new file mode 100644 index 0000000000..66a06592c0 --- /dev/null +++ b/compute/package.json @@ -0,0 +1,29 @@ +{ + "name": "nodejs-docs-samples-compute", + "license": "Apache-2.0", + "author": "Google Inc.", + "engines": { + "node": ">=12.0.0" + }, + "repository": "googleapis/nodejs-compute", + "private": true, + "files": [ + "*.js" + ], + "scripts": { + "test": "mocha test --timeout 1200000" + }, + "dependencies": { + "@google-cloud/compute": "^3.5.1", + "@sendgrid/mail": "^7.0.0", + "nodemailer": "^6.0.0", + "nodemailer-smtp-transport": "^2.7.4" + }, + "devDependencies": { + "@google-cloud/storage": "^5.8.5", + "chai": "^4.2.0", + "mocha": "^8.0.0", + "proxyquire": "^2.0.1", + "uuid": "^9.0.0" + } +} \ No newline at end of file diff --git a/compute/resetInstance.js b/compute/resetInstance.js new file mode 100644 index 0000000000..622b5eafa2 --- /dev/null +++ b/compute/resetInstance.js @@ -0,0 +1,60 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Resets a stopped Google Compute Engine instance (with unencrypted disks). + * + * @param {string} projectId - project ID or project number of the Cloud project your instance belongs to. + * @param {string} zone - name of the zone your instance belongs to. + * @param {string} instanceName - name of the instance your want to reset. + */ +function main(projectId, zone, instanceName) { + // [START compute_reset_instance] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + + const compute = require('@google-cloud/compute'); + + async function resetInstance() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.reset({ + project: projectId, + zone, + instance: instanceName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance reset.'); + } + + resetInstance(); + // [END compute_reset_instance] +} + +main(...process.argv.slice(2)); diff --git a/compute/sendgrid.js b/compute/sendgrid.js new file mode 100644 index 0000000000..cb3b79228b --- /dev/null +++ b/compute/sendgrid.js @@ -0,0 +1,32 @@ +// Copyright 2021 Google LLC +// +// 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 compute_send] +// This sample is based off of: +// https://github.com/sendgrid/sendgrid-nodejs/tree/master/packages/mail +const sendgrid = require('@sendgrid/mail'); +sendgrid.setApiKey(process.env.SENDGRID_API_KEY || ''); + +async function sendgridExample() { + await sendgrid.send({ + to: 'to_email@example.com', + from: 'from_email@example.com', + subject: 'Sendgrid test email from Node.js on Google Cloud Platform', + text: 'Well hello! This is a Sendgrid test email from Node.js on Google Cloud Platform.', + }); +} +sendgridExample(); +// [END compute_send] diff --git a/compute/setUsageExportBucket.js b/compute/setUsageExportBucket.js new file mode 100644 index 0000000000..8480fe488b --- /dev/null +++ b/compute/setUsageExportBucket.js @@ -0,0 +1,68 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Set Compute Engine usage export bucket for the Cloud project. + * This sample presents how to interpret the default value for the report name prefix parameter. + * + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} bucketName - Google Cloud Storage Bucket used to store Compute Engine usage reports. An existing Google Cloud Storage bucket is required. + * @param {string} reportNamePrefix - Report Name Prefix which defaults to an empty string to showcase default values behaviour. + */ +function main(projectId, bucketName, reportNamePrefix = '') { + // [START compute_usage_report_set] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const bucketName = 'YOUR_BUCKET_NAME'; + + const compute = require('@google-cloud/compute'); + const computeProtos = compute.protos.google.cloud.compute.v1; + + async function setUsageExportBucket() { + const usageExportLocationResource = new computeProtos.UsageExportLocation(); + usageExportLocationResource.bucketName = bucketName; + usageExportLocationResource.reportNamePrefix = reportNamePrefix; + + if (!reportNamePrefix) { + // Sending an empty value for reportNamePrefix results in the next usage report being generated with the default prefix value "usage_gce". + // (see: https://cloud.google.com/compute/docs/reference/rest/v1/projects/get) + console.log( + 'Setting reportNamePrefix to empty value causes the report to have the default prefix value `usage_gce`.' + ); + } + + // Set the usage export location. + const projectsClient = new compute.ProjectsClient(); + const operationsClient = new compute.GlobalOperationsClient(); + + let [operation] = await projectsClient.setUsageExportBucket({ + project: projectId, + usageExportLocationResource, + }); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + } + + setUsageExportBucket(); + // [END compute_usage_report_set] +} + +main(...process.argv.slice(2)); diff --git a/compute/snapshots/createSnapshot.js b/compute/snapshots/createSnapshot.js new file mode 100644 index 0000000000..a336064124 --- /dev/null +++ b/compute/snapshots/createSnapshot.js @@ -0,0 +1,130 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Creates a snapshot of a disk. + * + * You need to pass `zone` or `region` parameter relevant to the disk you want to + * snapshot, but not both. Pass `zone` parameter for zonal disks and `region` for + * regional disks. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} diskName - Name of the disk you want to snapshot. + * @param {string} snapshotName - Name of the snapshot to be created. + * @param {string} zone - Name of the zone in which is the disk you want to snapshot (for zonal disks). + * @param {string} region - Name of the region in which is the disk you want to snapshot (for regional disks). + * @param {string} location - The Cloud Storage multi-region or the Cloud Storage region where you + * want to store your snapshot. + * You can specify only one storage location. Available locations: + * https://cloud.google.com/storage/docs/locations#available-locations + * @param {string} diskProjectId - project ID or project number of the Cloud project that + * hosts the disk you want to snapshot. If not provided, will look for + * the disk in the `project_id` project. + */ + +function main( + projectId, + diskName, + snapshotName, + zone, + region, + location, + diskProjectId +) { + // [START compute_snapshot_create] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const diskName = 'YOUR_DISK_NAME'; + // const snapshotName = 'YOUR_SNAPSHOT_NAME'; + // const zone = 'europe-central2-b'; + // const region = ''; + // const location = 'europe-central2'; + // let diskProjectId = 'YOUR_DISK_PROJECT_ID'; + + const compute = require('@google-cloud/compute'); + + async function createSnapshot() { + const snapshotsClient = new compute.SnapshotsClient(); + + let disk; + + if (!zone && !region) { + throw new Error( + 'You need to specify `zone` or `region` for this function to work.' + ); + } + + if (zone && region) { + throw new Error("You can't set both `zone` and `region` parameters"); + } + + if (!diskProjectId) { + diskProjectId = projectId; + } + + if (zone) { + const disksClient = new compute.DisksClient(); + [disk] = await disksClient.get({ + project: diskProjectId, + zone, + disk: diskName, + }); + } else { + const regionDisksClient = new compute.RegionDisksClient(); + [disk] = await regionDisksClient.get({ + project: diskProjectId, + region, + disk: diskName, + }); + } + + const snapshotResource = { + name: snapshotName, + sourceDisk: disk.selfLink, + }; + + if (location) { + snapshotResource.storageLocations = [location]; + } + + const [response] = await snapshotsClient.insert({ + project: projectId, + snapshotResource, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create snapshot operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Snapshot created.'); + } + + createSnapshot(); + // [END compute_snapshot_create] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/snapshots/deleteSnapshot.js b/compute/snapshots/deleteSnapshot.js new file mode 100644 index 0000000000..8aa9fc1fb9 --- /dev/null +++ b/compute/snapshots/deleteSnapshot.js @@ -0,0 +1,61 @@ +// Copyright 2022 Google LLC +// +// 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. + +/** + * Delete a snapshot of a disk. + * + * @param {string} projectId - Project ID or project number of the Cloud project you want to use. + * @param {string} snapshotName - Name of the snapshot to delete. + */ +function main(projectId, snapshotName) { + // [START compute_snapshot_delete] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const snapshotName = 'YOUR_SNAPSHOT_NAME'; + + const compute = require('@google-cloud/compute'); + + async function deleteSnapshot() { + const snapshotsClient = new compute.SnapshotsClient(); + + const [response] = await snapshotsClient.delete({ + project: projectId, + snapshot: snapshotName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create disk operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + console.log('Snapshot deleted.'); + } + + deleteSnapshot(); + // [END compute_snapshot_delete] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/compute/startInstance.js b/compute/startInstance.js new file mode 100644 index 0000000000..289d3b8a76 --- /dev/null +++ b/compute/startInstance.js @@ -0,0 +1,60 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Starts a stopped Google Compute Engine instance (with unencrypted disks). + * + * @param {string} projectId - project ID or project number of the Cloud project your instance belongs to. + * @param {string} zone - name of the zone your instance belongs to. + * @param {string} instanceName - name of the instance your want to start. + */ +function main(projectId, zone, instanceName) { + // [START compute_start_instance] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + + const compute = require('@google-cloud/compute'); + + async function startInstance() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.start({ + project: projectId, + zone, + instance: instanceName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance started.'); + } + + startInstance(); + // [END compute_start_instance] +} + +main(...process.argv.slice(2)); diff --git a/compute/startInstanceWithEncKey.js b/compute/startInstanceWithEncKey.js new file mode 100644 index 0000000000..f83f0bba48 --- /dev/null +++ b/compute/startInstanceWithEncKey.js @@ -0,0 +1,80 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Starts a stopped Google Compute Engine instance (with encrypted disks). + * + * @param {string} projectId - project ID or project number of the Cloud project your instance belongs to. + * @param {string} zone - name of the zone your instance belongs to. + * @param {string} instanceName - name of the instance your want to start. + * @param {string} key - stringrepresenting a raw base64 encoded key to your machines boot disk. + * For more information about disk encryption see: + * https://cloud.google.com/compute/docs/disks/customer-supplied-encryption#specifications + */ +function main(projectId, zone, instanceName, key) { + // [START compute_start_enc_instance] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + // const key = 'YOUR_KEY_STRING' + + const compute = require('@google-cloud/compute'); + + async function startInstanceWithEncryptionKey() { + const instancesClient = new compute.InstancesClient(); + + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + const [response] = await instancesClient.startWithEncryptionKey({ + project: projectId, + zone, + instance: instanceName, + instancesStartWithEncryptionKeyRequestResource: { + disks: [ + { + source: instance.disks[0].source, + diskEncryptionKey: { + rawKey: key, + }, + }, + ], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance with encryption key started.'); + } + + startInstanceWithEncryptionKey(); + // [END compute_start_enc_instance] +} + +main(...process.argv.slice(2)); diff --git a/compute/stopInstance.js b/compute/stopInstance.js new file mode 100644 index 0000000000..1cf188a9a2 --- /dev/null +++ b/compute/stopInstance.js @@ -0,0 +1,60 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Stops a started Google Compute Engine instance. + * + * @param {string} projectId - project ID or project number of the Cloud project your instance belongs to. + * @param {string} zone - name of the zone your instance belongs to. + * @param {string} instanceName - name of the instance your want to stop. + */ +function main(projectId, zone, instanceName) { + // [START compute_stop_instance] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const zone = 'europe-central2-b' + // const instanceName = 'YOUR_INSTANCE_NAME' + + const compute = require('@google-cloud/compute'); + + async function stopInstance() { + const instancesClient = new compute.InstancesClient(); + + const [response] = await instancesClient.stop({ + project: projectId, + zone, + instance: instanceName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Instance stopped.'); + } + + stopInstance(); + // [END compute_stop_instance] +} + +main(...process.argv.slice(2)); diff --git a/compute/test/.eslintrc b/compute/test/.eslintrc new file mode 100644 index 0000000000..6db2a46c53 --- /dev/null +++ b/compute/test/.eslintrc @@ -0,0 +1,3 @@ +--- +env: + mocha: true diff --git a/compute/test/createInstanceTemplates.test.js b/compute/test/createInstanceTemplates.test.js new file mode 100644 index 0000000000..93c05c0352 --- /dev/null +++ b/compute/test/createInstanceTemplates.test.js @@ -0,0 +1,140 @@ +// Copyright 2022 Google LLC +// +// 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 langgsuage governing permissions and +// limitations under the License. + +'use strict'; + +const compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const createInstance = async (projectId, zone, instanceName) => { + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '250', + sourceImage: 'projects/debian-cloud/global/images/family/debian-10', + }, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + deviceName: 'disk-1', + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +describe('create instance templates tests', () => { + const templateName = generateTestId(); + const zone = 'europe-central2-b'; + const instanceName = generateTestId(); + const networkName = 'global/networks/default-compute'; + const subnetworkName = 'regions/asia-east1/subnetworks/default-compute'; + + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should create/get/list/delete instance templates', async () => { + const projectId = await instancesClient.getProjectId(); + + const outputCreate = execSync( + `node create-instance-templates/createTemplate ${projectId} ${templateName}` + ); + assert.match(outputCreate, /Instance template created./); + + const outputGet = execSync( + `node create-instance-templates/getInstanceTemplate ${projectId} ${templateName}` + ); + + assert.include(outputGet, `name: '${templateName}'`); + + const outputList = execSync( + `node create-instance-templates/listInstanceTemplates ${projectId} ${templateName}` + ); + assert.include(outputList, `- ${templateName}`); + + const outputDelete = execSync( + `node create-instance-templates/deleteInstanceTemplate ${projectId} ${templateName}` + ); + assert.match(outputDelete, /Instance template deleted./); + }); + + it('should create template from instance', async () => { + const projectId = await instancesClient.getProjectId(); + + await createInstance(projectId, zone, instanceName); + + const formattedInstanceName = `projects/${projectId}/zones/${zone}/instances/${instanceName}`; + const outputCreate = execSync( + `node create-instance-templates/createTemplateFromInstance ${projectId} ${formattedInstanceName} ${templateName}` + ); + assert.match(outputCreate, /Instance template created./); + + execSync( + `node create-instance-templates/deleteInstanceTemplate ${projectId} ${templateName}` + ); + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); + + it('should create template with subnet', async () => { + const projectId = await instancesClient.getProjectId(); + + await createInstance(projectId, zone, instanceName); + + const outputCreate = execSync( + `node create-instance-templates/createTemplateWithSubnet ${projectId} ${networkName} ${subnetworkName} ${templateName}` + ); + assert.match(outputCreate, /Instance template created./); + + execSync( + `node create-instance-templates/deleteInstanceTemplate ${projectId} ${templateName}` + ); + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); +}); diff --git a/compute/test/createStartInstance.test.js b/compute/test/createStartInstance.test.js new file mode 100644 index 0000000000..2abcaf620c --- /dev/null +++ b/compute/test/createStartInstance.test.js @@ -0,0 +1,246 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const uuid = require('uuid'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); +const imagesClient = new compute.ImagesClient(); +const snapshotsClient = new compute.SnapshotsClient(); +const disksClient = new compute.DisksClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const createDisk = async (projectId, zone, diskName, sourceImage) => { + const [response] = await disksClient.insert({ + project: projectId, + zone, + diskResource: { + sourceImage, + name: diskName, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +const deleteDisk = async (projectId, zone, diskName) => { + const [response] = await disksClient.delete({ + project: projectId, + zone, + disk: diskName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +const createDiskSnapshot = async (projectId, zone, diskName, snapshotName) => { + const [response] = await disksClient.createSnapshot({ + project: projectId, + zone, + disk: diskName, + snapshotResource: { + name: snapshotName, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +const deleteDiskSnapshot = async (projectId, snapshotName) => { + const [response] = await snapshotsClient.delete({ + project: projectId, + snapshot: snapshotName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } +}; + +describe('create start instance tests', () => { + const instanceName = generateTestId(); + const networkName = 'global/networks/default-compute'; + const subnetworkName = 'regions/europe-central2/subnetworks/default-compute'; + const diskName = `gcloud-test-disk-${uuid.v4().split('-')[0]}`; + const snapshotName = `gcloud-test-snapshot-${uuid.v4().split('-')[0]}`; + const zone = 'europe-central2-b'; + + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should create instance from snapshot', async () => { + const projectId = await instancesClient.getProjectId(); + + const [newestDebian] = await imagesClient.getFromFamily({ + project: 'debian-cloud', + family: 'debian-10', + }); + + await createDisk(projectId, zone, diskName, newestDebian.selfLink); + await createDiskSnapshot(projectId, zone, diskName, snapshotName); + + const diskSnapshotLink = `projects/${projectId}/global/snapshots/${snapshotName}`; + + let output; + try { + output = execSync( + `node instances/create-start-instance/createInstanceFromSnapshot ${projectId} ${zone} ${instanceName} ${diskSnapshotLink}` + ); + } catch (err) { + if (err.message.includes('already exists')) { + return; + } + throw err; + } + assert.match(output, /Instance created./); + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + + await deleteDiskSnapshot(projectId, snapshotName); + await deleteDisk(projectId, zone, diskName); + }); + + it('should create instance from custom image', async () => { + const projectId = await instancesClient.getProjectId(); + + const [newestDebian] = await imagesClient.getFromFamily({ + project: 'debian-cloud', + family: 'debian-10', + }); + + let output; + try { + output = execSync( + `node instances/create-start-instance/createInstanceFromCustomImage ${projectId} ${zone} ${instanceName} ${newestDebian.selfLink}` + ); + } catch (err) { + if (err.message.includes('already exists')) { + return; + } + throw err; + } + assert.match(output, /Instance created./); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); + + it('should create instance with additional disk', async () => { + const projectId = await instancesClient.getProjectId(); + + let output; + try { + output = execSync( + `node instances/create-start-instance/createInstanceWithAdditionalDisk ${projectId} ${zone} ${instanceName}` + ); + } catch (err) { + if (err.message.includes('already exists')) { + return; + } + throw err; + } + assert.match(output, /Instance created./); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); + + it('should create instance with subnet', async () => { + const projectId = await instancesClient.getProjectId(); + + let output; + try { + output = execSync( + `node instances/create-start-instance/createInstanceWithSubnet ${projectId} ${zone} ${instanceName} ${networkName} ${subnetworkName}` + ); + } catch (err) { + if (err.message.includes('already exists')) { + return; + } + throw err; + } + assert.match(output, /Instance created./); + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); + + it('should create instance with existing disks', async () => { + const projectId = await instancesClient.getProjectId(); + + const [newestDebian] = await imagesClient.getFromFamily({ + project: 'debian-cloud', + family: 'debian-11', + }); + + const bootDiskName = `gcloud-test-disk-${uuid.v4().split('-')[0]}`; + const diskName2 = `gcloud-test-disk-${uuid.v4().split('-')[0]}`; + + await createDisk(projectId, zone, bootDiskName, newestDebian.selfLink); + await createDisk(projectId, zone, diskName2, newestDebian.selfLink); + + const output = execSync( + `node instances/create-start-instance/createInstanceWithExistingDisks ${projectId} ${zone} ${instanceName} ${bootDiskName},${diskName2}` + ); + assert.match(output, /Instance created./); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + + await deleteDisk(projectId, zone, diskName2); + await deleteDisk(projectId, zone, bootDiskName); + }); +}); diff --git a/compute/test/creatingManagingWindows.test.js b/compute/test/creatingManagingWindows.test.js new file mode 100644 index 0000000000..c266b39401 --- /dev/null +++ b/compute/test/creatingManagingWindows.test.js @@ -0,0 +1,68 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('creating managing windows instances tests', () => { + const instanceName = generateTestId(); + const networkName = 'global/networks/default-compute'; + const subnetworkName = 'regions/europe-central2/subnetworks/default-compute'; + const zone = 'europe-central2-b'; + const machineType = 'n1-standard-1'; + const sourceImageFamily = 'windows-2012-r2'; + + before(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should create windows server instance with external IP', async () => { + const projectId = await instancesClient.getProjectId(); + + const output = execSync( + `node instances/windows/creating-managing-windows-instances/createWindowsServerInstanceExternalIP ${projectId} ${zone} ${instanceName}` + ); + assert.match(output, /Instance created./); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); + + it('should create windows server instance with internal IP and firewall rule', async () => { + const projectId = await instancesClient.getProjectId(); + + const output = execSync( + `node instances/windows/creating-managing-windows-instances/createWindowsServerInstanceInternalIP ${projectId} ${zone} ${instanceName} ${machineType} ${sourceImageFamily} ${networkName} ${subnetworkName}` + ); + + assert.match(output, /Instance created./); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); +}); diff --git a/compute/test/creatingWindowsOSImage.test.js b/compute/test/creatingWindowsOSImage.test.js new file mode 100644 index 0000000000..e033ee1518 --- /dev/null +++ b/compute/test/creatingWindowsOSImage.test.js @@ -0,0 +1,118 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); +const imagesClient = new compute.ImagesClient(); +const globalOperationsClient = new compute.GlobalOperationsClient(); +const zoneOperationsClient = new compute.ZoneOperationsClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('create windows os image tests', () => { + const instanceName = generateTestId(); + const diskName = generateTestId(); + const imageName = generateTestId(); + const zone = 'europe-central2-b'; + + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should create windows os image', async () => { + const projectId = await instancesClient.getProjectId(); + + const [insertResponse] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskName: diskName, + diskSizeGb: '64', + sourceImage: + 'projects/windows-cloud/global/images/windows-server-2012-r2-dc-core-v20220314', + }, + deviceName: diskName, + autoDelete: true, + boot: true, + type: 'PERSISTENT', + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = insertResponse.latestResponse; + + while (operation.status !== 'DONE') { + [operation] = await zoneOperationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + try { + execSync( + `node instances/windows/creatingWindowsOSImage ${projectId} ${zone} ${diskName} ${imageName}` + ); + } catch (err) { + assert.include( + err.stderr.toString(), + `Instance ${instanceName} should be stopped.` + ); + } + + const outputCreate2 = execSync( + `node instances/windows/creatingWindowsOSImage ${projectId} ${zone} ${diskName} ${imageName} eu true` + ); + assert.match(outputCreate2, /Image created./); + + const [deleteResponse] = await imagesClient.delete({ + project: projectId, + image: imageName, + }); + operation = deleteResponse.latestResponse; + + while (operation.status !== 'DONE') { + [operation] = await globalOperationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); +}); diff --git a/compute/test/customHostnameInstance.test.js b/compute/test/customHostnameInstance.test.js new file mode 100644 index 0000000000..56985dc9aa --- /dev/null +++ b/compute/test/customHostnameInstance.test.js @@ -0,0 +1,77 @@ +// Copyright 2021 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const uuid = require('uuid'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const instancesClient = new compute.InstancesClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const getInstance = async (projectId, zone, instanceName) => { + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + return instance; +}; + +describe('Instance with a custom hostname samples', () => { + const instanceName = `gcloud-test-instance-${uuid.v4().split('-')[0]}`; + const zone = 'europe-central2-b'; + const custom_hostname = 'host.domain.com'; + + it('should create instance with a custom hostname and return correct hostname', async () => { + const projectId = await instancesClient.getProjectId(); + let output = execSync( + `node custom-hostname-instance/createInstanceWithCustomHostname ${projectId} ${zone} ${instanceName} ${custom_hostname}` + ); + + const instance = await getInstance(projectId, zone, instanceName); + + assert.equal(instance.hostname, custom_hostname); + assert.match(output, /Instance created./); + + output = execSync( + `node custom-hostname-instance/getInstanceHostname ${projectId} ${zone} ${instanceName}` + ); + + assert.include( + output, + `Instance ${instanceName} has hostname: ${custom_hostname}` + ); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); + + it('should return undefined if hostname is not set', async () => { + const projectId = await instancesClient.getProjectId(); + + execSync(`node createInstance ${projectId} ${zone} ${instanceName}`); + const output = execSync( + `node custom-hostname-instance/getInstanceHostname ${projectId} ${zone} ${instanceName}` + ); + + assert.include(output, `Instance ${instanceName} has hostname: undefined`); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); +}); diff --git a/compute/test/customMachineType.test.js b/compute/test/customMachineType.test.js new file mode 100644 index 0000000000..ed86d47f9a --- /dev/null +++ b/compute/test/customMachineType.test.js @@ -0,0 +1,192 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const getInstance = async (projectId, zone, instanceName) => { + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + return instance; +}; + +describe('custom machine type tests', () => { + const instanceName = generateTestId(); + const zone = 'europe-central2-b'; + let projectId; + + describe('CustomMachineType class tests', () => { + it('should create correct CustomMachineType class', async () => { + const testData = [ + { + cpuSeries: 'custom', + memory: 8192, + cpu: 8, + out: 'URI: zones/europe-central2-b/machineTypes/custom-8-8192', + outMt: 'MachineType: custom-8-8192', + }, + { + cpuSeries: 'n2-custom', + memory: 4096, + cpu: 4, + out: 'URI: zones/europe-central2-b/machineTypes/n2-custom-4-4096', + outMt: 'MachineType: n2-custom-4-4096', + }, + { + cpuSeries: 'n2d-custom', + memory: 8192, + cpu: 4, + out: 'URI: zones/europe-central2-b/machineTypes/n2d-custom-4-8192', + outMt: 'MachineType: n2d-custom-4-8192', + }, + { + cpuSeries: 'e2-custom', + memory: 8192, + cpu: 8, + out: 'URI: zones/europe-central2-b/machineTypes/e2-custom-8-8192', + outMt: 'MachineType: e2-custom-8-8192', + }, + { + cpuSeries: 'e2-custom-small', + memory: 4096, + cpu: 0, + out: 'URI: zones/europe-central2-b/machineTypes/e2-custom-small-4096', + outMt: 'MachineType: e2-custom-small-4096', + }, + { + cpuSeries: 'e2-custom-micro', + memory: 2048, + cpu: 0, + out: 'URI: zones/europe-central2-b/machineTypes/e2-custom-micro-2048', + outMt: 'MachineType: e2-custom-micro-2048', + }, + { + cpuSeries: 'n2-custom', + memory: 638720, + cpu: 8, + out: 'URI: zones/europe-central2-b/machineTypes/n2-custom-8-638720-ext', + outMt: 'MachineType: n2-custom-8-638720-ext', + }, + ]; + + for (const test of testData) { + const output = execSync( + `node instances/custom-machine-type/helperClass ${zone} ${test.cpuSeries} ${test.cpu} ${test.memory}` + ); + + assert.include(output, test.out); + assert.include(output, test.outMt); + } + }); + + it('should throw an error if wrong memory provided to CustomMachineType class', async () => { + try { + execSync( + `node instances/custom-machine-type/helperClass ${zone} custom 8194 8` + ); + } catch (err) { + assert.include( + err.stderr.toString(), + 'Requested memory must be a multiple of 256 MB' + ); + } + }); + + it('should throw an error if wrong cpu count provided to CustomMachineType class', async () => { + try { + execSync( + `node instances/custom-machine-type/helperClass ${zone} n2-custom 8194 66` + ); + } catch (err) { + assert.include( + err.stderr.toString(), + 'Invalid number of cores requested' + ); + } + }); + }); + + // TODO: move tests to unit tests from integration tests or fix flakes. + describe.skip('instances with custom machine type tests', () => { + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + afterEach(async () => { + await deleteInstance(zone, instanceName); + }); + + beforeEach(async () => { + projectId = await instancesClient.getProjectId(); + }); + + it('should create instance with custom machine type with helper', async () => { + const output = execSync( + `node instances/custom-machine-type/createWithHelper ${projectId} ${zone} ${instanceName} e2-custom 4 8192` + ); + assert.match(output, /Instance created./); + + const instance = await getInstance(projectId, zone, instanceName); + assert.equal( + instance.machineType, + `https://www.googleapis.com/compute/v1/projects/${projectId}/zones/${zone}/machineTypes/e2-custom-4-8192` + ); + }); + + it('should create instance with custom machine type without helper', async () => { + const output = execSync( + `node instances/custom-machine-type/createWithoutHelper ${projectId} ${zone} ${instanceName} e2-custom 4 8192` + ); + assert.match(output, /Instance created./); + + const instance = await getInstance(projectId, zone, instanceName); + assert.equal( + instance.machineType, + `https://www.googleapis.com/compute/v1/projects/${projectId}/zones/${zone}/machineTypes/e2-custom-4-8192` + ); + }); + + it('should create instance with custom machine type with extra mem without helper', async () => { + const output = execSync( + `node instances/custom-machine-type/extraMemWithoutHelper ${projectId} ${zone} ${instanceName} custom 4 24320` + ); + assert.match(output, /Instance created./); + + const instance = await getInstance(projectId, zone, instanceName); + assert.equal( + instance.machineType, + `https://www.googleapis.com/compute/v1/projects/${projectId}/zones/${zone}/machineTypes/custom-4-24320-ext` + ); + }); + }); +}); diff --git a/compute/test/disks.test.js b/compute/test/disks.test.js new file mode 100644 index 0000000000..4ea882dd4a --- /dev/null +++ b/compute/test/disks.test.js @@ -0,0 +1,218 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const uuid = require('uuid'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); +const imagesClient = new compute.ImagesClient(); +const snapshotsClient = new compute.SnapshotsClient(); +const disksClient = new compute.DisksClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const createDisk = async (projectId, zone, diskName, sourceImage) => { + const [response] = await disksClient.insert({ + project: projectId, + zone, + diskResource: { + sourceImage, + name: diskName, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +const getInstance = async (projectId, zone, instanceName) => { + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + return instance; +}; + +const deleteDisk = async (projectId, zone, diskName) => { + const [response] = await disksClient.delete({ + project: projectId, + zone, + disk: diskName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +const createDiskSnapshot = async (projectId, zone, diskName, snapshotName) => { + const [response] = await disksClient.createSnapshot({ + project: projectId, + zone, + disk: diskName, + snapshotResource: { + name: snapshotName, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +const deleteDiskSnapshot = async (projectId, snapshotName) => { + const [response] = await snapshotsClient.delete({ + project: projectId, + snapshot: snapshotName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } +}; + +describe('disks tests', () => { + const instanceName = generateTestId(); + const diskName = `gcloud-test-disk-${uuid.v4().split('-')[0]}`; + const diskName2 = `gcloud-test-disk-${uuid.v4().split('-')[0]}`; + const snapshotName = `gcloud-test-snapshot-${uuid.v4().split('-')[0]}`; + const zone = 'europe-central2-b'; + const diskType = `zones/${zone}/diskTypes/pd-ssd`; + + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should create disk from snapshot and remove', async () => { + const projectId = await instancesClient.getProjectId(); + + const [newestDebian] = await imagesClient.getFromFamily({ + project: 'debian-cloud', + family: 'debian-11', + }); + + await createDisk(projectId, zone, diskName, newestDebian.selfLink); + await createDiskSnapshot(projectId, zone, diskName, snapshotName); + + const diskSnapshotLink = `projects/${projectId}/global/snapshots/${snapshotName}`; + + let output = execSync( + `node disks/createDiskFromSnapshot ${projectId} ${zone} ${diskName2} ${diskType} 10 ${diskSnapshotLink}` + ); + assert.match(output, /Disk created./); + + output = execSync( + `node disks/deleteDisk ${projectId} ${zone} ${diskName2}` + ); + assert.match(output, /Disk deleted./); + + await deleteDiskSnapshot(projectId, snapshotName); + await deleteDisk(projectId, zone, diskName); + }); + + it('should set autodelete field', async () => { + const projectId = await instancesClient.getProjectId(); + + const [newestDebian] = await imagesClient.getFromFamily({ + project: 'debian-cloud', + family: 'debian-11', + }); + + const [response] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '10', + sourceImage: newestDebian.selfLink, + diskName, + }, + deviceName: diskName, + autoDelete: false, + boot: true, + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + const output = execSync( + `node disks/setDiskAutodelete ${projectId} ${zone} ${instanceName} ${diskName} true` + ); + assert.match(output, /Disk autoDelete field updated./); + + const instance = await getInstance(projectId, zone, instanceName); + + assert.equal(instance.disks[0].autoDelete, true); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); +}); diff --git a/compute/test/mailjet.test.js b/compute/test/mailjet.test.js new file mode 100644 index 0000000000..b2de020f90 --- /dev/null +++ b/compute/test/mailjet.test.js @@ -0,0 +1,55 @@ +// Copyright 2021 Google LLC +// +// 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 proxyquire = require('proxyquire').noPreserveCache(); +const assert = require('assert'); + +process.env.MAILJET_API_KEY = 'foo'; +process.env.MAILJET_API_SECRET = 'bar'; + +describe('mailjet', () => { + it('should send an email', () => { + proxyquire('../mailjet', { + nodemailer: { + createTransport: arg => { + assert.strictEqual(arg, 'test'); + return { + sendMail: payload => { + assert.deepStrictEqual(payload, { + from: 'ANOTHER_EMAIL@ANOTHER_EXAMPLE.COM', + to: 'EMAIL@EXAMPLE.COM', + subject: 'test email from Node.js on Google Cloud Platform', + text: 'Hello!\n\nThis a test email from Node.js.', + }); + return 'done'; + }, + }; + }, + }, + 'nodemailer-smtp-transport': options => { + assert.deepStrictEqual(options, { + host: 'in.mailjet.com', + port: 2525, + auth: { + user: 'foo', + pass: 'bar', + }, + }); + return 'test'; + }, + }); + }); +}); diff --git a/compute/test/preemptible.test.js b/compute/test/preemptible.test.js new file mode 100644 index 0000000000..4660be6809 --- /dev/null +++ b/compute/test/preemptible.test.js @@ -0,0 +1,67 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('preemptible instances tests', () => { + const instanceName = generateTestId(); + const zone = 'europe-central2-b'; + + before(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('create and print preemptible instance', async () => { + const projectId = await instancesClient.getProjectId(); + + let output; + + output = execSync( + `node instances/preemptible/createPreemptible ${projectId} ${zone} ${instanceName}` + ); + assert.match(output, /Instance created./); + + output = execSync( + `node instances/preemptible/printPreemptible ${projectId} ${zone} ${instanceName}` + ); + assert.include(output, 'Is instance preemptible: true'); + + const filter = `'targetLink="https://www.googleapis.com/compute/v1/projects/${projectId}/zones/${zone}/instances/${instanceName}"'`; + + output = execSync( + `node instances/preemptible/preemptionHistory ${projectId} ${zone} ${instanceName} ${filter}` + ); + + assert.include(output, `- ${instanceName}`); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); +}); diff --git a/compute/test/preventAccidentalVMDeletion.test.js b/compute/test/preventAccidentalVMDeletion.test.js new file mode 100644 index 0000000000..d4dc7c8dd1 --- /dev/null +++ b/compute/test/preventAccidentalVMDeletion.test.js @@ -0,0 +1,73 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('prevent accidental vm deletion tests', () => { + const instanceName = generateTestId(); + const zone = 'europe-central2-b'; + + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should create instance and set delete protection', async () => { + const projectId = await instancesClient.getProjectId(); + + const outputCreate = execSync( + `node instances/preventing-accidental-vm-deletion/createInstance ${projectId} ${zone} ${instanceName} true` + ); + assert.match(outputCreate, /Instance created./); + + const outputGet = execSync( + `node instances/preventing-accidental-vm-deletion/getDeleteProtection ${projectId} ${zone} ${instanceName}` + ); + assert.include( + outputGet, + `Instance ${instanceName} has deletionProtection value: true` + ); + + const outputSet = execSync( + `node instances/preventing-accidental-vm-deletion/setDeleteProtection ${projectId} ${zone} ${instanceName} false` + ); + assert.include(outputSet, 'Instance updated.'); + + const outputGet2 = execSync( + `node instances/preventing-accidental-vm-deletion/getDeleteProtection ${projectId} ${zone} ${instanceName}` + ); + assert.include( + outputGet2, + `Instance ${instanceName} has deletionProtection value: false` + ); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); +}); diff --git a/compute/test/samples.test.js b/compute/test/samples.test.js new file mode 100644 index 0000000000..68e81d0459 --- /dev/null +++ b/compute/test/samples.test.js @@ -0,0 +1,508 @@ +// Copyright 2021 Google LLC +// +// 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 compute = require('@google-cloud/compute'); +const computeProtos = compute.protos.google.cloud.compute.v1; +const {Storage} = require('@google-cloud/storage'); + +const {describe, it} = require('mocha'); +const uuid = require('uuid'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); +const projectsClient = new compute.ProjectsClient(); +const firewallsClient = new compute.FirewallsClient(); +const instanceTemplatesClient = new compute.InstanceTemplatesClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +// A helper for delaying integration tests with an exponential backoff. +// See examples like: https://github.com/googleapis/nodejs-monitoring/issues/190, +// https://github.com/googleapis/nodejs-monitoring/issues/191. +const delay = async test => { + const retries = test.currentRetry(); + if (retries === 0) return; // no retry on the first failure. + // see: https://cloud.google.com/storage/docs/exponential-backoff: + const ms = Math.pow(2, retries) * 500 + Math.random() * 1000; + return new Promise(done => { + console.info(`retrying "${test.title}" in ${ms}ms`); + setTimeout(done, ms); + }); +}; + +const getInstance = async (projectId, zone, instanceName) => { + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + return instance; +}; + +describe('samples', () => { + const instanceName = generateTestId(); + const zone = 'europe-central2-b'; + const bucketName = `test-bucket-name-${uuid.v4().split('-')[0]}`; + + const storage = new Storage(); + + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should create instance', async () => { + const projectId = await instancesClient.getProjectId(); + const output = execSync( + `node createInstance ${projectId} ${zone} ${instanceName}` + ); + assert.match(output, /Instance created./); + }); + + it('should print instance data', async () => { + const projectId = await instancesClient.getProjectId(); + const output = execSync( + `node getInstance ${projectId} ${zone} ${instanceName}` + ); + assert.include(output, `"name": "${instanceName}"`); + }); + + it('should print instances list', async () => { + const projectId = await instancesClient.getProjectId(); + const output = execSync(`node listInstances ${projectId} ${zone}`); + assert.match(output, /Instances found in zone/); + }); + + it('should print all instances list', async () => { + const projectId = await instancesClient.getProjectId(); + const output = execSync(`node listAllInstances ${projectId}`); + assert.match(output, /Instances found:/); + }); + + it('should delete instance', async () => { + const projectId = await instancesClient.getProjectId(); + const output = execSync( + `node deleteInstance ${projectId} ${zone} ${instanceName}` + ); + assert.match(output, /Instance deleted./); + }); + + it('should wait for operation', async () => { + const projectId = await instancesClient.getProjectId(); + + const newinstanceName = `gcloud-test-intance-${uuid.v4().split('-')[0]}`; + + execSync(`node createInstance ${projectId} ${zone} ${newinstanceName}`); + + const [response] = await instancesClient.delete({ + project: projectId, + zone, + instance: newinstanceName, + }); + + const operationString = JSON.stringify(response.latestResponse); + + const output = execSync( + `node waitForOperation ${projectId} '${operationString}'` + ); + assert.match(output, /Operation finished./); + }); + + describe('usage export', () => { + before(async () => { + try { + await storage.createBucket(bucketName); + } catch (err) { + // Resource likely already existed due to retry. + } + }); + + after(async () => { + const projectId = await instancesClient.getProjectId(); + + await projectsClient.setUsageExportBucket({ + project: projectId, + usageExportLocationResource: {}, + }); + + await storage.bucket(bucketName).delete(); + }); + + it('should set empty default value in reportNamePrefix', async function () { + this.retries(3); + await delay(this.test); + const projectId = await instancesClient.getProjectId(); + + const output = execSync( + `node setUsageExportBucket ${projectId} ${bucketName}` + ); + await new Promise(resolve => setTimeout(resolve, 5000)); + + assert.match( + output, + /Setting reportNamePrefix to empty value causes the report to have the default prefix value `usage_gce`./ + ); + + const [project] = await projectsClient.get({ + project: projectId, + }); + + const usageExportLocation = project.usageExportLocation; + + assert.match(usageExportLocation.bucketName, /test-bucket-name/); + assert.equal(usageExportLocation.reportNamePrefix, ''); + }); + + it('should get current default value in reportNamePrefix', async function () { + this.retries(3); + await delay(this.test); + const projectId = await instancesClient.getProjectId(); + + execSync(`node setUsageExportBucket ${projectId} ${bucketName}`); + await new Promise(resolve => setTimeout(resolve, 5000)); + const output = execSync(`node getUsageExportBucket ${projectId}`); + + assert.match( + output, + /Report name prefix not set, replacing with default value of `usage_gce`./ + ); + + assert.match(output, /Returned reportNamePrefix: usage_gce/); + }); + }); + + describe('pagination', () => { + const projectId = 'windows-sql-cloud'; + + it('should automatically iterate throught the pages', async () => { + const output = execSync(`node listImages ${projectId}`); + const lines = output.split(' - '); + + assert(lines.length > 3); + }); + + it('should iterate page by page granularly', async () => { + const output = execSync(`node listImagesByPage ${projectId}`); + + assert.match(output, /Page 1/); + assert.match(output, /Page 2/); + }); + }); + + describe('start/stop instances', () => { + it('should start/stop instances', async () => { + const projectId = await instancesClient.getProjectId(); + const newInstanceName = `gcloud-test-instance-${uuid.v4().split('-')[0]}`; + + execSync(`node createInstance ${projectId} ${zone} ${newInstanceName}`); + + let instance = await getInstance(projectId, zone, newInstanceName); + assert.equal(instance.status, 'RUNNING'); + + const stopOutput = execSync( + `node stopInstance ${projectId} ${zone} ${newInstanceName}` + ); + assert.match(stopOutput, /Instance stopped/); + + instance = await getInstance(projectId, zone, newInstanceName); + assert.equal(instance.status, 'TERMINATED'); + + const startOutput = execSync( + `node startInstance ${projectId} ${zone} ${newInstanceName}` + ); + assert.match(startOutput, /Instance started/); + + instance = await getInstance(projectId, zone, newInstanceName); + assert.equal(instance.status, 'RUNNING'); + + execSync(`node deleteInstance ${projectId} ${zone} ${newInstanceName}`); + }); + + it('should start/stop instances with encrypted disks', async () => { + const projectId = await instancesClient.getProjectId(); + const newInstanceName = `gcloud-test-instance-${uuid.v4().split('-')[0]}`; + + const KEY = uuid.v4().split('-').join(''); + const KEY_BASE64 = Buffer.from(KEY).toString('base64'); + + const [response] = await instancesClient.insert({ + instanceResource: { + name: newInstanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '10', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-10', + }, + autoDelete: true, + boot: true, + type: computeProtos.AttachedDisk.Type.PERSISTENT, + diskEncryptionKey: { + rawKey: KEY_BASE64, + }, + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + const stopOutput = execSync( + `node stopInstance ${projectId} ${zone} ${newInstanceName}` + ); + assert.match(stopOutput, /Instance stopped/); + + let instance = await getInstance(projectId, zone, newInstanceName); + assert.equal(instance.status, 'TERMINATED'); + + const startOutput = execSync( + `node startInstanceWithEncKey ${projectId} ${zone} ${newInstanceName} ${KEY_BASE64}` + ); + assert.match(startOutput, /Instance with encryption key started/); + + instance = await getInstance(projectId, zone, newInstanceName); + assert.equal(instance.status, 'RUNNING'); + + execSync(`node deleteInstance ${projectId} ${zone} ${newInstanceName}`); + }); + + it('should reset instance', async () => { + const projectId = await instancesClient.getProjectId(); + const newInstanceName = `gcloud-test-instance-${uuid.v4().split('-')[0]}`; + + execSync(`node createInstance ${projectId} ${zone} ${newInstanceName}`); + + const resetOutput = execSync( + `node resetInstance ${projectId} ${zone} ${newInstanceName}` + ); + assert.match(resetOutput, /Instance reset/); + + execSync(`node deleteInstance ${projectId} ${zone} ${newInstanceName}`); + }); + }); + + describe('firewall', () => { + // Clean stale firewall rules, in case prior test runs have failed. + before(async () => { + const FOUR_HOURS = 1000 * 60 * 60 * 4; + const projectId = await instancesClient.getProjectId(); + for await (const rule of firewallsClient.listAsync({ + project: projectId, + })) { + const created = new Date(rule.creationTimestamp).getTime(); + // Delete firewalls that are older than 4 hours and match our + // test prefix. + if ( + created < Date.now() - FOUR_HOURS && + rule.name.startsWith('test-firewall-rule') + ) { + console.info(`deleting stale firewall ${rule.name}`); + await firewallsClient.delete({ + project: projectId, + firewall: rule.name, + }); + } + } + }); + + it('should create and delete firewall rule', async function () { + this.retries(3); + await delay(this.test); + + const projectId = await instancesClient.getProjectId(); + const firewallRuleName = `test-firewall-rule-${uuid.v4().split('-')[0]}`; + + let output = execSync( + `node firewall/createFirewallRule ${projectId} ${firewallRuleName}` + ); + assert.match(output, /Firewall rule created/); + + output = execSync( + `node firewall/deleteFirewallRule ${projectId} ${firewallRuleName}` + ); + assert.match(output, /Firewall rule deleted/); + }); + + it('should list firewall rules', async function () { + this.retries(3); + await delay(this.test); + + const projectId = await instancesClient.getProjectId(); + const firewallRuleName = `test-firewall-rule-${uuid.v4().split('-')[0]}`; + + execSync( + `node firewall/createFirewallRule ${projectId} ${firewallRuleName}` + ); + const output = execSync(`node firewall/listFirewallRules ${projectId}`); + assert.isTrue(output.includes(`- ${firewallRuleName}:`)); + + execSync( + `node firewall/deleteFirewallRule ${projectId} ${firewallRuleName}` + ); + }); + + it('should patch firewall rule', async function () { + this.retries(3); + await delay(this.test); + + const projectId = await instancesClient.getProjectId(); + const firewallRuleName = `test-firewall-rule-${uuid.v4().split('-')[0]}`; + + execSync( + `node firewall/createFirewallRule ${projectId} ${firewallRuleName}` + ); + + let [firewallRule] = await firewallsClient.get({ + project: projectId, + firewall: firewallRuleName, + }); + + assert.equal(firewallRule.priority, 1000); + + const output = execSync( + `node firewall/patchFirewallPriority ${projectId} ${firewallRuleName} 500` + ); + assert.match(output, /Firewall rule updated/); + + [firewallRule] = await firewallsClient.get({ + project: projectId, + firewall: firewallRuleName, + }); + + assert.equal(firewallRule.priority, 500); + + execSync( + `node firewall/deleteFirewallRule ${projectId} ${firewallRuleName}` + ); + }); + }); + + describe('instance template', () => { + const instanceTemplateName = `instance-template-${uuid.v4().split('-')[0]}`; + const zone = 'europe-central2-b'; + + before(async () => { + const projectId = await instancesClient.getProjectId(); + const [response] = await instanceTemplatesClient.insert({ + project: projectId, + instanceTemplateResource: { + name: instanceTemplateName, + properties: { + machineType: 'n1-standard-1', + disks: [ + { + initializeParams: { + diskSizeGb: '10', + sourceImage: + 'projects/debian-cloud/global/images/family/debian-10', + }, + autoDelete: true, + boot: true, + type: computeProtos.AttachedDisk.Type.PERSISTENT, + }, + ], + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + }, + }); + + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + }); + + after(async () => { + const projectId = await instancesClient.getProjectId(); + const [response] = await instanceTemplatesClient.delete({ + project: projectId, + instanceTemplate: instanceTemplateName, + }); + + let operation = response.latestResponse; + const operationsClient = new compute.GlobalOperationsClient(); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + }); + } + }); + + it('should create instance from template', async () => { + const projectId = await instancesClient.getProjectId(); + const instanceName = `instance-${uuid.v4().split('-')[0]}`; + + const output = execSync( + `node createInstanceFromTemplate ${projectId} ${zone} ${instanceName} global/instanceTemplates/${instanceTemplateName}` + ); + assert.include(output, 'Instance created.'); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); + + it('should create instance from template with overrides', async () => { + const projectId = await instancesClient.getProjectId(); + const instanceName = `instance-${uuid.v4().split('-')[0]}`; + + const output = execSync( + `node createInstanceFromTemplateWithOverrides ${projectId} ${zone} ${instanceName} ${instanceTemplateName}` + ); + assert.include(output, 'Instance created.'); + + const instance = await getInstance(projectId, zone, instanceName); + assert.equal(instance.disks.length, 2); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); + }); +}); diff --git a/compute/test/sendgrid.test.js b/compute/test/sendgrid.test.js new file mode 100644 index 0000000000..6b3c333959 --- /dev/null +++ b/compute/test/sendgrid.test.js @@ -0,0 +1,41 @@ +// Copyright 2021 Google LLC +// +// 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 proxyquire = require('proxyquire'); +const {assert} = require('chai'); + +process.env.SENDGRID_API_KEY = 'foo'; + +describe('sendgrid', () => { + it('should send an email', () => { + proxyquire('../sendgrid', { + '@sendgrid/mail': { + setApiKey: key => { + assert.strictEqual(key, 'foo'); + }, + send: msg => { + assert.deepStrictEqual(msg, { + to: 'to_email@example.com', + from: 'from_email@example.com', + subject: + 'Sendgrid test email from Node.js on Google Cloud Platform', + text: 'Well hello! This is a Sendgrid test email from Node.js on Google Cloud Platform.', + }); + }, + }, + }); + }); +}); diff --git a/compute/test/snapshots.test.js b/compute/test/snapshots.test.js new file mode 100644 index 0000000000..4c2faea2c3 --- /dev/null +++ b/compute/test/snapshots.test.js @@ -0,0 +1,172 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const uuid = require('uuid'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); +const imagesClient = new compute.ImagesClient(); +const disksClient = new compute.DisksClient(); +const regionDisksClient = new compute.RegionDisksClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const createRegionDisk = async (projectId, region, diskName) => { + const [response] = await regionDisksClient.insert({ + project: projectId, + region, + diskResource: { + sizeGb: 200, + name: diskName, + replicaZones: [ + `projects/${projectId}/zones/europe-central2-a`, + `projects/${projectId}/zones/europe-central2-b`, + ], + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.RegionOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + region: operation.region.split('/').pop(), + }); + } +}; + +const createDisk = async (projectId, zone, diskName, sourceImage) => { + const [response] = await disksClient.insert({ + project: projectId, + zone, + diskResource: { + sourceImage, + name: diskName, + }, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +const deleteDisk = async (projectId, zone, diskName) => { + const [response] = await disksClient.delete({ + project: projectId, + zone, + disk: diskName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } +}; + +const deleteRegionDisk = async (projectId, region, diskName) => { + const [response] = await regionDisksClient.delete({ + project: projectId, + region, + disk: diskName, + }); + let operation = response.latestResponse; + const operationsClient = new compute.RegionOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + region: operation.region.split('/').pop(), + }); + } +}; + +describe('snapshots tests', () => { + const diskName = `gcloud-test-disk-${uuid.v4().split('-')[0]}`; + const snapshotName = `gcloud-test-snapshot-${uuid.v4().split('-')[0]}`; + const zone = 'europe-central2-b'; + const location = 'europe-central2'; + + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should create zonal snapshot and remove', async () => { + const projectId = await instancesClient.getProjectId(); + + const [newestDebian] = await imagesClient.getFromFamily({ + project: 'debian-cloud', + family: 'debian-11', + }); + + await createDisk(projectId, zone, diskName, newestDebian.selfLink); + + let output = execSync( + `node snapshots/createSnapshot ${projectId} ${diskName} ${snapshotName} ${zone} '' ${location} ''` + ); + + assert.match(output, /Snapshot created./); + + output = execSync( + `node snapshots/deleteSnapshot ${projectId} ${snapshotName}` + ); + assert.match(output, /Snapshot deleted./); + + await deleteDisk(projectId, zone, diskName); + }); + + it('should create regional snapshot and remove', async () => { + const projectId = await instancesClient.getProjectId(); + + await createRegionDisk(projectId, location, diskName); + + let output = execSync( + `node snapshots/createSnapshot ${projectId} ${diskName} ${snapshotName} '' ${location} ${location} ''` + ); + + assert.match(output, /Snapshot created./); + + output = execSync( + `node snapshots/deleteSnapshot ${projectId} ${snapshotName}` + ); + assert.match(output, /Snapshot deleted./); + + await deleteRegionDisk(projectId, location, diskName); + }); +}); diff --git a/compute/test/suspendResume.test.js b/compute/test/suspendResume.test.js new file mode 100644 index 0000000000..f181cf5d3a --- /dev/null +++ b/compute/test/suspendResume.test.js @@ -0,0 +1,127 @@ +// Copyright 2022 Google LLC +// +// 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 compute = require('@google-cloud/compute'); + +const {describe, it} = require('mocha'); +const cp = require('child_process'); +const {assert} = require('chai'); + +const {generateTestId, getStaleVMInstances, deleteInstance} = require('./util'); + +const instancesClient = new compute.InstancesClient(); +const zoneOperationsClient = new compute.ZoneOperationsClient(); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('suspend/resume instance tests', () => { + const instanceName = generateTestId(); + const zone = 'europe-central2-b'; + + const delay = ms => new Promise(res => setTimeout(res, ms)); + + const getInstance = async (projectId, zone, instanceName) => { + const [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + return instance; + }; + + after(async () => { + const instances = await getStaleVMInstances(); + await Promise.all( + instances.map(instance => + deleteInstance(instance.zone, instance.instanceName) + ) + ); + }); + + it('should suspend and resume instance', async () => { + const projectId = await instancesClient.getProjectId(); + + const [insertResponse] = await instancesClient.insert({ + instanceResource: { + name: instanceName, + disks: [ + { + initializeParams: { + diskSizeGb: '64', + sourceImage: + 'projects/ubuntu-os-cloud/global/images/family/ubuntu-2004-lts', + }, + autoDelete: true, + boot: true, + }, + ], + machineType: `zones/${zone}/machineTypes/n1-standard-1`, + networkInterfaces: [ + { + name: 'global/networks/default', + }, + ], + }, + project: projectId, + zone, + }); + let operation = insertResponse.latestResponse; + + while (operation.status !== 'DONE') { + [operation] = await zoneOperationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + // Once the machine is running, give it some time to fully start all processes + // before trying to suspend it. + await delay(45 * 1000); + + let output = execSync( + `node instances/suspend-resume/suspend ${projectId} ${zone} ${instanceName}` + ); + assert.match(output, /Instance suspended./); + + let instance = await getInstance(projectId, zone, instanceName); + + while (instance.status === 'SUSPENDING') { + [instance] = await instancesClient.get({ + project: projectId, + zone, + instance: instanceName, + }); + + await delay(5 * 1000); + } + + instance = await getInstance(projectId, zone, instanceName); + + assert.equal(instance.status, 'SUSPENDED'); + + output = execSync( + `node instances/suspend-resume/resume ${projectId} ${zone} ${instanceName}` + ); + assert.match(output, /Instance resumed./); + + instance = await getInstance(projectId, zone, instanceName); + + assert.equal(instance.status, 'RUNNING'); + + execSync(`node deleteInstance ${projectId} ${zone} ${instanceName}`); + }); +}); diff --git a/compute/test/util.js b/compute/test/util.js new file mode 100644 index 0000000000..d192788ac1 --- /dev/null +++ b/compute/test/util.js @@ -0,0 +1,89 @@ +// Copyright 2022 Google LLC +// +// 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. + +const uuid = require('uuid'); +const compute = require('@google-cloud/compute'); + +const PREFIX = 'gcloud-test-'; + +const instancesClient = new compute.InstancesClient(); + +// Get a unique ID to use for test resources. +function generateTestId() { + return `${PREFIX}-${uuid.v4()}`.substr(0, 30); +} + +/** + * Get VM instances created more than one hour ago. + */ +async function getStaleVMInstances() { + const projectId = await instancesClient.getProjectId(); + const result = []; + const currentDate = new Date(); + currentDate.setHours(currentDate.getHours() - 3); + + const aggListRequest = instancesClient.aggregatedListAsync({ + project: projectId, + }); + + for await (const [zone, instancesObject] of aggListRequest) { + const instances = instancesObject.instances; + result.push( + ...instances + .filter( + instance => + new Date(instance.creationTimestamp) < currentDate && + instance.name.startsWith(PREFIX) + ) + .map(instance => { + return { + zone: zone.split('zones/')[1], + instanceName: instance.name, + timestamp: instance.creationTimestamp, + }; + }) + ); + } + + return result; +} + +async function deleteInstance(zone, instanceName) { + const projectId = await instancesClient.getProjectId(); + + const [response] = await instancesClient.delete({ + project: projectId, + instance: instanceName, + zone, + }); + let operation = response.latestResponse; + const operationsClient = new compute.ZoneOperationsClient(); + + console.log(`Deleting ${instanceName}`); + + // Wait for the create operation to complete. + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone, + }); + } +} + +module.exports = { + generateTestId, + getStaleVMInstances, + deleteInstance, +}; diff --git a/compute/waitForOperation.js b/compute/waitForOperation.js new file mode 100644 index 0000000000..e4e4c0df98 --- /dev/null +++ b/compute/waitForOperation.js @@ -0,0 +1,51 @@ +// Copyright 2021 Google LLC +// +// 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. + +/** + * Waits for an operation to be completed. Calling this function will block until the operation is finished. + * @param {string} projectId - ID or number of the project you want to use. + * @param {string} operationString - Operation instance you want to wait in string format. + */ +function main(projectId, operationString) { + // [START compute_instances_operation_check] + /** + * TODO(developer): Uncomment and replace these variables before running the sample. + */ + // const projectId = 'YOUR_PROJECT_ID'; + // const operationString = 'YOUR_OPERATION_STRING' + + const compute = require('@google-cloud/compute'); + + // Parse stringified operation to the object instance. + let operation = JSON.parse(operationString); + + async function waitForOperation() { + const operationsClient = new compute.ZoneOperationsClient(); + + while (operation.status !== 'DONE') { + [operation] = await operationsClient.wait({ + operation: operation.name, + project: projectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log('Operation finished.'); + } + + waitForOperation(); + // [END compute_instances_operation_check] +} + +main(...process.argv.slice(2));