datadog-ci 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -2
  3. data/ext/datadog_cov/datadog_cov.c +259 -67
  4. data/lib/datadog/ci/configuration/components.rb +149 -80
  5. data/lib/datadog/ci/configuration/settings.rb +6 -0
  6. data/lib/datadog/ci/contrib/cucumber/formatter.rb +13 -9
  7. data/lib/datadog/ci/contrib/minitest/runnable.rb +5 -1
  8. data/lib/datadog/ci/contrib/minitest/runner.rb +6 -2
  9. data/lib/datadog/ci/contrib/minitest/test.rb +7 -3
  10. data/lib/datadog/ci/contrib/rspec/example.rb +6 -2
  11. data/lib/datadog/ci/contrib/rspec/example_group.rb +5 -1
  12. data/lib/datadog/ci/contrib/rspec/knapsack_pro/runner.rb +6 -2
  13. data/lib/datadog/ci/contrib/rspec/runner.rb +6 -2
  14. data/lib/datadog/ci/ext/environment/providers/appveyor.rb +1 -1
  15. data/lib/datadog/ci/ext/environment/providers/aws_code_pipeline.rb +1 -1
  16. data/lib/datadog/ci/ext/environment/providers/azure.rb +1 -1
  17. data/lib/datadog/ci/ext/environment/providers/bitbucket.rb +1 -1
  18. data/lib/datadog/ci/ext/environment/providers/bitrise.rb +1 -1
  19. data/lib/datadog/ci/ext/environment/providers/buddy.rb +1 -1
  20. data/lib/datadog/ci/ext/environment/providers/buildkite.rb +1 -1
  21. data/lib/datadog/ci/ext/environment/providers/circleci.rb +1 -1
  22. data/lib/datadog/ci/ext/environment/providers/codefresh.rb +1 -1
  23. data/lib/datadog/ci/ext/environment/providers/github_actions.rb +1 -1
  24. data/lib/datadog/ci/ext/environment/providers/gitlab.rb +1 -1
  25. data/lib/datadog/ci/ext/environment/providers/jenkins.rb +1 -1
  26. data/lib/datadog/ci/ext/environment/providers/teamcity.rb +1 -1
  27. data/lib/datadog/ci/ext/environment/providers/travis.rb +1 -1
  28. data/lib/datadog/ci/ext/environment.rb +17 -0
  29. data/lib/datadog/ci/ext/settings.rb +1 -0
  30. data/lib/datadog/ci/ext/telemetry.rb +120 -0
  31. data/lib/datadog/ci/git/local_repository.rb +116 -25
  32. data/lib/datadog/ci/git/search_commits.rb +20 -1
  33. data/lib/datadog/ci/git/telemetry.rb +37 -0
  34. data/lib/datadog/ci/git/tree_uploader.rb +7 -0
  35. data/lib/datadog/ci/git/upload_packfile.rb +22 -1
  36. data/lib/datadog/ci/span.rb +3 -3
  37. data/lib/datadog/ci/test.rb +6 -1
  38. data/lib/datadog/ci/test_module.rb +1 -1
  39. data/lib/datadog/ci/{itr/runner.rb → test_optimisation/component.rb} +29 -12
  40. data/lib/datadog/ci/{itr → test_optimisation}/coverage/ddcov.rb +1 -1
  41. data/lib/datadog/ci/{itr → test_optimisation}/coverage/event.rb +1 -1
  42. data/lib/datadog/ci/{itr → test_optimisation}/coverage/transport.rb +11 -2
  43. data/lib/datadog/ci/{itr → test_optimisation}/coverage/writer.rb +1 -1
  44. data/lib/datadog/ci/{itr → test_optimisation}/skippable.rb +25 -1
  45. data/lib/datadog/ci/test_optimisation/telemetry.rb +56 -0
  46. data/lib/datadog/ci/test_session.rb +1 -1
  47. data/lib/datadog/ci/test_suite.rb +1 -1
  48. data/lib/datadog/ci/test_visibility/component.rb +282 -0
  49. data/lib/datadog/ci/test_visibility/{recorder.rb → context.rb} +39 -187
  50. data/lib/datadog/ci/test_visibility/{null_recorder.rb → null_component.rb} +6 -4
  51. data/lib/datadog/ci/test_visibility/{context → store}/global.rb +1 -1
  52. data/lib/datadog/ci/test_visibility/{context → store}/local.rb +1 -1
  53. data/lib/datadog/ci/test_visibility/telemetry.rb +69 -0
  54. data/lib/datadog/ci/test_visibility/transport.rb +9 -10
  55. data/lib/datadog/ci/transport/adapters/net.rb +42 -11
  56. data/lib/datadog/ci/transport/adapters/net_http_client.rb +17 -0
  57. data/lib/datadog/ci/transport/adapters/telemetry_webmock_safe_adapter.rb +28 -0
  58. data/lib/datadog/ci/transport/event_platform_transport.rb +42 -5
  59. data/lib/datadog/ci/transport/http.rb +49 -21
  60. data/lib/datadog/ci/transport/remote_settings_api.rb +39 -1
  61. data/lib/datadog/ci/transport/telemetry.rb +93 -0
  62. data/lib/datadog/ci/utils/identity.rb +20 -0
  63. data/lib/datadog/ci/utils/parsing.rb +2 -1
  64. data/lib/datadog/ci/utils/telemetry.rb +23 -0
  65. data/lib/datadog/ci/version.rb +1 -1
  66. data/lib/datadog/ci.rb +45 -15
  67. metadata +24 -14
