dd-trace 4.18.0 → 5.6.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 (209) hide show
  1. package/CONTRIBUTING.md +98 -0
  2. package/LICENSE-3rdparty.csv +4 -5
  3. package/MIGRATING.md +15 -0
  4. package/README.md +20 -140
  5. package/ci/cypress/after-run.js +1 -0
  6. package/ci/cypress/after-spec.js +1 -0
  7. package/ci/init.js +1 -4
  8. package/ext/kinds.d.ts +1 -0
  9. package/ext/kinds.js +2 -1
  10. package/ext/tags.d.ts +2 -1
  11. package/ext/tags.js +6 -1
  12. package/index.d.ts +1523 -1460
  13. package/package.json +19 -19
  14. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  15. package/packages/datadog-core/src/utils/src/get.js +11 -0
  16. package/packages/datadog-core/src/utils/src/has.js +14 -0
  17. package/packages/datadog-core/src/utils/src/kebabcase.js +16 -0
  18. package/packages/datadog-core/src/utils/src/pick.js +11 -0
  19. package/packages/datadog-core/src/utils/src/set.js +16 -0
  20. package/packages/datadog-core/src/utils/src/uniq.js +5 -0
  21. package/packages/datadog-esbuild/index.js +1 -20
  22. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  23. package/packages/datadog-instrumentations/src/amqplib.js +2 -2
  24. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  25. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  26. package/packages/datadog-instrumentations/src/child_process.js +150 -0
  27. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  28. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  29. package/packages/datadog-instrumentations/src/cucumber.js +163 -46
  30. package/packages/datadog-instrumentations/src/dns.js +2 -1
  31. package/packages/datadog-instrumentations/src/express.js +20 -0
  32. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  33. package/packages/datadog-instrumentations/src/grpc/client.js +56 -36
  34. package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
  35. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  36. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -3
  37. package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
  38. package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
  39. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  40. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  41. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  42. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  43. package/packages/datadog-instrumentations/src/jest.js +239 -52
  44. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  45. package/packages/datadog-instrumentations/src/mocha.js +154 -18
  46. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
  47. package/packages/datadog-instrumentations/src/mongoose.js +23 -10
  48. package/packages/datadog-instrumentations/src/mquery.js +65 -0
  49. package/packages/datadog-instrumentations/src/net.js +10 -2
  50. package/packages/datadog-instrumentations/src/next.js +35 -9
  51. package/packages/datadog-instrumentations/src/playwright.js +110 -16
  52. package/packages/datadog-instrumentations/src/restify.js +14 -1
  53. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  54. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  55. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -1
  56. package/packages/datadog-plugin-amqplib/src/producer.js +13 -1
  57. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
  58. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +163 -27
  59. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +46 -8
  60. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +129 -22
  61. package/packages/datadog-plugin-child_process/src/index.js +91 -0
  62. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +125 -0
  63. package/packages/datadog-plugin-cucumber/src/index.js +70 -13
  64. package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
  65. package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
  66. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
  67. package/packages/datadog-plugin-cypress/src/plugin.js +6 -454
  68. package/packages/datadog-plugin-cypress/src/support.js +50 -3
  69. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -0
  70. package/packages/datadog-plugin-graphql/src/index.js +1 -6
  71. package/packages/datadog-plugin-graphql/src/resolve.js +28 -18
  72. package/packages/datadog-plugin-grpc/src/client.js +16 -2
  73. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  74. package/packages/datadog-plugin-http/src/client.js +19 -2
  75. package/packages/datadog-plugin-jest/src/index.js +118 -12
  76. package/packages/datadog-plugin-jest/src/util.js +38 -16
  77. package/packages/datadog-plugin-kafkajs/src/consumer.js +76 -6
  78. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -8
  79. package/packages/datadog-plugin-mocha/src/index.js +87 -17
  80. package/packages/datadog-plugin-next/src/index.js +40 -14
  81. package/packages/datadog-plugin-playwright/src/index.js +71 -8
  82. package/packages/datadog-plugin-rhea/src/consumer.js +16 -1
  83. package/packages/datadog-plugin-rhea/src/producer.js +10 -0
  84. package/packages/dd-trace/src/appsec/activation.js +29 -0
  85. package/packages/dd-trace/src/appsec/addresses.js +5 -1
  86. package/packages/dd-trace/src/appsec/api_security_sampler.js +61 -0
  87. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  88. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  89. package/packages/dd-trace/src/appsec/channels.js +7 -3
  90. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  91. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
  92. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
  93. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  94. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +22 -17
  95. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -28
  96. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +10 -6
  97. package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +19 -0
  98. package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +90 -0
  99. package/packages/dd-trace/src/appsec/iast/context/kafka-ctx-plugin.js +14 -0
  100. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  101. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +13 -2
  102. package/packages/dd-trace/src/appsec/iast/index.js +15 -5
  103. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  104. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -0
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +10 -0
  107. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +53 -0
  108. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -46
  109. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +13 -9
  110. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +47 -0
  111. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +19 -6
  112. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +3 -1
  113. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +41 -3
  114. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  115. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  116. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  117. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  118. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  119. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  120. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  121. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  122. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  123. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
  124. package/packages/dd-trace/src/appsec/index.js +49 -33
  125. package/packages/dd-trace/src/appsec/recommended.json +1763 -106
  126. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -1
  127. package/packages/dd-trace/src/appsec/remote_config/index.js +42 -16
  128. package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -8
  129. package/packages/dd-trace/src/appsec/reporter.js +51 -34
  130. package/packages/dd-trace/src/appsec/rule_manager.js +11 -8
  131. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  132. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  133. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  134. package/packages/dd-trace/src/ci-visibility/{intelligent-test-runner/get-itr-configuration.js → early-flake-detection/get-known-tests.js} +17 -22
  135. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +25 -6
  136. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  137. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  139. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +95 -37
  140. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +134 -61
  141. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +37 -4
  142. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +131 -0
  143. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  144. package/packages/dd-trace/src/config.js +561 -470
  145. package/packages/dd-trace/src/data_streams_context.js +1 -1
  146. package/packages/dd-trace/src/datastreams/pathway.js +58 -1
  147. package/packages/dd-trace/src/datastreams/processor.js +196 -27
  148. package/packages/dd-trace/src/datastreams/writer.js +11 -5
  149. package/packages/dd-trace/src/dogstatsd.js +3 -5
  150. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -6
  151. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  152. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  153. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  154. package/packages/dd-trace/src/exporters/common/request.js +21 -3
  155. package/packages/dd-trace/src/format.js +30 -2
  156. package/packages/dd-trace/src/id.js +12 -0
  157. package/packages/dd-trace/src/iitm.js +1 -1
  158. package/packages/dd-trace/src/log/channels.js +1 -1
  159. package/packages/dd-trace/src/noop/proxy.js +4 -0
  160. package/packages/dd-trace/src/noop/span.js +1 -0
  161. package/packages/dd-trace/src/opentelemetry/span.js +104 -4
  162. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  163. package/packages/dd-trace/src/opentracing/propagation/text_map.js +16 -7
  164. package/packages/dd-trace/src/opentracing/span.js +48 -4
  165. package/packages/dd-trace/src/opentracing/span_context.js +15 -6
  166. package/packages/dd-trace/src/opentracing/tracer.js +4 -3
  167. package/packages/dd-trace/src/plugin_manager.js +1 -1
  168. package/packages/dd-trace/src/plugins/ci_plugin.js +78 -19
  169. package/packages/dd-trace/src/plugins/database.js +1 -1
  170. package/packages/dd-trace/src/plugins/index.js +7 -0
  171. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  172. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  173. package/packages/dd-trace/src/plugins/util/git.js +104 -22
  174. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  175. package/packages/dd-trace/src/plugins/util/test.js +60 -10
  176. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  177. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  178. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  179. package/packages/dd-trace/src/priority_sampler.js +30 -38
  180. package/packages/dd-trace/src/profiler.js +5 -3
  181. package/packages/dd-trace/src/profiling/config.js +77 -24
  182. package/packages/dd-trace/src/profiling/exporters/agent.js +77 -31
  183. package/packages/dd-trace/src/profiling/exporters/file.js +2 -1
  184. package/packages/dd-trace/src/profiling/profiler.js +33 -22
  185. package/packages/dd-trace/src/profiling/profilers/events.js +270 -0
  186. package/packages/dd-trace/src/profiling/profilers/shared.js +45 -0
  187. package/packages/dd-trace/src/profiling/profilers/space.js +18 -2
  188. package/packages/dd-trace/src/profiling/profilers/wall.js +146 -70
  189. package/packages/dd-trace/src/proxy.js +56 -24
  190. package/packages/dd-trace/src/ritm.js +1 -1
  191. package/packages/dd-trace/src/sampling_rule.js +130 -0
  192. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  193. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  194. package/packages/dd-trace/src/span_processor.js +9 -1
  195. package/packages/dd-trace/src/span_sampler.js +6 -64
  196. package/packages/dd-trace/src/spanleak.js +98 -0
  197. package/packages/dd-trace/src/startup-log.js +7 -1
  198. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  199. package/packages/dd-trace/src/telemetry/index.js +182 -53
  200. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  201. package/packages/dd-trace/src/telemetry/send-data.js +65 -7
  202. package/packages/dd-trace/src/tracer.js +12 -5
  203. package/register.js +4 -0
  204. package/scripts/install_plugin_modules.js +11 -3
  205. package/scripts/st.js +105 -0
  206. package/packages/datadog-instrumentations/src/child-process.js +0 -30
  207. package/packages/dd-trace/src/plugins/util/exec.js +0 -13
  208. package/packages/diagnostics_channel/index.js +0 -3
  209. package/packages/diagnostics_channel/src/index.js +0 -121
