newrelic 2.0.0 → 2.2.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 (44) hide show
  1. package/.eslintrc +1 -0
  2. package/.github/PULL_REQUEST_TEMPLATE.md +5 -0
  3. package/NEWS.md +75 -0
  4. package/README.md +24 -12
  5. package/api.js +74 -29
  6. package/index.js +6 -2
  7. package/lib/agent.js +15 -3
  8. package/lib/collector/api.js +11 -9
  9. package/lib/collector/facts.js +9 -9
  10. package/lib/config.default.js +24 -2
  11. package/lib/config.js +70 -7
  12. package/lib/db/parse-sql.js +3 -0
  13. package/lib/db/tracer.js +3 -3
  14. package/lib/feature_flags.js +1 -0
  15. package/lib/instrumentation/core/async_hooks.js +92 -0
  16. package/lib/instrumentation/core/child_process.js +35 -0
  17. package/lib/instrumentation/core/globals.js +4 -23
  18. package/lib/instrumentation/core/http.js +1 -1
  19. package/lib/instrumentation/hapi.js +67 -33
  20. package/lib/instrumentation/ioredis.js +1 -6
  21. package/lib/metrics/names.js +7 -2
  22. package/lib/shim/datastore-shim.js +13 -1
  23. package/lib/shim/shim.js +46 -8
  24. package/lib/shim/webframework-shim.js +4 -2
  25. package/lib/stats/index.js +1 -1
  26. package/lib/system-info.js +21 -42
  27. package/lib/transaction/handle.js +14 -0
  28. package/lib/transaction/index.js +10 -2
  29. package/lib/transaction/tracer/index.js +1 -0
  30. package/lib/util/hashes.js +8 -1
  31. package/lib/util/logger.js +4 -4
  32. package/lib/util/sql/obfuscate.js +10 -4
  33. package/lib/utilization/aws-info.js +46 -0
  34. package/lib/utilization/azure-info.js +54 -0
  35. package/lib/utilization/common.js +106 -0
  36. package/lib/utilization/docker-info.js +100 -0
  37. package/lib/utilization/gcp-info.js +56 -0
  38. package/lib/utilization/index.js +28 -0
  39. package/lib/utilization/pcf-info.js +38 -0
  40. package/newrelic.js +1 -2
  41. package/package.json +2 -1
  42. package/stub_api.js +26 -9
  43. package/lib/aws-info.js +0 -100
  44. package/lib/parse-dockerinfo.js +0 -57
@@ -1,12 +1,12 @@
1
1
  'use strict'
2
2
 
3
3
  var exec = require('child_process').exec
4
- var fetchAWSInfo = require('./aws-info')
5
- var fs = require('fs')
4
+ var readProc = require('./utilization/common').readProc
5
+ var getBootId = require('./utilization/docker-info').getBootId
6
+ var utilization = require('./utilization')
6
7
  var logger = require('./logger.js').child({component: 'system-info'})
7
8
  var os = require('os')
8
9
  var parseCpuInfo = require('./parse-proc-cpuinfo')
9
- var parseDockerInfo = require('./parse-dockerinfo')
10
10
  var parseMemInfo = require('./parse-proc-meminfo')
11
11
  var platform = os.platform()
12
12
 
