Skip to content

Commit

Permalink
adding AWS metadata client, and related tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jquatier committed Jan 19, 2016
1 parent f118440 commit 6195bec
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 0 deletions.
81 changes: 81 additions & 0 deletions src/aws-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import request from 'request';
import async from 'async';
import {Logger} from './Logger';

/*
Utility class for pulling AWS metadata that Eureka requires when registering as an Amazon instance (datacenter).
*/
export class AwsMetadata {

constructor(config = {}) {
this.logger = config.logger || new Logger();
this.host = config.host || '169.254.169.254';
}

fetchMetadata(resultsCallback) {
async.parallel({
'ami-id': callback => {
this.lookupMetadataKey('ami-id', callback);
},
'instance-id': callback => {
this.lookupMetadataKey('instance-id', callback);
},
'instance-type': callback => {
this.lookupMetadataKey('instance-type', callback);
},
'local-ipv4': callback => {
this.lookupMetadataKey('local-ipv4', callback);
},
'local-hostname': callback => {
this.lookupMetadataKey('local-hostname', callback);
},
'availability-zone': callback => {
this.lookupMetadataKey('placement/availability-zone', callback);
},
'public-hostname': callback => {
this.lookupMetadataKey('public-hostname', callback);
},
'public-ipv4': callback => {
this.lookupMetadataKey('public-ipv4', callback);
},
mac: callback => {
this.lookupMetadataKey('mac', callback);
},
accountId: callback => {
// the accountId is in the identity document.
this.lookupInstanceIdentity((error, identity) => {
callback(null, identity ? identity.accountId : null);
});
}
}, (error, results) => {
// we need the mac before we can lookup the vpcId...
this.lookupMetadataKey('network/interfaces/macs/' + results.mac + '/vpc-id', (error, vpcId) => {
results['vpc-id'] = vpcId;
this.logger.info('Instance AWS Metadata', results);
resultsCallback(results);
});
});
}

lookupMetadataKey(key, callback) {
request.get({
url: `http://${this.host}/latest/meta-data/${key}`
}, (error, response, body) => {
if (error) {
this.logger.error('Error requesting metadata key', error);
}
callback(null, (error || response.statusCode !== 200) ? null : body);
});
}

lookupInstanceIdentity(callback) {
request.get({
url: `http://${this.host}/latest/dynamic/instance-identity/document`
}, (error, response, body) => {
if (error) {
this.logger.error('Error requesting instance identity document', error);
}
callback(null, (error || response.statusCode !== 200) ? null : JSON.parse(body));
});
}
}
94 changes: 94 additions & 0 deletions test/aws-metadata.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import sinon from 'sinon';
import chai from 'chai';
import sinonChai from 'sinon-chai';
import request from 'request';
import {AwsMetadata} from '../src/aws-metadata';

let expect = chai.expect;
chai.use(sinonChai);

describe('AWS Metadata client', () => {

describe('fetchMetadata()', () => {

let client;
beforeEach(() => {
client = new AwsMetadata({host: '127.0.0.1:8888'});
});

afterEach(() => {
request.get.restore();
});

it('should call metadata URIs', () => {

let requestStub = sinon.stub(request, 'get');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/ami-id'
}).yields(null, {statusCode: 200}, 'ami-123');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/instance-id'
}).yields(null, {statusCode: 200}, 'i123');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/instance-type'
}).yields(null, {statusCode: 200}, 'medium');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/local-ipv4'
}).yields(null, {statusCode: 200}, '1.1.1.1');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/local-hostname'
}).yields(null, {statusCode: 200}, 'ip-127-0-0-1');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/placement/availability-zone'
}).yields(null, {statusCode: 200}, 'fake-1');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/public-hostname'
}).yields(null, {statusCode: 200}, 'ec2-127-0-0-1');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/public-ipv4'
}).yields(null, {statusCode: 200}, '2.2.2.2');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/mac'
}).yields(null, {statusCode: 200}, 'AB:CD:EF:GH:IJ');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/dynamic/instance-identity/document'
}).yields(null, {statusCode: 200}, '{"accountId":"123456"}');

requestStub.withArgs({
url:'http://127.0.0.1:8888/latest/meta-data/network/interfaces/macs/AB:CD:EF:GH:IJ/vpc-id'
}).yields(null, {statusCode: 200}, 'vpc123');

let fetchCb = sinon.spy();
client.fetchMetadata(fetchCb);

expect(request.get).to.have.been.callCount(11);

expect(fetchCb).to.have.been.calledWithMatch({
'accountId': '123456',
'ami-id': 'ami-123',
'availability-zone': 'fake-1',
'instance-id': 'i123',
'instance-type': 'medium',
'local-hostname': 'ip-127-0-0-1',
'local-ipv4': '1.1.1.1',
'mac': 'AB:CD:EF:GH:IJ',
'public-hostname': 'ec2-127-0-0-1',
'public-ipv4': '2.2.2.2',
'vpc-id': 'vpc123'
});

});

});

});

0 comments on commit 6195bec

Please sign in to comment.