Skip to content

Commit

Permalink
feat(functions/v2): add GCE + CAL sample (#2401)
Browse files Browse the repository at this point in the history
Notes:
1. This sample has an E2E test _only_. This is currently disabled, as I don't think the testing project is configured for V2. (We should enable it once GCF V2 is available within that project, **provided** we're OK with a non-GA platform being tested.)
2. This uses an alpha version of the GCE Client Library. We'll need to replace that once the library goes GA.
  • Loading branch information
Ace Nassri authored Nov 2, 2021
1 parent c17391c commit 550cabf
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 3 deletions.
47 changes: 47 additions & 0 deletions functions/v2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,50 @@ exports.helloAuditLog = cloudevent => {
console.log('Principal:', payload.authenticationInfo.principalEmail);
};
// [END functions_log_cloudevent]

// [START functions_label_gce_instance]
const compute = require('@google-cloud/compute');
const instancesClient = new compute.InstancesClient();

/**
* CloudEvent function that labels newly-created GCE instances with the entity
* (person or service account) that created them.
*
* @param {object} cloudevent A CloudEvent containing the Cloud Audit Log entry.
* @param {object} cloudevent.data.protoPayload The Cloud Audit Log entry itself.
*/
exports.autoLabelInstance = async cloudevent => {
// Extract parameters from the CloudEvent + Cloud Audit Log data
let creator = cloudevent.data.protoPayload.authenticationInfo.principalEmail;

// Get relevant VM instance details from the cloudevent's `subject` property
// Example value:
// compute.googleapis.com/projects/<PROJECT>/zones/<ZONE>/instances/<INSTANCE>
const params = cloudevent.subject.split('/');

// Format the 'creator' parameter to match GCE label validation requirements
creator = creator.toLowerCase().replace(/\W/g, '_');

// Get the newly-created VM instance's label fingerprint
// This is required by the Compute Engine API to prevent duplicate labels
const getInstanceRequest = {
project: params[2],
zone: params[4],
instance: params[6],
};
const [instance] = await instancesClient.get(getInstanceRequest);

// Label the instance with its creator
const setLabelsRequest = Object.assign(
{
instancesSetLabelsRequestResource: {
labels: {creator},
labelFingerprint: instance.labelFingerprint,
},
},
getInstanceRequest
);

return instancesClient.setLabels(setLabelsRequest);
};
// [END functions_label_gce_instance]
11 changes: 8 additions & 3 deletions functions/v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@
"node": ">=12.0.0"
},
"scripts": {
"test": "mocha test/*.test.js"
"test": "mocha test/index.test.js",
"e2e-test": "mocha test/*.test.js"
},
"dependencies": {
"@google-cloud/compute": "^3.0.0-alpha.4"
},
"devDependencies": {
"@google-cloud/functions-framework": "^1.1.1",
"gaxios": "^4.3.0",
"mocha": "^9.0.0",
"promise-retry": "^2.0.0",
"mocha": "^9.1.3",
"p-retry": "^4.6.1",
"supertest": "^6.0.0",
"uuid": "^8.3.2",
"wait-port": "^0.2.9"
}
}
104 changes: 104 additions & 0 deletions functions/v2/test/system.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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.

// Note: this assumes the function is deployed to the target project.
// IF THE FUNCTION IS NOT DEPLOYED, THIS TEST WILL FAIL.

const assert = require('assert');
const pRetry = require('p-retry');
const compute = require('@google-cloud/compute');
const uuid = require('uuid').v4;

const computeProtos = compute.protos.google.cloud.compute.v1;
const instancesClient = new compute.InstancesClient();
const opsClient = new compute.ZoneOperationsClient();

const gac = process.env.GOOGLE_APPLICATION_CREDENTIALS;

let USER_NAME = gac ? require(gac).client_email : process.env.USER;
USER_NAME = USER_NAME.toLowerCase().replace(/\W/g, '_');

const PROJECT = process.env.GCLOUD_PROJECT;
const ZONE = 'us-central1-a';
const MACHINE_TYPE = 'n1-standard-1';

const instanceName = `ace-test-${uuid()}`;

describe('functions_label_gce_instance', () => {
before(async () => {
// Create a Compute Engine instance
const [response] = await instancesClient.insert({
instanceResource: {
name: instanceName,
machineType: `zones/${ZONE}/machineTypes/${MACHINE_TYPE}`,
disks: [
{
initializeParams: {
diskSizeGb: '10',
sourceImage:
'projects/debian-cloud/global/images/family/debian-10',
},
boot: true,
autoDelete: true,
type: computeProtos.AttachedDisk.Type.PERSISTENT,
},
],
networkInterfaces: [{name: 'global/networks/default'}],
},
project: PROJECT,
zone: ZONE,
});

// Wait for the create operation to complete.
let operation = response;
while (operation.status !== 'DONE') {
const pollResponse = await opsClient.wait({
operation: operation.name,
project: PROJECT,
zone: ZONE,
});
operation = pollResponse[0];
}
});

it('should label an instance', async () => {
await pRetry(
async () => {
// Check the instance's labels
const [instance] = await instancesClient.get({
project: PROJECT,
zone: ZONE,
instance: instanceName,
});

console.log(instance.labels);
assert(instance.labels && instance.labels.creator);
assert.equal(instance.labels.creator.includes(USER_NAME), true);

// Signal completion
return Promise.resolve();
},
{retries: 3}
);
});

after(async () => {
// Delete the created instance
await instancesClient.delete({
project: PROJECT,
zone: ZONE,
instance: instanceName,
});
});
});

0 comments on commit 550cabf

Please sign in to comment.