Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions functions/v2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ exports.helloGCS = cloudevent => {

// [START functions_log_cloudevent]
/**
* CloudEvent function to be triggered by Cloud Audit Logging
* CloudEvent function to be triggered by an Eventarc Cloud Audit Logging trigger
* Note: this is NOT designed for second-party (Cloud Audit Logs -> Pub/Sub) triggers!
*
* @param {object} cloudevent A CloudEvent containing the Cloud Audit Log entry.
* @param {object} cloudevent.data.protoPayload The Cloud Audit Log entry itself.
Expand All @@ -68,8 +69,14 @@ exports.helloAuditLog = cloudevent => {
console.log('Subject:', cloudevent.subject);

// Print out details from the Cloud Audit Logging entry
const payload = cloudevent.data.protoPayload;
console.log('Principal:', payload.authenticationInfo.principalEmail);
const metadata =
cloudevent.data &&
cloudevent.data.protoPayload &&
cloudevent.data.protoPayload.requestMetadata;
if (metadata) {
console.log('Caller IP:', metadata.callerIp);
console.log('User Agent:', metadata.callerSuppliedUserAgent);
Comment thread
ace-n marked this conversation as resolved.
Outdated
}
};
// [END functions_log_cloudevent]

Expand All @@ -86,12 +93,19 @@ const instancesClient = new compute.InstancesClient();
*/
exports.autoLabelInstance = async cloudevent => {
// Extract parameters from the CloudEvent + Cloud Audit Log data
let creator = cloudevent.data.protoPayload.authenticationInfo.principalEmail;
const payload = cloudevent.data && cloudevent.data.protoPayload;
const authInfo = payload && payload.authenticationInfo;
let creator = authInfo && authInfo.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('/');
const params = cloudevent.subject && cloudevent.subject.split('/');

// Validate data
if (!creator || !params || params.length !== 7) {
throw new Error('Invalid event structure');
}

// Format the 'creator' parameter to match GCE label validation requirements
creator = creator.toLowerCase().replace(/\W/g, '_');
Expand Down
4 changes: 2 additions & 2 deletions functions/v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"node": ">=12.0.0"
},
"scripts": {
"test": "mocha test/index.test.js",
"e2e-test": "mocha test/*.test.js"
"test": "mocha --timeout 30s test/index.test.js",
"e2e-test": "mocha --timeout 60s test/*.test.js"
},
"dependencies": {
"@google-cloud/compute": "^3.0.0-alpha.4"
Expand Down
46 changes: 42 additions & 4 deletions functions/v2/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ const {exec} = require('child_process');
const waitPort = require('wait-port');

const startFF = async (target, signature, port) => {
console.log(
Comment thread
ace-n marked this conversation as resolved.
Outdated
`npx functions-framework --target=${target} --signature-type=${signature} --port=${port}`
);
const ff = exec(
`npx functions-framework --target=${target} --signature-type=${signature} --port=${port}`
);
Expand All @@ -31,8 +34,10 @@ const getFFOutput = ffProc => {
let stderr = '';
ffProc.stdout.on('data', data => (stdout += data));
ffProc.stderr.on('data', data => (stderr += data));

const stdall = stdout + stderr;
ffProc.on('error', reject).on('exit', code => {
code === 0 ? resolve(stdout) : reject(stderr);
code === 0 ? resolve(stdall) : reject(stdall);
});
});
};
Expand Down Expand Up @@ -149,8 +154,9 @@ describe('functions_log_cloudevent', () => {
'storage.googleapis.com/projects/_/buckets/my-bucket/objects/test.txt',
data: {
protoPayload: {
authenticationInfo: {
principalEmail: 'nobody@example.com',
requestMetadata: {
callerIp: '8.8.8.8',
callerSuppliedUserAgent: 'example-user-agent',
},
},
},
Expand All @@ -167,6 +173,38 @@ describe('functions_log_cloudevent', () => {
output,
/Subject: storage.googleapis.com\/projects\/_\/buckets\/my-bucket\/objects\/test\.txt/
);
assert.match(output, /Principal: nobody@example\.com/);
assert.match(output, /Caller IP: 8\.8\.8\.8/);
assert.match(output, /User Agent: example-user-agent/);
});
});

describe('functions_label_gce_instance', () => {
const PORT = 9084;
let ffProc;
let ffProcHandler;

before(async () => {
ffProc = await startFF('autoLabelInstance', 'cloudevent', PORT);
ffProcHandler = getFFOutput(ffProc);
});

after(() => {
// Stop any residual Functions Framework instances
try {
ffProc.kill();
} finally {
/* Do nothing */
}
});

it('should validate data', async () => {
const event = {
subject: 'invalid/subject/example',
};
const response = await invocation(PORT, event);
ffProc.kill();

await ffProcHandler;
assert.match(response.data, /Invalid event structure/);
});
});