@@ -4,87 +4,49 @@ require "datadog/tracing"
4
4
  require "datadog/tracing/contrib/component"
5
5
  require "datadog/tracing/trace_digest"
6
6
 
7
- require "rbconfig"
7
+ require_relative "store/global"
8
+ require_relative "store/local"
8
9
 
9
- require_relative "context/global"
10
- require_relative "context/local"
11
-
12
- require_relative "../codeowners/parser"
13
- require_relative "../contrib/contrib"
14
10
  require_relative "../ext/app_types"
15
- require_relative "../ext/test"
16
11
  require_relative "../ext/environment"
17
- require_relative "../git/local_repository"
12
+ require_relative "../ext/test"
18
13
 
19
14
  require_relative "../span"
20
15
  require_relative "../test"
21
16
  require_relative "../test_session"
22
17
  require_relative "../test_module"
23
18
  require_relative "../test_suite"
24
- require_relative "../worker"
25
19
 
26
20
  module Datadog
27
21
  module CI
28
22
  module TestVisibility
29
- # Common behavior for CI tests
30
- # Note: this class has too many responsibilities and should be split into multiple classes
31
- class Recorder
32
- attr_reader :environment_tags, :test_suite_level_visibility_enabled
33
-
34
- def initialize(
35
- itr:,
36
- remote_settings_api:,
37
- git_tree_upload_worker: DummyWorker.new,
38
- test_suite_level_visibility_enabled: false,
39
- codeowners: Codeowners::Parser.new(Git::LocalRepository.root).parse
40
- )
41
- @test_suite_level_visibility_enabled = test_suite_level_visibility_enabled
42
-
43
- @environment_tags = Ext::Environment.tags(ENV).freeze
44
- @local_context = Context::Local.new
45
- @global_context = Context::Global.new
46
-
47
- @codeowners = codeowners
48
-
49
- @itr = itr
50
- @remote_settings_api = remote_settings_api
51
- @git_tree_upload_worker = git_tree_upload_worker
52
- end
53
-
54
- def shutdown!
55
- @git_tree_upload_worker.stop
23
+ # Manages current in-memory context for test visibility (such as active test session, suite, test, etc.).
24
+ # Its responsibility includes building domain models for test visibility as well.
25
+ # Internally it uses Datadog::Tracing module to create spans.
26
+ class Context
27
+ def initialize
28
+ @local_context = Store::Local.new
29
+ @global_context = Store::Global.new
56
30
  end
