datadog-ci 0.8.3 → 1.0.0.beta2

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/LICENSE-3rdparty.csv +1 -1
  4. data/README.md +40 -55
  5. data/ext/datadog_cov/datadog_cov.c +192 -0
  6. data/ext/datadog_cov/extconf.rb +18 -0
  7. data/lib/datadog/ci/configuration/components.rb +43 -9
  8. data/lib/datadog/ci/configuration/settings.rb +7 -1
  9. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +0 -15
  10. data/lib/datadog/ci/contrib/cucumber/ext.rb +1 -5
  11. data/lib/datadog/ci/contrib/cucumber/formatter.rb +13 -18
  12. data/lib/datadog/ci/contrib/cucumber/integration.rb +1 -2
  13. data/lib/datadog/ci/contrib/cucumber/patcher.rb +3 -0
  14. data/lib/datadog/ci/contrib/cucumber/step.rb +27 -0
  15. data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +0 -15
  16. data/lib/datadog/ci/contrib/minitest/ext.rb +1 -5
  17. data/lib/datadog/ci/contrib/minitest/helpers.rb +1 -2
  18. data/lib/datadog/ci/contrib/minitest/hooks.rb +4 -2
  19. data/lib/datadog/ci/contrib/minitest/integration.rb +1 -1
  20. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -15
  21. data/lib/datadog/ci/contrib/rspec/example.rb +25 -23
  22. data/lib/datadog/ci/contrib/rspec/ext.rb +0 -4
  23. data/lib/datadog/ci/contrib/rspec/integration.rb +1 -2
  24. data/lib/datadog/ci/contrib/settings.rb +0 -3
  25. data/lib/datadog/ci/ext/environment/providers/base.rb +1 -1
  26. data/lib/datadog/ci/ext/environment/providers/bitbucket.rb +1 -1
  27. data/lib/datadog/ci/ext/environment/providers/local_git.rb +8 -79
  28. data/lib/datadog/ci/ext/environment.rb +11 -16
  29. data/lib/datadog/ci/ext/settings.rb +1 -0
  30. data/lib/datadog/ci/ext/test.rb +5 -0
  31. data/lib/datadog/ci/ext/transport.rb +12 -0
  32. data/lib/datadog/ci/git/local_repository.rb +238 -0
  33. data/lib/datadog/ci/git/packfiles.rb +70 -0
  34. data/lib/datadog/ci/git/search_commits.rb +77 -0
  35. data/lib/datadog/ci/git/tree_uploader.rb +90 -0
  36. data/lib/datadog/ci/git/upload_packfile.rb +66 -0
  37. data/lib/datadog/ci/git/user.rb +29 -0
  38. data/lib/datadog/ci/itr/coverage/ddcov.rb +14 -0
  39. data/lib/datadog/ci/itr/coverage/event.rb +81 -0
  40. data/lib/datadog/ci/itr/coverage/transport.rb +42 -0
  41. data/lib/datadog/ci/itr/coverage/writer.rb +108 -0
  42. data/lib/datadog/ci/itr/runner.rb +143 -6
  43. data/lib/datadog/ci/itr/skippable.rb +106 -0
  44. data/lib/datadog/ci/span.rb +9 -0
  45. data/lib/datadog/ci/test.rb +20 -14
  46. data/lib/datadog/ci/test_module.rb +2 -2
  47. data/lib/datadog/ci/test_session.rb +2 -2
  48. data/lib/datadog/ci/test_suite.rb +2 -2
  49. data/lib/datadog/ci/test_visibility/context/global.rb +1 -3
  50. data/lib/datadog/ci/test_visibility/null_recorder.rb +5 -2
  51. data/lib/datadog/ci/test_visibility/recorder.rb +63 -8
  52. data/lib/datadog/ci/test_visibility/serializers/base.rb +1 -1
  53. data/lib/datadog/ci/test_visibility/serializers/factories/test_level.rb +1 -1
  54. data/lib/datadog/ci/test_visibility/serializers/factories/test_suite_level.rb +1 -1
  55. data/lib/datadog/ci/test_visibility/transport.rb +11 -54
  56. data/lib/datadog/ci/transport/api/agentless.rb +8 -1
  57. data/lib/datadog/ci/transport/api/base.rb +23 -0
  58. data/lib/datadog/ci/transport/api/builder.rb +9 -1
  59. data/lib/datadog/ci/transport/api/evp_proxy.rb +8 -0
  60. data/lib/datadog/ci/transport/event_platform_transport.rb +88 -0
  61. data/lib/datadog/ci/transport/http.rb +43 -6
  62. data/lib/datadog/ci/transport/remote_settings_api.rb +12 -6
  63. data/lib/datadog/ci/utils/configuration.rb +2 -2
  64. data/lib/datadog/ci/utils/git.rb +6 -67
  65. data/lib/datadog/ci/utils/parsing.rb +16 -0
  66. data/lib/datadog/ci/utils/test_run.rb +13 -0
  67. data/lib/datadog/ci/version.rb +5 -5
  68. data/lib/datadog/ci/worker.rb +35 -0
  69. data/lib/datadog/ci.rb +4 -0
  70. metadata +36 -4
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "datadog/tracing"
4
+ require "datadog/tracing/contrib/component"
4
5
  require "datadog/tracing/trace_digest"
