Skip to content

Commit

Permalink
Implemented reading keyFile/credentials field from config object (#315)
Browse files Browse the repository at this point in the history
Clients may use config.keyFilename and config.credentials to supply a key file or its contents in lieu of GCLOUD_APPLICATION_CREDENTIALS.
  • Loading branch information
kjin committed Oct 31, 2016
1 parent a1650a4 commit f72983f
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 6 deletions.
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -82,9 +82,11 @@ If your application is running outside of Google Cloud Platform, such as locally

1. [Create a new JSON service account key][service-account].
2. Copy the key somewhere your application can access it. Be sure not to expose the key publicly.
3. Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the full path to the key. The debug agent will automatically look for this environment variable.
3. Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the full path to the key. The trace agent will automatically look for this environment variable.

3. Alternatively, if you are running your application on a development machine or test environment where you are using the [`gcloud` command line tools][gcloud-sdk], and are logged using `gcloud beta auth application-default login`, you already have sufficient credentials, and a service account key is not required.
If you are running your application on a development machine or test environment where you are using the [`gcloud` command line tools][gcloud-sdk], and are logged using `gcloud beta auth application-default login`, you already have sufficient credentials, and a service account key is not required.

Alternatively, you may set the `keyFilename` or `credentials` configuration field to the full path or contents to the key file, respectively. Setting either of these fields will override either setting `GOOGLE_APPLICATION_CREDENTIALS` or logging in using `gcloud`. (See the [default configuration](config.js) for more details.)

## Viewing your traces

Expand Down
12 changes: 11 additions & 1 deletion config.js
Expand Up @@ -88,6 +88,16 @@ module.exports = {
// this will also cause sampling decisions made by other distributed components to be
// ignored. This is useful for aggregating traces generated by different cloud platform
// projects.
ignoreContextHeader: false
ignoreContextHeader: false,

// A path to a key file relative to the current working directory. If this
// field is set, the contents of the pointed file will be used for
// authentication instead of your application default credentials.
keyFilename: null,

// The contents of a key file. If this field is set, its contents will be
// used for authentication instead of your application default credentials.
// If keyFilename is also set, the value of credentials will be ignored.
credentials: null
}
};
9 changes: 7 additions & 2 deletions lib/trace-writer.js
Expand Up @@ -29,7 +29,9 @@ headers[constants.TRACE_AGENT_REQUEST_HEADER] = 1;

/**
* Creates a basic trace writer.
* @param {!Logger} logger
* @param {!Logger} logger The Trace Agent's logger object.
* @param {Object} config A config object containing information about
* authorization credentials.
* @constructor
*/
function TraceWriter(logger, config) {
Expand All @@ -40,7 +42,10 @@ function TraceWriter(logger, config) {
this.config_ = config;

/** @private {function} authenticated request function */
this.request_ = utils.authorizedRequestFactory(SCOPES);
this.request_ = utils.authorizedRequestFactory(SCOPES, {
credentials: config.credentials,
keyFile: config.keyFilename
});

/** @private {Array<string>} stringified traces to be published */
this.buffer_ = [];
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -50,7 +50,7 @@
"tmp": "0.0.28"
},
"dependencies": {
"@google/cloud-diagnostics-common": "0.2.5",
"@google/cloud-diagnostics-common": "0.3.0",
"continuation-local-storage": "^3.1.4",
"lodash.findindex": "^4.4.0",
"lodash.isequal": "^4.0.0",
Expand Down
142 changes: 142 additions & 0 deletions test/standalone/test-config-credentials.js
@@ -0,0 +1,142 @@
/**
* 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
*
* 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 path = require('path');
var assert = require('assert');
var nock = require('nock');
var cls = require('../../lib/cls.js');

var queueSpans = function(n, privateAgent) {
for (var i = 0; i < n; i++) {
privateAgent.createRootSpanData('name', 1, 0).close();
}
};

process.env.GCLOUD_PROJECT = 0;

describe('test-config-credentials', function() {
it('should use the keyFilename field of the config object', function(done) {
var credentials = require('../fixtures/gcloud-credentials.json');
var config = {
bufferSize: 2,
samplingRate: 0,
keyFilename: path.join('test', 'fixtures', 'gcloud-credentials.json')
};
var agent = require('../..').start(config);
nock.disableNetConnect();
var scope = nock('https://accounts.google.com')
.intercept('/o/oauth2/token', 'POST', 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://cloudtrace.googleapis.com')
.intercept('/v1/projects/0/traces', 'PATCH', function() {
scope.done();
agent.stop();
setImmediate(done);
return true;
}).reply(200);
cls.getNamespace().run(function() {
queueSpans(2, agent.private_());
});
});

it('should use the credentials field of the config object', function(done) {
var config = {
bufferSize: 2,
samplingRate: 0,
credentials: require('../fixtures/gcloud-credentials.json')
};
var agent = require('../..').start(config);
nock.disableNetConnect();
var scope = nock('https://accounts.google.com')
.intercept('/o/oauth2/token', 'POST', 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://cloudtrace.googleapis.com')
.intercept('/v1/projects/0/traces', 'PATCH', function() {
scope.done();
agent.stop();
setImmediate(done);
return true;
}).reply(200);
cls.getNamespace().run(function() {
queueSpans(2, agent.private_());
});
});

it('should ignore credentials if keyFilename is provided', function(done) {
var correctCredentials = require('../fixtures/gcloud-credentials.json');
var config = {
bufferSize: 2,
samplingRate: 0,
keyFilename: path.join('test', 'fixtures', 'gcloud-credentials.json'),
credentials: {
client_id: 'a',
client_secret: 'b',
refresh_token: 'c',
type: 'authorized_user'
}
};
['client_id', 'client_secret', 'refresh_token'].forEach(function (field) {
assert(correctCredentials.hasOwnProperty(field));
assert(config.credentials.hasOwnProperty(field));
assert.notEqual(config.credentials[field],
correctCredentials[field]);
});
var agent = require('../..').start(config);
nock.disableNetConnect();
var scope = nock('https://accounts.google.com')
.intercept('/o/oauth2/token', 'POST', function(body) {
assert.equal(body.client_id, correctCredentials.client_id);
assert.equal(body.client_secret, correctCredentials.client_secret);
assert.equal(body.refresh_token, correctCredentials.refresh_token);
agent.stop();
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://cloudtrace.googleapis.com')
.intercept('/v1/projects/0/traces', 'PATCH', function() {
scope.done();
agent.stop();
setImmediate(done);
return true;
}).reply(200);
cls.getNamespace().run(function() {
queueSpans(2, agent.private_());
});
});
});

0 comments on commit f72983f

Please sign in to comment.