diff --git a/compute/reservations/createInstanceToConsumeAnyReservation.js b/compute/reservations/createInstanceToConsumeAnyReservation.js index 6ba59b3af3..d77e2cdcfd 100644 --- a/compute/reservations/createInstanceToConsumeAnyReservation.js +++ b/compute/reservations/createInstanceToConsumeAnyReservation.js @@ -18,17 +18,14 @@ async function main(instanceName) { // [START compute_consume_any_matching_reservation] - // Import the Compute library const computeLib = require('@google-cloud/compute'); const compute = computeLib.protos.google.cloud.compute.v1; - // Instantiate a reservationsClient const instancesClient = new computeLib.InstancesClient(); - // Instantiate a zoneOperationsClient const zoneOperationsClient = new computeLib.ZoneOperationsClient(); /** - * TODO(developer): Update/uncomment these variables before running the sample. + * TODO(developer): Customize the following variables before running the sample. */ // The ID of the project where you want to create instance. const projectId = await instancesClient.getProjectId(); @@ -39,9 +36,10 @@ async function main(instanceName) { // Machine type to use for VM. const machineType = 'n1-standard-4'; - // Create instance to consume reservation if their properties match the VM properties async function callCreateInstanceToConsumeAnyReservation() { // Describe the size and source image of the boot disk to attach to the instance. + // Uses a persistent disk so that data is preserved even if the VM is + // stopped or restarted. const disk = new compute.Disk({ boot: true, autoDelete: true, @@ -52,17 +50,24 @@ async function main(instanceName) { }, }); - // Define networkInterface + // Use the default network for simplicity. In production environments, + // you may want to specify a custom network with specific firewall rules + // and security configurations. const networkInterface = new compute.NetworkInterface({ name: 'global/networks/default', }); - // Define reservationAffinity + // Configure the instance to consume any available reservation that matches + // its requirements (CPU, memory, etc.). This is a reasonable default for cost + // optimization for long-running workloads. Consider changing depending on your + // specific needs or workloads. const reservationAffinity = new compute.ReservationAffinity({ consumeReservationType: 'ANY_RESERVATION', }); - // Create an instance + // Set the minimum CPU platform to ensure compatibility with + // machine type and to take advantage of specific CPU features. + // Change this based on performance and workload needs const instance = new compute.Instance({ name: instanceName, machineType: `zones/${zone}/machineTypes/${machineType}`, @@ -80,7 +85,6 @@ async function main(instanceName) { let operation = response.latestResponse; - // Wait for the create instance operation to complete. while (operation.status !== 'DONE') { [operation] = await zoneOperationsClient.wait({ operation: operation.name, diff --git a/compute/reservations/createInstanceToConsumeSharedReservation.js b/compute/reservations/createInstanceToConsumeSharedReservation.js new file mode 100644 index 0000000000..bcae9d32a1 --- /dev/null +++ b/compute/reservations/createInstanceToConsumeSharedReservation.js @@ -0,0 +1,120 @@ +/* + * Copyright 2024 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +async function main(instancesClient, zoneOperationsClient) { + // [START compute_consume_specific_shared_reservation] + // Import the Compute library + const computeLib = require('@google-cloud/compute'); + const compute = computeLib.protos.google.cloud.compute.v1; + + /** + * TODO(developer): Uncomment reservationsClient and zoneOperationsClient before running the sample. + */ + // Instantiate an instancesClient + // instancesClient = new computeLib.InstancesClient(); + // Instantiate a zoneOperationsClient + // zoneOperationsClient = new computeLib.ZoneOperationsClient(); + + /** + * TODO(developer): Update these variables before running the sample. + */ + // The ID of the project where instance will be consumed and created. + const reservationConsumerProjectId = 'reservation-consumer-project-id'; + // The ID of the project where reservation is created. + const reservationOwnerProjectId = 'reservation-project-id'; + // The name of the instance to create. + const instanceName = 'instance-01'; + // The name of the reservation to consume. + // Ensure that the specificReservationRequired field in reservation properties is set to true. + const reservationName = 'reservation-1'; + // Machine type to use for VM. + const machineType = 'n1-standard-1'; + // The zone in which to create instance. + const zone = 'us-central1-a'; + + // Create instance to consume shared reservation + async function callCreateInstanceToConsumeSharedReservation() { + // Describe the size and source image of the boot disk to attach to the instance. + // Ensure that the VM's properties match the reservation's VM properties, + // including the zone, machine type (machine family, vCPUs, and memory), + // minimum CPU platform, GPU amount and type, and local SSD interface and size + const disk = new compute.Disk({ + boot: true, + autoDelete: true, + type: 'PERSISTENT', + initializeParams: { + diskSizeGb: '10', + sourceImage: 'projects/debian-cloud/global/images/family/debian-12', + }, + }); + + // Define networkInterface + const networkInterface = new compute.NetworkInterface({ + name: 'global/networks/default', + }); + + // Define reservationAffinity + const reservationAffinity = new compute.ReservationAffinity({ + consumeReservationType: 'SPECIFIC_RESERVATION', + key: 'compute.googleapis.com/reservation-name', + values: [ + `projects/${reservationOwnerProjectId}/reservations/${reservationName}`, + ], + }); + + // Create an instance + const instance = new compute.Instance({ + name: instanceName, + machineType: `zones/${zone}/machineTypes/${machineType}`, + disks: [disk], + networkInterfaces: [networkInterface], + reservationAffinity, + }); + + const [response] = await instancesClient.insert({ + project: reservationConsumerProjectId, + instanceResource: instance, + zone, + }); + + let operation = response.latestResponse; + + // Wait for the create instance operation to complete. + while (operation.status !== 'DONE') { + [operation] = await zoneOperationsClient.wait({ + operation: operation.name, + project: reservationConsumerProjectId, + zone: operation.zone.split('/').pop(), + }); + } + + console.log(`Instance ${instanceName} created from shared reservation.`); + return response; + } + + return await callCreateInstanceToConsumeSharedReservation(); + // [END compute_consume_specific_shared_reservation] +} + +module.exports = main; + +// TODO(developer): Uncomment below lines before running the sample. +// main(...process.argv.slice(2)).catch(err => { +// console.error(err); +// process.exitCode = 1; +// }); diff --git a/compute/reservations/createInstanceToConsumeSingleProjectReservation.js b/compute/reservations/createInstanceToConsumeSingleProjectReservation.js index fd7de18c5f..0d06160f36 100644 --- a/compute/reservations/createInstanceToConsumeSingleProjectReservation.js +++ b/compute/reservations/createInstanceToConsumeSingleProjectReservation.js @@ -22,7 +22,7 @@ async function main(instanceName, reservationName) { const computeLib = require('@google-cloud/compute'); const compute = computeLib.protos.google.cloud.compute.v1; - // Instantiate a reservationsClient + // Instantiate an instancesClient const instancesClient = new computeLib.InstancesClient(); // Instantiate a zoneOperationsClient const zoneOperationsClient = new computeLib.ZoneOperationsClient(); diff --git a/compute/reservations/createInstanceToNotConsumeReservation.js b/compute/reservations/createInstanceToNotConsumeReservation.js index e90fffd463..5ede12b274 100644 --- a/compute/reservations/createInstanceToNotConsumeReservation.js +++ b/compute/reservations/createInstanceToNotConsumeReservation.js @@ -22,7 +22,7 @@ async function main(instanceName) { const computeLib = require('@google-cloud/compute'); const compute = computeLib.protos.google.cloud.compute.v1; - // Instantiate a reservationsClient + // Instantiate an instancesClient const instancesClient = new computeLib.InstancesClient(); // Instantiate a zoneOperationsClient const zoneOperationsClient = new computeLib.ZoneOperationsClient(); diff --git a/compute/test/consumeReservations.test.js b/compute/test/consumeReservations.test.js index 1eab8b202e..0a089e87bf 100644 --- a/compute/test/consumeReservations.test.js +++ b/compute/test/consumeReservations.test.js @@ -18,7 +18,7 @@ const path = require('path'); const assert = require('node:assert/strict'); -const {before, describe, it} = require('mocha'); +const {before, after, describe, it} = require('mocha'); const cp = require('child_process'); const {ReservationsClient} = require('@google-cloud/compute').v1; const { @@ -40,6 +40,9 @@ describe('Consume reservations', async () => { before(async () => { projectId = await reservationsClient.getProjectId(); + }); + + after(async () => { // Cleanup resources const instances = await getStaleVMInstances(instancePrefix); await Promise.all( diff --git a/compute/test/createComputeHyperdisk.test.js b/compute/test/createComputeHyperdisk.test.js index 0930175249..64f23839a9 100644 --- a/compute/test/createComputeHyperdisk.test.js +++ b/compute/test/createComputeHyperdisk.test.js @@ -18,9 +18,8 @@ const path = require('path'); const assert = require('node:assert/strict'); -const {before, after, describe, it} = require('mocha'); +const {after, describe, it} = require('mocha'); const cp = require('child_process'); -const {DisksClient} = require('@google-cloud/compute').v1; const {getStaleDisks, deleteDisk} = require('./util'); const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); @@ -30,24 +29,13 @@ describe('Create compute hyperdisk', async () => { const prefix = 'hyperdisk-name-941ad2d'; const diskName = `${prefix}${Math.floor(Math.random() * 1000 + 1)}`; const zone = 'europe-central2-b'; - const disksClient = new DisksClient(); - let projectId; - before(async () => { - projectId = await disksClient.getProjectId(); + after(async () => { // Cleanup resources const disks = await getStaleDisks(prefix); await Promise.all(disks.map(disk => deleteDisk(disk.zone, disk.diskName))); }); - after(async () => { - await disksClient.delete({ - project: projectId, - disk: diskName, - zone, - }); - }); - it('should create a new hyperdisk', () => { const response = execSync( `node ./disks/createComputeHyperdisk.js ${diskName} ${zone}`, diff --git a/compute/test/createComputeHyperdiskFromPool.test.js b/compute/test/createComputeHyperdiskFromPool.test.js index 3c04f27ec5..6e4007b950 100644 --- a/compute/test/createComputeHyperdiskFromPool.test.js +++ b/compute/test/createComputeHyperdiskFromPool.test.js @@ -18,7 +18,7 @@ const path = require('path'); const assert = require('node:assert/strict'); -const {after, before, describe, it} = require('mocha'); +const {after, describe, it} = require('mocha'); const cp = require('child_process'); const { getStaleDisks, @@ -37,7 +37,7 @@ describe('Create compute hyperdisk from pool', async () => { const storagePoolName = `${poolPrefix}${Math.floor(Math.random() * 1000 + 1)}5f`; const zone = 'us-central1-a'; - before(async () => { + after(async () => { // Cleanup resources const disks = await getStaleDisks(diskPrefix); await Promise.all(disks.map(disk => deleteDisk(disk.zone, disk.diskName))); @@ -49,12 +49,6 @@ describe('Create compute hyperdisk from pool', async () => { ); }); - after(async () => { - // Cleanup resources - await deleteDisk(zone, diskName); - await deleteStoragePool(zone, storagePoolName); - }); - it('should create a new storage pool', async () => { const response = execSync( `node ./disks/createComputeHyperdiskPool.js ${storagePoolName} ${zone}`, diff --git a/compute/test/createInstanceTemplates.test.js b/compute/test/createInstanceTemplates.test.js index ea5040b1a4..544cde9b2a 100644 --- a/compute/test/createInstanceTemplates.test.js +++ b/compute/test/createInstanceTemplates.test.js @@ -16,7 +16,7 @@ const compute = require('@google-cloud/compute'); -const {describe, it} = require('mocha'); +const {after, describe, it} = require('mocha'); const cp = require('child_process'); const {assert} = require('chai'); diff --git a/compute/test/createInstanceToConsumeSharedReservation.test.js b/compute/test/createInstanceToConsumeSharedReservation.test.js new file mode 100644 index 0000000000..901dcf4e84 --- /dev/null +++ b/compute/test/createInstanceToConsumeSharedReservation.test.js @@ -0,0 +1,67 @@ +/* + * Copyright 2024 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 + * + * https://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 {beforeEach, afterEach, describe, it} = require('mocha'); +const assert = require('node:assert/strict'); +const sinon = require('sinon'); +const createInstanceToConsumeSharedReservation = require('../reservations/createInstanceToConsumeSharedReservation.js'); + +describe('Create instance to consume shared reservation', async () => { + const instanceName = 'instance-1'; + let instancesClientMock; + let zoneOperationsClientMock; + + beforeEach(() => { + instancesClientMock = { + insert: sinon.stub().resolves([ + { + name: instanceName, + latestResponse: { + status: 'DONE', + name: 'operation-1234567890', + zone: { + value: 'us-central1-a', + }, + }, + }, + ]), + }; + zoneOperationsClientMock = { + wait: sinon.stub().resolves([ + { + latestResponse: { + status: 'DONE', + }, + }, + ]), + }; + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should create instance', async () => { + const response = await createInstanceToConsumeSharedReservation( + instancesClientMock, + zoneOperationsClientMock + ); + + assert(response.name.includes(instanceName)); + }); +}); diff --git a/compute/test/createReservationFromVM.test.js b/compute/test/createReservationFromVM.test.js index e16aef8c50..6f3d4d8400 100644 --- a/compute/test/createReservationFromVM.test.js +++ b/compute/test/createReservationFromVM.test.js @@ -43,6 +43,16 @@ describe('Compute reservation from VM', async () => { before(async () => { projectId = await reservationsClient.getProjectId(); + // Create VM + execSync( + `node ./createInstance.js ${projectId} ${zone} ${vmName} ${machineType}`, + { + cwd, + } + ); + }); + + after(async () => { // Cleanup resources const instances = await getStaleVMInstances(instancePrefix); await Promise.all( @@ -56,25 +66,6 @@ describe('Compute reservation from VM', async () => { deleteReservation(reservation.zone, reservation.reservationName) ) ); - // Create VM - execSync( - `node ./createInstance.js ${projectId} ${zone} ${vmName} ${machineType}`, - { - cwd, - } - ); - }); - - after(() => { - // Delete reservation - execSync(`node ./reservations/deleteReservation.js ${reservationName}`, { - cwd, - }); - - // Delete VM - execSync(`node ./deleteInstance.js ${projectId} ${zone} ${vmName}`, { - cwd, - }); }); it('should create a new reservation from vm', () => { diff --git a/compute/test/createReservationGlobalInstanceTemplate.test.js b/compute/test/createReservationGlobalInstanceTemplate.test.js index 153830d162..7732428664 100644 --- a/compute/test/createReservationGlobalInstanceTemplate.test.js +++ b/compute/test/createReservationGlobalInstanceTemplate.test.js @@ -36,13 +36,6 @@ describe('Create compute reservation using global instance template', async () = before(async () => { projectId = await reservationsClient.getProjectId(); - // Cleanup resources - const reservations = await getStaleReservations(reservationPrefix); - await Promise.all( - reservations.map(reservation => - deleteReservation(reservation.zone, reservation.reservationName) - ) - ); // Create template execSync( `node ./create-instance-templates/createTemplate.js ${projectId} ${instanceTemplateName}`, @@ -52,11 +45,14 @@ describe('Create compute reservation using global instance template', async () = ); }); - after(() => { - // Delete reservation - execSync(`node ./reservations/deleteReservation.js ${reservationName}`, { - cwd, - }); + after(async () => { + // Cleanup resources + const reservations = await getStaleReservations(reservationPrefix); + await Promise.all( + reservations.map(reservation => + deleteReservation(reservation.zone, reservation.reservationName) + ) + ); // Delete template execSync( `node ./create-instance-templates/deleteInstanceTemplate.js ${projectId} ${instanceTemplateName}`, diff --git a/compute/test/createReservationRegionalInstanceTemplate.test.js b/compute/test/createReservationRegionalInstanceTemplate.test.js index 79c58c329e..8c11a71be3 100644 --- a/compute/test/createReservationRegionalInstanceTemplate.test.js +++ b/compute/test/createReservationRegionalInstanceTemplate.test.js @@ -18,7 +18,7 @@ const path = require('path'); const assert = require('node:assert/strict'); -const {after, before, describe, it} = require('mocha'); +const {after, describe, it} = require('mocha'); const cp = require('child_process'); const {getStaleReservations, deleteReservation} = require('./util'); @@ -31,7 +31,7 @@ describe('Create compute reservation using regional instance template', async () const instanceTemplateName = 'pernament-region-template-name'; const location = 'regions/us-central1'; - before(async () => { + after(async () => { // Cleanup resources const reservations = await getStaleReservations(reservationPrefix); await Promise.all( @@ -41,13 +41,6 @@ describe('Create compute reservation using regional instance template', async () ); }); - after(() => { - // Delete reservation - execSync(`node ./reservations/deleteReservation.js ${reservationName}`, { - cwd, - }); - }); - it('should create a new reservation', () => { const response = execSync( `node ./reservations/createReservationInstanceTemplate.js ${reservationName} ${location} ${instanceTemplateName}`, diff --git a/compute/test/reservations.test.js b/compute/test/reservations.test.js index a81d8084e0..46275b1ed2 100644 --- a/compute/test/reservations.test.js +++ b/compute/test/reservations.test.js @@ -18,7 +18,7 @@ const path = require('path'); const assert = require('node:assert/strict'); -const {before, describe, it} = require('mocha'); +const {before, after, describe, it} = require('mocha'); const cp = require('child_process'); const {ReservationsClient} = require('@google-cloud/compute').v1; const {getStaleReservations, deleteReservation} = require('./util'); @@ -35,6 +35,9 @@ describe('Compute reservation', async () => { before(async () => { projectId = await reservationsClient.getProjectId(); + }); + + after(async () => { // Cleanup resorces const reservations = await getStaleReservations(reservationPrefix); await Promise.all(