5
6
 
6
7
  require "rbconfig"
@@ -12,13 +13,14 @@ require_relative "../codeowners/parser"
12
13
  require_relative "../ext/app_types"
13
14
  require_relative "../ext/test"
14
15
  require_relative "../ext/environment"
15
- require_relative "../utils/git"
16
+ require_relative "../git/local_repository"
16
17
 
17
18
  require_relative "../span"
18
19
  require_relative "../test"
19
20
  require_relative "../test_session"
20
21
  require_relative "../test_module"
21
22
  require_relative "../test_suite"
23
+ require_relative "../worker"
22
24
 
23
25
  module Datadog
24
26
  module CI
@@ -29,8 +31,11 @@ module Datadog
29
31
  attr_reader :environment_tags, :test_suite_level_visibility_enabled
30
32
 
31
33
  def initialize(
32
- itr:, remote_settings_api:, test_suite_level_visibility_enabled: false,
33
- codeowners: Codeowners::Parser.new(Utils::Git.root).parse
34
+ itr:,
35
+ remote_settings_api:,
36
+ git_tree_upload_worker: DummyWorker.new,
37
+ test_suite_level_visibility_enabled: false,
38
+ codeowners: Codeowners::Parser.new(Git::LocalRepository.root).parse
34
39
  )
35
40
  @test_suite_level_visibility_enabled = test_suite_level_visibility_enabled
36
41
 
@@ -42,6 +47,11 @@ module Datadog
42
47
 
43
48
  @itr = itr
44
49
  @remote_settings_api = remote_settings_api
50
+ @git_tree_upload_worker = git_tree_upload_worker
51
+ end
52
+
53
+ def shutdown!
54
+ @git_tree_upload_worker.stop
45
55
  end
46
56
 
47
57
  def start_test_session(service: nil, tags: {})
@@ -55,6 +65,7 @@ module Datadog
55
65
 
56
66
  test_session = build_test_session(tracer_span, tags)
57
67
 
68
+ @git_tree_upload_worker.perform(test_session.git_repository_url)
58
69
  configure_library(test_session)
59
70
 
60
71
  test_session
@@ -116,14 +127,19 @@ module Datadog
116
127
  test = build_test(tracer_span, tags)
117
128
 
118
129
  @local_context.activate_test(test) do
119
- block.call(test)
130
+ on_test_started(test)
131
+ res = block.call(test)
132
+ on_test_finished(test)
133
+ res
120
134
  end
121
135
  end
122
136
  else
123
137
  tracer_span = start_datadog_tracer_span(test_name, span_options)
124
-
125
138
  test = build_test(tracer_span, tags)
139
+
126
140
  @local_context.activate_test(test)
141
+ on_test_started(test)
142
+
127
143
  test
128
144
  end
129
145
  end
@@ -168,10 +184,16 @@ module Datadog
168
184
  end
169
185
 
170
186
  def deactivate_test
187
+ test = active_test
188
+ on_test_finished(test) if test
189
+
171
190
  @local_context.deactivate_test
172
191
  end
173
192
 
174
193
  def deactivate_test_session
194
+ test_session = active_test_session
195
+ on_test_session_finished(test_session) if test_session
196
+
175
197
  @global_context.deactivate_test_session!
176
198
  end
177
199
 
@@ -194,16 +216,34 @@ module Datadog
194
216
  return unless itr_enabled?
195
217
 
