Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Allow credentials to be loaded asynchronously

* Add Config.getCredentials
* Add Credentials.{get,needsRefresh}
* Add EC2MetadataCredentials
  • Loading branch information...
commit 2041e095c1411925432b319dafa647d08292fc22 1 parent 187bc6c
@lsegal lsegal authored
View
224 lib/config.js
@@ -16,6 +16,7 @@
var AWS = require('./core');
require('./event_listeners');
require('./event_emitter');
+require('./metadata_service');
var inherit = AWS.util.inherit;
/**
@@ -127,6 +128,59 @@ AWS.Config = inherit({
},
/**
+ * @api private
+ */
+ getCredentials: function getCredentials(callback) {
+ var self = this;
+
+ function finish(err) {
+ callback(err, err ? null : self.credentials);
+ }
+
+ function credError(msg, err) {
+ return new AWS.util.error(err || new Error(), {
+ code: 'CredentialsError', message: msg
+ });
+ }
+
+ function getAsyncCredentials() {
+ self.credentials.get(function(err) {
+ if (err) {
+ var msg = 'Could not load credentials from ' + self.credentials.name;
+ err = credError(msg, err);
+ }
+ finish(err);
+ });
+ }
+
+ function getStaticCredentials() {
+ var err = null;
+ if (!self.credentials.accessKeyId || !self.credentials.secretAccessKey) {
+ err = credError('Missing credentials');
+ }
+ finish(err);
+ }
+
+ if (self.credentials) {
+ if (typeof self.credentials.get === 'function') {
+ getAsyncCredentials();
+ } else { // static credentials
+ getStaticCredentials();
+ }
+ } else if (self.credentialProvider) {
+ self.credentialProvider.resolve(function(err, creds) {
+ if (err) {
+ err = credError('Could not load credentials from any providers', err);
+ }
+ self.credentials = creds;
+ finish(err);
+ });
+ } else {
+ finish(credError('No credentials to load'));
+ }
+ },
+
+ /**
* Loads configuration data from a JSON file into this config object.
* @note Loading configuration will reset all existing configuration
* on the object.
@@ -140,7 +194,10 @@ AWS.Config = inherit({
var fileSystemCreds = new AWS.FileSystemCredentials(path, this);
var chain = new AWS.CredentialProviderChain();
chain.providers.unshift(fileSystemCreds);
- options.credentials = chain.resolve();
+ chain.resolve(function (err, creds) {
+ if (err) throw err;
+ else options.credentials = creds;
+ });
this.constructor(options);
@@ -160,6 +217,7 @@ AWS.Config = inherit({
// reset credential provider
this.set('credentials', undefined);
+ this.set('credentialProvider', undefined);
},
/**
@@ -190,7 +248,19 @@ AWS.Config = inherit({
*/
keys: {
credentials: function () {
- return new AWS.CredentialProviderChain().resolve();
+ var credentials = null;
+ new AWS.CredentialProviderChain([
+ function () { return new AWS.EnvironmentCredentials('AWS'); },
+ function () { return new AWS.EnvironmentCredentials('AMAZON'); }
+ ]).resolve(function(err, creds) {
+ if (!err) credentials = creds;
+ });
+ return credentials;
+ },
+ credentialProvider: function() {
+ return new AWS.CredentialProviderChain([
+ function() { return new AWS.EC2MetadataCredentials(); }
+ ]);
},
region: function() {
return process.env.AWS_REGION || process.env.AMAZON_REGION;
@@ -244,6 +314,9 @@ AWS.Config = inherit({
* some network storage. The method should reset the credential attributes
* on the object.
*
+ * @!attribute expired
+ * @return [Boolean] whether the credentials have been expired and
+ * require a refresh
* @!attribute accessKeyId
* @return [String] the AWS access key ID
* @!attribute secretAccessKey
@@ -252,7 +325,6 @@ AWS.Config = inherit({
* @return [String] an optional AWS session token
*/
AWS.Credentials = inherit({
-
/**
* A credentials object can be created using positional arguments or an options
* hash.
@@ -277,6 +349,7 @@ AWS.Credentials = inherit({
* });
*/
constructor: function Credentials() {
+ this.expired = false;
if (arguments.length == 1 && typeof arguments[0] === 'object') {
var creds = arguments[0].credentials || arguments[0];
this.accessKeyId = creds.accessKeyId;
@@ -289,6 +362,22 @@ AWS.Credentials = inherit({
}
},
+ needsRefresh: function needsRefresh() {
+ return this.expired || !this.accessKeyId || !this.secretAccessKey;
+ },
+
+ get: function get(callback) {
+ var self = this;
+ if (this.needsRefresh()) {
+ this.refresh(function(err) {
+ if (!err) self.expired = false; // reset expired flag
+ callback(err);
+ });
+ } else {
+ callback();
+ }
+ },
+
/**
* Refreshes the credentials.
*
@@ -296,8 +385,9 @@ AWS.Credentials = inherit({
* {accessKeyId}, {secretAccessKey} and optional {sessionToken}
* on the credentials object.
*/
- refresh: function refresh() { }
-
+ refresh: function refresh(callback) {
+ callback();
+ }
});
/**
@@ -334,14 +424,23 @@ AWS.FileSystemCredentials = inherit(AWS.Credentials, {
constructor: function FileSystemCredentials(filename, initialCredentials) {
this.filename = filename;
AWS.Credentials.call(this, initialCredentials);
- if (!this.accessKeyId) this.refresh();
+ this.get(function() {});
},
/**
- * Refreshes the credentials from the {filename} on disk.
+ * Loads the credentials from the {filename} on disk.
*/
- refresh: function refresh() {
- AWS.Credentials.call(this, JSON.parse(AWS.util.readFileSync(this.filename)));
+ refresh: function refresh(callback) {
+ if (!callback) callback = function(err) { if (err) throw err; };
+ try {
+ AWS.Credentials.call(this, JSON.parse(AWS.util.readFileSync(this.filename)));
+ if (!this.accessKeyId || !this.secretAccessKey) {
+ throw new Error('Credentials not set in ' + this.filename);
+ }
+ callback();
+ } catch (err) {
+ callback(err);
+ }
}
});
@@ -385,31 +484,66 @@ AWS.EnvironmentCredentials = inherit(AWS.Credentials, {
*/
constructor: function EnvironmentCredentials(envPrefix) {
this.envPrefix = envPrefix;
- this.refresh();
+ this.get(function() {});
},
/**
- * Refreshes credentials from the environment using the prefixed
+ * Loads credentials from the environment using the prefixed
* environment variables.
*/
- refresh: function refresh() {
- if (process === undefined) return;
+ refresh: function refresh(callback) {
+ /*jshint maxcomplexity:10*/
+ if (!callback) callback = function(err) { if (err) throw err; };
+
+ if (process === undefined) {
+ callback(new Error('No process info available'));
+ return;
+ }
var keys = ['ACCESS_KEY_ID', 'SECRET_ACCESS_KEY', 'SESSION_TOKEN'];
var values = [];
- /*jshint forin:false*/
- for (var i in keys) {
+ for (var i = 0; i < keys.length; i++) {
var prefix = '';
if (this.envPrefix) prefix = this.envPrefix + '_';
values[i] = process.env[prefix + keys[i]];
+ if (!values[i] && keys[i] !== 'SESSION_TOKEN') {
+ callback(new Error('Variable ' + prefix + keys[i] + ' not set.'));
+ return;
+ }
}
AWS.Credentials.apply(this, values);
+ callback();
}
});
+AWS.EC2MetadataCredentials = inherit(AWS.Credentials, {
+ constructor: function EC2MetadataCredentials(options) {
+ this.serviceError = null;
+ this.metadataService = new AWS.MetadataService(options);
+ },
+
+ refresh: function refresh(callback) {
+ var self = this;
+ if (!callback) callback = function(err) { if (err) throw err; };
+ if (self.serviceError) {
+ callback(self.serviceError);
+ return;
+ }
+
+ self.metadataService.loadCredentials(function (err, creds) {
+ if (err) {
+ self.serviceError = err;
+ } else {
+ AWS.util.update(self, creds);
+ }
+ callback(err, creds);
+ });
+ }
+});
+
/**
* Creates a credential provider chain that searches for AWS credentials
* in a list of credential providers specified by the {providers} property.
@@ -452,40 +586,67 @@ AWS.EnvironmentCredentials = inherit(AWS.Credentials, {
* {defaultProviders}.
* @see defaultProviders
*/
-AWS.CredentialProviderChain = inherit({
+AWS.CredentialProviderChain = inherit(AWS.Credentials, {
/**
* Creates a new CredentialProviderChain with a default set of providers
* specified by {defaultProviders}.
*/
- constructor: function CredentialProviderChain() {
- this.providers = AWS.CredentialProviderChain.defaultProviders.slice(0);
+ constructor: function CredentialProviderChain(providers) {
+ if (providers) {
+ this.providers = providers;
+ } else {
+ this.providers = AWS.CredentialProviderChain.defaultProviders.slice(0);
+ }
},
/**
* Resolves the provider chain by searching for the first set of
* credentials in {providers}.
*
- * @return [AWS.Credentials] the first set of credentials discovered
- * in the chain of {providers}.
- * @return [null] if no credentials are found.
+ * @callback callback function(err, credentials)
+ * Called when the provider resolves the chain to a credentials object
+ * or null if no credentials can be found.
+ *
+ * @param err [Error] the error object returned if no credentials are
+ * found.
+ * @param credentials [AWS.Credentials] the credentials object resolved
+ * by the provider chain.
+ * @return [AWS.CredentialProviderChain] the provider, for chaining.
*/
- resolve: function resolve() {
- var finalCreds;
- AWS.util.arrayEach(this.providers, function (provider) {
- var creds;
+ resolve: function resolve(callback) {
+ if (this.providers.length === 0) {
+ callback(new Error('No providers'));
+ return;
+ }
+
+ var index = 0;
+ var providers = this.providers.slice(0);
+
+ function resolveNext(err, creds) {
+ if ((!err && creds) || index === providers.length) {
+ callback(err, creds);
+ return;
+ }
+
+ var provider = providers[index++];
if (typeof provider === 'function') {
creds = provider.call();
} else {
creds = provider;
}
- if (creds.accessKeyId) {
- finalCreds = creds;
- return AWS.util.abort;
+ if (creds.get) {
+ creds.get(function(err) {
+ resolveNext(err, err ? null : creds);
+ });
+ } else {
+ resolveNext(null, creds);
}
- });
- return finalCreds ? finalCreds : new AWS.Credentials();
+ }
+
+ resolveNext();
+ return this;
}
});
@@ -495,7 +656,8 @@ AWS.CredentialProviderChain = inherit({
*/
AWS.CredentialProviderChain.defaultProviders = [
function () { return new AWS.EnvironmentCredentials('AWS'); },
- function () { return new AWS.EnvironmentCredentials('AMAZON'); }
+ function () { return new AWS.EnvironmentCredentials('AMAZON'); },
+ function () { return new AWS.EC2MetadataCredentials(); }
];
/**
View
1  lib/core.js
@@ -53,6 +53,7 @@ require('./client');
require('./service');
require('./signers/request_signer');
require('./param_validator');
+require('./metadata_service');
/**
* @readonly
View
50 lib/event_listeners.js
@@ -84,12 +84,15 @@ AWS.EventListeners = {
AWS.EventListeners = {
Core: new AWS.EventEmitter().addNamedListeners(function(add, addAsync) {
- add('VALIDATE_CREDENTIALS', 'validate', function VALIDATE_CREDENTIALS(req) {
- if (!req.client.config.credentials.accessKeyId ||
- !req.client.config.credentials.secretAccessKey) {
- throw AWS.util.error(new Error(),
- {code: 'SigningError', message: 'Missing credentials in config'});
- }
+ addAsync('VALIDATE_CREDENTIALS', 'validate',
+ function VALIDATE_CREDENTIALS(req, doneCallback) {
+ req.client.config.getCredentials(function(err) {
+ if (err) {
+ err = AWS.util.error(err,
+ {code: 'SigningError', message: 'Missing credentials in config'});
+ }
+ doneCallback(err);
+ });
});
add('VALIDATE_REGION', 'validate', function VALIDATE_REGION(req) {
@@ -111,21 +114,26 @@ AWS.EventListeners = {
}
});
- add('SIGN', 'sign', function SIGN(req) {
- var date = AWS.util.date.getDate();
- var sigVersion = req.client.api.signatureVersion;
- var credentials = AWS.util.copy(req.client.config.credentials);
- var SignerClass = AWS.Signers.RequestSigner.getVersion(sigVersion);
- var signer = new SignerClass(req.httpRequest,
- req.client.api.signingName || req.client.api.endpointPrefix);
-
- // clear old authorization headers
- delete req.httpRequest.headers['Authorization'];
- delete req.httpRequest.headers['Date'];
- delete req.httpRequest.headers['X-Amz-Date'];
-
- // add new authorization
- signer.addAuthorization(credentials, date);
+ addAsync('SIGN', 'sign', function SIGN(req, doneCallback) {
+ req.client.config.getCredentials(function (err, credentials) {
+ if (err) return doneCallback(err);
+
+ var date = AWS.util.date.getDate();
+ var sigVersion = req.client.api.signatureVersion;
+ var SignerClass = AWS.Signers.RequestSigner.getVersion(sigVersion);
+ var signer = new SignerClass(req.httpRequest,
+ req.client.api.signingName || req.client.api.endpointPrefix);
+
+ // clear old authorization headers
+ delete req.httpRequest.headers['Authorization'];
+ delete req.httpRequest.headers['Date'];
+ delete req.httpRequest.headers['X-Amz-Date'];
+
+ // add new authorization
+ credentials = AWS.util.copy(credentials);
+ signer.addAuthorization(credentials, date);
+ doneCallback();
+ });
});
add('SETUP_ERROR', 'extractError', function SETUP_ERROR(resp) {
View
1  lib/http.js
@@ -114,7 +114,6 @@ AWS.HttpRequest = inherit({
this.headers['User-Agent'] = AWS.util.userAgent();
this.body = '';
this.endpoint = endpoint;
- this.host
this.region = region;
},
View
60 test/config.spec.coffee
@@ -108,7 +108,7 @@ describe 'AWS.Config', ->
describe 'clear', ->
it 'should be able to clear all key values from a config object', ->
- config = new AWS.Config(maxRetries: 300, sslEnabled: 'foo')
+ config = new AWS.Config(credentials: {}, maxRetries: 300, sslEnabled: 'foo')
expect(config.maxRetries).toEqual(300)
expect(config.sslEnabled).toEqual('foo')
expect(config.credentials).not.toEqual(undefined)
@@ -117,7 +117,8 @@ describe 'AWS.Config', ->
expect(config.maxRetries).toEqual(undefined)
expect(config.sslEnabled).toEqual(undefined)
- expect(config.credentials).not.toEqual(undefined)
+ expect(config.credentials).not.toBe(undefined)
+ expect(config.credentialProvider).not.toEqual(undefined)
describe 'update', ->
it 'should be able to update keyed values', ->
@@ -141,6 +142,61 @@ describe 'AWS.Config', ->
expect(config.credentials.secretAccessKey).toEqual('secret')
expect(config.credentials.sessionToken).toEqual('session')
+ describe 'getCredentials', ->
+ spy = null
+ config = null
+ beforeEach ->
+ spy = jasmine.createSpy('getCredentials callback')
+
+ expectValid = (options, key) ->
+ if options instanceof AWS.Config
+ config = options
+ else
+ config = new AWS.Config(options)
+ config.getCredentials(spy)
+ expect(spy).toHaveBeenCalled()
+ expect(spy.argsForCall[0][0]).toEqual(null)
+ if key
+ expect(config.credentials.accessKeyId).toEqual(key)
+
+ expectError = (options, message) ->
+ if options instanceof AWS.Config
+ config = options
+ else
+ config = new AWS.Config(options)
+ config.getCredentials(spy)
+ expect(spy).toHaveBeenCalled()
+ expect(spy.argsForCall[0][0].code).toEqual('CredentialsError')
+ expect(spy.argsForCall[0][0].message).toEqual(message)
+
+ it 'should check credentials for static object first', ->
+ expectValid credentials: accessKeyId: '123', secretAccessKey: '456'
+
+ it 'should error if static credentials are not available', ->
+ expectError(credentials: {}, 'Missing credentials')
+
+ it 'should check credentials for async get() method', ->
+ expectValid credentials: get: (cb) -> cb()
+
+ it 'should error if credentials.get() cannot resolve', ->
+ options = credentials:
+ name: 'CustomCredentials'
+ get: (cb) -> cb(new Error('Error!'), null)
+ expectError options, 'Could not load credentials from CustomCredentials'
+
+ it 'should check credentialProvider if no credentials', ->
+ expectValid credentials: null, credentialProvider:
+ resolve: (cb) -> cb(null, accessKeyId: 'key', secretAccessKey: 'secret')
+
+ it 'should error if credentialProvider fails to resolve', ->
+ options = credentials: null, credentialProvider:
+ resolve: (cb) -> cb(new Error('Error!'), null)
+ expectError options, 'Could not load credentials from any providers'
+
+ it 'should error if no credentials or credentialProvider', ->
+ options = credentials: null, credentialProvider: null
+ expectError options, 'No credentials to load'
+
describe 'AWS.config', ->
it 'should be a default Config object', ->
expect(AWS.config.sslEnabled).toEqual(true)
View
77 test/credential_provider_chain.spec.coffee
@@ -16,23 +16,22 @@ AWS = require('../lib/core')
describe 'AWS.CredentialProviderChain', ->
describe 'resolve', ->
-
chain = null
defaultProviders = AWS.CredentialProviderChain.defaultProviders
beforeEach ->
process.env = {}
- chain = new AWS.CredentialProviderChain()
+ chain = new AWS.CredentialProviderChain [
+ -> new AWS.EnvironmentCredentials('AWS'),
+ -> new AWS.EnvironmentCredentials('AMAZON')
+ ]
- # restore the defaultProviders to the original values
afterEach ->
AWS.CredentialProviderChain.defaultProviders = defaultProviders
- it 'returns an empty credentials object by default', ->
- creds = chain.resolve()
- expect(creds.accessKeyId).toEqual(undefined)
- expect(creds.secretAccessKey).toEqual(undefined)
- expect(creds.sessionToken).toEqual(undefined)
+ it 'returns an error by default', ->
+ chain.resolve (err) ->
+ expect(err.message).toEqual('Variable AMAZON_ACCESS_KEY_ID not set.')
it 'returns AWS-prefixed credentials found in ENV', ->
@@ -40,10 +39,10 @@ describe 'AWS.CredentialProviderChain', ->
process.env['AWS_SECRET_ACCESS_KEY'] = 'secret'
process.env['AWS_SESSION_TOKEN'] = 'session'
- creds = chain.resolve()
- expect(creds.accessKeyId).toEqual('akid')
- expect(creds.secretAccessKey).toEqual('secret')
- expect(creds.sessionToken).toEqual('session')
+ chain.resolve (err, creds) ->
+ expect(creds.accessKeyId).toEqual('akid')
+ expect(creds.secretAccessKey).toEqual('secret')
+ expect(creds.sessionToken).toEqual('session')
it 'returns AMAZON-prefixed credentials found in ENV', ->
@@ -51,10 +50,10 @@ describe 'AWS.CredentialProviderChain', ->
process.env['AMAZON_SECRET_ACCESS_KEY'] = 'secret'
process.env['AMAZON_SESSION_TOKEN'] = 'session'
- creds = chain.resolve()
- expect(creds.accessKeyId).toEqual('akid')
- expect(creds.secretAccessKey).toEqual('secret')
- expect(creds.sessionToken).toEqual('session')
+ chain.resolve (err, creds) ->
+ expect(creds.accessKeyId).toEqual('akid')
+ expect(creds.secretAccessKey).toEqual('secret')
+ expect(creds.sessionToken).toEqual('session')
it 'prefers AWS credentials to AMAZON credentials', ->
@@ -66,10 +65,10 @@ describe 'AWS.CredentialProviderChain', ->
process.env['AMAZON_SECRET_ACCESS_KEY'] = 'secret2'
process.env['AMAZON_SESSION_TOKEN'] = 'session2'
- creds = chain.resolve()
- expect(creds.accessKeyId).toEqual('akid')
- expect(creds.secretAccessKey).toEqual('secret')
- expect(creds.sessionToken).toEqual('session')
+ chain.resolve (err, creds) ->
+ expect(creds.accessKeyId).toEqual('akid')
+ expect(creds.secretAccessKey).toEqual('secret')
+ expect(creds.sessionToken).toEqual('session')
it 'uses the defaultProviders property on the constructor', ->
@@ -82,32 +81,22 @@ describe 'AWS.CredentialProviderChain', ->
process.env['AWS_SESSION_TOKEN'] = 'session'
chain = new AWS.CredentialProviderChain()
- creds = chain.resolve()
- expect(creds.accessKeyId).toEqual(undefined)
- expect(creds.secretAccessKey).toEqual(undefined)
- expect(creds.sessionToken).toEqual(undefined)
+ chain.resolve (err) ->
+ expect(err.message).toEqual('No providers')
it 'calls resolve on each provider in the chain, stopping for akid', ->
-
- staticCreds = { accessKeyId: 'abc', secretAccessKey: 'xyz' }
-
- AWS.CredentialProviderChain.defaultProviders.unshift(staticCreds)
-
- chain = new AWS.CredentialProviderChain()
- creds = chain.resolve()
- expect(creds.accessKeyId).toEqual('abc')
- expect(creds.secretAccessKey).toEqual('xyz')
- expect(creds.sessionToken).toEqual(undefined)
+ staticCreds = accessKeyId: 'abc', secretAccessKey: 'xyz'
+ chain = new AWS.CredentialProviderChain([staticCreds])
+ chain.resolve (err, creds) ->
+ expect(creds.accessKeyId).toEqual('abc')
+ expect(creds.secretAccessKey).toEqual('xyz')
+ expect(creds.sessionToken).toEqual(undefined)
it 'accepts providers as functions, elavuating them during resolution', ->
-
provider = ->
- { accessKeyId: 'abc', secretAccessKey: 'xyz' }
-
- AWS.CredentialProviderChain.defaultProviders.unshift(provider)
-
- chain = new AWS.CredentialProviderChain()
- creds = chain.resolve()
- expect(creds.accessKeyId).toEqual('abc')
- expect(creds.secretAccessKey).toEqual('xyz')
- expect(creds.sessionToken).toEqual(undefined)
+ accessKeyId: 'abc', secretAccessKey: 'xyz'
+ chain = new AWS.CredentialProviderChain([provider])
+ chain.resolve (err, creds) ->
+ expect(creds.accessKeyId).toEqual('abc')
+ expect(creds.secretAccessKey).toEqual('xyz')
+ expect(creds.sessionToken).toEqual(undefined)
View
113 test/credentials.spec.coffee
@@ -19,26 +19,65 @@ validateCredentials = (creds, key, secret, session) ->
expect(creds.sessionToken).toEqual(session || 'session')
describe 'AWS.Credentials', ->
- it 'should allow setting of credentials with keys', ->
- config = new AWS.Config(
- accessKeyId: 'akid'
- secretAccessKey: 'secret'
- sessionToken: 'session'
- )
- validateCredentials(config.credentials)
-
- it 'should allow setting of credentials as object', ->
- creds =
- accessKeyId: 'akid'
- secretAccessKey: 'secret'
- sessionToken: 'session'
- validateCredentials(new AWS.Credentials(creds))
-
- it 'defaults credentials to undefined when not passed', ->
- creds = new AWS.Credentials()
- expect(creds.accessKeyId).toBe(undefined)
- expect(creds.secretAccessKey).toBe(undefined)
- expect(creds.sessionToken).toBe(undefined)
+ describe 'constructor', ->
+ it 'should allow setting of credentials with keys', ->
+ config = new AWS.Config(
+ accessKeyId: 'akid'
+ secretAccessKey: 'secret'
+ sessionToken: 'session'
+ )
+ validateCredentials(config.credentials)
+
+ it 'should allow setting of credentials as object', ->
+ creds =
+ accessKeyId: 'akid'
+ secretAccessKey: 'secret'
+ sessionToken: 'session'
+ validateCredentials(new AWS.Credentials(creds))
+
+ it 'defaults credentials to undefined when not passed', ->
+ creds = new AWS.Credentials()
+ expect(creds.accessKeyId).toBe(undefined)
+ expect(creds.secretAccessKey).toBe(undefined)
+ expect(creds.sessionToken).toBe(undefined)
+
+ describe 'needsRefresh', ->
+ it 'needs refresh if credentials are not set', ->
+ creds = new AWS.Credentials()
+ expect(creds.needsRefresh()).toEqual(true)
+ creds = new AWS.Credentials('akid')
+ expect(creds.needsRefresh()).toEqual(true)
+
+ it 'does not need refresh if credentials are set', ->
+ creds = new AWS.Credentials('akid', 'secret')
+ expect(creds.needsRefresh()).toEqual(false)
+
+ it 'needs refresh if creds are expired', ->
+ creds = new AWS.Credentials('akid', 'secret')
+ creds.expired = true
+ expect(creds.needsRefresh()).toEqual(true)
+
+ describe 'get', ->
+ it 'does not call refresh if not needsRefresh', ->
+ spy = jasmine.createSpy('done callback')
+ creds = new AWS.Credentials('akid', 'secret')
+ refresh = spyOn(creds, 'refresh')
+ creds.get(spy)
+ expect(refresh).not.toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
+ expect(spy.argsForCall[0][0]).toEqual(null)
+ expect(creds.expired).toEqual(false)
+
+ it 'calls refresh only if needsRefresh', ->
+ spy = jasmine.createSpy('done callback')
+ creds = new AWS.Credentials('akid', 'secret')
+ creds.expired = true
+ refresh = spyOn(creds, 'refresh').andCallThrough()
+ creds.get(spy)
+ expect(refresh).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
+ expect(spy.argsForCall[0][0]).toEqual(null)
+ expect(creds.expired).toEqual(false)
describe 'AWS.EnvironmentCredentials', ->
beforeEach ->
@@ -62,6 +101,7 @@ describe 'AWS.EnvironmentCredentials', ->
describe 'refresh', ->
it 'can refresh credentials', ->
process.env.AWS_ACCESS_KEY_ID = 'akid'
+ process.env.AWS_SECRET_ACCESS_KEY = 'secret'
creds = new AWS.EnvironmentCredentials('AWS')
expect(creds.accessKeyId).toEqual('akid')
creds.accessKeyId = 'not_akid'
@@ -111,3 +151,36 @@ describe 'AWS.FileSystemCredentials', ->
creds.refresh()
validateCredentials(creds, 'RELOADED', 'RELOADED', 'RELOADED')
+
+ it 'fails if credentials are not in the file', ->
+ mock = '{"credentials":{}}'
+ spyOn(AWS.util, 'readFileSync').andReturn(mock)
+
+ values =
+ accessKeyId: "akid"
+ secretAccessKey: "secret"
+ sessionToken: "session"
+ creds = new AWS.FileSystemCredentials('foo', values)
+ validateCredentials(creds)
+ expect(-> creds.refresh()).toThrow('Credentials not set in foo')
+
+describe 'AWS.EC2MetadataCredentials', ->
+ describe 'constructor', ->
+ it 'allows passing of AWS.MetadataService options', ->
+ creds = new AWS.EC2MetadataCredentials(host: 'host')
+ expect(creds.metadataService.host).toEqual('host')
+
+ describe 'refresh', ->
+ it 'loads credentials from EC2 Metadata service'
+
+ it 'does not try to load creds second time if Metadata service failed', ->
+ creds = new AWS.EC2MetadataCredentials(host: 'host')
+ spy = spyOn(creds.metadataService, 'loadCredentials').andCallFake (cb) ->
+ cb(new Error('INVALID SERVICE'))
+
+ creds.refresh (err) ->
+ expect(err.message).toEqual('INVALID SERVICE')
+ creds.refresh ->
+ creds.refresh ->
+ creds.refresh ->
+ expect(spy.calls.length).toEqual(1)
View
3  test/event_listeners.spec.coffee
@@ -67,10 +67,11 @@ describe 'AWS.EventListeners', ->
expect(response.error).toEqual("ERROR")
it 'sends error event if credentials are not set', ->
- errorHandler = createSpy()
+ errorHandler = createSpy('errorHandler')
request = makeRequest()
request.on('error', errorHandler)
+ client.config.credentialProvider = null
client.config.credentials.accessKeyId = null
request.send()
View
20 test/node_http_client.spec.coffee
@@ -29,20 +29,22 @@ describe 'AWS.NodeHttpClient', ->
waitsFor -> done
it 'emits error event', ->
- done = false
+ error = null
req = new AWS.HttpRequest 'http://invalid'
runs ->
http.handleRequest req, {}, null, (err) ->
- expect(err.code).toEqual 'ENOTFOUND'
- done = true
- waitsFor -> done
+ error = err
+ waitsFor -> error
+ runs ->
+ expect(error.code).toEqual 'ENOTFOUND'
it 'supports timeout in httpOptions', ->
- done = false
+ error = null
req = new AWS.HttpRequest 'http://1.1.1.1'
runs ->
http.handleRequest req, {timeout: 12}, null, (err) ->
- expect(err.code).toEqual 'TimeoutError'
- expect(err.message).toEqual 'Connection timed out after 12ms'
- done = true
- waitsFor -> done
+ error = err
+ waitsFor -> error
+ runs ->
+ expect(error.code).toEqual 'TimeoutError'
+ expect(error.message).toEqual 'Connection timed out after 12ms'
Please sign in to comment.
Something went wrong with that request. Please try again.