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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -0
- data/LICENSE-3rdparty.csv +1 -1
- data/README.md +40 -55
- data/ext/datadog_cov/datadog_cov.c +192 -0
- data/ext/datadog_cov/extconf.rb +18 -0
- data/lib/datadog/ci/configuration/components.rb +43 -9
- data/lib/datadog/ci/configuration/settings.rb +7 -1
- data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +0 -15
- data/lib/datadog/ci/contrib/cucumber/ext.rb +1 -5
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +13 -18
- data/lib/datadog/ci/contrib/cucumber/integration.rb +1 -2
- data/lib/datadog/ci/contrib/cucumber/patcher.rb +3 -0
- data/lib/datadog/ci/contrib/cucumber/step.rb +27 -0
- data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +0 -15
- data/lib/datadog/ci/contrib/minitest/ext.rb +1 -5
- data/lib/datadog/ci/contrib/minitest/helpers.rb +1 -2
- data/lib/datadog/ci/contrib/minitest/hooks.rb +4 -2
- data/lib/datadog/ci/contrib/minitest/integration.rb +1 -1
- data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -15
- data/lib/datadog/ci/contrib/rspec/example.rb +25 -23
- data/lib/datadog/ci/contrib/rspec/ext.rb +0 -4
- data/lib/datadog/ci/contrib/rspec/integration.rb +1 -2
- data/lib/datadog/ci/contrib/settings.rb +0 -3
- data/lib/datadog/ci/ext/environment/providers/base.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/bitbucket.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/local_git.rb +8 -79
- data/lib/datadog/ci/ext/environment.rb +11 -16
- data/lib/datadog/ci/ext/settings.rb +1 -0
- data/lib/datadog/ci/ext/test.rb +5 -0
- data/lib/datadog/ci/ext/transport.rb +12 -0
- data/lib/datadog/ci/git/local_repository.rb +238 -0
- data/lib/datadog/ci/git/packfiles.rb +70 -0
- data/lib/datadog/ci/git/search_commits.rb +77 -0
- data/lib/datadog/ci/git/tree_uploader.rb +90 -0
- data/lib/datadog/ci/git/upload_packfile.rb +66 -0
- data/lib/datadog/ci/git/user.rb +29 -0
- data/lib/datadog/ci/itr/coverage/ddcov.rb +14 -0
- data/lib/datadog/ci/itr/coverage/event.rb +81 -0
- data/lib/datadog/ci/itr/coverage/transport.rb +42 -0
- data/lib/datadog/ci/itr/coverage/writer.rb +108 -0
- data/lib/datadog/ci/itr/runner.rb +143 -6
- data/lib/datadog/ci/itr/skippable.rb +106 -0
- data/lib/datadog/ci/span.rb +9 -0
- data/lib/datadog/ci/test.rb +20 -14
- data/lib/datadog/ci/test_module.rb +2 -2
- data/lib/datadog/ci/test_session.rb +2 -2
- data/lib/datadog/ci/test_suite.rb +2 -2
- data/lib/datadog/ci/test_visibility/context/global.rb +1 -3
- data/lib/datadog/ci/test_visibility/null_recorder.rb +5 -2
- data/lib/datadog/ci/test_visibility/recorder.rb +63 -8
- data/lib/datadog/ci/test_visibility/serializers/base.rb +1 -1
- data/lib/datadog/ci/test_visibility/serializers/factories/test_level.rb +1 -1
- data/lib/datadog/ci/test_visibility/serializers/factories/test_suite_level.rb +1 -1
- data/lib/datadog/ci/test_visibility/transport.rb +11 -54
- data/lib/datadog/ci/transport/api/agentless.rb +8 -1
- data/lib/datadog/ci/transport/api/base.rb +23 -0
- data/lib/datadog/ci/transport/api/builder.rb +9 -1
- data/lib/datadog/ci/transport/api/evp_proxy.rb +8 -0
- data/lib/datadog/ci/transport/event_platform_transport.rb +88 -0
- data/lib/datadog/ci/transport/http.rb +43 -6
- data/lib/datadog/ci/transport/remote_settings_api.rb +12 -6
- data/lib/datadog/ci/utils/configuration.rb +2 -2
- data/lib/datadog/ci/utils/git.rb +6 -67
- data/lib/datadog/ci/utils/parsing.rb +16 -0
- data/lib/datadog/ci/utils/test_run.rb +13 -0
- data/lib/datadog/ci/version.rb +5 -5
- data/lib/datadog/ci/worker.rb +35 -0
- data/lib/datadog/ci.rb +4 -0
- 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 "../
|
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:,
|
33
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
@@ -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
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
41
|
+
def encode_events(traces)
|
74
42
|
traces.flat_map do |trace|
|
75
|
-
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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? || !
|
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
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|