@depup/elastic-apm-node 4.15.0-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/LICENSE +26 -0
  2. package/NOTICE.md +442 -0
  3. package/README.md +48 -0
  4. package/changes.json +78 -0
  5. package/index.d.ts +398 -0
  6. package/index.js +11 -0
  7. package/lib/InflightEventSet.js +53 -0
  8. package/lib/activation-method.js +119 -0
  9. package/lib/agent.js +941 -0
  10. package/lib/apm-client/apm-client.js +313 -0
  11. package/lib/apm-client/http-apm-client/CHANGELOG.md +271 -0
  12. package/lib/apm-client/http-apm-client/README.md +485 -0
  13. package/lib/apm-client/http-apm-client/central-config.js +41 -0
  14. package/lib/apm-client/http-apm-client/container-info.js +111 -0
  15. package/lib/apm-client/http-apm-client/detect-hostname.js +96 -0
  16. package/lib/apm-client/http-apm-client/index.js +1975 -0
  17. package/lib/apm-client/http-apm-client/logging.js +31 -0
  18. package/lib/apm-client/http-apm-client/ndjson.js +20 -0
  19. package/lib/apm-client/http-apm-client/truncate.js +434 -0
  20. package/lib/apm-client/noop-apm-client.js +73 -0
  21. package/lib/async-hooks-polyfill.js +58 -0
  22. package/lib/cloud-metadata/aws.js +175 -0
  23. package/lib/cloud-metadata/azure.js +123 -0
  24. package/lib/cloud-metadata/callback-coordination.js +159 -0
  25. package/lib/cloud-metadata/gcp.js +133 -0
  26. package/lib/cloud-metadata/index.js +175 -0
  27. package/lib/config/config.js +458 -0
  28. package/lib/config/normalizers.js +701 -0
  29. package/lib/config/schema.js +1007 -0
  30. package/lib/constants.js +35 -0
  31. package/lib/errors.js +303 -0
  32. package/lib/filters/sanitize-field-names.js +69 -0
  33. package/lib/http-request.js +249 -0
  34. package/lib/instrumentation/azure-functions.js +519 -0
  35. package/lib/instrumentation/context.js +56 -0
  36. package/lib/instrumentation/dropped-spans-stats.js +112 -0
  37. package/lib/instrumentation/elasticsearch-shared.js +63 -0
  38. package/lib/instrumentation/express-utils.js +91 -0
  39. package/lib/instrumentation/generic-span.js +322 -0
  40. package/lib/instrumentation/http-shared.js +424 -0
  41. package/lib/instrumentation/ids.js +39 -0
  42. package/lib/instrumentation/index.js +1127 -0
  43. package/lib/instrumentation/modules/@apollo/server.js +30 -0
  44. package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
  45. package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
  46. package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
  47. package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
  48. package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
  49. package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
  50. package/lib/instrumentation/modules/@opentelemetry/api.js +86 -0
  51. package/lib/instrumentation/modules/@opentelemetry/sdk-metrics.js +79 -0
  52. package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
  53. package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
  54. package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
  55. package/lib/instrumentation/modules/_lambda-handler.js +40 -0
  56. package/lib/instrumentation/modules/apollo-server-core.js +49 -0
  57. package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
  58. package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
  59. package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
  60. package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
  61. package/lib/instrumentation/modules/aws-sdk.js +76 -0
  62. package/lib/instrumentation/modules/bluebird.js +93 -0
  63. package/lib/instrumentation/modules/cassandra-driver.js +280 -0
  64. package/lib/instrumentation/modules/elasticsearch.js +191 -0
  65. package/lib/instrumentation/modules/express-graphql.js +66 -0
  66. package/lib/instrumentation/modules/express-queue.js +28 -0
  67. package/lib/instrumentation/modules/express.js +162 -0
  68. package/lib/instrumentation/modules/fastify.js +172 -0
  69. package/lib/instrumentation/modules/finalhandler.js +41 -0
  70. package/lib/instrumentation/modules/generic-pool.js +85 -0
  71. package/lib/instrumentation/modules/graphql.js +256 -0
  72. package/lib/instrumentation/modules/handlebars.js +22 -0
  73. package/lib/instrumentation/modules/http.js +112 -0
  74. package/lib/instrumentation/modules/http2.js +320 -0
  75. package/lib/instrumentation/modules/https.js +68 -0
  76. package/lib/instrumentation/modules/ioredis.js +94 -0
  77. package/lib/instrumentation/modules/jade.js +18 -0
  78. package/lib/instrumentation/modules/kafkajs.js +476 -0
  79. package/lib/instrumentation/modules/knex.js +91 -0
  80. package/lib/instrumentation/modules/koa-router.js +74 -0
  81. package/lib/instrumentation/modules/koa.js +15 -0
  82. package/lib/instrumentation/modules/memcached.js +99 -0
  83. package/lib/instrumentation/modules/mimic-response.js +45 -0
  84. package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
  85. package/lib/instrumentation/modules/mongodb-core.js +206 -0
  86. package/lib/instrumentation/modules/mongodb.js +259 -0
  87. package/lib/instrumentation/modules/mysql.js +200 -0
  88. package/lib/instrumentation/modules/mysql2.js +140 -0
  89. package/lib/instrumentation/modules/pg.js +148 -0
  90. package/lib/instrumentation/modules/pug.js +18 -0
  91. package/lib/instrumentation/modules/redis.js +176 -0
  92. package/lib/instrumentation/modules/restify.js +52 -0
  93. package/lib/instrumentation/modules/tedious.js +159 -0
  94. package/lib/instrumentation/modules/undici.js +270 -0
  95. package/lib/instrumentation/modules/ws.js +59 -0
  96. package/lib/instrumentation/noop-transaction.js +81 -0
  97. package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
  98. package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
  99. package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
  100. package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
  101. package/lib/instrumentation/run-context/RunContext.js +151 -0
  102. package/lib/instrumentation/run-context/index.js +23 -0
  103. package/lib/instrumentation/shimmer.js +123 -0
  104. package/lib/instrumentation/span-compression.js +239 -0
  105. package/lib/instrumentation/span.js +621 -0
  106. package/lib/instrumentation/template-shared.js +43 -0
  107. package/lib/instrumentation/timer.js +84 -0
  108. package/lib/instrumentation/transaction.js +571 -0
  109. package/lib/lambda.js +992 -0
  110. package/lib/load-source-map.js +100 -0
  111. package/lib/logging.js +212 -0
  112. package/lib/metrics/index.js +92 -0
  113. package/lib/metrics/platforms/generic/index.js +40 -0
  114. package/lib/metrics/platforms/generic/process-cpu.js +22 -0
  115. package/lib/metrics/platforms/generic/process-top.js +157 -0
  116. package/lib/metrics/platforms/generic/stats.js +34 -0
  117. package/lib/metrics/platforms/generic/system-cpu.js +51 -0
  118. package/lib/metrics/platforms/linux/index.js +19 -0
  119. package/lib/metrics/platforms/linux/stats.js +213 -0
  120. package/lib/metrics/queue.js +90 -0
  121. package/lib/metrics/registry.js +52 -0
  122. package/lib/metrics/reporter.js +119 -0
  123. package/lib/metrics/runtime.js +77 -0
  124. package/lib/middleware/connect.js +16 -0
  125. package/lib/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js +150 -0
  126. package/lib/opentelemetry-bridge/OTelBridgeRunContext.js +124 -0
  127. package/lib/opentelemetry-bridge/OTelContextManager.js +82 -0
  128. package/lib/opentelemetry-bridge/OTelSpan.js +344 -0
  129. package/lib/opentelemetry-bridge/OTelTracer.js +201 -0
  130. package/lib/opentelemetry-bridge/OTelTracerProvider.js +25 -0
  131. package/lib/opentelemetry-bridge/README.md +244 -0
  132. package/lib/opentelemetry-bridge/index.js +15 -0
  133. package/lib/opentelemetry-bridge/oblog.js +23 -0
  134. package/lib/opentelemetry-bridge/opentelemetry-core-mini/README.md +3 -0
  135. package/lib/opentelemetry-bridge/opentelemetry-core-mini/internal/validators.js +52 -0
  136. package/lib/opentelemetry-bridge/opentelemetry-core-mini/trace/TraceState.js +109 -0
  137. package/lib/opentelemetry-bridge/otelutils.js +99 -0
  138. package/lib/opentelemetry-bridge/setup.js +76 -0
  139. package/lib/opentelemetry-metrics/ElasticApmMetricExporter.js +285 -0
  140. package/lib/opentelemetry-metrics/index.js +50 -0
  141. package/lib/parsers.js +225 -0
  142. package/lib/propwrap.js +147 -0
  143. package/lib/stacktraces.js +537 -0
  144. package/lib/symbols.js +15 -0
  145. package/lib/tracecontext/index.js +118 -0
  146. package/lib/tracecontext/traceparent.js +185 -0
  147. package/lib/tracecontext/tracestate.js +388 -0
  148. package/lib/wildcard-matcher.js +52 -0
  149. package/loader.mjs +7 -0
  150. package/package.json +299 -0
  151. package/start.d.ts +8 -0
  152. package/start.js +29 -0
  153. package/types/aws-lambda.d.ts +98 -0
  154. package/types/connect.d.ts +23 -0