@@ -5,6 +5,16 @@ const log = require('../../../log')
5
5
 
6
6
  const { AgentlessCiVisibilityEncoder } = require('../../../encode/agentless-ci-visibility')
7
7
  const BaseWriter = require('../../../exporters/common/writer')
8
+ const {
9
+ incrementCountMetric,
10
+ distributionMetric,
11
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS,
12
+ TELEMETRY_ENDPOINT_PAYLOAD_BYTES,
13
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
14
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
15
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
16
+ getErrorTypeFromStatusCode
17
+ } = require('../../../ci-visibility/telemetry')
8
18
 
9
19
  class Writer extends BaseWriter {
10
20
  constructor ({ url, tags, evpProxyPrefix = '' }) {
@@ -35,8 +45,27 @@ class Writer extends BaseWriter {
35
45
 
36
46
  log.debug(() => `Request to the intake: ${safeJSONStringify(options)}`)
37
47
 
38
- request(data, options, (err, res) => {
48
+ const startRequestTime = Date.now()
49
+
50
+ incrementCountMetric(TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS, { endpoint: 'test_cycle' })
51
+ distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_BYTES, { endpoint: 'test_cycle' }, data.length)
52
+
53
+ request(data, options, (err, res, statusCode) => {
54
+ distributionMetric(
55
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
56
+ { endpoint: 'test_cycle' },
57
+ Date.now() - startRequestTime
58
+ )
39
59
  if (err) {
60
+ const errorType = getErrorTypeFromStatusCode(statusCode)
61
+ incrementCountMetric(
62
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
63
+ { endpoint: 'test_cycle', errorType }
64
+ )
65
+ incrementCountMetric(
66
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
67
+ { endpoint: 'test_cycle' }
68
+ )
40
69
  log.error(err)
41
70
  done()
42
71
  return
@@ -3,8 +3,9 @@
3
3
  const URL = require('url').URL
4
4
 
5
5
  const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata')
6
- const { getItrConfiguration: getItrConfigurationRequest } = require('../intelligent-test-runner/get-itr-configuration')
6
+ const { getLibraryConfiguration: getLibraryConfigurationRequest } = require('../requests/get-library-configuration')
7
7
  const { getSkippableSuites: getSkippableSuitesRequest } = require('../intelligent-test-runner/get-skippable-suites')
8
+ const { getKnownTests: getKnownTestsRequest } = require('../early-flake-detection/get-known-tests')
8
9
  const log = require('../../log')
9
10
  const AgentInfoExporter = require('../../exporters/common/agent-info-exporter')
10
11
 
@@ -76,11 +77,18 @@ class CiVisibilityExporter extends AgentInfoExporter {
76
77
  shouldRequestSkippableSuites () {
77
78
  return !!(this._config.isIntelligentTestRunnerEnabled &&
78
79
  this._canUseCiVisProtocol &&
79
- this._itrConfig &&
80
- this._itrConfig.isSuitesSkippingEnabled)
80
+ this._libraryConfig?.isSuitesSkippingEnabled)
81
81
  }
82
82
 
83
- shouldRequestItrConfiguration () {
83
+ shouldRequestKnownTests () {
84
+ return !!(
85
+ this._config.isEarlyFlakeDetectionEnabled &&
86
+ this._canUseCiVisProtocol &&
87
+ this._libraryConfig?.isEarlyFlakeDetectionEnabled
88
+ )
89
+ }
90
+
91
+ shouldRequestLibraryConfiguration () {
84
92
  return this._config.isIntelligentTestRunnerEnabled
85
93
  }
86
94
 
@@ -92,6 +100,19 @@ class CiVisibilityExporter extends AgentInfoExporter {
92
100
  return this._canUseCiVisProtocol
93
101
  }
94
102
 
103
+ getRequestConfiguration (testConfiguration) {
104
+ return {
105
+ url: this._getApiUrl(),
106
+ env: this._config.env,
107
+ service: this._config.service,
108
+ isEvpProxy: !!this._isUsingEvpProxy,
109
+ isGzipCompatible: this._isGzipCompatible,
110
+ evpProxyPrefix: this.evpProxyPrefix,
111
+ custom: getTestConfigurationTags(this._config.tags),
112
+ ...testConfiguration
113
+ }
114
+ }
115
+
95
116
  // We can't call the skippable endpoint until git upload has finished,
96
117
  // hence the this._gitUploadPromise.then
97
118
  getSkippableSuites (testConfiguration, callback) {
@@ -102,52 +123,84 @@ class CiVisibilityExporter extends AgentInfoExporter {
102
123
  if (gitUploadError) {
103
124
  return callback(gitUploadError, [])
104
125
  }
105
- const configuration = {
106
- url: this._getApiUrl(),
107
- site: this._config.site,
108
- env: this._config.env,
109
- service: this._config.service,
110
- isEvpProxy: !!this._isUsingEvpProxy,
111
- custom: getTestConfigurationTags(this._config.tags),
112
- ...testConfiguration
113
- }
114
- getSkippableSuitesRequest(configuration, callback)
126
+ getSkippableSuitesRequest(this.getRequestConfiguration(testConfiguration), callback)
115
127
  })
116
128
  }
117
129
 
130
+ getKnownTests (testConfiguration, callback) {
131
+ if (!this.shouldRequestKnownTests()) {
132
+ return callback(null)
133
+ }
134
+ getKnownTestsRequest(this.getRequestConfiguration(testConfiguration), callback)
135
+ }
136
+
118
137
  /**
119
- * We can't request ITR configuration until we know whether we can use the
138
+ * We can't request library configuration until we know whether we can use the
120
139
  * CI Visibility Protocol, hence the this._canUseCiVisProtocol promise.
121
140
  */
122
- getItrConfiguration (testConfiguration, callback) {
141
+ getLibraryConfiguration (testConfiguration, callback) {
123
142
  const { repositoryUrl } = testConfiguration
124
143
  this.sendGitMetadata(repositoryUrl)
125
- if (!this.shouldRequestItrConfiguration()) {
144
+ if (!this.shouldRequestLibraryConfiguration()) {
126
145
  return callback(null, {})
127
146
  }
128
147
  this._canUseCiVisProtocolPromise.then((canUseCiVisProtocol) => {
129
148
  if (!canUseCiVisProtocol) {
130
149
  return callback(null, {})
131
150
  }
132
- const configuration = {
133
- url: this._getApiUrl(),
134
- env: this._config.env,
135
- service: this._config.service,
136
- isEvpProxy: !!this._isUsingEvpProxy,
137
- custom: getTestConfigurationTags(this._config.tags),
138
- ...testConfiguration
139
- }
140
- getItrConfigurationRequest(configuration, (err, itrConfig) => {
151
+ const configuration = this.getRequestConfiguration(testConfiguration)
152
+
153
+ getLibraryConfigurationRequest(configuration, (err, libraryConfig) => {
141
154
  /**
142
- * **Important**: this._itrConfig remains empty in testing frameworks
143
- * where the tests run in a subprocess, because `getItrConfiguration` is called only once.
155
+ * **Important**: this._libraryConfig remains empty in testing frameworks
156
+ * where the tests run in a subprocess, like Jest,
157
+ * because `getLibraryConfiguration` is called only once in the main process.
144
158
  */
145
- this._itrConfig = itrConfig
146
- callback(err, itrConfig)
159
+ this._libraryConfig = this.filterConfiguration(libraryConfig)
160
+
161
+ if (err) {
162
+ callback(err, {})
163
+ } else if (libraryConfig?.requireGit) {
164
+ // If the backend requires git, we'll wait for the upload to finish and request settings again
165
+ this._gitUploadPromise.then(gitUploadError => {
166
+ if (gitUploadError) {
167
+ return callback(gitUploadError, {})
168
+ }
169
+ getLibraryConfigurationRequest(configuration, (err, finalLibraryConfig) => {
170
+ this._libraryConfig = this.filterConfiguration(finalLibraryConfig)
171
+ callback(err, this._libraryConfig)
172
+ })
173
+ })
174
+ } else {
175
+ callback(null, this._libraryConfig)
176
+ }
147
177
  })
148
178
  })
149
179
  }
150
180
 
181
+ // Takes into account potential kill switches
182
+ filterConfiguration (remoteConfiguration) {
183
+ if (!remoteConfiguration) {
184
+ return {}
185
+ }
186
+ const {
187
+ isCodeCoverageEnabled,
188
+ isSuitesSkippingEnabled,
189
+ isItrEnabled,
190
+ requireGit,
191
+ isEarlyFlakeDetectionEnabled,
192
+ earlyFlakeDetectionNumRetries
193
+ } = remoteConfiguration
194
+ return {
195
+ isCodeCoverageEnabled,
196
+ isSuitesSkippingEnabled,
197
+ isItrEnabled,
198
+ requireGit,
199
+ isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
200
+ earlyFlakeDetectionNumRetries
201
+ }
202
+ }
203
+
151
204
  sendGitMetadata (repositoryUrl) {
152
205
  if (!this._config.isGitUploadEnabled) {
153
206
  return
@@ -156,14 +209,19 @@ class CiVisibilityExporter extends AgentInfoExporter {
156
209
  if (!canUseCiVisProtocol) {
157
210
  return
158
211
  }
159
- sendGitMetadataRequest(this._getApiUrl(), !!this._isUsingEvpProxy, repositoryUrl, (err) => {
160
- if (err) {
161
- log.error(`Error uploading git metadata: ${err.message}`)
162
- } else {
163
- log.debug('Successfully uploaded git metadata')
212
+ sendGitMetadataRequest(
213
+ this._getApiUrl(),
214
+ { isEvpProxy: !!this._isUsingEvpProxy, evpProxyPrefix: this.evpProxyPrefix },
215
+ repositoryUrl,
216
+ (err) => {
217
+ if (err) {
218
+ log.error(`Error uploading git metadata: ${err.message}`)
219
+ } else {
220
+ log.debug('Successfully uploaded git metadata')
221
+ }
222
+ this._resolveGit(err)
164
223
  }
165
- this._resolveGit(err)
166
- })
224
+ )
167
225
  })
168
226
  }
169
227
 
@@ -10,11 +10,25 @@ const {
10
10
  getLatestCommits,
11
11
  getRepositoryUrl,
12
12
  generatePackFilesForCommits,
13
- getCommitsToUpload,
13
+ getCommitsRevList,
14
14
  isShallowRepository,
15
15
  unshallowRepository
16
16
  } = require('../../../plugins/util/git')
17
17
 
18
+ const {
19
+ incrementCountMetric,
20
+ distributionMetric,
21
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS,
22
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_MS,
23
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS,
24
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_NUM,
25
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES,
26
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_MS,
27
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS,
28
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES,
29
+ getErrorTypeFromStatusCode
30
+ } = require('../../../ci-visibility/telemetry')
31
+
18
32
  const isValidSha1 = (sha) => /^[0-9a-f]{40}$/.test(sha)
19
33
  const isValidSha256 = (sha) => /^[0-9a-f]{64}$/.test(sha)
20
34
 
@@ -46,11 +60,7 @@ function getCommonRequestOptions (url) {
46
60
  * The response are the commits for which the backend already has information
47
61
  * This response is used to know which commits can be ignored from there on
48
62
  */
49
- function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
50
- const latestCommits = getLatestCommits()
51
-
52
- log.debug(`There were ${latestCommits.length} commits since last month.`)
53
-
63
+ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy, evpProxyPrefix }, callback) {
54
64
  const commonOptions = getCommonRequestOptions(url)
55
65
 
56
66
  const options = {
@@ -63,7 +73,7 @@ function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
63
73
  }
64
74
 
65
75
  if (isEvpProxy) {
66
- options.path = '/evp_proxy/v2/api/v2/git/repository/search_commits'
76
+ options.path = `${evpProxyPrefix}/api/v2/git/repository/search_commits`
67
77
  options.headers['X-Datadog-EVP-Subdomain'] = 'api'
68
78
  delete options.headers['dd-api-key']
69
79
  }
@@ -78,25 +88,41 @@ function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
78
88
  }))
79
89
  })
80
90
 
81
- request(localCommitData, options, (err, response) => {
91
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS)
92
+ const startTime = Date.now()
93
+ request(localCommitData, options, (err, response, statusCode) => {
94
+ distributionMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_MS, {}, Date.now() - startTime)
82
95
  if (err) {
96
+ const errorType = getErrorTypeFromStatusCode(statusCode)
97
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS, { errorType })
83
98
  const error = new Error(`Error fetching commits to exclude: ${err.message}`)
84
99
  return callback(error)
85
100
  }
86
- let commitsToExclude
101
+ let alreadySeenCommits
87
102
  try {
88
- commitsToExclude = validateCommits(JSON.parse(response).data)
103
+ alreadySeenCommits = validateCommits(JSON.parse(response).data)
89
104
  } catch (e) {
105
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS, { errorType: 'network' })
90
106
  return callback(new Error(`Can't parse commits to exclude response: ${e.message}`))
91
107
  }
92
- callback(null, commitsToExclude, latestCommits)
108
+ log.debug(`There are ${alreadySeenCommits.length} commits to exclude.`)
109
+ const commitsToInclude = latestCommits.filter((commit) => !alreadySeenCommits.includes(commit))
110
+ log.debug(`There are ${commitsToInclude.length} commits to include.`)
111
+
112
+ if (!commitsToInclude.length) {
113
+ return callback(null, [])
114
+ }
115
+
116
+ const commitsToUpload = getCommitsRevList(alreadySeenCommits, commitsToInclude)
117
+
118
+ callback(null, commitsToUpload)
93
119
  })
94
120
  }
95
121
 
96
122
  /**
97
123
  * This function uploads a git packfile
98
124
  */
99
- function uploadPackFile ({ url, isEvpProxy, packFileToUpload, repositoryUrl, headCommit }, callback) {
125
+ function uploadPackFile ({ url, isEvpProxy, evpProxyPrefix, packFileToUpload, repositoryUrl, headCommit }, callback) {
100
126
  const form = new FormData()
101
127
 
102
128
  const pushedSha = JSON.stringify({
@@ -136,24 +162,86 @@ function uploadPackFile ({ url, isEvpProxy, packFileToUpload, repositoryUrl, hea
136
162
  }
137
163
 
138
164
  if (isEvpProxy) {
139
- options.path = '/evp_proxy/v2/api/v2/git/repository/packfile'
165
+ options.path = `${evpProxyPrefix}/api/v2/git/repository/packfile`
140
166
  options.headers['X-Datadog-EVP-Subdomain'] = 'api'
141
167
  delete options.headers['dd-api-key']
142
168
  }
143
169
 
170
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES)
171
+
172
+ const uploadSize = form.size()
173
+
174
+ const startTime = Date.now()
144
175
  request(form, options, (err, _, statusCode) => {
176
+ distributionMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_MS, {}, Date.now() - startTime)
145
177
  if (err) {
178
+ const errorType = getErrorTypeFromStatusCode(statusCode)
179
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS, { errorType })
146
180
  const error = new Error(`Could not upload packfiles: status code ${statusCode}: ${err.message}`)
147
- return callback(error)
181
+ return callback(error, uploadSize)
148
182
  }
149
- callback(null)
183
+ callback(null, uploadSize)
150
184
  })
151
185
  }
152
186
 
187
+ function generateAndUploadPackFiles ({
188
+ url,
189
+ isEvpProxy,
190
+ evpProxyPrefix,
191
+ commitsToUpload,
192
+ repositoryUrl,
193
+ headCommit
194
+ }, callback) {
195
+ log.debug(`There are ${commitsToUpload.length} commits to upload`)
196
+
197
+ const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
198
+
199
+ log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
200
+
201
+ if (!packFilesToUpload.length) {
202
+ return callback(new Error('Failed to generate packfiles'))
203
+ }
204
+
205
+ distributionMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_NUM, {}, packFilesToUpload.length)
206
+ let packFileIndex = 0
207
+ let totalUploadedBytes = 0
208
+ // This uploads packfiles sequentially
209
+ const uploadPackFileCallback = (err, byteLength) => {
210
+ totalUploadedBytes += byteLength
211
+ if (err || packFileIndex === packFilesToUpload.length) {
212
+ distributionMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES, {}, totalUploadedBytes)
213
+ return callback(err)
214
+ }
215
+ return uploadPackFile(
216
+ {
217
+ packFileToUpload: packFilesToUpload[packFileIndex++],
218
+ url,
219
+ isEvpProxy,
220
+ evpProxyPrefix,
221
+ repositoryUrl,
222
+ headCommit
223
+ },
224
+ uploadPackFileCallback
225
+ )
226
+ }
227
+
228
+ uploadPackFile(
229
+ {
230
+ packFileToUpload: packFilesToUpload[packFileIndex++],
231
+ url,
232
+ isEvpProxy,
233
+ evpProxyPrefix,
234
+ repositoryUrl,
235
+ headCommit
236
+ },
237
+ uploadPackFileCallback
238
+ )
239
+ }
240
+
153
241
  /**
154
242
  * This function uploads git metadata to CI Visibility's backend.
155
243
  */
156
- function sendGitMetadata (url, isEvpProxy, configRepositoryUrl, callback) {
244
+ function sendGitMetadata (url, { isEvpProxy, evpProxyPrefix }, configRepositoryUrl, callback) {
157
245
  let repositoryUrl = configRepositoryUrl
158
246
  if (!repositoryUrl) {
159
247
  repositoryUrl = getRepositoryUrl()
@@ -165,65 +253,50 @@ function sendGitMetadata (url, isEvpProxy, configRepositoryUrl, callback) {
165
253
  return callback(new Error('Repository URL is empty'))
166
254
  }
167
255
 
168
- if (isShallowRepository()) {
169
- log.debug('It is shallow clone, unshallowing...')
170
- unshallowRepository()
171
- }
256
+ const latestCommits = getLatestCommits()
257
+ log.debug(`There were ${latestCommits.length} commits since last month.`)
258
+ const [headCommit] = latestCommits
172
259
 
173
- getCommitsToExclude({ url, repositoryUrl, isEvpProxy }, (err, commitsToExclude, latestCommits) => {
260
+ const getOnFinishGetCommitsToUpload = (hasCheckedShallow) => (err, commitsToUpload) => {
174
261
  if (err) {
175
262
  return callback(err)
176
263
  }
177
- log.debug(`There are ${commitsToExclude.length} commits to exclude.`)
178
- const [headCommit] = latestCommits
179
- const commitsToInclude = latestCommits.filter((commit) => !commitsToExclude.includes(commit))
180
- log.debug(`There are ${commitsToInclude.length} commits to include.`)
181
-
182
- const commitsToUpload = getCommitsToUpload(commitsToExclude, commitsToInclude)
183
264
 
184
265
  if (!commitsToUpload.length) {
185
266
  log.debug('No commits to upload')
186
267
  return callback(null)
187
268
  }
188
- log.debug(`There are ${commitsToUpload.length} commits to upload`)
189
269
 
190
- const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
191
-
192
- log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
193
-
194
- if (!packFilesToUpload.length) {
195
- return callback(new Error('Failed to generate packfiles'))
196
- }
197
-
198
- let packFileIndex = 0
199
- // This uploads packfiles sequentially
200
- const uploadPackFileCallback = (err) => {
201
- if (err || packFileIndex === packFilesToUpload.length) {
202
- return callback(err)
203
- }
204
- return uploadPackFile(
205
- {
206
- packFileToUpload: packFilesToUpload[packFileIndex++],
207
- url,
208
- isEvpProxy,
209
- repositoryUrl,
210
- headCommit
211
- },
212
- uploadPackFileCallback
213
- )
214
- }
215
-
216
- uploadPackFile(
217
- {
218
- packFileToUpload: packFilesToUpload[packFileIndex++],
270
+ // If it has already unshallowed or the clone is not shallow, we move on
271
+ if (hasCheckedShallow || !isShallowRepository()) {
272
+ return generateAndUploadPackFiles({
219
273
  url,
220
274
  isEvpProxy,
275
+ evpProxyPrefix,
276
+ commitsToUpload,
221
277
  repositoryUrl,
222
278
  headCommit
223
- },
224
- uploadPackFileCallback
225
- )
226
- })
279
+ }, callback)
280
+ }
281
+ // Otherwise we unshallow and get commits to upload again
282
+ log.debug('It is shallow clone, unshallowing...')
283
+ unshallowRepository()
284
+ getCommitsToUpload({
285
+ url,
286
+ repositoryUrl,
287
+ latestCommits,
288
+ isEvpProxy,
289
+ evpProxyPrefix
290
+ }, getOnFinishGetCommitsToUpload(true))
291
+ }
292
+
293
+ getCommitsToUpload({
294
+ url,
295
+ repositoryUrl,
296
+ latestCommits,
297
+ isEvpProxy,
298
+ evpProxyPrefix
299
+ }, getOnFinishGetCommitsToUpload(false))
227
300
  }
228
301
 
229
302
  module.exports = {
@@ -1,9 +1,22 @@
1
1
  const request = require('../../exporters/common/request')
2
2
  const log = require('../../log')
3
+ const {
4
+ incrementCountMetric,
5
+ distributionMetric,
6
+ TELEMETRY_ITR_SKIPPABLE_TESTS,
7
+ TELEMETRY_ITR_SKIPPABLE_TESTS_MS,
8
+ TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS,
9
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
10
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS,
11
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES,
12
+ getErrorTypeFromStatusCode
13
+ } = require('../../ci-visibility/telemetry')
3
14
 
4
15
  function getSkippableSuites ({
5
16
  url,
6
17
  isEvpProxy,
18
+ evpProxyPrefix,
19
+ isGzipCompatible,
7
20
  env,
8
21
  service,
9
22
  repositoryUrl,
@@ -26,8 +39,12 @@ function getSkippableSuites ({
26
39
  url
27
40
  }
28
41
 
42
+ if (isGzipCompatible) {
43
+ options.headers['accept-encoding'] = 'gzip'
44
+ }
45
+
29
46
  if (isEvpProxy) {
30
- options.path = '/evp_proxy/v2/api/v2/ci/tests/skippable'
47
+ options.path = `${evpProxyPrefix}/api/v2/ci/tests/skippable`
31
48
  options.headers['X-Datadog-EVP-Subdomain'] = 'api'
32
49
  } else {
33
50
  const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
@@ -59,13 +76,21 @@ function getSkippableSuites ({
59
76
  }
60
77
  })
61
78
 
62
- request(data, options, (err, res) => {
79
+ incrementCountMetric(TELEMETRY_ITR_SKIPPABLE_TESTS)
80
+
81
+ const startTime = Date.now()
82
+
83
+ request(data, options, (err, res, statusCode) => {
84
+ distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_MS, {}, Date.now() - startTime)
63
85
  if (err) {
86
+ const errorType = getErrorTypeFromStatusCode(statusCode)
87
+ incrementCountMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS, { errorType })
64
88
  done(err)
65
89
  } else {
66
90
  let skippableSuites = []
67
91
  try {
68
- skippableSuites = JSON.parse(res)
92
+ const parsedResponse = JSON.parse(res)
93
+ skippableSuites = parsedResponse
69
94
  .data
70
95
  .filter(({ type }) => type === testLevel)
71
96
  .map(({ attributes: { suite, name } }) => {
@@ -74,8 +99,16 @@ function getSkippableSuites ({
74
99
  }
75
100
  return { suite, name }
76
101
  })
102
+ const { meta: { correlation_id: correlationId } } = parsedResponse
103
+ incrementCountMetric(
104
+ testLevel === 'test'
105
+ ? TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS : TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
106
+ {},
107
+ skippableSuites.length
108
+ )
109
+ distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, {}, res.length)
77
110
  log.debug(() => `Number of received skippable ${testLevel}s: ${skippableSuites.length}`)
78
- done(null, skippableSuites)
111
+ done(null, skippableSuites, correlationId)
79
112
  } catch (err) {
80
113
  done(err)
81
114
  }