196
218
  remote_configuration = @remote_settings_api.fetch_library_settings(test_session)
197
- @itr.configure(remote_configuration.payload, test_session)
219
+ # sometimes we can skip code coverage for default branch if there are no changes in the repository
220
+ # backend needs git metadata uploaded for this test session to check if we can skip code coverage
221
+ if remote_configuration.require_git?
222
+ Datadog.logger.debug { "Library configuration endpoint requires git upload to be finished, waiting..." }
223
+ @git_tree_upload_worker.wait_until_done
224
+
225
+ Datadog.logger.debug { "Requesting library configuration again..." }
226
+ remote_configuration = @remote_settings_api.fetch_library_settings(test_session)
227
+
228
+ if remote_configuration.require_git?
229
+ Datadog.logger.debug { "git metadata upload did not complete in time when configuring library" }
230
+ end
231
+ end
232
+
233
+ @itr.configure(
234
+ remote_configuration.payload,
235
+ test_session: test_session,
236
+ git_tree_upload_worker: @git_tree_upload_worker
237
+ )
198
238
  end
199
239
 
200
240
  def skip_tracing(block = nil)
201
- block.call(nil) if block
241
+ block&.call(nil)
202
242
  end
203
243
 
204
244
  # Sets trace's origin to ciapp-test
205
245
  def set_trace_origin(trace)
206
- trace.origin = Ext::Test::CONTEXT_ORIGIN if trace
246
+ trace&.origin = Ext::Test::CONTEXT_ORIGIN
207
247
  end
208
248
 
209
249
  def build_test_session(tracer_span, tags)
@@ -357,6 +397,21 @@ module Datadog
357
397
  end
358
398
  end
359
399
  end
400
+
401
+ # TODO: use kind of event system to notify about test finished?
402
+ def on_test_finished(test)
403
+ @itr.stop_coverage(test)
404
+ @itr.count_skipped_test(test)
405
+ end
406
+
407
+ def on_test_started(test)
408
+ @itr.mark_if_skippable(test)
409
+ @itr.start_coverage(test)
410
+ end
411
+
412
+ def on_test_session_finished(test_session)
413
+ @itr.write_test_session_tags(test_session)
414
+ end
360
415
  end
361
416
  end
362
417
  end
@@ -241,7 +241,7 @@ module Datadog
241
241
  end
242
242
 
243
243
  def to_integer(value)
244
- value.to_i if value
244
+ value&.to_i
245
245
  end
246
246
  end
247
247
  end
@@ -8,7 +8,7 @@ module Datadog
8
8
  module TestVisibility
9
9
  module Serializers
10
10
  module Factories
11
- # This factory takes care of creating citestcycle serializers when test-level visibility is enabled
11
+ # This factory takes care of creating msgpack serializers when test-level visibility is enabled
12
12
  # NOTE: citestcycle is a protocol Datadog uses to submit test execution tracing information to CI visibility
13
13
  # backend
14
14
  module TestLevel
@@ -11,7 +11,7 @@ module Datadog
11
11
  module TestVisibility
12
12
  module Serializers
13
13
  module Factories
14
- # This factory takes care of creating citestcycle serializers when test-suite-level visibility is enabled
14
+ # This factory takes care of creating msgpack serializers when test-suite-level visibility is enabled
15
15
  module TestSuiteLevel
16
16
  module_function
17
17
 
@@ -1,64 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "msgpack"
4
- require "uri"
5
-
6
- require "datadog/core/encoding"
7
3
  require "datadog/core/environment/identity"
8
- require "datadog/core/chunker"
9
4
 
10
5
  require_relative "serializers/factories/test_level"
11
6
  require_relative "../ext/transport"
7
+ require_relative "../transport/event_platform_transport"
12
8
 
13
9
  module Datadog
14
10
  module CI
15
11
  module TestVisibility
16
- class Transport
17
- # CI test cycle intake's limit is 5.1MB uncompressed
18
- # We will use a bit more conservative value 5MB
19
- DEFAULT_MAX_PAYLOAD_SIZE = 5 * 1024 * 1024
20
-
21
- attr_reader :serializers_factory,
22
- :api,
23
- :max_payload_size,
24
- :dd_env
12
+ class Transport < Datadog::CI::Transport::EventPlatformTransport
13
+ attr_reader :serializers_factory, :dd_env
25
14
 
