1 | var AWS = require('./core');
|
2 | require('./http');
|
3 | var inherit = AWS.util.inherit;
|
4 |
|
5 | /**
|
6 | * Represents a metadata service available on EC2 instances. Using the
|
7 | * {request} method, you can receieve metadata about any available resource
|
8 | * on the metadata service.
|
9 | *
|
10 | * You can disable the use of the IMDS by setting the AWS_EC2_METADATA_DISABLED
|
11 | * environment variable to a truthy value.
|
12 | *
|
13 | * @!attribute [r] httpOptions
|
14 | * @return [map] a map of options to pass to the underlying HTTP request:
|
15 | *
|
16 | * * **timeout** (Number) — a timeout value in milliseconds to wait
|
17 | * before aborting the connection. Set to 0 for no timeout.
|
18 | *
|
19 | * @!macro nobrowser
|
20 | */
|
21 | AWS.MetadataService = inherit({
|
22 | /**
|
23 | * @return [String] the hostname of the instance metadata service
|
24 | */
|
25 | host: '169.254.169.254',
|
26 |
|
27 | /**
|
28 | * @!ignore
|
29 | */
|
30 |
|
31 | /**
|
32 | * Default HTTP options. By default, the metadata service is set to not
|
33 | * timeout on long requests. This means that on non-EC2 machines, this
|
34 | * request will never return. If you are calling this operation from an
|
35 | * environment that may not always run on EC2, set a `timeout` value so
|
36 | * the SDK will abort the request after a given number of milliseconds.
|
37 | */
|
38 | httpOptions: { timeout: 0 },
|
39 |
|
40 | /**
|
41 | * Creates a new MetadataService object with a given set of options.
|
42 | *
|
43 | * @option options host [String] the hostname of the instance metadata
|
44 | * service
|
45 | * @option options httpOptions [map] a map of options to pass to the
|
46 | * underlying HTTP request:
|
47 | *
|
48 | * * **timeout** (Number) — a timeout value in milliseconds to wait
|
49 | * before aborting the connection. Set to 0 for no timeout.
|
50 | * @option options maxRetries [Integer] the maximum number of retries to
|
51 | * perform for timeout errors
|
52 | * @option options retryDelayOptions [map] A set of options to configure the
|
53 | * retry delay on retryable errors. See AWS.Config for details.
|
54 | */
|
55 | constructor: function MetadataService(options) {
|
56 | AWS.util.update(this, options);
|
57 | },
|
58 |
|
59 | /**
|
60 | * Sends a request to the instance metadata service for a given resource.
|
61 | *
|
62 | * @param path [String] the path of the resource to get
|
63 | * @callback callback function(err, data)
|
64 | * Called when a response is available from the service.
|
65 | * @param err [Error, null] if an error occurred, this value will be set
|
66 | * @param data [String, null] if the request was successful, the body of
|
67 | * the response
|
68 | */
|
69 | request: function request(path, callback) {
|
70 | if (process.env[AWS.util.imdsDisabledEnv]) {
|
71 | callback(new Error('EC2 Instance Metadata Service access disabled'));
|
72 | return;
|
73 | }
|
74 |
|
75 | path = path || '/';
|
76 | var httpRequest = new AWS.HttpRequest('http://' + this.host + path);
|
77 | httpRequest.method = 'GET';
|
78 | AWS.util.handleRequestWithRetries(httpRequest, this, callback);
|
79 | },
|
80 |
|
81 | /**
|
82 | * @api private
|
83 | */
|
84 | loadCredentialsCallbacks: [],
|
85 |
|
86 | /**
|
87 | * Loads a set of credentials stored in the instance metadata service
|
88 | *
|
89 | * @api private
|
90 | * @callback callback function(err, credentials)
|
91 | * Called when credentials are loaded from the resource
|
92 | * @param err [Error] if an error occurred, this value will be set
|
93 | * @param credentials [Object] the raw JSON object containing all
|
94 | * metadata from the credentials resource
|
95 | */
|
96 | loadCredentials: function loadCredentials(callback) {
|
97 | var self = this;
|
98 | var basePath = '/latest/meta-data/iam/security-credentials/';
|
99 | self.loadCredentialsCallbacks.push(callback);
|
100 | if (self.loadCredentialsCallbacks.length > 1) { return; }
|
101 |
|
102 | function callbacks(err, creds) {
|
103 | var cb;
|
104 | while ((cb = self.loadCredentialsCallbacks.shift()) !== undefined) {
|
105 | cb(err, creds);
|
106 | }
|
107 | }
|
108 |
|
109 | self.request(basePath, function (err, roleName) {
|
110 | if (err) callbacks(err);
|
111 | else {
|
112 | roleName = roleName.split('\n')[0]; // grab first (and only) role
|
113 | self.request(basePath + roleName, function (credErr, credData) {
|
114 | if (credErr) callbacks(credErr);
|
115 | else {
|
116 | try {
|
117 | var credentials = JSON.parse(credData);
|
118 | callbacks(null, credentials);
|
119 | } catch (parseError) {
|
120 | callbacks(parseError);
|
121 | }
|
122 | }
|
123 | });
|
124 | }
|
125 | });
|
126 | }
|
127 | });
|
128 |
|
129 | /**
|
130 | * @api private
|
131 | */
|
132 | module.exports = AWS.MetadataService;
|