diff --git a/monitoring/README.md b/monitoring/README.md index 16417fe7b2..658e745d50 100644 --- a/monitoring/README.md +++ b/monitoring/README.md @@ -6,47 +6,39 @@ Monitoring API to retrieve API data. `create_custom_metric.js` demonstrates how to create a custom metric, write a timeseries value to it, and read it back. - # Run locally Create local credentials by running the following command and following the oauth2 flow: gcloud beta auth application-default login -Then to run: +Then to run: npm install node list_resources.js node create_custom_metric.js - ## Running on GCE, GAE, or other environments On Google App Engine, the credentials should be found automatically. On Google Compute Engine, the credentials should be found automatically, but require that -you create the instance with the correct scopes. +you create the instance with the correct scopes. gcloud compute instances create --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/compute,https://www.googleapis.com/auth/compute.readonly" test-instance -If you did not create the instance with the right scopes, you can still upload a JSON service +If you did not create the instance with the right scopes, you can still upload a JSON service account and set GOOGLE_APPLICATION_CREDENTIALS as described below. - ## Using a Service Account In non-Google Cloud environments, GCE instances created without the correct scopes, or local -workstations if the `gcloud beta auth application-default login` command fails, use a Service +workstations if the `gcloud beta auth application-default login` command fails, use a Service Account by doing the following: * Go to API Manager -> Credentials -* Click 'New Credentials', and create a Service Account or [click here](https://console.cloud.google -.com/project/_/apiui/credential/serviceaccount) - Download the JSON for this service account, and set the `GOOGLE_APPLICATION_CREDENTIALS` - environment variable to point to the file containing the JSON credentials. - +* Click 'New Credentials', and create a Service Account or [click here](https://console.cloud.google.com/project/_/apiui/credential/serviceaccount) +* Download the JSON for this service account, and set the `GOOGLE_APPLICATION_CREDENTIALS` +environment variable to point to the file containing the JSON credentials. export GOOGLE_APPLICATION_CREDENTIALS=~/Downloads/-0123456789abcdef.json - - - diff --git a/monitoring/create_custom_metric.js b/monitoring/create_custom_metric.js index 596f16e7a3..cbf62eb944 100644 --- a/monitoring/create_custom_metric.js +++ b/monitoring/create_custom_metric.js @@ -23,39 +23,19 @@ var google = require('googleapis'); var async = require('async'); -var args = process.argv.slice(2); -if (args.length !== 1) { - console.log('Usage: node auth_and_list_env.js '); - process.exit(); -} - var monitoringScopes = [ - 'https://www.googleapis.com/auth/cloud-platform', - 'https://www.googleapis.com/auth/monitoring', - 'https://www.googleapis.com/auth/monitoring.read', - 'https://www.googleapis.com/auth/monitoring.write' + 'https://www.googleapis.com/auth/cloud-platform', + 'https://www.googleapis.com/auth/monitoring', + 'https://www.googleapis.com/auth/monitoring.read', + 'https://www.googleapis.com/auth/monitoring.write' ]; - -/** The project resource created from the project ID */ -var PROJECT_RESOURCE = 'projects/' + args[0]; - -/** This domain should be used for all custom metrics. */ -var CUSTOM_METRIC_DOMAIN = 'custom.googleapis.com'; - -/** This is the type of the custom metric */ -var CUSTOM_METRIC_TYPE = CUSTOM_METRIC_DOMAIN + '/custom_measurement'; - -/** This is the name of the custom metric */ -var CUSTOM_METRIC_NAME = PROJECT_RESOURCE + '/metricDescriptors/' + - CUSTOM_METRIC_TYPE; - /** * Returns the current timestamp in RFC33339 with milliseconds format. */ function getNow() { - var d = new Date(); - return JSON.parse(JSON.stringify(d).replace('Z', '000Z')); + var d = new Date(); + return JSON.parse(JSON.stringify(d).replace('Z', '000Z')); } /** @@ -63,60 +43,63 @@ function getNow() { * to start the window to view the metric written in. */ function getStartTime() { - var d = new Date(); - d.setHours(d.getHours() - 1); - return JSON.parse(JSON.stringify(d).replace('Z', '000Z')); + var d = new Date(); + d.setHours(d.getHours() - 1); + return JSON.parse(JSON.stringify(d).replace('Z', '000Z')); } +var CUSTOM_METRIC_DOMAIN = 'custom.googleapis.com'; + /** - * Gets random integer between low and high (exclusive). Used to fill in - * a random value for the measurement. + * Constructor function. The CustomMetrics class stores the type of metric + * in its instance class allowing unique ones to be used in tests. */ -function getRandomInt(low, high) { - return Math.floor(Math.random() * (high - low) + low); +function CustomMetrics(projectName, metricType) { + this.projectResource = 'projects/' + projectName; + this.metricType = CUSTOM_METRIC_DOMAIN + '/' + metricType; + this.metricName = this.projectResource + + '/metricDescriptors/' + this.metricType; + this.valueOverride = false; } - /** - * Creates a custo metric. For demonstration purposes, this is a hypothetical + * Creates a custom metric. For demonstration purposes, this is a hypothetical * measurement (measured in the 'items' unit, with random values written to * it. - * @param authClient The authorized Monitoring client. - * @param projectId - * @param callback + * @param {Object} authClient The authorized Monitoring client. + * @param {Function} callback Callback function. */ -function createCustomMetric(authClient, projectResource, callback) { - var monitoring = google.monitoring('v3'); +CustomMetrics.prototype.createCustomMetric = function (client, callback) { + var monitoring = google.monitoring('v3'); - monitoring.projects.metricDescriptors.create({ - auth: authClient, - name: projectResource, - resource: { - name: CUSTOM_METRIC_NAME, - type: CUSTOM_METRIC_TYPE, - labels: [ - { - key: 'environment', - valueType: 'STRING', - description: 'An abritrary measurement' - } - ], - metricKind: 'GAUGE', - valueType: 'INT64', - unit: 'items', - description: 'An arbitrary measurement.', - displayName: 'Custom Metric' - } - }, function (error, customMetric) { - if (error) { - console.error('Error Creating Custom Metric', error); - return; + monitoring.projects.metricDescriptors.create({ + auth: client, + name: this.projectResource, + resource: { + name: this.metricName, + type: this.metricType, + labels: [ + { + key: 'environment', + valueType: 'STRING', + description: 'An abritrary measurement' } - console.log('createCustomMetric: '); - console.log(customMetric); - callback(); - }); -} + ], + metricKind: 'GAUGE', + valueType: 'INT64', + unit: 'items', + description: 'An arbitrary measurement.', + displayName: 'Custom Metric' + } + }, function (err, customMetric) { + if (err) { + return callback(err); + } + + console.log('Created custom metric', customMetric); + callback(null, customMetric); + }); +}; /** * Writes a time series value for the custom metric just created. It uses a @@ -124,90 +107,117 @@ function createCustomMetric(authClient, projectResource, callback) { * demonstration purposes, this is a random value. For GAUGE measurements, * the start time and end time of the value must be the same. The * resource for this value is a hypothetical GCE instance. - * @param authClient The authorized Google Cloud Monitoring API client - * @param projectResource The project resource created from the project ID - * @param callback + * @param {Object} authClient The authorized Google Cloud Monitoring API client + * @param {Function} callback Callback Function. */ -function writeTimeSeriesForCustomMetric(client, projectResource, callback) { +CustomMetrics.prototype.writeTimeSeriesForCustomMetric = + function (client, callback) { var monitoring = google.monitoring('v3'); var now = getNow(); + monitoring.projects.timeSeries.create({ - auth: client, - name: projectResource, - resource: { - timeSeries: [{ - metric: { - type: CUSTOM_METRIC_TYPE, - labels: { - environment: 'STAGING' - } - }, - resource: { - type: 'gce_instance', - labels: { - instance_id: 'test_instance', - zone: 'us-central1-f' - } - }, - metricKind: 'GAUGE', - valueType: 'INT64', - points: { - interval: { - startTime: now, - endTime: now - }, - value: { - int64Value: getRandomInt(1, 20) - } - } - }] - } - }, function (error, timeSeries) { - if (error) { - console.error('Error writing time series', error); - return; - } - console.log('timeSeries: '); - console.log(timeSeries); - callback(); + auth: client, + name: this.projectResource, + resource: { + timeSeries: [{ + metric: { + type: this.metricType + }, + resource: { + type: 'gce_instance', + labels: { + instance_id: 'test_instance', + zone: 'us-central1-f' + } + }, + metricKind: 'GAUGE', + valueType: 'INT64', + points: { + interval: { + startTime: now, + endTime: now + }, + value: { + int64Value: this.getRandomInt(1, 20) + } + } + }] + } + }, function (err, timeSeries) { + if (err) { + return callback(err); + } + + console.log('Wrote time series', timeSeries); + callback(null, timeSeries); }); -} + }; /** * Lists the time series written for the custom metric. The window * to read the timeseries starts an hour ago and extends unti the current * time, so should include the metric value written by * the earlier calls. - * @param authClient The authorized Google Cloud Monitoring API client - * @param projectResource The project resource created from the project ID - * @param callback + * @param {Object} authClient The authorized Google Cloud Monitoring API client + * @param {Function} callback Callback function. */ -function listTimeSeries(client, projectResource, callback) { - var monitoring = google.monitoring('v3'); - var startTime = getStartTime(); - var endTime = getNow(); - monitoring.projects.timeSeries.list({ - auth: client, - name: projectResource, - filter: 'metric.type="' + CUSTOM_METRIC_TYPE + '"', - pageSize: 3, - 'interval.startTime': startTime, - 'interval.endTime': endTime - }, function (error, timeSeries) { - if (error) { - console.error('Error readTimeseries', error); - return; - } - console.log('readTimeseries '); - console.log(JSON.stringify(timeSeries)); - callback(); - }); -} +CustomMetrics.prototype.listTimeSeries = function (client, callback) { + var monitoring = google.monitoring('v3'); + var startTime = getStartTime(); + var endTime = getNow(); + + console.log('Reading metric type', this.metricType); + + monitoring.projects.timeSeries.list({ + auth: client, + name: this.projectResource, + filter: 'metric.type="' + this.metricType + '"', + pageSize: 3, + 'interval.startTime': startTime, + 'interval.endTime': endTime + }, function (err, timeSeries) { + if (err) { + return callback(err); + } + + console.log('Time series', timeSeries); + callback(null, timeSeries); + }); +}; + +/** + * @param {Object} authClient The authorized Google Cloud Monitoring API client + * @param {Function} callback Callback function. + */ +CustomMetrics.prototype.deleteMetric = function (client, callback) { + var monitoring = google.monitoring('v3'); -google.auth.getApplicationDefault(function (error, authClient) { - if (error) { - console.error(error); - process.exit(1); + console.log(this.metricName); + monitoring.projects.metricDescriptors.delete({ + auth: client, + name: this.metricName + }, function (err, result) { + if (err) { + return callback(err); + } + + console.log('Deleted metric', result); + callback(null, result); + }); +}; + +/** + * Gets random integer between low and high (exclusive). Used to fill in + * a random value for the measurement. + */ +CustomMetrics.prototype.getRandomInt = function (low, high) { + return Math.floor(Math.random() * (high - low) + low); +}; + +CustomMetrics.prototype.getMonitoringClient = function (callback) { + google.auth.getApplicationDefault(function (err, authClient) { + if (err) { + return callback(err); } // Depending on the environment that provides the default credentials @@ -215,21 +225,47 @@ google.auth.getApplicationDefault(function (error, authClient) { // require you to specify the scopes you need explicitly. // Check for this case, and inject the Cloud Storage scope if required. if (authClient.createScopedRequired && - authClient.createScopedRequired()) { - authClient = authClient.createScoped(monitoringScopes); + authClient.createScopedRequired()) { + authClient = authClient.createScoped(monitoringScopes); } + callback(null, authClient); + }); +}; +// Run the examples +exports.main = function (projectId, name, cb) { + var customMetrics = new CustomMetrics(projectId, name); + customMetrics.getMonitoringClient(function (err, authClient) { + if (err) { + return cb(err); + } // Create the service object. async.series([ - function (callback) { - createCustomMetric(authClient, PROJECT_RESOURCE, callback); - }, function (callback) { - writeTimeSeriesForCustomMetric(authClient, - PROJECT_RESOURCE, callback); - }, function (callback) { - // wait 2 seconds for the write to be received - setTimeout(function () { - listTimeSeries(authClient, PROJECT_RESOURCE, callback); - }, 2000); - }]); -}); + function (cb) { + customMetrics.createCustomMetric(authClient, cb); + }, + function (cb) { + setTimeout(function () { + customMetrics.writeTimeSeriesForCustomMetric(authClient, cb); + }, 5000); + }, + function (cb) { + setTimeout(function() { + customMetrics.listTimeSeries(authClient, cb); + }, 5000); + }, + function (cb) { + customMetrics.deleteMetric(authClient, cb); + } + ], cb); + }); +}; + +if (require.main === module) { + var args = process.argv.slice(2); + exports.main( + args[0] || process.env.GCLOUD_PROJECT, + args[1] || 'custom_measurement', + console.log + ); +} diff --git a/monitoring/list_resources.js b/monitoring/list_resources.js index bf8a6c2ccb..14fcb3f1e2 100644 --- a/monitoring/list_resources.js +++ b/monitoring/list_resources.js @@ -20,147 +20,170 @@ var google = require('googleapis'); var async = require('async'); -var args = process.argv.slice(2); -if (args.length !== 1) { - console.log('Usage: node list_resources.js '); - process.exit(); -} - var monitoringScopes = [ - 'https://www.googleapis.com/auth/cloud-platform', - 'https://www.googleapis.com/auth/monitoring', - 'https://www.googleapis.com/auth/monitoring.read', - 'https://www.googleapis.com/auth/monitoring.write' + 'https://www.googleapis.com/auth/cloud-platform', + 'https://www.googleapis.com/auth/monitoring', + 'https://www.googleapis.com/auth/monitoring.read', + 'https://www.googleapis.com/auth/monitoring.write' ]; -var PROJECT_ID = 'projects/' + args[0]; var METRIC = 'compute.googleapis.com/instance/cpu/usage_time'; - /** * Returns an hour ago minus 5 minutes in RFC33339 format. */ function getStartTime() { - var d = new Date(); - d.setHours(d.getHours() - 1); - d.setMinutes(d.getMinutes() - 5); - return JSON.parse(JSON.stringify(d).replace('Z', '000Z')); + var d = new Date(); + d.setHours(d.getHours() - 1); + d.setMinutes(d.getMinutes() - 5); + return JSON.parse(JSON.stringify(d).replace('Z', '000Z')); } /** * Returns an hour ago in RFC33339 format. */ function getEndTime() { - var d = new Date(); - d.setHours(d.getHours() - 1); - return JSON.parse(JSON.stringify(d).replace('Z', '000Z')); + var d = new Date(); + d.setHours(d.getHours() - 1); + return JSON.parse(JSON.stringify(d).replace('Z', '000Z')); } -/** - * This Lists all the resources available to be monitored in the API. - * - * @param {googleAuthClient} authClient - The authenticated Google api client - * @param {String} projectId - the project id - * @param {requestCallback} callback - a function to be called when the server - * responds with the list of monitored resource descriptors - */ -function listMonitoredResourceDescriptors(authClient, projectId, callback) { +var ListResources = { + + /** + * This Lists all the resources available to be monitored in the API. + * + * @param {googleAuthClient} authClient - The authenticated Google api client + * @param {String} projectId - the project id + * @param {requestCallback} callback - a function to be called when the server + * responds with the list of monitored resource descriptors + */ + listMonitoredResourceDescriptors: function (authClient, projectId, callback) { var monitoring = google.monitoring('v3'); monitoring.projects.monitoredResourceDescriptors.list({ - auth: authClient, - name: projectId, - }, function (error, monitoredResources) { - if (error) { - console.error( - 'Error Retrieving Monitored Resource Descriptors', error); - return; - } - console.log('listMonitoredResourceDescriptors: '); - console.log(monitoredResources); - callback(); + auth: authClient, + name: projectId, + pageSize: 3 + }, function (err, monitoredResources) { + if (err) { + return callback(err); + } + + console.log('Monitored resources', monitoredResources); + callback(null, monitoredResources); }); -} - -/** - * This Lists the metric descriptors that start with our METRIC name, in this - * case the CPU usage time. - * @param {googleAuthClient} authClient - The authenticated Google api client - * @param {String} projectId - the project id - * @param {requestCallback} callback - a function to be called when the server - * responds with the list of monitored resource descriptors - */ -function listMetricDescriptors(authClient, projectId, callback) { + }, + + /** + * This Lists the metric descriptors that start with our METRIC name, in this + * case the CPU usage time. + * @param {googleAuthClient} authClient - The authenticated Google api client + * @param {String} projectId - the project id + * @param {requestCallback} callback - a function to be called when the server + * responds with the list of monitored resource descriptors + */ + listMetricDescriptors: function (authClient, projectId, callback) { var monitoring = google.monitoring('v3'); monitoring.projects.metricDescriptors.list({ - auth: authClient, - filter: 'metric.type="' + METRIC + '"', - name: projectId - }, function (error, metricDescriptors) { - if (error) { - console.error('Error Retrieving Metric Descriptors', error); - return; - } - console.log('listMetricDescriptors'); - console.log(metricDescriptors); - callback(); + auth: authClient, + filter: 'metric.type="' + METRIC + '"', + pageSize: 3, + name: projectId + }, function (err, metricDescriptors) { + if (err) { + return callback(err); + } + + console.log('Metric descriptors', metricDescriptors); + callback(null, metricDescriptors); }); -} - -/** - * This Lists all the timeseries created between START_TIME and END_TIME - * for our METRIC. - * @param {googleAuthClient} authClient - The authenticated Google api client - * @param {String} projectId - the project id - * @param {requestCallback} callback - a function to be called when the server - * responds with the list of monitored resource descriptors - */ -function listTimeseries(authClient, projectId, callback) { + }, + + /** + * This Lists all the timeseries created between START_TIME and END_TIME + * for our METRIC. + * @param {googleAuthClient} authClient - The authenticated Google api client + * @param {String} projectId - the project id + * @param {requestCallback} callback - a function to be called when the server + * responds with the list of monitored resource descriptors + */ + listTimeseries: function (authClient, projectId, callback) { var monitoring = google.monitoring('v3'); var startTime = getStartTime(); var endTime = getEndTime(); monitoring.projects.timeSeries.list({ - auth: authClient, - filter: 'metric.type="' + METRIC + '"', - pageSize: 3, - 'interval.startTime': startTime, - 'interval.endTime': endTime, - name: projectId - }, function (error, timeSeries) { - if (error) { - console.error('Error Retrieving Timeseries', error); - return; - } - console.log('listTimeseries'); - console.log(timeSeries); - callback(); + auth: authClient, + filter: 'metric.type="' + METRIC + '"', + pageSize: 3, + 'interval.startTime': startTime, + 'interval.endTime': endTime, + name: projectId + }, function (err, timeSeries) { + if (err) { + return callback(err); + } + + console.log('Time series', timeSeries); + callback(null, timeSeries); }); -} - - -google.auth.getApplicationDefault(function (error, authClient) { - if (error) { - console.error(error); - process.exit(); - } - - // Depending on the environment that provides the default credentials - // (e.g. Compute Engine, App Engine), the credentials retrieved may require - // you to specify the scopes you need explicitly. - // Check for this case, and inject the Cloud Storage scope if required. - if (authClient.createScopedRequired && + }, + + getMonitoringClient: function (callback) { + google.auth.getApplicationDefault(function (err, authClient) { + if (err) { + return callback(err); + } + // Depending on the environment that provides the default credentials + // (e.g. Compute Engine, App Engine), the credentials retrieved may + // require you to specify the scopes you need explicitly. + // Check for this case, and inject the Cloud Storage scope if required. + if (authClient.createScopedRequired && authClient.createScopedRequired()) { authClient = authClient.createScoped(monitoringScopes); + } + callback(null, authClient); + }); + } +}; + +exports.main = function (projectId, cb) { + var projectName = 'projects/' + projectId; + ListResources.getMonitoringClient(function (err, authClient) { + if (err) { + return cb(err); } - // Create the service object. async.series([ - function (callback) { - listMonitoredResourceDescriptors(authClient, PROJECT_ID, callback); - }, function (callback) { - listMetricDescriptors(authClient, PROJECT_ID, callback); - }, function (callback) { - listTimeseries(authClient, PROJECT_ID, callback); - }] - ); -}); + function (cb) { + ListResources.listMonitoredResourceDescriptors( + authClient, + projectName, + cb + ); + }, + function(cb) { + ListResources.listMetricDescriptors( + authClient, + projectName, + cb + ); + }, + function(cb) { + ListResources.listTimeseries( + authClient, + projectName, + cb + ); + } + ], cb); + }); +}; + +if (require.main === module) { + var args = process.argv.slice(2); + exports.main( + args[0] || process.env.GCLOUD_PROJECT, + console.log + ); +} diff --git a/test/monitoring/create_custom_metric.test.js b/test/monitoring/create_custom_metric.test.js new file mode 100644 index 0000000000..83a5092a2d --- /dev/null +++ b/test/monitoring/create_custom_metric.test.js @@ -0,0 +1,44 @@ +// Copyright 2015-2016, Google, Inc. +// 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'; + +var assert = require('assert'); +var customMetricsExample = require('../../monitoring/create_custom_metric'); + +/** Refactored out to keep lines shorter */ +function getPointValue(timeSeries) { + return timeSeries.timeSeries[0].points[0].value.int64Value; +} + +it('should create and read back a custom metric', function (done) { + this.timeout(20000); + customMetricsExample.main( + process.env.GCLOUD_PROJECT, + Math.random().toString(36).substring(7), + function (err, results) { + assert.ifError(err); + assert.equal(results.length, 4); + // Result of creating metric + assert.ok(typeof results[0].name === 'string'); + // Result of writing time series + assert.deepEqual(results[1], {}); + // Result of reading time series + assert.ok(typeof getPointValue(results[2]) === 'string'); + assert.ok(!isNaN(parseInt(getPointValue(results[2]), 10))); + // Result of deleting metric + assert.deepEqual(results[3], {}); + done(); + } + ); +}); diff --git a/test/monitoring/list_resources.test.js b/test/monitoring/list_resources.test.js new file mode 100644 index 0000000000..b610dcc61a --- /dev/null +++ b/test/monitoring/list_resources.test.js @@ -0,0 +1,35 @@ +// Copyright 2015-2016, Google, Inc. +// 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'; + +var assert = require('assert'); +var listResourcesExample = require('../../monitoring/list_resources'); + +it('should list a bunch of stuff', function (done) { + this.timeout(20000); + listResourcesExample.main( + process.env.GCLOUD_PROJECT, + function (err, results) { + assert.ifError(err); + assert.equal(results.length, 3); + // Monitored resources + assert.ok(Array.isArray(results[0].resourceDescriptors)); + // Metric descriptors + assert.ok(Array.isArray(results[1].metricDescriptors)); + // Time series + assert.ok(Array.isArray(results[2].timeSeries)); + done(); + } + ); +});