From 638f902287235922878cdb4f45de30747b22ed85 Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh Date: Tue, 13 Dec 2016 22:56:04 -0800 Subject: [PATCH] refactorings * rename debugletapi to controller * rename apiclasses.js to status-message.js * add a debuggee class * add a basic debuggee object --- .jshintrc | 1 - src/agent/debuglet.js | 4 +- src/agent/state.js | 2 +- src/agent/v8debugapi.js | 3 +- src/{debugletapi.js => controller.js} | 93 +++----------- src/debuggee.js | 114 ++++++++++++++++++ src/index.js | 34 +++--- src/{apiclasses.js => status-message.js} | 10 +- ...test-debugletapi.js => test-controller.js} | 5 +- ...test-debugletapi.js => test-controller.js} | 9 +- test/test-debuggee.js | 68 +++++++++++ test/test-v8debugapi.js | 2 +- 12 files changed, 230 insertions(+), 115 deletions(-) rename src/{debugletapi.js => controller.js} (71%) create mode 100644 src/debuggee.js rename src/{apiclasses.js => status-message.js} (93%) rename system-test/{test-debugletapi.js => test-controller.js} (95%) rename test/{test-debugletapi.js => test-controller.js} (96%) create mode 100644 test/test-debuggee.js diff --git a/.jshintrc b/.jshintrc index bf58ca09..5f971405 100644 --- a/.jshintrc +++ b/.jshintrc @@ -8,7 +8,6 @@ "indent": 2, "latedef": "nofunc", "maxlen": 100, - "newcap": true, "node": true, "noarg": true, "quotmark": "single", diff --git a/src/agent/debuglet.js b/src/agent/debuglet.js index e12a6e56..2347878c 100644 --- a/src/agent/debuglet.js +++ b/src/agent/debuglet.js @@ -23,10 +23,10 @@ var util = require('util'); var semver = require('semver'); var v8debugapi = require('./v8debugapi.js'); -var DebugletApi = require('../debugletapi.js'); +var DebugletApi = require('../controller.js'); var scanner = require('./scanner.js'); var Logger = require('@google/cloud-diagnostics-common').logger; -var StatusMessage = require('../apiclasses.js').StatusMessage; +var StatusMessage = require('../status-message.js'); var SourceMapper = require('./sourcemapper.js'); var assert = require('assert'); diff --git a/src/agent/state.js b/src/agent/state.js index a0b147d1..fa988a7d 100644 --- a/src/agent/state.js +++ b/src/agent/state.js @@ -32,7 +32,7 @@ var remove = lodash.remove; var flatten = lodash.flatten; var isEmpty = lodash.isEmpty; -var StatusMessage = require('../apiclasses.js').StatusMessage; +var StatusMessage = require('../status-message.js'); // Error message indices into the resolved variable table. var BUFFER_FULL_MESSAGE_INDEX = 0; diff --git a/src/agent/v8debugapi.js b/src/agent/v8debugapi.js index 9a4f6580..cd29495d 100644 --- a/src/agent/v8debugapi.js +++ b/src/agent/v8debugapi.js @@ -23,8 +23,7 @@ /** @const */ var state = require('./state.js'); /** @const */ var logModule = require('@google/cloud-diagnostics-common').logger; -/** @const */ var apiclasses = require('../apiclasses.js'); -/** @const */ var StatusMessage = apiclasses.StatusMessage; +/** @const */ var StatusMessage = require('../status-message.js'); /** @const */ var messages = { INVALID_BREAKPOINT: 'invalid snapshot - id or location missing', diff --git a/src/debugletapi.js b/src/controller.js similarity index 71% rename from src/debugletapi.js rename to src/controller.js index e80508d8..89f15c8a 100644 --- a/src/debugletapi.js +++ b/src/controller.js @@ -16,28 +16,23 @@ 'use strict'; +/*! + * @module debug/controller + */ + var fs = require('fs'); -var path = require('path'); var assert = require('assert'); -var crypto = require('crypto'); -var pjson = require('../package.json'); var qs = require('querystring'); var utils = require('@google/cloud-diagnostics-common').utils; -var StatusMessage = require('./apiclasses.js').StatusMessage; +var Debuggee = require('./debuggee.js'); /** @const {string} Cloud Debug API endpoint */ var API = 'https://clouddebugger.googleapis.com/v2/controller'; -/* c.f. the Java Debugger agent */ -/** @const {string} */ var DEBUGGEE_MODULE_LABEL = 'module'; -/** @const {string} */ var DEBUGGEE_MAJOR_VERSION_LABEL = 'version'; -/** @const {string} */ var DEBUGGEE_MINOR_VERSION_LABEL = 'minorversion'; - - /** * @constructor */ -function DebugletApi(config, debug) { +function Controller(config, debug) { config = config || {}; /** @priavate {Debug} */ @@ -68,7 +63,7 @@ function DebugletApi(config, debug) { * @param {Logger} logger a logger * @param {!function(?Error)} callback */ -DebugletApi.prototype.init = function(uid, logger, callback) { +Controller.prototype.init = function(uid, logger, callback) { var that = this; that.uid_ = uid; that.nextWaitToken_ = null; @@ -105,7 +100,7 @@ DebugletApi.prototype.init = function(uid, logger, callback) { * Register to the API * @param {!function(?Error,Object=)} callback */ -DebugletApi.prototype.register = function(callback) { +Controller.prototype.register = function(callback) { this.register_(null, callback); }; @@ -114,7 +109,7 @@ DebugletApi.prototype.register = function(callback) { * Register an error to the API * @param {!string} errorMessage to be reported to the Debug API */ -DebugletApi.prototype.registerError = function(message) { +Controller.prototype.registerError = function(message) { this.register_(message, function() {}); }; @@ -126,66 +121,12 @@ DebugletApi.prototype.registerError = function(message) { * @param {!function(?Error,Object=)} callback * @private */ -DebugletApi.prototype.register_ = function(errorMessage, callback) { +Controller.prototype.register_ = function(errorMessage, callback) { var that = this; - - var cwd = process.cwd(); - var mainScript = path.relative(cwd, process.argv[1]); - - var version = 'google.com/node-' + - (that.onGCP ? 'gcp' : 'standalone') + - '/v' + pjson.version; - var desc = process.title + ' ' + mainScript; - var labels = { - 'main script': mainScript, - 'process.title': process.title, - 'node version': process.versions.node, - 'V8 version': process.versions.v8, - 'agent.name': pjson.name, - 'agent.version': pjson.version, - 'projectid': that.project_ - }; - - var serviceName = that.serviceName_; - if (serviceName) { - desc += ' module:' + serviceName; - labels[DEBUGGEE_MODULE_LABEL] = serviceName; - } - - var serviceVersion = that.serviceVersion_; - if (serviceVersion) { - desc += ' version:' + serviceVersion; - if (serviceVersion !== 'default') { - labels[DEBUGGEE_MAJOR_VERSION_LABEL] = serviceVersion; - } - } - - var descriptor = that.descriptor_; - if (descriptor) { - desc += ' description:' + descriptor; - } - - if (process.env.GAE_MINOR_VERSION) { - labels[DEBUGGEE_MINOR_VERSION_LABEL] = process.env.GAE_MINOR_VERSION; - } - - var uniquifier = desc + version + that.uid_ + that.sourceContext_ + - JSON.stringify(labels); - uniquifier = crypto.createHash('sha1').update(uniquifier).digest('hex'); - - var debuggee = { - project: that.project_, - uniquifier: uniquifier, - description: desc, - agentVersion: version, - labels: labels, - sourceContexts: [that.sourceContext_] - }; - - if (errorMessage) { - debuggee.status = new StatusMessage(StatusMessage.UNSPECIFIED, errorMessage, - true); - } + var debuggee = new Debuggee( + that.project_, that.uid_, + {service: that.serviceName_, version: that.serviceVersion_}, + that.sourceContext_, that.descriptor_, errorMessage, that.onGCP); var options = { uri: API + '/debuggees/register', @@ -214,7 +155,7 @@ DebugletApi.prototype.register_ = function(errorMessage, callback) { * Fetch the list of breakpoints from the server. Assumes we have registered. * @param {!function(?Error,Object=,Object=)} callback accepting (err, response, body) */ -DebugletApi.prototype.listBreakpoints = function(callback) { +Controller.prototype.listBreakpoints = function(callback) { var that = this; assert(that.debuggeeId_, 'should register first'); var query = { success_on_timeout: true }; @@ -250,7 +191,7 @@ DebugletApi.prototype.listBreakpoints = function(callback) { * @param {!Breakpoint} breakpoint * @param {!Function} callback accepting (err, body) */ -DebugletApi.prototype.updateBreakpoint = +Controller.prototype.updateBreakpoint = function(breakpoint, callback) { assert(this.debuggeeId_, 'should register first'); @@ -280,4 +221,4 @@ DebugletApi.prototype.updateBreakpoint = } }; -module.exports = DebugletApi; +module.exports = Controller; diff --git a/src/debuggee.js b/src/debuggee.js new file mode 100644 index 00000000..cc5ac821 --- /dev/null +++ b/src/debuggee.js @@ -0,0 +1,114 @@ +/** + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 crypto = require('crypto'); +var path = require('path'); +var pjson = require('../package.json'); +var StatusMessage = require('./status-message.js'); +var _ = require('lodash'); + +/** + * Creates a Debuggee service object. + * @ref https://cloud.google.com/debugger/api/reference/rest/v2/Debuggee + * + * @param {string} projectId - Google Cloud Project ID + * @param {string} uid - unique identified for the source code on this instance. + * @param {?object} serviceContext + * @param {string} serviceContext.service - A string identifying the service/ + * module that this instance belongs to. + * @param {string} serviceContext.version - A string identifying the version of + * the service. + * @param {?object} sourceContext + * @param {?string} description - A user specified string identifying this + * debuggable instance. + * @param {?string} errorMessage - A error string to register this as a erroring + * debuggable instance. This is useful if we have a problem starting the + * debugger support, and want to report to the API so that the users has a + * way of noticing. + * @param {?boolean} onGCP - set to true when the debuggee is running inside + * Google Cloud Platform. + */ +function Debuggee(projectId, uid, serviceContext, sourceContext, description, + errorMessage, onGCP) { + if (!(this instanceof Debuggee)) { + return new Debuggee(projectId, uid, serviceContext, sourceContext, + description, errorMessage, onGCP); + } + + if (!_.isString(projectId)) { + throw new Error('projectId must be a string'); + } + if (!_.isString(uid)) { + throw new Error('uid must be a string'); + } + + var cwd = process.cwd(); + var mainScript = path.relative(cwd, process.argv[1]); + + var version = 'google.com/node-' + (onGCP ? 'gcp' : 'standalone') + '/v' + + pjson.version; + var desc = process.title + ' ' + mainScript; + + var labels = { + 'main script': mainScript, + 'process.title': process.title, + 'node version': process.versions.node, + 'V8 version': process.versions.v8, + 'agent.name': pjson.name, + 'agent.version': pjson.version, + 'projectid': projectId + }; + + if (serviceContext) { + if (_.isString(serviceContext.service) && + serviceContext.service !== 'default') { + // As per app-engine-ids, the module label is not reported + // when it happens to be 'default'. + labels.module = serviceContext.service; + desc += ' module:' + serviceContext.service; + } + + if (_.isString(serviceContext.version)) { + labels.version = serviceContext.version; + desc += ' version:' + serviceContext.version; + } + } + + if (description) { + desc += ' description:' + description; + } + + var uniquifier = + desc + version + uid + sourceContext + JSON.stringify(labels); + uniquifier = crypto.createHash('sha1').update(uniquifier).digest('hex'); + + if (errorMessage) { + this.statusMessage = + new StatusMessage(StatusMessage.UNSPECIFIED, errorMessage, true); + } + + this.project = projectId; + this.uniquifier = uniquifier; + this.description = desc; + this.agentVersion = version; + this.labels = labels; + if (sourceContext) { + this.sourceContexts = [sourceContext]; + } +} + +module.exports = Debuggee; diff --git a/src/index.js b/src/index.js index 59deac75..0bf03b18 100644 --- a/src/index.js +++ b/src/index.js @@ -24,26 +24,25 @@ var _ = require('lodash'); /** *

