cdk-stepfunctions-patterns
Version:
A set of Step Functions high-level patterns.
236 lines (218 loc) • 7.05 kB
JavaScript
var AWS = require('./core');
require('./http');
var inherit = AWS.util.inherit;
/**
* Represents a metadata service available on EC2 instances. Using the
* {request} method, you can receieve metadata about any available resource
* on the metadata service.
*
* You can disable the use of the IMDS by setting the AWS_EC2_METADATA_DISABLED
* environment variable to a truthy value.
*
* @!attribute [r] httpOptions
* @return [map] a map of options to pass to the underlying HTTP request:
*
* * **timeout** (Number) — a timeout value in milliseconds to wait
* before aborting the connection. Set to 0 for no timeout.
*
* @!macro nobrowser
*/
AWS.MetadataService = inherit({
/**
* @return [String] the hostname of the instance metadata service
*/
host: '169.254.169.254',
/**
* @!ignore
*/
/**
* Default HTTP options. By default, the metadata service is set to not
* timeout on long requests. This means that on non-EC2 machines, this
* request will never return. If you are calling this operation from an
* environment that may not always run on EC2, set a `timeout` value so
* the SDK will abort the request after a given number of milliseconds.
*/
httpOptions: { timeout: 0 },
/**
* when enabled, metadata service will not fetch token
*/
disableFetchToken: false,
/**
* Creates a new MetadataService object with a given set of options.
*
* @option options host [String] the hostname of the instance metadata
* service
* @option options httpOptions [map] a map of options to pass to the
* underlying HTTP request:
*
* * **timeout** (Number) — a timeout value in milliseconds to wait
* before aborting the connection. Set to 0 for no timeout.
* @option options maxRetries [Integer] the maximum number of retries to
* perform for timeout errors
* @option options retryDelayOptions [map] A set of options to configure the
* retry delay on retryable errors. See AWS.Config for details.
*/
constructor: function MetadataService(options) {
AWS.util.update(this, options);
},
/**
* Sends a request to the instance metadata service for a given resource.
*
* @param path [String] the path of the resource to get
*
* @param options [map] an optional map used to make request
*
* * **method** (String) — HTTP request method
*
* * **headers** (map<String,String>) — a map of response header keys and their respective values
*
* @callback callback function(err, data)
* Called when a response is available from the service.
* @param err [Error, null] if an error occurred, this value will be set
* @param data [String, null] if the request was successful, the body of
* the response
*/
request: function request(path, options, callback) {
if (arguments.length === 2) {
callback = options;
options = {};
}
if (process.env[AWS.util.imdsDisabledEnv]) {
callback(new Error('EC2 Instance Metadata Service access disabled'));
return;
}
path = path || '/';
var httpRequest = new AWS.HttpRequest('http://' + this.host + path);
httpRequest.method = options.method || 'GET';
if (options.headers) {
httpRequest.headers = options.headers;
}
AWS.util.handleRequestWithRetries(httpRequest, this, callback);
},
/**
* @api private
*/
loadCredentialsCallbacks: [],
/**
* Fetches metadata token used for getting credentials
*
* @api private
* @callback callback function(err, token)
* Called when token is loaded from the resource
*/
fetchMetadataToken: function fetchMetadataToken(callback) {
var self = this;
var tokenFetchPath = '/latest/api/token';
self.request(
tokenFetchPath,
{
'method': 'PUT',
'headers': {
'x-aws-ec2-metadata-token-ttl-seconds': '21600'
}
},
callback
);
},
/**
* Fetches credentials
*
* @api private
* @callback cb function(err, creds)
* Called when credentials are loaded from the resource
*/
fetchCredentials: function fetchCredentials(options, cb) {
var self = this;
var basePath = '/latest/meta-data/iam/security-credentials/';
self.request(basePath, options, function (err, roleName) {
if (err) {
self.disableFetchToken = !(err.statusCode === 401);
cb(AWS.util.error(
err,
{
message: 'EC2 Metadata roleName request returned error'
}
));
return;
}
roleName = roleName.split('\n')[0]; // grab first (and only) role
self.request(basePath + roleName, options, function (credErr, credData) {
if (credErr) {
self.disableFetchToken = !(credErr.statusCode === 401);
cb(AWS.util.error(
credErr,
{
message: 'EC2 Metadata creds request returned error'
}
));
return;
}
try {
var credentials = JSON.parse(credData);
cb(null, credentials);
} catch (parseError) {
cb(parseError);
}
});
});
},
/**
* Loads a set of credentials stored in the instance metadata service
*
* @api private
* @callback callback function(err, credentials)
* Called when credentials are loaded from the resource
* @param err [Error] if an error occurred, this value will be set
* @param credentials [Object] the raw JSON object containing all
* metadata from the credentials resource
*/
loadCredentials: function loadCredentials(callback) {
var self = this;
self.loadCredentialsCallbacks.push(callback);
if (self.loadCredentialsCallbacks.length > 1) { return; }
function callbacks(err, creds) {
var cb;
while ((cb = self.loadCredentialsCallbacks.shift()) !== undefined) {
cb(err, creds);
}
}
if (self.disableFetchToken) {
self.fetchCredentials({}, callbacks);
} else {
self.fetchMetadataToken(function(tokenError, token) {
if (tokenError) {
if (tokenError.code === 'TimeoutError') {
self.disableFetchToken = true;
} else if (tokenError.retryable === true) {
callbacks(AWS.util.error(
tokenError,
{
message: 'EC2 Metadata token request returned error'
}
));
return;
} else if (tokenError.statusCode === 400) {
callbacks(AWS.util.error(
tokenError,
{
message: 'EC2 Metadata token request returned 400'
}
));
return;
}
}
var options = {};
if (token) {
options.headers = {
'x-aws-ec2-metadata-token': token
};
}
self.fetchCredentials(options, callbacks);
});
}
}
});
/**
* @api private
*/
module.exports = AWS.MetadataService;