UNPKG

sigfox-gcloud

Version:

Framework for building a Sigfox server, based on Google Cloud Functions

141 lines (132 loc) 5.39 kB
// Get and set Google Cloud common instance metadata. // This is a key-value store that's shared by all programs running in the // same Google Cloud project. Note the limit of 32768 bytes for each // individual metadata entry and 512KB for the total metadata server. const isGoogleCloud = !!(process.env.GAE_SERVICE || process.env.FUNCTION_NAME); const google = require('googleapis'); const common = require('../index'); // eslint-disable-next-line import/no-unresolved const credentials = isGoogleCloud ? null : require('../google-credentials.json'); const scopes = ['https://www.googleapis.com/auth/cloud-platform']; const compute = google.compute('v1'); function authorizeMetadata(req, scopes0) { // Return an authClient for getting and setting metadata. // Returns a promise. const scopes2 = scopes0 || scopes; return new Promise((resolve, reject) => { const callback = (err, res) => (err ? reject(err) : resolve(res)); return isGoogleCloud // If running in cloud, use default cloud credentials. ? google.auth.getApplicationDefault(callback) // Else use service account credentials. : google.auth.fromJSON(credentials, callback); }) .then((authClient0) => { let authClient = authClient0; if (authClient.createScopedRequired && authClient.createScopedRequired()) { authClient = authClient.createScoped(scopes2); } common.log(req, 'google-metadata/authorize', { result: 'OK', scopes2, device: req.device }); return authClient; }) .catch((error) => { common.log(req, 'google-metadata/authorize', { error, scopes2, device: req.device }); throw error; }); } function convertMetadata(req, metadata) { // Given metadata = { "fingerprint": "yiXZGhVkCGQ=", // "items": [{"key": "a", "value": "1"}, ...]} // return as a Javascript object {a:1, ...} const result = {}; for (const item of metadata.items || []) { const key = item.key; result[key] = item.value; } return result; } function updateMetadata(req, metadata, updateObj) { /* metadata contains: { "fingerprint": "yiXZGhVkCGQ=", "items": [{"key": "a", "value": "1"}, ...]} Return a new copy of metadata with items updated to the key=value pairs in updateObj. */ const updatedItems = []; let updateKeys = Object.keys(updateObj); for (const item of metadata.items || []) { // Look for a key in updateObj that is present in item. const key = item.key; if (updateKeys.indexOf(key) >= 0) { // Output the new value according to updateObj and remove from the keys. const value = updateObj[key]; updatedItems.push({ key, value }); updateKeys = updateKeys.filter(x => (x !== key)); } else { // Key doesn't exist in updateObj. Just copy over. updatedItems.push(item); } } // Update the remaining keys not found in metadata. for (const key of updateKeys) { const value = updateObj[key]; updatedItems.push({ key, value }); } const result = Object.assign({}, metadata); result.items = updatedItems; return result; } function composeRequest(req, authClient) { // Return a standard request template for making metadata requests. const request = { project: process.env.GCLOUD_PROJECT, // Google Cloud project ID. auth: authClient, }; return request; } function getMetadata(req, authClient) { // Return the common instance metadata for the Google Cloud project: // { "fingerprint": "yiXZGhVkCGQ=", // "items": [{"key": "a", "value": "1"}, ...]} // Returns a promise. const request = composeRequest(req, authClient); return new Promise((resolve, reject) => compute.projects.get(request, (err, res) => (err ? reject(err) : resolve(res)))) .then((res) => { // Remove the "kind" field. Leave only "fingerprint" and "items". const metadata = Object.assign({}, res.commonInstanceMetadata); if (metadata.kind) delete metadata.kind; const result = convertMetadata(req, metadata); common.log(req, 'google-metadata/getMetadata', { result, device: req.device }); return result; }) .catch((error) => { common.log(req, 'google-metadata/getMetadata', { error, device: req.device }); throw error; }); } function setMetadata(req, authClient, metadata) { // Set the common instance metadata for the Google Cloud project. // metadata contains: { "fingerprint": "yiXZGhVkCGQ=", // "items": [{"key": "a", "value": "1"}, ...]} // Note the limit of 32768 bytes for each individual metadata entry // and 512KB for the total metadata server. Returns a promise. const request = composeRequest(req, authClient); request.resource = Object.assign({}, metadata); // Delete the etag if it exists. if (request.resource.etag) delete request.resource.etag; return new Promise((resolve, reject) => compute.projects.setCommonInstanceMetadata(request, (err, res) => (err ? reject(err) : resolve(res)))) .then((result) => { common.log(req, 'google-metadata/setMetadata', { result, metadata, device: req.device }); return result; }) .catch((error) => { common.log(req, 'google-metadata/setMetadata', { error, metadata, device: req.device }); throw error; }); } module.exports = { authorizeMetadata, updateMetadata, getMetadata, setMetadata, };