57
31
 
58
32
  def start_test_session(service: nil, tags: {})
59
- return skip_tracing unless test_suite_level_visibility_enabled
60
-
61
- # finds and instruments additional test libraries that we support (ex: selenium-webdriver)
62
- Contrib.auto_instrument_on_session_start!
63
-
64
33
  @global_context.fetch_or_activate_test_session do
65
34
  tracer_span = start_datadog_tracer_span(
66
- "test.session", build_span_options(service, Ext::AppTypes::TYPE_TEST_SESSION)
35
+ "test.session", build_tracing_span_options(service, Ext::AppTypes::TYPE_TEST_SESSION)
67
36
  )
68
37
  set_session_context(tags, tracer_span)
69
38
 
70
- test_session = build_test_session(tracer_span, tags)
71
-
72
- @git_tree_upload_worker.perform(test_session.git_repository_url)
73
- configure_library(test_session)
74
-
75
- test_session
39
+ build_test_session(tracer_span, tags)
76
40
  end
77
41
  end
78
42
 
79
43
  def start_test_module(test_module_name, service: nil, tags: {})
80
- return skip_tracing unless test_suite_level_visibility_enabled
81
-
82
44
  @global_context.fetch_or_activate_test_module do
83
45
  set_inherited_globals(tags)
84
46
  set_session_context(tags)
85
47
 
86
48
  tracer_span = start_datadog_tracer_span(
87
- test_module_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_MODULE)
49
+ test_module_name, build_tracing_span_options(service, Ext::AppTypes::TYPE_TEST_MODULE)
88
50
  )
89
51
  set_module_context(tags, tracer_span)
90
52
 
@@ -93,15 +55,13 @@ module Datadog
93
55
  end
94
56
 
95
57
  def start_test_suite(test_suite_name, service: nil, tags: {})
96
- return skip_tracing unless test_suite_level_visibility_enabled
97
-
98
58
  @global_context.fetch_or_activate_test_suite(test_suite_name) do
99
59
  set_inherited_globals(tags)
100
60
  set_session_context(tags)
101
61
  set_module_context(tags)
102
62
 
103
63
  tracer_span = start_datadog_tracer_span(
104
- test_suite_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_SUITE)
64
+ test_suite_name, build_tracing_span_options(service, Ext::AppTypes::TYPE_TEST_SUITE)
105
65
  )
106
66
  set_suite_context(tags, span: tracer_span)
107
67
 
@@ -118,7 +78,7 @@ module Datadog
118
78
  tags[Ext::Test::TAG_NAME] = test_name
119
79
  tags[Ext::Test::TAG_TYPE] ||= Ext::Test::Type::TEST
120
80
 