@@ -71,7 +71,9 @@ function fetchSystemInfo(agent, callback) {
71
71
  var tasksDone = 0
72
72
  var numTasks = 5
73
73
  function finishedResponse() {
74
- if (++tasksDone === numTasks) return callback(systemInfo)
74
+ if (++tasksDone === numTasks) {
75
+ callback(systemInfo)
76
+ }
75
77
  }
76
78
 
77
79
  module.exports._getProcessorStats(function getProcessCB(processorStats) {
@@ -88,16 +90,16 @@ function fetchSystemInfo(agent, callback) {
88
90
  systemInfo.kernelVersion = kernelVersion
89
91
  finishedResponse()
90
92
  })
91
- module.exports._getDockerContainerId(agent, function getContainerId(containerId) {
92
- if (containerId) {
93
- systemInfo.docker = {
94
- id: containerId
95
- }
93
+ utilization.getVendors(agent, function getVendorInfo(err, vendors) {
94
+ if (vendors) {
95
+ systemInfo.vendors = vendors
96
96
  }
97
97
  finishedResponse()
98
98
  })
99
- fetchAWSInfo(agent, function getAWSInfo(aws) {
100
- systemInfo.aws = aws
99
+ getBootId(agent, function reportBootId(err, bootId) {
100
+ if (bootId) {
101
+ systemInfo.bootId = bootId
102
+ }
101
103
  finishedResponse()
102
104
  })
103
105
  }
@@ -136,11 +138,11 @@ module.exports._getProcessorStats = function getProcessorStats(callback) {
136
138
  callback(processorStats)
137
139
  })
138
140
  } else if (platform.match(/linux/i)) {
139
- readProc('/proc/cpuinfo', function parseProc(data) {
141
+ readProc('/proc/cpuinfo', function parseProc(err, data) {
140
142
  callback(parseCpuInfo(data))
141
143
  })
142
144
  } else {
143
- logger.debug('Unknown platform: ' + platform + ', could not retrieve processor info')
145
+ logger.debug('Unknown platform: %s; could not retrieve processor info', platform)
144
146
  callback(processorStats)
145
147
  }
146
148
  }
@@ -156,11 +158,11 @@ module.exports._getMemoryStats = function getMemoryStats(callback) {
156
158
  callback(parseInt(memory, 10) / (1024 * 1024))
157
159
  })
158
160
  } else if (platform.match(/linux/i)) {
159
- readProc('/proc/meminfo', function parseProc(data) {
161
+ readProc('/proc/meminfo', function parseProc(err, data) {
160
162
  callback(parseMemInfo(data))
161
163
  })
162
164
  } else {
163
- logger.debug('Unknown platform: ' + platform + ', could not retrieve memory info')
165
+ logger.debug('Unknown platform: %s; could not retrieve memory info', platform)
164
166
  callback(null)
165
167
  }
166
168
  }
@@ -175,27 +177,15 @@ function getKernelVersion(callback) {
175
177
  callback(version)
176
178
  })
177
179
  } else if (platform.match(/linux/i)) {
178
- readProc('/proc/version', function parseProc(data) {
180
+ readProc('/proc/version', function parseProc(err, data) {
179
181
  callback(data)
180
182
  })
181
183
  } else {
182
- logger.debug('Unknown platform' + platform + ', could not read kernel version')
184
+ logger.debug('Unknown platform: %s; could not read kernel version', platform)
183
185
  callback(null)
184
186
  }
185
187
  }
186
188
 
187
- module.exports._getDockerContainerId = function getDockerContainerId(agent, callback) {
188
- if (!platform.match(/linux/i)) {
189
- logger.debug('Platform is not a flavor of linux, omitting docker info')
190
- callback(null)
191
- } else {
192
- readProc('/proc/self/cgroup', function getCGroup(data) {
193
- if (!data) callback(null)
194
- else callback(parseDockerInfo(agent, data))
195
- })
196
- }
197
- }
198
-
199
189
  function getSysctlValue(names, callback) {
200
190
  if (!names) return callback(null)
201
191
  var returned = false
@@ -206,7 +196,7 @@ function getSysctlValue(names, callback) {
206
196
  function respond(err, stdout, stderr) {
207
197
  if (returned) return
208
198
  if (err) {
209
- logger.debug('Error when trying to run: sysctl -n ' + name + ': %s', err.message)
199
+ logger.debug('Error when trying to run: sysctl -n %s: %s', name, err.message)
210
200
  callback(null)
211
201
  returned = true
212
202
  } else if (!stderr) {
@@ -214,20 +204,9 @@ function getSysctlValue(names, callback) {
214
204
  returned = true
215
205
  }
216
206
  if (++ran === names.length && !returned) {
217
- logger.debug('No sysctl info found for names: ' + names.toString())
207
+ logger.debug('No sysctl info found for names: %j', names)
218
208
  callback(null)
219
209
  }
220
210
  }
221
211
  })
222
212
  }
223
-
224
- function readProc(path, callback) {
225
- fs.readFile(path, function readProcFile(err, data) {
226
- if (err) {
227
- logger.error('Error when trying to read ' + path, err)
228
- callback(null)
229
- } else {
230
- callback(data.toString())
231
- }
232
- })
233
- }
@@ -13,10 +13,21 @@ module.exports.stub = {
13
13
  }
14
14
  }
15
15
 
16
+ /**
17
+ * A light representation of a transaction instance, returned by calling
18
+ * {@link API#getTransaction}.
19
+ *
20
+ * @constructor
21
+ */
16
22
  function TransactionHandle(transaction) {
17
23
  this._transaction = transaction
18
24
  }
19
25
 
26
+ /**
27
+ * End the transaction.
28
+ *
29
+ * @param {Function} callback
30
+ */
20
31
  TransactionHandle.prototype.end = function handleEnd(callback) {
21
32
  if (!this._transaction.name) {
22
33
  this._transaction.finalizeName(null) // Use existing partial name.
@@ -27,6 +38,9 @@ TransactionHandle.prototype.end = function handleEnd(callback) {
27
38
  this._transaction.end(callback)
28
39
  }
29
40
 
41
+ /**
42
+ * Mark the transaction to be ignored.
43
+ */
30
44
  TransactionHandle.prototype.ignore = function handleIgnore() {
31
45
  this._transaction.setForceIgnore(true)
32
46
  }
@@ -53,7 +53,8 @@ function Transaction(agent) {
53
53
  agent.metricNameNormalizer
54
54
  )
55
55
 
56
- agent.activeTransactions++
56
+ ++agent.activeTransactions
57
+ ++agent.transactionCreatedInHarvest
57
58
 
58
59
  this.numSegments = 0
59
60
 
@@ -144,8 +145,9 @@ Transaction.prototype.end = function end(done) {
144
145
  )
145
146
  }
146
147
 
147
- var transaction = this
148
+ this.agent.recordSupportability('Transactions/Segments', this.numSegments)
148
149
 
150
+ var transaction = this
149
151
  transaction.trace.end()
150
152
  process.nextTick(function nextTickedEnd() {
151
153
  // recorders must be run before the trace is collected
@@ -404,6 +406,12 @@ function finalizeNameFromUri(requestURL, statusCode) {
404
406
  * the current partial name is used.
405
407
  */
406
408
  Transaction.prototype.finalizeName = function finalizeName(name) {
409
+ // If no name is given, and this is a web transaction with a url, then
410
+ // finalize the name using the stored url.
411
+ if (name == null && this.type === 'web' && this.url) {
412
+ return this.finalizeNameFromUri(this.url, this.statusCode)
413
+ }
414
+
407
415
  this._partialName = name || this._partialName
408
416
 
409
417
  var fullName = TYPE_METRICS[this.type] + '/' + this._partialName
@@ -100,6 +100,7 @@ function transactionProxy(handler) {
100
100
  transaction: {id: segment.transaction.id, name: segment.transaction.getName()},
101
101
  segment: segment.name
102
102
  }, 'Active transaction when creating non-nested transaction')
103
+ tracer.agent.recordSupportability('Transactions/Nested')
103
104
  return handler.apply(this, arguments)
104
105
  }
105
106
  var transaction = new Transaction(tracer.agent)
@@ -1,9 +1,16 @@
1
1
  'use strict'
2
2
 
3
3
  var crypto = require('crypto')
4
+ var semver = require('semver')
4
5
 
5
6
  // TODO: Just use Buffer.from once Node <5.10.0 is deprecated.
6
- var makeBuffer = Buffer.from || function makeBuffer(v, enc) {
7
+ var makeBuffer = Buffer.from
8
+
9
+ if (semver.satisfies(process.version, '<5.10.0')) {
10
+ makeBuffer = bufferFactory
11
+ }
12
+
13
+ function bufferFactory(v, enc) {
7
14
  return new Buffer(v, enc)
8
15
  }
9
16
 
@@ -129,12 +129,12 @@ Logger.prototype.child = function child(extra) {
129
129
  var parent = this
130
130
  childLogger.options = parent.options
131
131
 
132
- childLogger.write = function write(level, args, extra) {
133
- extra = getPropertiesToLog(extra)
132
+ childLogger.write = function write(level, args, _extra) {
133
+ _extra = getPropertiesToLog(_extra)
134
134
  var selfExtra = util._extend({}, this.extra)
135
135
 
136
- extra = util._extend(selfExtra, extra)
137
- return parent.write(level, args, extra)
136
+ _extra = util._extend(selfExtra, _extra)
137
+ return parent.write(level, args, _extra)
138
138
  }
139
139
 
140
140
  childLogger.setEnabled = Logger.prototype.setEnabled
@@ -13,7 +13,7 @@ var hex = /0x[0-9a-f]+/
13
13
  var boolean = /true|false|null/
14
14
  var number = /\b-?(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?/
15
15
 
16
- var dialects = {}
16
+ var dialects = obfuscate.dialects = {}
17
17
 
18
18
  dialects.mysql = [
19
19
  replacer(join(
@@ -28,7 +28,7 @@ dialects.postgres = [
28
28
  [dollarQuote, singleQuote, comment, multilineComment, uuid, boolean, number],
29
29
  'gi'
30
30
  )),
31
- unmatchedPairs(/'|\/\*|\*\/|\$/)
31
+ unmatchedPairs(/'|\/\*|\*\/|(?:\$(?!\?))/)
32
32
  ]
33
33
 
34
34
  dialects.cassandra = [
@@ -72,13 +72,19 @@ function toPart(expressions) {
72
72
  }
73
73
 
74
74
  function replacer(regex) {
75
- return function replace(sql) {
75
+ function replace(sql) {
76
76
  return sql.replace(regex, '?')
77
77
  }
78
+ replace.regex = regex
79
+
80
+ return replace
78
81
  }
79
82
 
80
83
  function unmatchedPairs(regex) {
81
- return function check(sql) {
84
+ function check(sql) {
82
85
  return regex.test(sql) ? '?' : sql
83
86
  }
87
+ check.regex = regex
88
+
89
+ return check
84
90
  }
@@ -0,0 +1,46 @@
1
+ 'use strict'
2
+
3
+ var logger = require('../logger.js').child({component: 'aws-info'})
4
+ var common = require('./common')
5
+ var NAMES = require('../metrics/names.js')
6
+
7
+ module.exports = fetchAWSInfo
8
+ module.exports.clearCache = function clearAWSCache() {
9
+ results = null
10
+ }
11
+
12
+ var results = null
13
+
14
+ function fetchAWSInfo(agent, callback) {
15
+ if (!agent.config.utilization || !agent.config.utilization.detect_aws) {
16
+ return setImmediate(callback, null)
17
+ }
18
+
19
+ if (results) {
20
+ return setImmediate(callback, null, results)
21
+ }
22
+
23
+ var instanceHost = '169.254.169.254'
24
+ var apiVersion = '2016-09-02'
25
+ var endpoint = 'dynamic/instance-identity/document'
26
+ var url = 'http://' + instanceHost + '/' + apiVersion + '/' + endpoint
27
+ common.request(url, agent, function getMetadata(err, data) {
28
+ if (err) {
29
+ return callback(err)
30
+ }
31
+
32
+ try {
33
+ data = JSON.parse(data)
34
+ } catch (e) {
35
+ logger.debug(e, 'Failed to parse AWS metadata.')
36
+ data = null
37
+ }
38
+
39
+ results = common.getKeys(data, ['availabilityZone', 'instanceId', 'instanceType'])
40
+ if (results == null) {
41
+ logger.debug('AWS metadata was invalid.')
42
+ agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.AWS_ERROR).incrementCallCount()
43
+ }
44
+ callback(null, results)
45
+ })
46
+ }
@@ -0,0 +1,54 @@
1
+ 'use strict'
2
+
3
+ var common = require('./common')
4
+ var logger = require('../logger.js').child({component: 'azure-info'})
5
+ var NAMES = require('../metrics/names.js')
6
+
7
+
8
+ module.exports = fetchAzureInfo
9
+ module.exports.clearCache = function clearAzureCache() {
10
+ results = null
11
+ }
12
+
13
+ var results = null
14
+
15
+ function fetchAzureInfo(agent, callback) {
16
+ if (!agent.config.utilization || !agent.config.utilization.detect_azure) {
17
+ return setImmediate(callback, null, null)
18
+ }
19
+
20
+ if (results) {
21
+ return setImmediate(callback, null, results)
22
+ }
23
+
24
+ var instanceHost = '169.254.169.254'
25
+ var apiVersion = '2017-03-01'
26
+ var endpoint = '/metadata/instance/compute'
27
+ common.request({
28
+ host: instanceHost,
29
+ path: endpoint + '?api-version=' + apiVersion,
30
+ headers: {Metadata: 'true'}
31
+ }, agent, function getMetadata(err, data) {
32
+ if (err) {
33
+ return callback(err)
34
+ }
35
+
36
+ // Hopefully the data is parsable as JSON.
37
+ try {
38
+ data = JSON.parse(data)
39
+ } catch (e) {
40
+ logger.debug(e, 'Failed to parse Azure metadata.')
41
+ data = null
42
+ }
43
+
44
+ // Get out just the keys we care about.
45
+ results = common.getKeys(data, ['location', 'name', 'vmId', 'vmSize'])
46
+ if (results == null) {
47
+ logger.debug('Azure metadata was invalid.')
48
+ agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.AZURE_ERROR).incrementCallCount()
49
+ }
50
+
51
+ // Call back!
52
+ callback(null, results)
53
+ })
54
+ }
@@ -0,0 +1,106 @@
1
+ 'use strict'
2
+
3
+ var concat = require('concat-stream')
4
+ var http = require('http')
5
+ var logger = require('../logger').child({component: 'utilization-request'})
6
+ var fs = require('fs')
7
+ var properties = require('../util/properties')
8
+
9
+
10
+ exports.checkValueString = checkValueString
11
+ function checkValueString(str) {
12
+ if (!str || !str.length || Buffer.byteLength(str) > 255) {
13
+ return false
14
+ }
15
+
16
+ var len = str.length
17
+ var validCharacters = /[0-9a-zA-Z_ ./-]/
18
+ for (var i = 0; i < len; ++i) {
19
+ if (str.charCodeAt(i) < 128 && !validCharacters.test(str[i])) {
20
+ return false
21
+ }
22
+ }
23
+ return true
24
+ }
25
+
26
+ exports.getKeys = function getKeys(data, keys) {
27
+ if (!data) {
28
+ return null
29
+ }
30
+
31
+ var results = {}
32
+ for (var i = 0; i < keys.length; ++i) {
33
+ var key = keys[i]
34
+ if (!properties.hasOwn(data, key) || !data[key]) {
35
+ logger.debug('Key %s missing from metadata', key)
36
+ return null
37
+ }
38
+ var value = data[key]
39
+ if (typeof value === 'number') {
40
+ value = value.toString()
41
+ }
42
+
43
+ // If any value is invalid, the whole thing must be trashed.
44
+ if (!checkValueString(value)) {
45
+ logger.debug('Invalid metadata value found: %s -> %s', key, value)
46
+ return null
47
+ }
48
+ results[key] = value
49
+ }
50
+
51
+ return results
52
+ }
53
+
54
+ exports.request = function request(opts, agent, cb) {
55
+ var req = http.get(opts, function awsRequest(res) {
56
+ res.pipe(concat(respond))
57
+ function respond(data) {
58
+ agent.removeListener('errored', abortRequest)
59
+ agent.removeListener('stopped', abortRequest)
60
+
61
+ if (res.statusCode !== 200) {
62
+ logger.debug(
63
+ 'Got %d %s from metadata request %j',
64
+ res.statusCode, res.statusMessage || '<n/a>', opts
65
+ )
66
+ return cb(new Error('Request for metadata failed.'))
67
+ } else if (!data) {
68
+ logger.debug('Got no response data?')
69
+ return cb(new Error('No reponse data received.'))
70
+ }
71
+
72
+ cb(null, data.toString('utf8'))
73
+ }
74
+ })
75
+
76
+ req.setTimeout(1000, function requestTimeout() {
77
+ logger.debug('Request for metadata %j timed out', opts)
78
+ cb(new Error('Timeout'))
79
+ })
80
+ req.on('error', function requestError(err) {
81
+ logger.debug('Message for metadata %j: %s', opts, err.message)
82
+ cb(err)
83
+ })
84
+
85
+ agent.once('errored', abortRequest)
86
+ agent.once('stopped', abortRequest)
87
+
88
+ function abortRequest() {
89
+ logger.debug('Aborting request for metadata at %j', opts)
90
+ req.abort()
91
+ agent.removeListener('errored', abortRequest)
92
+ agent.removeListener('stopped', abortRequest)
93
+ }
94
+ }
95
+
96
+ exports.readProc = readProc
97
+ function readProc(path, callback) {
98
+ fs.readFile(path, function readProcFile(err, data) {
99
+ if (err) {
100
+ logger.error(err, 'Error when trying to read %s', path)
101
+ callback(err, null)
102
+ } else {
103
+ callback(null, data.toString())
104
+ }
105
+ })
106
+ }
@@ -0,0 +1,100 @@
1
+ 'use strict'
2
+
3
+ var logger = require('../logger').child({component: 'docker-info'})
4
+ var common = require('./common')
5
+ var NAMES = require('../metrics/names')
6
+ var os = require('os')
7
+
8
+ module.exports.getVendorInfo = fetchDockerVendorInfo
9
+ module.exports.clearVendorCache = function clearDockerVendorCache() {
10
+ vendorInfo = null
11
+ }
12
+
13
+ module.exports.getBootId = function getBootId(agent, callback) {
14
+ if (!/linux/i.test(os.platform())) {
15
+ logger.debug('Platform is not a flavor of linux, omitting boot info')
16
+ return setImmediate(callback, null, null)
17
+ }
18
+
19
+ common.readProc('/proc/sys/kernel/random/boot_id', function readProcBootId(err, data) {
20
+ if (!data) {
21
+ bootIdError()
22
+ return callback(null, null)
23
+ }
24
+
25
+ data = data.trim()
26
+ var asciiData = (new Buffer(data, 'ascii')).toString()
27
+
28
+ if (data !== asciiData) {
29
+ bootIdError()
30
+ return callback(null, null)
31
+ }
32
+
33
+ if (data.length !== 36) {
34
+ bootIdError()
35
+ if (data.length > 128) {
36
+ data = data.substr(0, 128)
37
+ }
38
+ }
39
+
40
+ return callback(null, data)
41
+ })
42
+
43
+ function bootIdError() {
44
+ agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.BOOT_ID_ERROR)
45
+ .incrementCallCount()
46
+ }
47
+ }
48
+
49
+ var vendorInfo = null
50
+
51
+ function fetchDockerVendorInfo(agent, callback) {
52
+ if (!agent.config.utilization || !agent.config.utilization.detect_docker) {
53
+ return callback(null, null)
54
+ }
55
+
56
+ if (vendorInfo) {
57
+ return callback(null, vendorInfo)
58
+ }
59
+
60
+ if (!os.platform().match(/linux/i)) {
61
+ logger.debug('Platform is not a flavor of linux, omitting docker info')
62
+ return callback(null)
63
+ }
64
+
65
+ common.readProc('/proc/self/cgroup', function getCGroup(err, data) {
66
+ if (!data) {
67
+ return callback(null)
68
+ }
69
+
70
+ var id = null
71
+ findCGroups(data, 'cpu', function forEachCpuGroup(cpuGroup) {
72
+ var match = /(?:^|[^0-9a-f])([0-9a-f]{64})(?:[^0-9a-f]|$)/.exec(cpuGroup)
73
+ if (match) {
74
+ id = match[1]
75
+ return false
76
+ }
77
+
78
+ return true
79
+ })
80
+
81
+ if (id) {
82
+ vendorInfo = {id: id}
83
+ callback(null, vendorInfo)
84
+ } else {
85
+ logger.debug('No matching cpu group found.')
86
+ callback(null, null)
87
+ }
88
+ })
89
+ }
90
+
91
+ function findCGroups(info, cgroup, eachCb) {
92
+ var target = new RegExp('^\\d+:[^:]*?\\b' + cgroup + '\\b[^:]*:')
93
+ var lines = info.split('\n')
94
+ for (var i = 0; i < lines.length; ++i) {
95
+ var line = lines[i]
96
+ if (target.test(line) && !eachCb(line.split(':')[2])) {
97
+ break
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,56 @@
1
+ 'use strict'
2
+
3
+ var logger = require('../logger.js').child({component: 'gcp-info'})
4
+ var common = require('./common')
5
+ var NAMES = require('../metrics/names.js')
6
+
7
+ module.exports = fetchGCPInfo
8
+ module.exports.clearCache = function clearGCPCache() {
9
+ resultDict = null
10
+ }
11
+
12
+ var resultDict
13
+
14
+ function fetchGCPInfo(agent, callback) {
15
+ if (!agent.config.utilization || !agent.config.utilization.detect_gcp) {
16
+ return setImmediate(callback, null)
17
+ }
18
+
19
+ if (resultDict) {
20
+ return setImmediate(callback, null, resultDict)
21
+ }
22
+
23
+ common.request({
24
+ host: 'metadata.google.internal',
25
+ path: '/computeMetadata/v1/instance/?recursive=true',
26
+ headers: {
27
+ 'Metadata-Flavor': 'Google'
28
+ }
29
+ }, agent, function getMetadata(err, data) {
30
+ if (err) {
31
+ return callback(err)
32
+ }
33
+
34
+ try {
35
+ data = JSON.parse(data)
36
+ } catch (e) {
37
+ logger.debug(e, 'Failed to parse GCP metadata.')
38
+ data = null
39
+ }
40
+
41
+ var results = common.getKeys(data, ['id', 'machineType', 'name', 'zone'])
42
+ if (results == null) {
43
+ logger.debug('GCP metadata was invalid.')
44
+ agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.GCP_ERROR).incrementCallCount()
45
+ } else {
46
+ // normalize
47
+ results.machineType =
48
+ results.machineType.substr(results.machineType.lastIndexOf('/') + 1)
49
+ results.zone =
50
+ results.zone.substr(results.zone.lastIndexOf('/') + 1)
51
+
52
+ resultDict = results
53
+ }
54
+ callback(null, results)
55
+ })
56
+ }
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ module.exports.getVendors = getVendors
4
+ function getVendors(agent, callback) {
5
+ var vendorMethods = {
6
+ aws: require('./aws-info'),
7
+ pcf: require('./pcf-info'),
8
+ azure: require('./azure-info'),
9
+ gcp: require('./gcp-info'),
10
+ docker: require('./docker-info').getVendorInfo
11
+ }
12
+
13
+ var done = 0
14
+ var vendors = null
15
+ var vendorNames = Object.keys(vendorMethods)
16
+ vendorNames.forEach(function getVendorInfo(vendor) {
17
+ vendorMethods[vendor](agent, function getInfo(err, result) {
18
+ if (result) {
19
+ vendors = vendors || {}
20
+ vendors[vendor] = result
21
+ }
22
+
23
+ if (++done === vendorNames.length) {
24
+ callback(null, vendors)
25
+ }
26
+ })
27
+ })
28
+ }