datadog-ci 0.8.2 → 1.0.0.beta1
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 +30 -1
- data/LICENSE-3rdparty.csv +1 -1
- data/README.md +22 -9
- data/ext/datadog_cov/datadog_cov.c +192 -0
- data/ext/datadog_cov/extconf.rb +18 -0
- data/lib/datadog/ci/configuration/settings.rb +1 -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 +17 -14
- data/lib/datadog/ci/contrib/cucumber/integration.rb +1 -2
- 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/integration.rb +1 -1
- data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +0 -15
- data/lib/datadog/ci/contrib/rspec/example.rb +18 -20
- 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/extractor.rb +3 -2
- 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/github_actions.rb +3 -2
- data/lib/datadog/ci/ext/environment.rb +1 -1
- data/lib/datadog/ci/ext/transport.rb +4 -0
- data/lib/datadog/ci/itr/coverage/ddcov.rb +14 -0
- data/lib/datadog/ci/itr/coverage/event.rb +64 -0
- data/lib/datadog/ci/itr/coverage/transport.rb +42 -0
- data/lib/datadog/ci/itr/runner.rb +28 -0
- data/lib/datadog/ci/test.rb +1 -2
- data/lib/datadog/ci/test_visibility/context/global.rb +1 -3
- data/lib/datadog/ci/test_visibility/null_recorder.rb +1 -1
- data/lib/datadog/ci/test_visibility/recorder.rb +20 -3
- 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 +10 -53
- 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 +14 -5
- 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 +19 -2
- data/lib/datadog/ci/utils/git.rb +2 -2
- data/lib/datadog/ci/version.rb +5 -5
- metadata +25 -5
- data/lib/datadog/ci/utils/url.rb +0 -15
@@ -103,7 +103,7 @@ module Datadog
|
|
103
103
|
@branch = @tag = nil
|
104
104
|
|
105
105
|
# @type var branch_or_tag_string: untyped
|
106
|
-
if branch_or_tag_string
|
106
|
+
if branch_or_tag_string&.include?("tags/")
|
107
107
|
@tag = branch_or_tag_string
|
108
108
|
else
|
109
109
|
@branch = branch_or_tag_string
|
@@ -2,8 +2,9 @@
|
|
2
2
|
|
3
3
|
require "json"
|
4
4
|
|
5
|
+
require "datadog/core/utils/url"
|
6
|
+
|
5
7
|
require_relative "base"
|
6
|
-
require_relative "../../../utils/url"
|
7
8
|
|
8
9
|
module Datadog
|
9
10
|
module CI
|
@@ -79,7 +80,7 @@ module Datadog
|
|
79
80
|
def github_server_url
|
80
81
|
return @github_server_url if defined?(@github_server_url)
|
81
82
|
|
82
|
-
@github_server_url ||= Utils::Url.
|
83
|
+
@github_server_url ||= Datadog::Core::Utils::Url.filter_basic_auth(env["GITHUB_SERVER_URL"])
|
83
84
|
end
|
84
85
|
end
|
85
86
|
end
|
@@ -23,6 +23,9 @@ module Datadog
|
|
23
23
|
TEST_VISIBILITY_INTAKE_HOST_PREFIX = "citestcycle-intake"
|
24
24
|
TEST_VISIBILITY_INTAKE_PATH = "/api/v2/citestcycle"
|
25
25
|
|
26
|
+
TEST_COVERAGE_INTAKE_HOST_PREFIX = "citestcov-intake"
|
27
|
+
TEST_COVERAGE_INTAKE_PATH = "/api/v2/citestcov"
|
28
|
+
|
26
29
|
DD_API_HOST_PREFIX = "api"
|
27
30
|
DD_API_SETTINGS_PATH = "/api/v2/libraries/tests/services/setting"
|
28
31
|
DD_API_SETTINGS_TYPE = "ci_app_test_service_libraries_settings"
|
@@ -35,6 +38,7 @@ module Datadog
|
|
35
38
|
|
36
39
|
CONTENT_TYPE_MESSAGEPACK = "application/msgpack"
|
37
40
|
CONTENT_TYPE_JSON = "application/json"
|
41
|
+
CONTENT_TYPE_MULTIPART_FORM_DATA = "multipart/form-data"
|
38
42
|
CONTENT_ENCODING_GZIP = "gzip"
|
39
43
|
end
|
40
44
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "msgpack"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module CI
|
7
|
+
module ITR
|
8
|
+
module Coverage
|
9
|
+
class Event
|
10
|
+
attr_reader :test_id, :test_suite_id, :test_session_id, :coverage
|
11
|
+
|
12
|
+
def initialize(test_id:, test_suite_id:, test_session_id:, coverage:)
|
13
|
+
@test_id = test_id
|
14
|
+
@test_suite_id = test_suite_id
|
15
|
+
@test_session_id = test_session_id
|
16
|
+
@coverage = coverage
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
valid = true
|
21
|
+
|
22
|
+
[:test_id, :test_suite_id, :test_session_id, :coverage].each do |key|
|
23
|
+
next unless send(key).nil?
|
24
|
+
|
25
|
+
Datadog.logger.warn("citestcov event is invalid: [#{key}] is nil. Event: #{self}")
|
26
|
+
valid = false
|
27
|
+
end
|
28
|
+
|
29
|
+
valid
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_msgpack(packer = nil)
|
33
|
+
packer ||= MessagePack::Packer.new
|
34
|
+
|
35
|
+
packer.write_map_header(4)
|
36
|
+
|
37
|
+
packer.write("test_session_id")
|
38
|
+
packer.write(test_session_id.to_i)
|
39
|
+
|
40
|
+
packer.write("test_suite_id")
|
41
|
+
packer.write(test_suite_id.to_i)
|
42
|
+
|
43
|
+
packer.write("span_id")
|
44
|
+
packer.write(test_id.to_i)
|
45
|
+
|
46
|
+
files = coverage.keys
|
47
|
+
packer.write("files")
|
48
|
+
packer.write_array_header(files.size)
|
49
|
+
|
50
|
+
files.each do |filename|
|
51
|
+
packer.write_map_header(1)
|
52
|
+
packer.write("filename")
|
53
|
+
packer.write(filename)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
"Coverage::Event[test_id=#{test_id}, test_suite_id=#{test_suite_id}, test_session_id=#{test_session_id}, coverage=#{coverage}]"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "event"
|
4
|
+
require_relative "../../transport/event_platform_transport"
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module CI
|
8
|
+
module ITR
|
9
|
+
module Coverage
|
10
|
+
class Transport < Datadog::CI::Transport::EventPlatformTransport
|
11
|
+
private
|
12
|
+
|
13
|
+
def send_payload(encoded_payload)
|
14
|
+
api.citestcov_request(
|
15
|
+
path: Ext::Transport::TEST_COVERAGE_INTAKE_PATH,
|
16
|
+
payload: encoded_payload
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def encode_events(events)
|
21
|
+
events.filter_map do |event|
|
22
|
+
next unless event.valid?
|
23
|
+
|
24
|
+
encoded = encoder.encode(event)
|
25
|
+
next if event_too_large?(event, encoded)
|
26
|
+
|
27
|
+
encoded
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def write_payload_header(packer)
|
32
|
+
packer.write_map_header(2)
|
33
|
+
packer.write("version")
|
34
|
+
packer.write(2)
|
35
|
+
|
36
|
+
packer.write("coverages")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require_relative "../ext/test"
|
4
4
|
require_relative "../ext/transport"
|
5
5
|
|
6
|
+
require_relative "../utils/git"
|
7
|
+
|
6
8
|
module Datadog
|
7
9
|
module CI
|
8
10
|
module ITR
|
@@ -41,6 +43,8 @@ module Datadog
|
|
41
43
|
# we skip tests, not suites
|
42
44
|
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_TYPE, Ext::Test::ITR_TEST_SKIPPING_MODE)
|
43
45
|
|
46
|
+
load_datadog_cov! if @code_coverage_enabled
|
47
|
+
|
44
48
|
Datadog.logger.debug("Configured ITR Runner with enabled: #{@enabled}, skipping_tests: #{@test_skipping_enabled}, code_coverage: #{@code_coverage_enabled}")
|
45
49
|
end
|
46
50
|
|
@@ -56,8 +60,32 @@ module Datadog
|
|
56
60
|
@code_coverage_enabled
|
57
61
|
end
|
58
62
|
|
63
|
+
def start_coverage
|
64
|
+
return if !enabled? || !code_coverage?
|
65
|
+
|
66
|
+
coverage_collector&.start
|
67
|
+
end
|
68
|
+
|
69
|
+
def stop_coverage(_test)
|
70
|
+
return if !enabled? || !code_coverage?
|
71
|
+
|
72
|
+
coverage_collector&.stop
|
73
|
+
end
|
74
|
+
|
59
75
|
private
|
60
76
|
|
77
|
+
def coverage_collector
|
78
|
+
Thread.current[:dd_coverage_collector] ||= Coverage::DDCov.new(root: Utils::Git.root)
|
79
|
+
end
|
80
|
+
|
81
|
+
def load_datadog_cov!
|
82
|
+
require "datadog_cov.#{RUBY_VERSION}_#{RUBY_PLATFORM}"
|
83
|
+
rescue LoadError => e
|
84
|
+
Datadog.logger.error("Failed to load coverage collector: #{e}. Code coverage will not be collected.")
|
85
|
+
|
86
|
+
@code_coverage_enabled = false
|
87
|
+
end
|
88
|
+
|
61
89
|
def convert_to_bool(value)
|
62
90
|
value.to_s == "true"
|
63
91
|
end
|
data/lib/datadog/ci/test.rb
CHANGED
@@ -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"
|
@@ -116,14 +117,22 @@ module Datadog
|
|
116
117
|
test = build_test(tracer_span, tags)
|
117
118
|
|
118
119
|
@local_context.activate_test(test) do
|
119
|
-
|
120
|
+
@itr.start_coverage
|
121
|
+
|
122
|
+
res = block.call(test)
|
123
|
+
on_test_finished(test)
|
124
|
+
|
125
|
+
res
|
120
126
|
end
|
121
127
|
end
|
122
128
|
else
|
123
129
|
tracer_span = start_datadog_tracer_span(test_name, span_options)
|
124
130
|
|
125
131
|
test = build_test(tracer_span, tags)
|
132
|
+
|
126
133
|
@local_context.activate_test(test)
|
134
|
+
@itr.start_coverage
|
135
|
+
|
127
136
|
test
|
128
137
|
end
|
129
138
|
end
|
@@ -168,6 +177,9 @@ module Datadog
|
|
168
177
|
end
|
169
178
|
|
170
179
|
def deactivate_test
|
180
|
+
test = active_test
|
181
|
+
on_test_finished(test) if test
|
182
|
+
|
171
183
|
@local_context.deactivate_test
|
172
184
|
end
|
173
185
|
|
@@ -198,12 +210,12 @@ module Datadog
|
|
198
210
|
end
|
199
211
|
|
200
212
|
def skip_tracing(block = nil)
|
201
|
-
block
|
213
|
+
block&.call(nil)
|
202
214
|
end
|
203
215
|
|
204
216
|
# Sets trace's origin to ciapp-test
|
205
217
|
def set_trace_origin(trace)
|
206
|
-
trace
|
218
|
+
trace&.origin = Ext::Test::CONTEXT_ORIGIN
|
207
219
|
end
|
208
220
|
|
209
221
|
def build_test_session(tracer_span, tags)
|
@@ -357,6 +369,11 @@ module Datadog
|
|
357
369
|
end
|
358
370
|
end
|
359
371
|
end
|
372
|
+
|
373
|
+
# TODO: use kind of event system to notify about test finished?
|
374
|
+
def on_test_finished(test)
|
375
|
+
@itr.stop_coverage(test)
|
376
|
+
end
|
360
377
|
end
|
361
378
|
end
|
362
379
|
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,27 +1,16 @@
|
|
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:,
|
@@ -29,36 +18,15 @@ module Datadog
|
|
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,15 +24,24 @@ 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)
|
31
39
|
agent_settings = Datadog::Core::Configuration::AgentSettingsResolver.call(settings)
|
32
|
-
negotiation = Datadog::Core::Remote::Negotiation.new(
|
33
|
-
|
34
|
-
|
35
|
-
|
40
|
+
negotiation = Datadog::Core::Remote::Negotiation.new(
|
41
|
+
settings,
|
42
|
+
agent_settings,
|
43
|
+
suppress_logging: {no_config_endpoint: true}
|
44
|
+
)
|
36
45
|
|
37
46
|
evp_proxy_path_prefix = Ext::Transport::EVP_PROXY_PATH_PREFIXES.find do |path_prefix|
|
38
47
|
negotiation.endpoint?(path_prefix)
|
@@ -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
|