26
15
  def initialize(
27
16
  api:,
28
- dd_env: nil,
17
+ dd_env:,
29
18
  serializers_factory: Datadog::CI::TestVisibility::Serializers::Factories::TestLevel,
30
19
  max_payload_size: DEFAULT_MAX_PAYLOAD_SIZE
31
20
  )
21
+ super(api: api, max_payload_size: max_payload_size)
22
+
32
23
  @serializers_factory = serializers_factory
33
- @max_payload_size = max_payload_size
34
24
  @dd_env = dd_env
35
- @api = api
36
25
  end
37
26
 
27
+ # this method is needed for compatibility with Datadog::Tracing::Writer that uses this Transport
38
28
  def send_traces(traces)
39
- return [] if traces.nil? || traces.empty?
40
-
41
- Datadog.logger.debug { "Sending #{traces.count} traces..." }
42
-
43
- encoded_events = encode_traces(traces)
44
- if encoded_events.empty?
45
- Datadog.logger.debug { "Empty encoded events list, skipping send" }
46
- return []
47
- end
48
-
49
- responses = []
50
- Datadog::Core::Chunker.chunk_by_size(encoded_events, max_payload_size).map do |chunk|
51
- encoded_payload = pack_events(chunk)
52
- Datadog.logger.debug do
53
- "Send chunk of #{chunk.count} events; payload size #{encoded_payload.size}"
54
- end
55
-
56
- response = send_payload(encoded_payload)
57
-
58
- responses << response
59
- end
60
-
61
- responses
29
+ send_events(traces)
62
30
  end
63
31
 
64
32
  private
@@ -70,15 +38,9 @@ module Datadog
70
38
  )
71
39
  end
72
40
 
73
- def encode_traces(traces)
41
+ def encode_events(traces)
74
42
  traces.flat_map do |trace|
75
- spans = trace.spans
76
- # TODO: remove condition when 1.0 is released
77
- if spans.respond_to?(:filter_map)
78
- spans.filter_map { |span| encode_span(trace, span) }
79
- else
80
- spans.map { |span| encode_span(trace, span) }.reject(&:nil?)
81
- end
43
+ trace.spans.filter_map { |span| encode_span(trace, span) }
82
44
  end
83
45
  end
84
46
 
@@ -107,9 +69,7 @@ module Datadog
107
69
  Datadog::Core::Encoding::MsgpackEncoder
108
70
  end
109
71
 
110
- def pack_events(encoded_events)
111
- packer = MessagePack::Packer.new
112
-
72
+ def write_payload_header(packer)
113
73
  packer.write_map_header(3) # Set header with how many elements in the map
114
74
 
115
75
  packer.write("version")
@@ -137,9 +97,6 @@ module Datadog
137
97
  packer.write(Datadog::CI::VERSION::STRING)
138
98
 
139
99
  packer.write("events")
140
- packer.write_array_header(encoded_events.size)
141
-
142
- (packer.buffer.to_a + encoded_events).join
143
100
  end
144
101
  end
145
102
  end
@@ -10,10 +10,11 @@ module Datadog
10
10
  class Agentless < Base
11
11
  attr_reader :api_key
12
12
 
13
- def initialize(api_key:, citestcycle_url:, api_url:)
13
+ def initialize(api_key:, citestcycle_url:, api_url:, citestcov_url:)
14
14
  @api_key = api_key
15
15
  @citestcycle_http = build_http_client(citestcycle_url, compress: true)
16
16
  @api_http = build_http_client(api_url, compress: false)
17
+ @citestcov_http = build_http_client(citestcov_url, compress: true)
17
18
  end
18
19
 
19
20
  def citestcycle_request(path:, payload:, headers: {}, verb: "post")
@@ -28,6 +29,12 @@ module Datadog
28
29
  perform_request(@api_http, path: path, payload: payload, headers: headers, verb: verb)
29
30
  end
30
31
 
32
+ def citestcov_request(path:, payload:, headers: {}, verb: "post")
33
+ super(path: path, payload: payload, headers: headers, verb: verb)
34
+
35
+ perform_request(@citestcov_http, path: path, payload: @citestcov_payload, headers: headers, verb: verb)
36
+ end
37
+
31
38
  private
32
39
 
33
40
  def perform_request(http_client, path:, payload:, headers:, verb:)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "securerandom"
4
+
3
5
  require_relative "../../ext/transport"
4
6
 