- * **This is an experimental release of Stackdriver Debug.** This API is not + * **This is an experimental release of Stackdriver Debug.** This API is not * covered by any SLA of deprecation policy and may be subject to backwards * incompatible changes. *

- * - * This module provides Stackdriver Debugger support for Node.js applications. - * [Stackdriver Debugger](https://cloud.google.com/debug/) is a feature of + * + * This module provides Stackdriver Debugger support for Node.js applications. + * [Stackdriver Debugger](https://cloud.google.com/debug/) is a feature of * [Google Cloud Platform](https://cloud.google.com/) that lets you debug your * applications in production without stopping or pausing your application. - * + * * This module provides an agent that lets you automatically enable debugging - * without changes to your application. - * + * without changes to your application. + * * @constructor * @alias module:debug - * + * * @resource [What is Stackdriver Debug]{@link https://cloud.google.com/debug/} - * - * @param {object} options - [Configuration object](#/docs). NOTE: at the moment - * this parameter is ignored. + * + * @param {object} options - [Configuration object](#/docs) */ function Debug(options) { if (!(this instanceof Debug)) { @@ -52,9 +51,10 @@ function Debug(options) { } var config = { + projectIdRequired: false, baseUrl: 'https://clouddebugger.googleapis.com/v2', scopes: [ - // TODO: do we still need cloud-platform scope? + // TODO: do we still need cloud-platform scope? 'https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/cloud_debugletcontroller' // TODO: the client library probably wants cloud_debugger scope as well. @@ -90,13 +90,14 @@ var debuglet; /** * Start the Debug agent that will make your application available for debugging * with Stackdriver Debug. - * + * * @param {object=} config - Debug configuration. TODO(ofrobots): get rid of * config.js and include jsdoc here? * TODO: add an optional callback function. - * - * @resource [Introductory video]{@link https://www.youtube.com/watch?v=tyHcK_kAOpw} - * + * + * @resource [Introductory video]{@link + * https://www.youtube.com/watch?v=tyHcK_kAOpw} + * * @example * debug.startAgent(); */ @@ -114,4 +115,3 @@ Debug.prototype.startAgent = function(config) { }; module.exports = Debug; - diff --git a/src/apiclasses.js b/src/status-message.js similarity index 93% rename from src/apiclasses.js rename to src/status-message.js index 215aa8b8..fd3e346a 100644 --- a/src/apiclasses.js +++ b/src/status-message.js @@ -1,6 +1,6 @@ /** - * Copyright 2015 Google Inc. All Rights Reserved. * + * Copyright 2015 Google Inc. All Rights Reserved. * 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 @@ -14,14 +14,8 @@ * limitations under the License. */ -// Classes corresponding to the Cloud Debuglet API - 'use strict'; -module.exports = { - StatusMessage: StatusMessage -}; - /** * Status Message to be sent to the server * @constructor @@ -41,3 +35,5 @@ function StatusMessage(refersTo, description, isError) { /** @const */ StatusMessage.BREAKPOINT_EXPRESSION = 'BREAKPOINT_EXPRESSION'; /** @const */ StatusMessage.VARIABLE_NAME = 'VARIABLE_NAME'; /** @const */ StatusMessage.VARIABLE_VALUE = 'VARIABLE_VALUE'; + +module.exports = StatusMessage; diff --git a/system-test/test-debugletapi.js b/system-test/test-controller.js similarity index 95% rename from system-test/test-debugletapi.js rename to system-test/test-controller.js index 285a37b1..2f183bb3 100644 --- a/system-test/test-debugletapi.js +++ b/system-test/test-controller.js @@ -25,9 +25,10 @@ assert.ok( 'Need to have GCLOUD_PROJECT defined ' + 'along with valid application default credentials to be able to run this ' + 'test'); +var config = {}; -var debug = require('../')(); -var DebugletApi = require('../src/debugletapi.js'); +var debug = require('../')(config); +var DebugletApi = require('../src/controller.js'); describe('Debugletapi', function() { diff --git a/test/test-debugletapi.js b/test/test-controller.js similarity index 96% rename from test/test-debugletapi.js rename to test/test-controller.js index fe85435b..70b519ca 100644 --- a/test/test-debugletapi.js +++ b/test/test-controller.js @@ -19,7 +19,6 @@ var assert = require('assert'); var nock = require('nock'); var request = require('./auth-request.js'); var proxyquire = require('proxyquire'); -var agentVersion = require('../package.json').version; // the tests in this file rely on the GCLOUD_PROJECT environment variable // not being set @@ -30,7 +29,7 @@ delete process.env.GCLOUD_PROJECT; var utils = { getProjectNumber: function(callback) { callback(null, 'project123'); } }; -var DebugletApi = proxyquire('../src/debugletapi.js', { +var DebugletApi = proxyquire('../src/controller.js', { '@google/cloud-diagnostics-common': { logger: null, utils: utils @@ -86,8 +85,7 @@ describe('Debuglet API', function() { it('should get a debuggeeId', function(done) { var scope = nock(url) .post(api + '/debuggees/register', function (body) { - return body.debuggee.agentVersion === - ('google.com/node-gcp/v' + agentVersion); + return body.debuggee.agentVersion.indexOf('node-gcp') !== -1; }) .reply(200, { debuggee: { id: 'fake-debuggee' }, @@ -112,8 +110,7 @@ describe('Debuglet API', function() { debugletapi.init('uid1234', { warn: function() {} }, function(err, project) { var scope = nock(url) .post(api + '/debuggees/register', function (body) { - return body.debuggee.agentVersion === - ('google.com/node-standalone/v' + agentVersion); + return body.debuggee.agentVersion.indexOf('standalone') !== -1; }) .reply(200, { debuggee: { id: 'fake-debuggee' }, diff --git a/test/test-debuggee.js b/test/test-debuggee.js new file mode 100644 index 00000000..6ad68bc2 --- /dev/null +++ b/test/test-debuggee.js @@ -0,0 +1,68 @@ +/** + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 Debuggee = require('../src/debuggee.js'); + +describe('Debuggee', function() { + + it('should create a Debuggee instance on valid input', function() { + var debuggee = new Debuggee('project', 'uid'); + assert.ok(debuggee instanceof Debuggee); + }); + + it('should create a Debuggee on a call without new', function() { + var debuggee = Debuggee('project', 'uid'); + assert.ok(debuggee instanceof Debuggee); + }); + + it('should throw on invalid input', function() { + assert.throws(function() { new Debuggee(); }); + assert.throws(function() { new Debuggee(5); }); + assert.throws(function() { new Debuggee(undefined); }); + assert.throws(function() { new Debuggee('test'); }); + assert.throws(function() { new Debuggee('test', null); }); + }); + + it('should have sensible labels', function() { + var debuggee = new Debuggee('some project', 'id', { + service: 'some-service', + version: 'production' + }); + assert.ok(debuggee); + assert.ok(debuggee.labels); + assert.strictEqual(debuggee.labels.module, 'some-service'); + assert.strictEqual(debuggee.labels.version, 'production'); + }); + + it('should not add a module label when service is default', function() { + var debuggee = new Debuggee('fancy-project', 'very-unique', + {service: 'default', version: 'yellow.5'}); + assert.ok(debuggee); + assert.ok(debuggee.labels); + assert.strictEqual(debuggee.labels.module, undefined); + assert.strictEqual(debuggee.labels.version, 'yellow.5'); + }); + + it('should have an error statusMessage with the appropriate arg', function() { + var debuggee = new Debuggee('a', 'b', undefined, undefined, undefined, + 'Some Error Message'); + assert.ok(debuggee); + assert.ok(debuggee.statusMessage); + }); + +}); diff --git a/test/test-v8debugapi.js b/test/test-v8debugapi.js index 06d16f93..60be0a91 100644 --- a/test/test-v8debugapi.js +++ b/test/test-v8debugapi.js @@ -35,7 +35,7 @@ var assert = require('assert'); var v8debugapi = require('../src/agent/v8debugapi.js'); var logModule = require('@google/cloud-diagnostics-common').logger; var config = require('../src/config.js').debug; -var StatusMessage = require('../src/apiclasses.js').StatusMessage; +var StatusMessage = require('../src/status-message.js'); var scanner = require('../src/agent/scanner.js'); var SourceMapper = require('../src/agent/sourcemapper.js'); var path = require('path');