121
- span_options = build_span_options(
81
+ span_options = build_tracing_span_options(
122
82
  service,
123
83
  Ext::AppTypes::TYPE_TEST,
124
84
  # :resource is needed for the agent APM protocol to work correctly (for older agent versions)
@@ -131,25 +91,19 @@ module Datadog
131
91
  test = build_test(tracer_span, tags)
132
92
 
133
93
  @local_context.activate_test(test) do
134
- on_test_started(test)
135
- res = block.call(test)
136
- on_test_finished(test)
137
- res
94
+ block.call(test)
138
95
  end
139
96
  end
140
97
  else
141
98
  tracer_span = start_datadog_tracer_span(test_name, span_options)
142
99
  test = build_test(tracer_span, tags)
143
-
144
100
  @local_context.activate_test(test)
145
- on_test_started(test)
146
-
147
101
  test
148
102
  end
149
103
  end
150
104
 
151
105
  def trace(span_name, type: "span", tags: {}, &block)
152
- span_options = build_span_options(
106
+ span_options = build_tracing_span_options(
153
107
  nil, # service name is completely optional for custom spans
154
108
  type,
155
109
  {resource: span_name}
@@ -187,17 +141,15 @@ module Datadog
187
141
  @global_context.active_test_suite(test_suite_name)
188
142
  end
189
143
 
190
- def deactivate_test
191
- test = active_test
192
- on_test_finished(test) if test
144
+ def single_active_test_suite
145
+ @global_context.fetch_single_test_suite
146
+ end
193
147
 
148
+ def deactivate_test
194
149
  @local_context.deactivate_test
195
150
  end
196
151
 
197
152
  def deactivate_test_session
198
- test_session = active_test_session
199
- on_test_session_finished(test_session) if test_session
200
-
201
153
  @global_context.deactivate_test_session!
202
154
  end
203
155
 
@@ -209,47 +161,9 @@ module Datadog
209
161
  @global_context.deactivate_test_suite!(test_suite_name)
210
162
  end
211
163
 
212
- def itr_enabled?
213
- @itr.enabled?
214
- end
215
-
216
164
  private
217
165
 
218
- def configure_library(test_session)
219
- # this will change when EFD is implemented
220
- return unless itr_enabled?
221
-
222
- remote_configuration = @remote_settings_api.fetch_library_settings(test_session)
223
- # sometimes we can skip code coverage for default branch if there are no changes in the repository
224
- # backend needs git metadata uploaded for this test session to check if we can skip code coverage
225
- if remote_configuration.require_git?
226
- Datadog.logger.debug { "Library configuration endpoint requires git upload to be finished, waiting..." }
227
- @git_tree_upload_worker.wait_until_done
228
-
229
- Datadog.logger.debug { "Requesting library configuration again..." }
230
- remote_configuration = @remote_settings_api.fetch_library_settings(test_session)
231
-
232
- if remote_configuration.require_git?
233
- Datadog.logger.debug { "git metadata upload did not complete in time when configuring library" }
234
- end
235
- end
236
-
237
- @itr.configure(
238
- remote_configuration.payload,
239
- test_session: test_session,
240
- git_tree_upload_worker: @git_tree_upload_worker
241
- )
242
- end
243
-
244
- def skip_tracing(block = nil)
245
- block&.call(nil)
246
- end
247
-
248
- # Sets trace's origin to ciapp-test
249
- def set_trace_origin(trace)
250
- trace&.origin = Ext::Test::CONTEXT_ORIGIN
251
- end
252
-
166
+ # BUILDING DOMAIN MODELS
253
167
  def build_test_session(tracer_span, tags)
254
168
  test_session = TestSession.new(tracer_span)
255
169
  set_initial_tags(test_session, tags)
@@ -271,14 +185,6 @@ module Datadog
271
185
  def build_test(tracer_span, tags)
272
186
  test = Test.new(tracer_span)
273
187
  set_initial_tags(test, tags)
274
-
275
- # sometimes test suite is not being assigned correctly
276
- # fix it by fetching the one single running test suite from the global context
277
- fix_test_suite!(test) if test.test_suite_id.nil?
278
-
279
- validate_test_suite_level_visibility_correctness(test)
280
- set_codeowners(test)
281
-
282
188
  test
283
189
  end
284
190
 
@@ -288,13 +194,18 @@ module Datadog
288
194
  span
289
195
  end
290
196
 
291
- def build_span_options(service, type, other_options = {})
292
- other_options[:service] = service || @global_context.service
293
- other_options[:type] = type
197
+ # TAGGING
198
+ def set_initial_tags(ci_span, tags)
199
+ @environment_tags ||= Ext::Environment.tags(ENV).freeze
294
200
 
295
- other_options
201
+ ci_span.set_default_tags
202
+ ci_span.set_environment_runtime_tags
203
+
204
+ ci_span.set_tags(tags)
205
+ ci_span.set_tags(@environment_tags)
296
206
  end
297
207
 
208
+ # PROPAGATING CONTEXT FROM TOP-LEVEL TO THE LOWER LEVELS
298
209
  def set_inherited_globals(tags)
299
210
  # this code achieves the same as @global_context.inheritable_session_tags.merge(tags)
300
211
  # but without allocating a new hash
@@ -303,14 +214,6 @@ module Datadog
303
214
  end
304
215
  end
305
216
 
306
- def set_initial_tags(ci_span, tags)
307
- ci_span.set_default_tags
308
- ci_span.set_environment_runtime_tags
309
-
310
- ci_span.set_tags(tags)
311
- ci_span.set_tags(environment_tags)
312
- end
313
-
314
217
  def set_session_context(tags, test_session = nil)
315
218
  test_session ||= active_test_session
316
219
  tags[Ext::Test::TAG_TEST_SESSION_ID] = test_session.id.to_s if test_session
@@ -324,12 +227,6 @@ module Datadog
324
227
  end
325
228
  end
326
229
 
327
- def set_codeowners(test)
328
- source = test.source_file
329
- owners = @codeowners.list_owners(source) if source
330
- test.set_tag(Ext::Test::TAG_CODEOWNERS, owners) unless owners.nil?
331
- end
332
-
333
230
  def set_suite_context(tags, span: nil, name: nil)
334
231
  return if span.nil? && name.nil?
335
232
 
@@ -343,24 +240,7 @@ module Datadog
343
240
  end
344
241
  end
345
242
 
346
- def fix_test_suite!(test)
347
- test_suite = @global_context.fetch_single_test_suite
348
- unless test_suite
349
- Datadog.logger.debug do
350
- "Trying to fix test suite for test [#{test.name}] but no single test suite is running."
351
- end
352
- return
353
- end
354
-
355
- Datadog.logger.debug do
356
- "For test [#{test.name}]: expected test suite [#{test.test_suite_name}] to be running, " \
357
- "but it was not found. Fixing it by assigning test suite [#{test_suite.name}] to the test."
358
- end
359
-
360
- test.set_tag(Ext::Test::TAG_TEST_SUITE_ID, test_suite.id.to_s)
361
- test.set_tag(Ext::Test::TAG_SUITE, test_suite.name)
362
- end
363
-
243
+ # INTERACTIONS WITH TRACING
364
244
  def start_datadog_tracer_span(span_name, span_options, &block)
365
245
  if block
366
246
  Datadog::Tracing.trace(span_name, **span_options) do |tracer_span, trace|
@@ -377,44 +257,16 @@ module Datadog
377
257
  end
378
258
  end
379
259
 
380
- def validate_test_suite_level_visibility_correctness(test)
381
- return unless test_suite_level_visibility_enabled
382
-
383
- if test.test_suite_id.nil?
384
- Datadog.logger.debug do
385
- "Test [#{test.name}] does not have a test suite associated with it. " \
386
- "Expected test suite [#{test.test_suite_name}] to be running."
387
- end
388
- end
389
-
390
- if test.test_module_id.nil?
391
- Datadog.logger.debug do
392
- "Test [#{test.name}] does not have a test module associated with it. " \
393
- "Make sure that there is a test module running within a session."
394
- end
395
- end
396
-
397
- if test.test_session_id.nil?
398
- Datadog.logger.debug do
399
- "Test [#{test.name}] does not have a test session associated with it. " \
400
- "Make sure that there is a test session running."
401
- end
402
- end
403
- end
404
-
405
- # TODO: use kind of event system to notify about test finished?
406
- def on_test_finished(test)
407
- @itr.stop_coverage(test)
408
- @itr.count_skipped_test(test)
260
+ # Sets trace's origin to ciapp-test because tracing requires so
261
+ def set_trace_origin(trace)
262
+ trace&.origin = Ext::Test::CONTEXT_ORIGIN
409
263
  end
410
264
 
411
- def on_test_started(test)
412
- @itr.mark_if_skippable(test)
413
- @itr.start_coverage(test)
414
- end
265
+ def build_tracing_span_options(service, type, other_options = {})
266
+ other_options[:service] = service || @global_context.service
267
+ other_options[:type] = type
415
268
 
416
- def on_test_session_finished(test_session)
417
- @itr.write_test_session_tags(test_session)
269
+ other_options
418
270
  end
419
271
  end
420
272
  end
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "recorder"
4
-
5
3
  module Datadog
6
4
  module CI
7
5
  module TestVisibility
8
- # Special recorder that does not record anything
9
- class NullRecorder
6
+ # Special test visibility component that does not record anything
7
+ class NullComponent
10
8
  def start_test_session(service: nil, tags: {})
11
9
  skip_tracing
12
10
  end
@@ -45,6 +43,10 @@ module Datadog
45
43
  def shutdown!
46
44
  end
47
45
 
46
+ def itr_enabled?
47
+ false
48
+ end
49
+
48
50
  private
49
51
 
50
52
  def skip_tracing(block = nil)
@@ -3,7 +3,7 @@
3
3
  module Datadog
4
4
  module CI
5
5
  module TestVisibility
6
- module Context
6
+ module Store
7
7
  # This context is shared between threads and represents the current test session and test module.
8
8
  class Global
9
9
  def initialize
@@ -3,7 +3,7 @@
3
3
  module Datadog
4
4
  module CI
5
5
  module TestVisibility
6
- module Context
6
+ module Store
7
7
  class Local
8
8
  def initialize
9
9
  @key = :datadog_ci_active_test
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../ext/app_types"
4
+ require_relative "../ext/environment"
5
+ require_relative "../ext/telemetry"
6
+ require_relative "../ext/test"
7
+ require_relative "../utils/telemetry"
8
+
9
+ module Datadog
10
+ module CI
11
+ module TestVisibility
12
+ # Telemetry for test visibility
13
+ module Telemetry
14
+ SPAN_TYPE_TO_TELEMETRY_EVENT_TYPE = {
15
+ Ext::AppTypes::TYPE_TEST => Ext::Telemetry::EventType::TEST,
16
+ Ext::AppTypes::TYPE_TEST_SUITE => Ext::Telemetry::EventType::SUITE,
17
+ Ext::AppTypes::TYPE_TEST_MODULE => Ext::Telemetry::EventType::MODULE,
18
+ Ext::AppTypes::TYPE_TEST_SESSION => Ext::Telemetry::EventType::SESSION
19
+ }.freeze
20
+
21
+ def self.event_created(span)
22
+ Utils::Telemetry.inc(Ext::Telemetry::METRIC_EVENT_CREATED, 1, event_tags_from_span(span))
23
+ end
24
+
25
+ def self.event_finished(span)
26
+ tags = event_tags_from_span(span)
27
+ add_browser_tags!(span, tags)
28
+ Utils::Telemetry.inc(Ext::Telemetry::METRIC_EVENT_FINISHED, 1, tags)
29
+ end
30
+
31
+ def self.test_session_started(test_session)
32
+ Utils::Telemetry.inc(
33
+ Ext::Telemetry::METRIC_TEST_SESSION,
34
+ 1,
35
+ {
36
+ Ext::Telemetry::TAG_AUTO_INJECTED => "false", # ruby doesn't support auto injection yet
37
+ Ext::Telemetry::TAG_PROVIDER =>
38
+ test_session.get_tag(Ext::Environment::TAG_PROVIDER_NAME) ||
39
+ Ext::Telemetry::Provider::UNSUPPORTED
40
+ }
41
+ )
42
+ end
43
+
44
+ def self.event_tags_from_span(span)
45
+ # base tags for span
46
+ # @type var tags: Hash[String, String]
47
+ tags = {
48
+ Ext::Telemetry::TAG_EVENT_TYPE => SPAN_TYPE_TO_TELEMETRY_EVENT_TYPE.fetch(span.type, "unknown"),
49
+ Ext::Telemetry::TAG_TEST_FRAMEWORK => span.get_tag(Ext::Test::TAG_FRAMEWORK)
50
+ }
51
+
52
+ # ci provider tag
53
+ tags[Ext::Telemetry::TAG_IS_UNSUPPORTED_CI] = "true" if span.get_tag(Ext::Environment::TAG_PROVIDER_NAME).nil?
54
+
55
+ # codeowner tag
56
+ tags[Ext::Telemetry::TAG_HAS_CODEOWNER] = "true" if span.get_tag(Ext::Test::TAG_CODEOWNERS)
57
+
58
+ tags
59
+ end
60
+
61
+ def self.add_browser_tags!(span, tags)
62
+ tags[Ext::Telemetry::TAG_IS_RUM] = "true" if span.get_tag(Ext::Test::TAG_IS_RUM_ACTIVE)
63
+ browser_driver = span.get_tag(Ext::Test::TAG_BROWSER_DRIVER)
64
+ tags[Ext::Telemetry::TAG_BROWSER_DRIVER] = browser_driver if browser_driver
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -3,8 +3,10 @@
3
3
  require "datadog/core/environment/identity"
4
4
 
5
5
  require_relative "serializers/factories/test_level"
6
+ require_relative "../ext/telemetry"
6
7
  require_relative "../ext/transport"
7
8
  require_relative "../transport/event_platform_transport"
9
+ require_relative "../transport/telemetry"
8
10
 
9
11
  module Datadog
10
12
  module CI
@@ -31,6 +33,10 @@ module Datadog
31
33
 
32
34
  private
33
35
 
36
+ def telemetry_endpoint_tag
37
+ Ext::Telemetry::Endpoint::TEST_CYCLE
38
+ end
39
+
34
40
  def send_payload(encoded_payload)
35
41
  api.citestcycle_request(
36
42
  path: Datadog::CI::Ext::Transport::TEST_VISIBILITY_INTAKE_PATH,
@@ -46,21 +52,14 @@ module Datadog
46
52
 
47
53
  def encode_span(trace, span)
48
54
  serializer = serializers_factory.serializer(trace, span, options: {itr_correlation_id: itr&.correlation_id})
49
-
50
55
  if serializer.valid?
51
56
  encoded = encoder.encode(serializer)
52
-
53
- if encoded.size > max_payload_size
54
- # This single event is too large, we can't flush it
55
- Datadog.logger.warn("Dropping test event. Payload too large: '#{span.inspect}'")
56
- Datadog.logger.warn(encoded)
57
-
58
- return nil
59
- end
57
+ return nil if event_too_large?(span, encoded)
60
58
 
61
59
  encoded
62
60
  else
63
61
  Datadog.logger.warn("Invalid event skipped: #{serializer} Errors: #{serializer.validation_errors}")
62
+ CI::Transport::Telemetry.endpoint_payload_dropped(1, endpoint: telemetry_endpoint_tag)
64
63
  nil
65
64
  end
66
65
  end
@@ -100,7 +99,7 @@ module Datadog
100
99
  end
101
100
 
102
101
  def itr
103
- @itr ||= Datadog::CI.send(:itr_runner)
102
+ @test_optimisation ||= Datadog::CI.send(:test_optimisation)
104
103
  end
105
104
  end
106
105
  end
@@ -3,7 +3,9 @@
3
3
  require "datadog/core/transport/response"
4
4
  require "datadog/core/transport/ext"
5
5
 
6
+ require_relative "net_http_client"
6
7
  require_relative "../gzip"
8
+ require_relative "../../ext/telemetry"
7
9
  require_relative "../../ext/transport"
8
10
 
9
11
  module Datadog
@@ -26,7 +28,7 @@ module Datadog
26
28
  end
27
29
 
28
30
  def open(&block)
29
- req = net_http_client.new(hostname, port)
31
+ req = NetHttpClient.original_net_http.new(hostname, port)
30
32
 
31
33
  req.use_ssl = ssl
32
34
  req.open_timeout = req.read_timeout = timeout
@@ -63,6 +65,8 @@ module Datadog
63
65
  include Datadog::Core::Transport::Response
64
66
 
65
67
  attr_reader :http_response
68
+ # Stats for telemetry
69
+ attr_accessor :duration_ms, :request_compressed, :request_size
66
70
 
67
71
  def initialize(http_response)
68
72
  @http_response = http_response
@@ -77,6 +81,10 @@ module Datadog
77
81
  @decompressed_payload = Gzip.decompress(http_response.body)
78
82
  end
79
83
 
84
+ def response_size
85
+ http_response.body.bytesize
86
+ end
87
+
80
88
  def header(name)
81
89
  http_response[name]
82
90
  end
@@ -86,7 +94,10 @@ module Datadog
86
94
  end
87
95
 
88
96
  def ok?
89
- code.between?(200, 299)
97
+ http_code = code
98
+ return false if http_code.nil?
99
+
100
+ http_code.between?(200, 299)
90
101
  end
91
102
 
92
103
  def unsupported?
@@ -98,11 +109,17 @@ module Datadog
98
109
  end
99
110
 
100
111
  def client_error?
101
- code.between?(400, 499)
112
+ http_code = code
113
+ return false if http_code.nil?
114
+
115
+ http_code.between?(400, 499)
102
116
  end
103
117
 
104
118
  def server_error?
105
- code.between?(500, 599)
119
+ http_code = code
120
+ return false if http_code.nil?
121
+
122
+ http_code.between?(500, 599)
106
123
  end
107
124
 
108
125
  def gzipped_content?
@@ -119,17 +136,31 @@ module Datadog
119
136
  first_bytes.b == Ext::Transport::GZIP_MAGIC_NUMBER
120
137
  end
121
138
 
122
- def inspect
123
- "#{super}, http_response:#{http_response}"
139
+ def error
140
+ nil
124
141
  end
125
- end
126
142
 
127
- private
143
+ def telemetry_error_type
144
+ return nil if ok?
145
+
146
+ case error
147
+ when nil
148
+ Ext::Telemetry::ErrorType::STATUS_CODE
149
+ when Timeout::Error
150
+ Ext::Telemetry::ErrorType::TIMEOUT
151
+ else
152
+ Ext::Telemetry::ErrorType::NETWORK
153
+ end
154
+ end
128
155
 
129
- def net_http_client
130
- return ::Net::HTTP unless defined?(WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP)
156
+ # compatibility with Datadog::Tracing transport layer
157
+ def trace_count
158
+ 0
159
+ end
131
160
 
132
- WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP
161
+ def inspect
162
+ "#{super}, http_response:#{http_response}"
163
+ end
133
164
  end
134
165
  end
135
166
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module Transport
6
+ module Adapters
7
+ module NetHttpClient
8
+ def self.original_net_http
9
+ return ::Net::HTTP unless defined?(WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP)
10
+
11
+ WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "net_http_client"
4
+
5
+ module Datadog
6
+ module CI
7
+ module Transport
8
+ module Adapters
9
+ module TelemetryWebmockSafeAdapter
10
+ def self.included(base)
11
+ base.prepend(InstanceMethods)
12
+ end
13
+
14
+ module InstanceMethods
15
+ def open(&block)
16
+ req = NetHttpClient.original_net_http.new(@hostname, @port)
17
+
18
+ req.use_ssl = @ssl
19
+ req.open_timeout = req.read_timeout = @timeout
20
+
21
+ req.start(&block)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end