5
7
  module Datadog
@@ -15,6 +17,27 @@ module Datadog
15
17
  headers[Ext::Transport::HEADER_CONTENT_TYPE] ||= Ext::Transport::CONTENT_TYPE_MESSAGEPACK
16
18
  end
17
19
 
20
+ def citestcov_request(path:, payload:, headers: {}, verb: "post")
21
+ citestcov_request_boundary = ::SecureRandom.uuid
22
+
23
+ headers[Ext::Transport::HEADER_CONTENT_TYPE] ||=
24
+ "#{Ext::Transport::CONTENT_TYPE_MULTIPART_FORM_DATA}; boundary=#{citestcov_request_boundary}"
25
+
26
+ @citestcov_payload = [
27
+ "--#{citestcov_request_boundary}",
28
+ 'Content-Disposition: form-data; name="event"; filename="event.json"',
29
+ "Content-Type: application/json",
30
+ "",
31
+ '{"dummy":true}',
32
+ "--#{citestcov_request_boundary}",
33
+ 'Content-Disposition: form-data; name="coverage1"; filename="coverage1.msgpack"',
34
+ "Content-Type: application/msgpack",
35
+ "",
36
+ payload,
37
+ "--#{citestcov_request_boundary}--"
38
+ ].join("\r\n")
39
+ end
40
+
18
41
  def headers_with_default(headers)
19
42
  request_headers = default_headers
20
43
  request_headers.merge!(headers)
@@ -24,7 +24,15 @@ module Datadog
24
24
  api_url = settings.ci.agentless_url ||
25
25
  "https://#{Ext::Transport::DD_API_HOST_PREFIX}.#{dd_site}:443"
26
26
 
27
- Agentless.new(api_key: settings.api_key, citestcycle_url: citestcycle_url, api_url: api_url)
27
+ citestcov_url = settings.ci.agentless_url ||
28
+ "https://#{Ext::Transport::TEST_COVERAGE_INTAKE_HOST_PREFIX}.#{dd_site}:443"
29
+
30
+ Agentless.new(
31
+ api_key: settings.api_key,
32
+ citestcycle_url: citestcycle_url,
33
+ api_url: api_url,
34
+ citestcov_url: citestcov_url
35
+ )
28
36
  end
29
37
 
30
38
  def self.build_evp_proxy_api(settings)
@@ -38,6 +38,14 @@ module Datadog
38
38
  perform_request(@agent_api_http, path: path, payload: payload, headers: headers, verb: verb)
39
39
  end
40
40
 
41
+ def citestcov_request(path:, payload:, headers: {}, verb: "post")
42
+ super(path: path, payload: payload, headers: headers, verb: verb)
43
+
44
+ headers[Ext::Transport::HEADER_EVP_SUBDOMAIN] = Ext::Transport::TEST_COVERAGE_INTAKE_HOST_PREFIX
45
+
46
+ perform_request(@agent_intake_http, path: path, payload: @citestcov_payload, headers: headers, verb: verb)
47
+ end
48
+
41
49
  private
42
50
 
