Skip to content
This repository has been archived by the owner on Apr 3, 2024. It is now read-only.

Commit

Permalink
fix precedence for where the projectId is acquired from (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
ofrobots authored Dec 16, 2016
1 parent 638f902 commit f7de637
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 151 deletions.
102 changes: 51 additions & 51 deletions src/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ function Controller(config, debug) {
/** @priavate {Debug} */
this.debug_ = debug;

/** @private {string} numeric project id */
this.project_ = null;
/** @private {string} project id */
this.project_ = config.projectId || process.env.GCLOUD_PROJECT;

/** @private {string} debuggee id provided by the server once registered */
this.debuggeeId_ = null;
Expand Down Expand Up @@ -68,31 +68,34 @@ Controller.prototype.init = function(uid, logger, callback) {
that.uid_ = uid;
that.nextWaitToken_ = null;

function complete(err, project) {
if (err) {
callback(err, project);
return;
// We need to figure out whether we are running on GCP. We can use our ability
// to access the metadata service as a test for that.
// TODO: change this to getProjectId in the future.
utils.getProjectNumber(function(err, metadataProject) {
// We should get an error if we are not on GCP.
that.onGCP = !err;

// We prefer to use the locally available projectId as that is least
// surprising to users.
var project = that.project_ || metadataProject;

// We if don't have a projectId by now, we fail with an error.
if (!project) {
return callback(err);
} else {
that.project_ = project;
}
that.project_ = project;

// Locate the source context.
fs.readFile('source-context.json', 'utf8', function(err, data) {
try {
that.sourceContext_ = JSON.parse(data);
} catch (e) {
logger.warn('Malformed source-context.json file.');
// But we keep on going.
}
callback(null, project);
return callback(null, project);
});
}

utils.getProjectNumber(function(err, project) {
that.onGCP = !!project;
if (process.env.GCLOUD_PROJECT) {
complete(null, process.env.GCLOUD_PROJECT);
} else {
complete(err, project);
}
});
};

Expand Down Expand Up @@ -132,13 +135,14 @@ Controller.prototype.register_ = function(errorMessage, callback) {
uri: API + '/debuggees/register',
method: 'POST',
json: true,
body: { debuggee: debuggee }
body: {debuggee: debuggee}
};
this.debug_.request(options, function(err, body, response) {
if (err) {
callback(err);
} else if (response.statusCode !== 200) {
callback(new Error('unable to register, statusCode ' + response.statusCode));
callback(
new Error('unable to register, statusCode ' + response.statusCode));
} else if (!body.debuggee) {
callback(new Error('invalid response body from server'));
} else if (body.debuggee.isDisabled) {
Expand All @@ -153,18 +157,19 @@ Controller.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)
* @param {!function(?Error,Object=,Object=)} callback accepting (err, response,
* body)
*/
Controller.prototype.listBreakpoints = function(callback) {
var that = this;
assert(that.debuggeeId_, 'should register first');
var query = { success_on_timeout: true };
var query = {success_on_timeout: true};
if (that.nextWaitToken_) {
query.waitToken = that.nextWaitToken;
}

var uri = API + '/debuggees/' + encodeURIComponent(that.debuggeeId_) +
'/breakpoints?' + qs.stringify(query);
'/breakpoints?' + qs.stringify(query);
that.debug_.request({uri: uri, json: true}, function(err, body, response) {
if (!response) {
callback(err || new Error('unknown error - request response missing'));
Expand All @@ -176,7 +181,7 @@ Controller.prototype.listBreakpoints = function(callback) {
return;
} else if (response.statusCode !== 200) {
callback(new Error('unable to list breakpoints, status code ' +
response.statusCode));
response.statusCode));
return;
} else {
body = body || {};
Expand All @@ -191,34 +196,29 @@ Controller.prototype.listBreakpoints = function(callback) {
* @param {!Breakpoint} breakpoint
* @param {!Function} callback accepting (err, body)
*/
Controller.prototype.updateBreakpoint =
function(breakpoint, callback) {
assert(this.debuggeeId_, 'should register first');

breakpoint.action = 'capture';
breakpoint.isFinalState = true;
var options = {
uri: API + '/debuggees/' + encodeURIComponent(this.debuggeeId_) +
'/breakpoints/' + encodeURIComponent(breakpoint.id),
json: true,
method: 'PUT',
body: {
debuggeeId: this.debuggeeId_,
breakpoint: breakpoint
}
};

// We need to have a try/catch here because a JSON.stringify will be done
// by request. Some V8 debug mirror objects get a throw when we attempt to
// stringify them. The try-catch keeps it resilient and avoids crashing the
// user's app.
try {
this.debug_.request(options, function(err, body, response) {
callback(err, body);
});
} catch (error) {
callback(error);
}
Controller.prototype.updateBreakpoint = function(breakpoint, callback) {
assert(this.debuggeeId_, 'should register first');

breakpoint.action = 'capture';
breakpoint.isFinalState = true;
var options = {
uri: API + '/debuggees/' + encodeURIComponent(this.debuggeeId_) +
'/breakpoints/' + encodeURIComponent(breakpoint.id),
json: true,
method: 'PUT',
body: {debuggeeId: this.debuggeeId_, breakpoint: breakpoint}
};

// We need to have a try/catch here because a JSON.stringify will be done
// by request. Some V8 debug mirror objects get a throw when we attempt to
// stringify them. The try-catch keeps it resilient and avoids crashing the
// user's app.
try {
this.debug_.request(options,
function(err, body, response) { callback(err, body); });
} catch (error) {
callback(error);
}
};

module.exports = Controller;
166 changes: 100 additions & 66 deletions test/standalone/test-config-credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,115 +23,149 @@ var logger = require('@google/cloud-diagnostics-common').logger;
var defaultConfig = require('../../src/config.js').debug;
var Debuglet = require('../../src/agent/debuglet.js');

var envProject = process.env.GCLOUD_PROJECT;

nock.disableNetConnect();
process.env.GCLOUD_PROJECT = 0;

function accept() {
return true;
}

function nockOAuth2(validator) {
return nock('https://accounts.google.com')
.post('/o/oauth2/token', validator)
.reply(200, {
refresh_token: 'hello',
access_token: 'goodbye',
expiry_date: new Date(9999, 1, 1)
});
}

function nockRegister(validator) {
return nock('https://clouddebugger.googleapis.com')
.post('/v2/controller/debuggees/register', validator)
.reply(200);
}

describe('test-config-credentials', function() {
var debuglet = null;

beforeEach(function() {
delete process.env.GCLOUD_PROJECT;
assert.equal(debuglet, null);
});

afterEach(function() {
assert.ok(debuglet);
debuglet.stop();
debuglet = null;
process.env.GCLOUD_PROJECT = envProject;
});

it('should use config.projectId in preference to the environment variable',
function(done) {
process.env.GCLOUD_PROJECT = 'should-not-be-used';

var config = extend({}, defaultConfig, {
projectId: 'project-via-config',
credentials: require('../fixtures/gcloud-credentials.json')
});
var debug = require('../..')(config);

// TODO: also make sure we don't request the project from metadata
// service.

var scope = nockOAuth2(accept);
nockRegister(function(body) {
assert.ok(body.debuggee);
assert.equal(body.debuggee.project, 'project-via-config');
scope.done();
setImmediate(done);
return true;
});

debuglet =
new Debuglet(debug, config, logger.create(logger.WARN, 'testing'));
debuglet.start();
});

it('should use the keyFilename field of the config object', function(done) {
process.env.GCLOUD_PROJECT = '0';
var credentials = require('../fixtures/gcloud-credentials.json');
var config = extend({}, defaultConfig, {
keyFilename: path.join('test', 'fixtures', 'gcloud-credentials.json')
});
var debug = require('../..')(config);
var scope = nock('https://accounts.google.com')
.post('/o/oauth2/token', function(body) {
assert.equal(body.client_id, credentials.client_id);
assert.equal(body.client_secret, credentials.client_secret);
assert.equal(body.refresh_token, credentials.refresh_token);
return true;
}).reply(200, {
refresh_token: 'hello',
access_token: 'goodbye',
expiry_date: new Date(9999, 1, 1)
});
// Since we have to get an auth token, this always gets intercepted second
nock('https://clouddebugger.googleapis.com')
.post('/v2/controller/debuggees/register', function() {
scope.done();
setImmediate(done);
return true;
}).reply(200);
debuglet = new Debuglet(debug, config, logger.create(logger.WARN, 'testing'));
var scope = nockOAuth2(function(body) {
assert.equal(body.client_id, credentials.client_id);
assert.equal(body.client_secret, credentials.client_secret);
assert.equal(body.refresh_token, credentials.refresh_token);
return true;
});
// Since we have to get an auth token, this always gets intercepted second.
nockRegister(function() {
scope.done();
setImmediate(done);
return true;
});
debuglet =
new Debuglet(debug, config, logger.create(logger.WARN, 'testing'));
debuglet.start();
});

it('should use the credentials field of the config object', function(done) {
var config = extend({}, defaultConfig, {
credentials: require('../fixtures/gcloud-credentials.json')
});
process.env.GCLOUD_PROJECT = '0';
var config =
extend({}, defaultConfig,
{credentials: require('../fixtures/gcloud-credentials.json')});
var debug = require('../..')(config);
var scope = nock('https://accounts.google.com')
.post('/o/oauth2/token', function(body) {
assert.equal(body.client_id, config.credentials.client_id);
assert.equal(body.client_secret, config.credentials.client_secret);
assert.equal(body.refresh_token, config.credentials.refresh_token);
return true;
}).reply(200, {
refresh_token: 'hello',
access_token: 'goodbye',
expiry_date: new Date(9999, 1, 1)
});
// Since we have to get an auth token, this always gets intercepted second
nock('https://clouddebugger.googleapis.com')
.post('/v2/controller/debuggees/register', function() {
scope.done();
setImmediate(done);
return true;
}).reply(200);
var scope = nockOAuth2(function(body) {
assert.equal(body.client_id, config.credentials.client_id);
assert.equal(body.client_secret, config.credentials.client_secret);
assert.equal(body.refresh_token, config.credentials.refresh_token);
return true;
});
// Since we have to get an auth token, this always gets intercepted second.
nockRegister(function() {
scope.done();
setImmediate(done);
return true;
});
debuglet = new Debuglet(debug, config, logger.create(undefined, 'testing'));
debuglet.start();
});

it('should ignore keyFilename if credentials is provided', function(done) {
process.env.GCLOUD_PROJECT = '0';
var fileCredentials = require('../fixtures/gcloud-credentials.json');
var credentials = {
client_id: 'a',
client_secret: 'b',
refresh_token: 'c',
type: 'authorized_user'
client_id: 'a',
client_secret: 'b',
refresh_token: 'c',
type: 'authorized_user'
};
var config = extend({}, defaultConfig, {
keyFilename: path.join('test', 'fixtures', 'gcloud-credentials.json'),
credentials: credentials
});
var debug = require('../..')(config);
['client_id', 'client_secret', 'refresh_token'].forEach(function (field) {
var scope = nockOAuth2(function(body) {
assert.equal(body.client_id, credentials.client_id);
assert.equal(body.client_secret, credentials.client_secret);
assert.equal(body.refresh_token, credentials.refresh_token);
return true;
});
// Since we have to get an auth token, this always gets intercepted second.
nockRegister(function() {
scope.done();
setImmediate(done);
return true;
});
['client_id', 'client_secret', 'refresh_token'].forEach(function(field) {
assert(fileCredentials.hasOwnProperty(field));
assert(config.credentials.hasOwnProperty(field));
assert.notEqual(config.credentials[field],
fileCredentials[field]);
assert.notEqual(config.credentials[field], fileCredentials[field]);
});
var scope = nock('https://accounts.google.com')
.post('/o/oauth2/token', function(body) {
assert.equal(body.client_id, credentials.client_id);
assert.equal(body.client_secret, credentials.client_secret);
assert.equal(body.refresh_token, credentials.refresh_token);
return true;
}).reply(200, {
refresh_token: 'hello',
access_token: 'goodbye',
expiry_date: new Date(9999, 1, 1)
});
// Since we have to get an auth token, this always gets intercepted second
nock('https://clouddebugger.googleapis.com')
.post('/v2/controller/debuggees/register', function() {
scope.done();
setImmediate(done);
return true;
}).reply(200);
debuglet = new Debuglet(debug, config, logger.create(undefined, 'testing'));
debuglet.start();
});
Expand Down
Loading

0 comments on commit f7de637

Please sign in to comment.