@@ -0,0 +1,175 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+ const URL = require('url').URL;
9
+ const { httpRequest } = require('../http-request');
10
+ const DEFAULT_BASE_URL = new URL('/', 'http://169.254.169.254:80');
11
+
12
+ function sanitizeHttpHeaderValue(value) {
13
+ // no newlines, carriage returns, or ascii characters outside of 32 (\x20) - 127 (\x7F)
14
+ const newValue = value.replace(/[\r\n]/g, '').replace(/[^\x20-\x7F]/g, '');
15
+ return newValue;
16
+ }
17
+
18
+ /**
19
+ * Logic for making request to /latest/dynamic/instance-identity/document
20
+ *
21
+ * The headers parameter allow us to, if needed, set the IMDSv2 token
22
+ */
23
+ function getMetadataAwsWithHeaders(
24
+ baseUrl,
25
+ headers,
26
+ connectTimeoutMs,
27
+ resTimeoutMs,
28
+ logger,
29
+ cb,
30
+ ) {
31
+ const options = {
32
+ method: 'GET',
33
+ timeout: resTimeoutMs,
34
+ connectTimeout: connectTimeoutMs,
35
+ };
36
+ if (headers) {
37
+ options.headers = headers;
38
+ }
39
+ const url = baseUrl + 'latest/dynamic/instance-identity/document';
40
+ const req = httpRequest(url, options, function (res) {
41
+ const finalData = [];
42
+ res.on('data', function (data) {
43
+ finalData.push(data);
44
+ });
45
+
46
+ res.on('end', function (data) {
47
+ let result;
48
+ try {
49
+ result = formatMetadataStringIntoObject(finalData.join(''));
50
+ } catch (err) {
51
+ logger.trace(
52
+ 'aws metadata server responded, but there was an ' +
53
+ 'error parsing the result: %o',
54
+ err,
55
+ );
56
+ cb(err);
57
+ return;
58
+ }
59
+ cb(null, result);
60
+ });
61
+ });
62
+
63
+ req.on('timeout', function () {
64
+ req.destroy(new Error('request to metadata server timed out'));
65
+ });
66
+
67
+ req.on('connectTimeout', function () {
68
+ req.destroy(new Error('could not ping metadata server'));
69
+ });
70
+
71
+ req.on('error', function (err) {
72
+ cb(err);
73
+ });
74
+
75
+ req.end();
76
+ }
77
+
78
+ /**
79
+ * Fetches metadata from either an IMDSv2 or IMDSv1 endpoint
80
+ *
81
+ * Attempts to fetch a token for an IMDSv2 service call. If this call
82
+ * is a success, then we call the instance endpoint with the token. If
83
+ * this call fails, then we call the instance endpoint without the token.
84
+ *
85
+ * A "success" to the token endpoint means an HTTP status code of 200
86
+ * and a non-zero-length return value for the token.
87
+ *
88
+ * https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
89
+ */
90
+ function getMetadataAws(
91
+ connectTimeoutMs,
92
+ resTimeoutMs,
93
+ logger,
94
+ baseUrlOverride,
95
+ cb,
96
+ ) {
97
+ const baseUrl = baseUrlOverride || DEFAULT_BASE_URL;
98
+ const url = baseUrl + 'latest/api/token';
99
+ const options = {
100
+ method: 'PUT',
101
+ headers: {
102
+ 'X-aws-ec2-metadata-token-ttl-seconds': 300,
103
+ },
104
+ timeout: resTimeoutMs,
105
+ connectTimeout: connectTimeoutMs,
106
+ };
107
+ const req = httpRequest(url, options, function (res) {
108
+ const finalData = [];
109
+ res.on('data', function (data) {
110
+ finalData.push(data);
111
+ });
112
+
113
+ res.on('end', function () {
114
+ const token = sanitizeHttpHeaderValue(finalData.join(''));
115
+ const headers = {};
116
+ if (token && res.statusCode === 200) {
117
+ // uses return value from call to latest/api/token as token,
118
+ // and takes extra steps to ensure characters are valid
119
+ headers['X-aws-ec2-metadata-token'] = token;
120
+ }
121
+
122
+ getMetadataAwsWithHeaders(
123
+ baseUrl,
124
+ headers,
125
+ connectTimeoutMs,
126
+ resTimeoutMs,
127
+ logger,
128
+ cb,
129
+ );
130
+ });
131
+ });
132
+ req.on('timeout', function () {
133
+ req.destroy(new Error('request for metadata token timed out'));
134
+ });
135
+
136
+ req.on('connectTimeout', function () {
137
+ req.destroy(
138
+ new Error('socket connection to metadata token server timed out'),
139
+ );
140
+ });
141
+
142
+ req.on('error', function (err) {
143
+ cb(err);
144
+ });
145
+
146
+ req.end();
147
+ }
148
+
149
+ /**
150
+ * Builds metadata object
151
+ *
152
+ * Takes the response from a /latest/dynamic/instance-identity/document
153
+ * service request and formats it into the cloud metadata object
154
+ */
155
+ function formatMetadataStringIntoObject(string) {
156
+ const data = JSON.parse(string);
157
+ const metadata = {
158
+ account: {
159
+ id: String(data.accountId),
160
+ },
161
+ instance: {
162
+ id: String(data.instanceId),
163
+ },
164
+ availability_zone: String(data.availabilityZone),
165
+ machine: {
166
+ type: String(data.instanceType),
167
+ },
168
+ provider: 'aws',
169
+ region: String(data.region),
170
+ };
171
+
172
+ return metadata;
173
+ }
174
+
175
+ module.exports = { getMetadataAws };
@@ -0,0 +1,123 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+ const URL = require('url').URL;
9
+ const { httpRequest } = require('../http-request');
10
+
11
+ const DEFAULT_BASE_URL = new URL('/', 'http://169.254.169.254:80');
12
+
13
+ /**
14
+ * Checks for metadata server then fetches data
15
+ *
16
+ * The getMetadataAzure method will fetch cloud metadata information
17
+ * from Amazon's IMDSv1 endpoint and return (via callback)
18
+ * the formatted metadata.
19
+ *
20
+ * Before fetching data, the server will be "pinged" by attempting
21
+ * to connect via TCP with a short timeout (`connectTimeoutMs`).
22
+ *
23
+ * https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=windows
24
+ */
25
+ function getMetadataAzure(
26
+ connectTimeoutMs,
27
+ resTimeoutMs,
28
+ logger,
29
+ baseUrlOverride,
30
+ cb,
31
+ ) {
32
+ const baseUrl = baseUrlOverride || DEFAULT_BASE_URL;
33
+ const options = {
34
+ method: 'GET',
35
+ timeout: resTimeoutMs,
36
+ connectTimeout: connectTimeoutMs,
37
+ headers: {
38
+ Metadata: 'true',
39
+ },
40
+ };
41
+
42
+ const url = baseUrl.toString() + 'metadata/instance?api-version=2020-09-01';
43
+
44
+ const req = httpRequest(url, options, function (res) {
45
+ const finalData = [];
46
+ res.on('data', function (data) {
47
+ finalData.push(data);
48
+ });
49
+
50
+ res.on('end', function (data) {
51
+ let result;
52
+ try {
53
+ result = formatMetadataStringIntoObject(finalData.join(''));
54
+ } catch (err) {
55
+ logger.trace(
56
+ 'azure metadata server responded, but there was an ' +
57
+ 'error parsing the result: %o',
58
+ err,
59
+ );
60
+ cb(err);
61
+ return;
62
+ }
63
+ cb(null, result);
64
+ });
65
+ });
66
+
67
+ req.on('timeout', function () {
68
+ req.destroy(new Error('request to azure metadata server timed out'));
69
+ });
70
+
71
+ req.on('connectTimeout', function () {
72
+ req.destroy(new Error('could not ping azure metadata server'));
73
+ });
74
+
75
+ req.on('error', function (err) {
76
+ cb(err);
77
+ });
78
+
79
+ req.end();
80
+ }
81
+
82
+ /**
83
+ * Builds metadata object
84
+ *
85
+ * Takes the response from /metadata/instance?api-version=2020-09-01
86
+ * service request and formats it into the cloud metadata object
87
+ */
88
+ function formatMetadataStringIntoObject(string) {
89
+ const metadata = {
90
+ account: {
91
+ id: null,
92
+ },
93
+ instance: {
94
+ id: null,
95
+ name: null,
96
+ },
97
+ project: {
98
+ name: null,
99
+ },
100
+ availability_zone: null,
101
+ machine: {
102
+ type: null,
103
+ },
104
+ provider: 'azure',
105
+ region: null,
106
+ };
107
+ const parsed = JSON.parse(string);
108
+ if (!parsed.compute) {
109
+ return metadata;
110
+ }
111
+ const data = parsed.compute;
112
+ metadata.account.id = String(data.subscriptionId);
113
+ metadata.instance.id = String(data.vmId);
114
+ metadata.instance.name = String(data.name);
115
+ metadata.project.name = String(data.resourceGroupName);
116
+ metadata.availability_zone = String(data.zone);
117
+ metadata.machine.type = String(data.vmSize);
118
+ metadata.region = String(data.location);
119
+
120
+ return metadata;
121
+ }
122
+
123
+ module.exports = { getMetadataAzure };
@@ -0,0 +1,159 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+ const { EventEmitter } = require('events');
9
+
10
+ /**
11
+ * Coordinates fetching of metadata from multiple providers
12
+ *
13
+ * Implements event based coordination for fetching metadata
14
+ * from multiple providers. The first provider to return
15
+ * a non-error result "wins". When this happens the CallbackCoordination
16
+ * object will emit a `result` event.
17
+ *
18
+ * If all the metadata providers fail to return a result, then the
19
+ * object will emit an error event indicating failure to collect metadata
20
+ * from any event.
21
+ *
22
+ * Since a scheduled callback may (accidently) fail to call its own
23
+ * callback function, the CallbackCoordination object includes its
24
+ * own timeout timer to avoid deadlock situation.
25
+ */
26
+ class CallbackCoordination extends EventEmitter {
27
+ constructor(maxWaitMS = -1, logger) {
28
+ super();
29
+
30
+ this.logger = logger;
31
+ // how many results have we seen
32
+ this.resultCount = 0;
33
+ this.expectedResults = 0;
34
+ this.errors = [];
35
+ this.scheduled = [];
36
+ this.done = false;
37
+ this.started = false;
38
+ this.timeout = null;
39
+ if (maxWaitMS !== -1) {
40
+ this.timeout = setTimeout(() => {
41
+ if (!this.done) {
42
+ this.complete();
43
+ this.logger.warn(
44
+ 'cloud metadata requests timed out, using default values instead',
45
+ );
46
+ const error = new CallbackCoordinationError(
47
+ 'callback coordination reached timeout',
48
+ this.errors,
49
+ );
50
+ this.emit('error', error);
51
+ }
52
+ }, maxWaitMS);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Finishes coordination
58
+ *
59
+ * Marks as `done`, cleans up timeout (if necessary)
60
+ */
61
+ complete() {
62
+ this.done = true;
63
+ if (this.timeout) {
64
+ clearTimeout(this.timeout);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Accepts and schedules a callback function
70
+ *
71
+ * Callback will be in the form
72
+ * function(fetcher) {
73
+ * //... work to fetch data ...
74
+ *
75
+ * // this callback calls the recordResult method
76
+ * // method of the fetcher
77
+ * fetcher.recordResult(error, result)
78
+ * }
79
+ *
80
+ * Method also increments expectedResults counter to keep track
81
+ * of how many callbacks we've scheduled
82
+ */
83
+ schedule(fetcherCallback) {
84
+ if (this.started) {
85
+ this.logger.error('Can not schedule callback, already started');
86
+ return;
87
+ }
88
+
89
+ this.expectedResults++;
90
+ this.scheduled.push(fetcherCallback);
91
+ }
92
+
93
+ /**
94
+ * Starts processing of the callbacks scheduled by the `schedule` method
95
+ */
96
+ start() {
97
+ this.started = true;
98
+ // if called with nothing, send an error through so we don't hang
99
+ if (this.scheduled.length === 0) {
100
+ const error = new CallbackCoordinationError('no callbacks to run');
101
+ this.recordResult(error);
102
+ }
103
+
104
+ for (const cb of this.scheduled) {
105
+ process.nextTick(cb.bind(null, this));
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Receives calls from scheduled callbacks.
111
+ *
112
+ * If called with a non-error, the method will emit a `result` event
113
+ * and include the results as an argument. Only a single result
114
+ * is emitted -- if other callbacks respond with a result this method
115
+ * will ignore them.
116
+ *
117
+ * If called by _all_ scheduled callbacks without a non-error, this method
118
+ * will issue the error event.
119
+ */
120
+ recordResult(error, result) {
121
+ // console.log('.')
122
+ this.resultCount++;
123
+ if (error) {
124
+ this.errors.push(error);
125
+ if (this.resultCount >= this.expectedResults && !this.done) {
126
+ this.complete();
127
+ // we've made every request without success, signal an error
128
+ const error = new CallbackCoordinationError(
129
+ 'no response from any callback, no cloud metadata will be set (normal outside of cloud env.)',
130
+ this.errors,
131
+ );
132
+ this.logger.debug('no cloud metadata servers responded');
133
+ this.emit('error', error);
134
+ }
135
+ }
136
+
137
+ if (!error && result && !this.done) {
138
+ this.complete();
139
+ this.emit('result', result);
140
+ }
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Error for CallbackCoordination class
146
+ *
147
+ * Includes the individual errors from each callback
148
+ * of the CallbackCoordination object
149
+ */
150
+ class CallbackCoordinationError extends Error {
151
+ constructor(message, allErrors = []) {
152
+ super(message);
153
+ this.allErrors = allErrors;
154
+ }
155
+ }
156
+
157
+ module.exports = {
158
+ CallbackCoordination,
159
+ };
@@ -0,0 +1,133 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const URL = require('url').URL;
10
+ const JSONBigInt = require('json-bigint');
11
+ const { httpRequest } = require('../http-request');
12
+
13
+ const DEFAULT_BASE_URL = new URL('/', 'http://metadata.google.internal:80');
14
+
15
+ /**
16
+ * Checks for metadata server then fetches data
17
+ *
18
+ * The getMetadataGcp function will fetch cloud metadata information
19
+ * from Amazon's IMDSv1 endpoint and return (via callback)
20
+ * the formatted metadata.
21
+ *
22
+ * Before fetching data, the server will be "pinged" by attempting
23
+ * to connect via TCP with a short timeout. (`connectTimeoutMs`)
24
+ *
25
+ * https://cloud.google.com/compute/docs/storing-retrieving-metadata
26
+ */
27
+ function getMetadataGcp(
28
+ connectTimeoutMs,
29
+ resTimeoutMs,
30
+ logger,
31
+ baseUrlOverride,
32
+ cb,
33
+ ) {
34
+ const baseUrl = baseUrlOverride || DEFAULT_BASE_URL;
35
+ const options = {
36
+ method: 'GET',
37
+ timeout: resTimeoutMs,
38
+ connectTimeout: connectTimeoutMs,
39
+ headers: {
40
+ 'Metadata-Flavor': 'Google',
41
+ },
42
+ };
43
+ const url = baseUrl + 'computeMetadata/v1/?recursive=true';
44
+ const req = httpRequest(url, options, function (res) {
45
+ const finalData = [];
46
+ res.on('data', function (data) {
47
+ finalData.push(data);
48
+ });
49
+
50
+ res.on('end', function (data) {
51
+ if (res.statusCode !== 200) {
52
+ logger.debug('gcp metadata: unexpected statusCode: %s', res.statusCode);
53
+ cb(
54
+ new Error(
55
+ 'error fetching gcp metadata: unexpected statusCode: ' +
56
+ res.statusCode,
57
+ ),
58
+ );
59
+ return;
60
+ }
61
+ // Note: We could also guard on the response having the
62
+ // 'Metadata-Flavor: Google' header as done by:
63
+ // https://github.com/googleapis/gcp-metadata/blob/v6.0.0/src/index.ts#L109-L112
64
+
65
+ let result;
66
+ try {
67
+ result = formatMetadataStringIntoObject(finalData.join(''));
68
+ } catch (err) {
69
+ logger.debug(
70
+ 'gcp metadata server responded, but there was an ' +
71
+ 'error parsing the result: %o',
72
+ err,
73
+ );
74
+ cb(err);
75
+ return;
76
+ }
77
+ cb(null, result);
78
+ });
79
+ });
80
+
81
+ req.on('timeout', function () {
82
+ req.destroy(new Error('request to metadata server timed out'));
83
+ });
84
+
85
+ req.on('connectTimeout', function () {
86
+ req.destroy(new Error('could not ping metadata server'));
87
+ });
88
+
89
+ req.on('error', function (err) {
90
+ cb(err);
91
+ });
92
+
93
+ req.end();
94
+ }
95
+
96
+ /**
97
+ * Builds metadata object
98
+ *
99
+ * Convert a GCP Cloud Engine VM metadata response
100
+ * (https://cloud.google.com/compute/docs/metadata/default-metadata-values)
101
+ * to the APM intake cloud metadata object
102
+ * (https://github.com/elastic/apm/blob/main/specs/agents/metadata.md#gcp-metadata).
103
+ *
104
+ * See discussion about big int values here:
105
+ * https://github.com/googleapis/gcp-metadata#take-care-with-large-number-valued-properties
106
+ * This implementation is using the same 'json-bigint' library as 'gcp-metadata'.
107
+ */
108
+ function formatMetadataStringIntoObject(string) {
109
+ const data = JSONBigInt.parse(string);
110
+
111
+ // E.g., 'projects/513326162531/zones/us-west1-b' -> 'us-west1-b'
112
+ const az = data.instance.zone.split('/').pop();
113
+
114
+ const metadata = {
115
+ provider: 'gcp',
116
+ instance: {
117
+ id: data.instance.id.toString(), // We expect this to be a BigInt.
118
+ name: data.instance.name,
119
+ },
120
+ project: {
121
+ id: data.project.projectId,
122
+ },
123
+ availability_zone: az,
124
+ region: az.slice(0, az.lastIndexOf('-')), // 'us-west1-b' -> 'us-west1'
125
+ machine: {
126
+ type: data.instance.machineType.split('/').pop(),
127
+ },
128
+ };
129
+
130
+ return metadata;
131
+ }
132
+
133
+ module.exports = { getMetadataGcp };