43
51
  def perform_request(http_client, path:, payload:, headers:, verb:)
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "msgpack"
4
+
5
+ require "datadog/core/encoding"
6
+ require "datadog/core/chunker"
7
+
8
+ module Datadog
9
+ module CI
10
+ module Transport
11
+ class EventPlatformTransport
12
+ DEFAULT_MAX_PAYLOAD_SIZE = 5 * 1024 * 1024
13
+
14
+ attr_reader :api,
15
+ :max_payload_size
16
+
17
+ def initialize(api:, max_payload_size: DEFAULT_MAX_PAYLOAD_SIZE)
18
+ @api = api
19
+ @max_payload_size = max_payload_size
20
+ end
21
+
22
+ def send_events(events)
23
+ return [] if events.nil? || events.empty?
24
+
25
+ Datadog.logger.debug { "[#{self.class.name}] Sending #{events.count} events..." }
26
+
27
+ encoded_events = encode_events(events)
28
+ if encoded_events.empty?
29
+ Datadog.logger.debug { "[#{self.class.name}] Empty encoded events list, skipping send" }
30
+ return []
31
+ end
32
+
33
+ responses = []
34
+
35
+ Datadog::Core::Chunker.chunk_by_size(encoded_events, max_payload_size).map do |chunk|
36
+ encoded_payload = pack_events(chunk)
37
+ Datadog.logger.debug do
38
+ "[#{self.class.name}] Send chunk of #{chunk.count} events; payload size #{encoded_payload.size}"
39
+ end
40
+
41
+ response = send_payload(encoded_payload)
42
+
43
+ responses << response
44
+ end
45
+
46
+ responses
47
+ end
48
+
49
+ private
50
+
51
+ def encoder
52
+ Datadog::Core::Encoding::MsgpackEncoder
53
+ end
54
+
55
+ def pack_events(encoded_events)
56
+ packer = MessagePack::Packer.new
57
+
58
+ write_payload_header(packer)
59
+
60
+ packer.write_array_header(encoded_events.count)
61
+ (packer.buffer.to_a + encoded_events).join
62
+ end
63
+
64
+ def event_too_large?(event, encoded_event)
65
+ return false unless encoded_event.size > max_payload_size
66
+
67
+ # This single event is too large, we can't flush it
68
+ Datadog.logger.warn("[#{self.class.name}] Dropping coverage event. Payload too large: '#{event.inspect}'")
69
+ Datadog.logger.warn(encoded_event)
70
+
71
+ true
72
+ end
73
+
74
+ def send_payload(encoded_payload)
75
+ raise NotImplementedError
76
+ end
77
+
78
+ def encode_events(events)
79
+ raise NotImplementedError
80
+ end
81
+
82
+ def write_payload_header(packer)
83
+ raise NotImplementedError
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -4,6 +4,7 @@ require "delegate"
4
4
  require "datadog/core/transport/http/adapters/net"
5
5
  require "datadog/core/transport/http/env"
6
6
  require "datadog/core/transport/request"
7
+ require "socket"
7
8
 
8
9
  require_relative "gzip"
9
10
  require_relative "../ext/transport"
@@ -20,6 +21,8 @@ module Datadog
20
21
  :compress
21
22
 
22
23
  DEFAULT_TIMEOUT = 30
24
+ MAX_RETRIES = 3
25
+ INITIAL_BACKOFF = 1
23
26
 
24
27
  def initialize(host:, timeout: DEFAULT_TIMEOUT, port: nil, ssl: true, compress: false)
25
28
  @host = host
@@ -29,7 +32,7 @@ module Datadog
29
32
  @compress = compress.nil? ? false : compress
30
33
  end
31
34
 
32
- def request(path:, payload:, headers:, verb: "post")
35
+ def request(path:, payload:, headers:, verb: "post", retries: MAX_RETRIES, backoff: INITIAL_BACKOFF)
33
36
  if compress
34
37
  headers[Ext::Transport::HEADER_CONTENT_ENCODING] = Ext::Transport::CONTENT_ENCODING_GZIP
35
38
  payload = Gzip.compress(payload)
@@ -41,9 +44,7 @@ module Datadog
41
44
  end
42
45
 
43
46
  response = ResponseDecorator.new(
44
- adapter.call(
45
- build_env(path: path, payload: payload, headers: headers, verb: verb)
46
- )
47
+ perform_http_call(path: path, payload: payload, headers: headers, verb: verb, retries: retries, backoff: backoff)
47
48
  )
48
49
 
49
50
  Datadog.logger.debug do
@@ -55,6 +56,25 @@ module Datadog
55
56
 
56
57
  private
57
58
 
59
+ def perform_http_call(path:, payload:, headers:, verb:, retries: MAX_RETRIES, backoff: INITIAL_BACKOFF)
60
+ adapter.call(
61
+ build_env(path: path, payload: payload, headers: headers, verb: verb)
62
+ )
63
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, SocketError, Net::HTTPBadResponse => e
64
+ Datadog.logger.debug("Failed to send request with #{e} (#{e.message})")
65
+
66
+ if retries.positive?
67
+ sleep(backoff)
68
+
69
+ perform_http_call(
70
+ path: path, payload: payload, headers: headers, verb: verb, retries: retries - 1, backoff: backoff * 2
71
+ )
72
+ else
73
+ Datadog.logger.error("Failed to send request after #{MAX_RETRIES} retries")
74
+ raise e
75
+ end
76
+ end
77
+
58
78
  def build_env(path:, payload:, headers:, verb:)
59
79
  env = Datadog::Core::Transport::HTTP::Env.new(
60
80
  Datadog::Core::Transport::Request.new
@@ -67,16 +87,33 @@ module Datadog
67
87
  end
68
88
 
69
89
  def adapter
70
- @adapter ||= Datadog::Core::Transport::HTTP::Adapters::Net.new(host, port, timeout: timeout, ssl: ssl)
90
+ settings = AdapterSettings.new(hostname: host, port: port, ssl: ssl, timeout_seconds: timeout)
91
+ @adapter ||= Datadog::Core::Transport::HTTP::Adapters::Net.new(settings)
71
92
  end
72
93
 
73
94
  # this is needed because Datadog::Tracing::Writer is not fully compatiple with Datadog::Core::Transport
74
- # TODO: remove before 1.0 when CI implements its own worker
95
+ # TODO: remove when CI implements its own worker
75
96
  class ResponseDecorator < ::SimpleDelegator
76
97
  def trace_count
77
98
  0
78
99
  end
79
100
  end
101
+
102
+ class AdapterSettings
103
+ attr_reader :hostname, :port, :ssl, :timeout_seconds
104
+
105
+ def initialize(hostname:, port: nil, ssl: true, timeout_seconds: nil)
106
+ @hostname = hostname
107
+ @port = port
108
+ @ssl = ssl
109
+ @timeout_seconds = timeout_seconds
110
+ end
111
+
112
+ def ==(other)
113
+ hostname == other.hostname && port == other.port && ssl == other.ssl &&
114
+ timeout_seconds == other.timeout_seconds
115
+ end
116
+ end
80
117
  end
81
118
  end
82
119
  end
@@ -5,6 +5,7 @@ require "json"
5
5
  require "datadog/core/environment/identity"
6
6
 
7
7
  require_relative "../ext/transport"
8
+ require_relative "../utils/parsing"
8
9
 
9
10
  module Datadog
10
11
  module CI
@@ -28,7 +29,7 @@ module Datadog
28
29
  return cached unless cached.nil?
29
30
 
30
31
  resp = @http_response
31
- return @json = default_payload if resp.nil? || !resp.ok?
32
+ return @json = default_payload if resp.nil? || !ok?
32
33
 
33
34
  begin
34
35
  @json = JSON.parse(resp.payload).dig(*Ext::Transport::DD_API_SETTINGS_RESPONSE_DIG_KEYS) ||
@@ -39,6 +40,10 @@ module Datadog
39
40
  end
40
41
  end
41
42
 
43
+ def require_git?
44
+ Utils::Parsing.convert_to_bool(payload[Ext::Transport::DD_API_SETTINGS_RESPONSE_REQUIRE_GIT_KEY])
45
+ end
46
+
42
47
  private
43
48
 
44
49
  def default_payload
@@ -46,7 +51,7 @@ module Datadog
46
51
  end
47
52
  end
48
53
 
49
- def initialize(api: nil, dd_env: nil)
54
+ def initialize(dd_env:, api: nil)
50
55
  @api = api
51
56
  @dd_env = dd_env
52
57
  end
@@ -81,10 +86,11 @@ module Datadog
81
86
  "sha" => test_session.git_commit_sha,
82
87
  "test_level" => Ext::Test::ITR_TEST_SKIPPING_MODE,
83
88
  "configurations" => {
84
- "os.platform" => test_session.os_platform,
85
- "os.arch" => test_session.os_architecture,
86
- "runtime.name" => test_session.runtime_name,
87
- "runtime.version" => test_session.runtime_version
89
+ Ext::Test::TAG_OS_PLATFORM => test_session.os_platform,
90
+ Ext::Test::TAG_OS_ARCHITECTURE => test_session.os_architecture,
91
+ Ext::Test::TAG_OS_VERSION => test_session.os_version,
92
+ Ext::Test::TAG_RUNTIME_NAME => test_session.runtime_name,
93
+ Ext::Test::TAG_RUNTIME_VERSION => test_session.runtime_version
88
94
  }
89
95
  }
90
96
  }
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "git"
3
+ require_relative "../git/local_repository"
4
4
 
5
5
  module Datadog
6
6
  module CI
7
7
  module Utils
8
8
  module Configuration
9
9
  def self.fetch_service_name(default)
10
- Datadog.configuration.service_without_fallback || Git.repository_name || default
10
+ Datadog.configuration.service_without_fallback || CI::Git::LocalRepository.repository_name || default
11
11
  end
12
12
  end
13
13
  end