datadog-ci 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -2
- data/lib/datadog/ci/async_writer.rb +112 -0
- data/lib/datadog/ci/configuration/components.rb +36 -6
- data/lib/datadog/ci/configuration/settings.rb +17 -0
- data/lib/datadog/ci/contrib/activesupport/configuration/settings.rb +25 -0
- data/lib/datadog/ci/contrib/activesupport/ext.rb +14 -0
- data/lib/datadog/ci/contrib/activesupport/integration.rb +43 -0
- data/lib/datadog/ci/contrib/activesupport/logs_formatter.rb +41 -0
- data/lib/datadog/ci/contrib/activesupport/patcher.rb +50 -0
- data/lib/datadog/ci/contrib/lograge/configuration/settings.rb +25 -0
- data/lib/datadog/ci/contrib/lograge/ext.rb +14 -0
- data/lib/datadog/ci/contrib/lograge/integration.rb +43 -0
- data/lib/datadog/ci/contrib/lograge/log_subscriber.rb +41 -0
- data/lib/datadog/ci/contrib/lograge/patcher.rb +32 -0
- data/lib/datadog/ci/contrib/minitest/runner.rb +4 -1
- data/lib/datadog/ci/contrib/parallel_tests/cli.rb +84 -0
- data/lib/datadog/ci/contrib/parallel_tests/configuration/settings.rb +32 -0
- data/lib/datadog/ci/contrib/parallel_tests/ext.rb +16 -0
- data/lib/datadog/ci/contrib/parallel_tests/integration.rb +42 -0
- data/lib/datadog/ci/contrib/parallel_tests/patcher.rb +23 -0
- data/lib/datadog/ci/contrib/rspec/example.rb +7 -0
- data/lib/datadog/ci/contrib/rspec/example_group.rb +18 -8
- data/lib/datadog/ci/contrib/rspec/helpers.rb +18 -0
- data/lib/datadog/ci/contrib/rspec/runner.rb +2 -0
- data/lib/datadog/ci/contrib/semantic_logger/configuration/settings.rb +25 -0
- data/lib/datadog/ci/contrib/semantic_logger/ext.rb +14 -0
- data/lib/datadog/ci/contrib/semantic_logger/integration.rb +42 -0
- data/lib/datadog/ci/contrib/semantic_logger/logger.rb +32 -0
- data/lib/datadog/ci/contrib/semantic_logger/patcher.rb +32 -0
- data/lib/datadog/ci/ext/settings.rb +3 -0
- data/lib/datadog/ci/ext/test.rb +23 -2
- data/lib/datadog/ci/ext/transport.rb +2 -0
- data/lib/datadog/ci/git/local_repository.rb +1 -1
- data/lib/datadog/ci/git/tree_uploader.rb +9 -0
- data/lib/datadog/ci/logs/component.rb +46 -0
- data/lib/datadog/ci/logs/transport.rb +73 -0
- data/lib/datadog/ci/readonly_test_module.rb +28 -0
- data/lib/datadog/ci/readonly_test_session.rb +31 -0
- data/lib/datadog/ci/remote/component.rb +43 -16
- data/lib/datadog/ci/test.rb +10 -0
- data/lib/datadog/ci/test_management/component.rb +34 -1
- data/lib/datadog/ci/test_management/tests_properties.rb +2 -1
- data/lib/datadog/ci/test_optimisation/component.rb +31 -5
- data/lib/datadog/ci/test_retries/driver/retry_new.rb +1 -1
- data/lib/datadog/ci/test_session.rb +7 -1
- data/lib/datadog/ci/test_visibility/component.rb +82 -28
- data/lib/datadog/ci/test_visibility/context.rb +77 -29
- data/lib/datadog/ci/test_visibility/null_component.rb +7 -1
- data/lib/datadog/ci/test_visibility/store/{local.rb → fiber_local.rb} +1 -1
- data/lib/datadog/ci/test_visibility/store/{global.rb → process.rb} +23 -18
- data/lib/datadog/ci/test_visibility/transport.rb +1 -2
- data/lib/datadog/ci/transport/api/agentless.rb +8 -1
- data/lib/datadog/ci/transport/api/base.rb +4 -0
- data/lib/datadog/ci/transport/api/builder.rb +5 -1
- data/lib/datadog/ci/transport/api/evp_proxy.rb +4 -0
- data/lib/datadog/ci/transport/http.rb +1 -1
- data/lib/datadog/ci/utils/file_storage.rb +57 -0
- data/lib/datadog/ci/utils/stateful.rb +52 -0
- data/lib/datadog/ci/version.rb +1 -1
- data/lib/datadog/ci.rb +7 -3
- metadata +32 -6
- data/lib/datadog/ci/test_optimisation/coverage/writer.rb +0 -116
- data/lib/datadog/ci/test_visibility/capabilities.rb +0 -36
@@ -4,8 +4,9 @@ require "datadog/tracing"
|
|
4
4
|
require "datadog/tracing/contrib/component"
|
5
5
|
require "datadog/tracing/trace_digest"
|
6
6
|
|
7
|
-
require_relative "store/
|
8
|
-
require_relative "store/
|
7
|
+
require_relative "store/process"
|
8
|
+
require_relative "store/fiber_local"
|
9
|
+
require_relative "telemetry"
|
9
10
|
|
10
11
|
require_relative "../ext/app_types"
|
11
12
|
require_relative "../ext/environment"
|
@@ -29,9 +30,11 @@ module Datadog
|
|
29
30
|
class Context
|
30
31
|
attr_reader :total_tests_count, :tests_skipped_by_tia_count
|
31
32
|
|
32
|
-
def initialize
|
33
|
-
@
|
34
|
-
|
33
|
+
def initialize(test_visibility_component:)
|
34
|
+
@test_visibility_component = test_visibility_component
|
35
|
+
|
36
|
+
@fiber_local_context = Store::FiberLocal.new
|
37
|
+
@process_context = Store::Process.new
|
35
38
|
|
36
39
|
@mutex = Mutex.new
|
37
40
|
|
@@ -40,18 +43,23 @@ module Datadog
|
|
40
43
|
end
|
41
44
|
|
42
45
|
def start_test_session(service: nil, tags: {})
|
43
|
-
@
|
46
|
+
@process_context.fetch_or_activate_test_session do
|
44
47
|
tracer_span = start_datadog_tracer_span(
|
45
48
|
"test.session", build_tracing_span_options(service, Ext::AppTypes::TYPE_TEST_SESSION)
|
46
49
|
)
|
47
50
|
set_session_context(tags, tracer_span)
|
48
51
|
|
49
|
-
build_test_session(tracer_span, tags)
|
52
|
+
test_session = build_test_session(tracer_span, tags)
|
53
|
+
|
54
|
+
Telemetry.test_session_started(test_session)
|
55
|
+
Telemetry.event_created(test_session)
|
56
|
+
|
57
|
+
test_session
|
50
58
|
end
|
51
59
|
end
|
52
60
|
|
53
61
|
def start_test_module(test_module_name, service: nil, tags: {})
|
54
|
-
@
|
62
|
+
@process_context.fetch_or_activate_test_module do
|
55
63
|
set_inherited_globals(tags)
|
56
64
|
set_session_context(tags)
|
57
65
|
|
@@ -60,12 +68,16 @@ module Datadog
|
|
60
68
|
)
|
61
69
|
set_module_context(tags, tracer_span)
|
62
70
|
|
63
|
-
build_test_module(tracer_span, tags)
|
71
|
+
test_module = build_test_module(tracer_span, tags)
|
72
|
+
|
73
|
+
Telemetry.event_created(test_module)
|
74
|
+
|
75
|
+
test_module
|
64
76
|
end
|
65
77
|
end
|
66
78
|
|
67
79
|
def start_test_suite(test_suite_name, service: nil, tags: {})
|
68
|
-
@
|
80
|
+
@process_context.fetch_or_activate_test_suite(test_suite_name) do
|
69
81
|
set_inherited_globals(tags)
|
70
82
|
set_session_context(tags)
|
71
83
|
set_module_context(tags)
|
@@ -75,7 +87,11 @@ module Datadog
|
|
75
87
|
)
|
76
88
|
set_suite_context(tags, test_suite: tracer_span)
|
77
89
|
|
78
|
-
build_test_suite(tracer_span, tags)
|
90
|
+
test_suite = build_test_suite(tracer_span, tags)
|
91
|
+
|
92
|
+
Telemetry.event_created(test_suite)
|
93
|
+
|
94
|
+
test_suite
|
79
95
|
end
|
80
96
|
end
|
81
97
|
|
@@ -100,14 +116,14 @@ module Datadog
|
|
100
116
|
start_datadog_tracer_span(test_name, span_options) do |tracer_span|
|
101
117
|
test = build_test(tracer_span, tags)
|
102
118
|
|
103
|
-
@
|
119
|
+
@fiber_local_context.activate_test(test) do
|
104
120
|
block.call(test)
|
105
121
|
end
|
106
122
|
end
|
107
123
|
else
|
108
124
|
tracer_span = start_datadog_tracer_span(test_name, span_options)
|
109
125
|
test = build_test(tracer_span, tags)
|
110
|
-
@
|
126
|
+
@fiber_local_context.activate_test(test)
|
111
127
|
test
|
112
128
|
end
|
113
129
|
end
|
@@ -136,43 +152,43 @@ module Datadog
|
|
136
152
|
end
|
137
153
|
|
138
154
|
def active_test
|
139
|
-
@
|
155
|
+
@fiber_local_context.active_test
|
140
156
|
end
|
141
157
|
|
142
158
|
def active_test_session
|
143
|
-
@
|
159
|
+
@process_context.active_test_session
|
144
160
|
end
|
145
161
|
|
146
162
|
def active_test_module
|
147
|
-
@
|
163
|
+
@process_context.active_test_module
|
148
164
|
end
|
149
165
|
|
150
166
|
def active_test_suite(test_suite_name)
|
151
|
-
@
|
167
|
+
@process_context.active_test_suite(test_suite_name)
|
152
168
|
end
|
153
169
|
|
154
170
|
def single_active_test_suite
|
155
|
-
@
|
171
|
+
@process_context.fetch_single_test_suite
|
156
172
|
end
|
157
173
|
|
158
174
|
def stop_all_test_suites
|
159
|
-
@
|
175
|
+
@process_context.stop_all_test_suites
|
160
176
|
end
|
161
177
|
|
162
178
|
def deactivate_test
|
163
|
-
@
|
179
|
+
@fiber_local_context.deactivate_test
|
164
180
|
end
|
165
181
|
|
166
182
|
def deactivate_test_session
|
167
|
-
@
|
183
|
+
@process_context.deactivate_test_session!
|
168
184
|
end
|
169
185
|
|
170
186
|
def deactivate_test_module
|
171
|
-
@
|
187
|
+
@process_context.deactivate_test_module!
|
172
188
|
end
|
173
189
|
|
174
190
|
def deactivate_test_suite(test_suite_name)
|
175
|
-
@
|
191
|
+
@process_context.deactivate_test_suite!(test_suite_name)
|
176
192
|
end
|
177
193
|
|
178
194
|
def incr_total_tests_count
|
@@ -231,20 +247,21 @@ module Datadog
|
|
231
247
|
|
232
248
|
# PROPAGATING CONTEXT FROM TOP-LEVEL TO THE LOWER LEVELS
|
233
249
|
def set_inherited_globals(tags)
|
234
|
-
#
|
235
|
-
|
236
|
-
@global_context.inheritable_session_tags.each do |key, value|
|
250
|
+
# Copy inheritable tags from the test session context to the provided tags
|
251
|
+
test_session_context&.inheritable_tags&.each do |key, value|
|
237
252
|
tags[key] = value unless tags.key?(key)
|
238
253
|
end
|
239
254
|
end
|
240
255
|
|
241
256
|
def set_session_context(tags, test_session = nil)
|
242
|
-
|
257
|
+
# we need to call TestVisibility::Component here because active test session might be remote
|
258
|
+
test_session ||= test_session_context
|
243
259
|
tags[Ext::Test::TAG_TEST_SESSION_ID] = test_session.id.to_s if test_session
|
244
260
|
end
|
245
261
|
|
246
262
|
def set_module_context(tags, test_module = nil)
|
247
|
-
|
263
|
+
# we need to call TestVisibility::Component here because active test module might be remote
|
264
|
+
test_module ||= test_module_context
|
248
265
|
if test_module
|
249
266
|
tags[Ext::Test::TAG_TEST_MODULE_ID] = test_module.id.to_s
|
250
267
|
tags[Ext::Test::TAG_MODULE] = test_module.name
|
@@ -281,11 +298,42 @@ module Datadog
|
|
281
298
|
end
|
282
299
|
|
283
300
|
def build_tracing_span_options(service, type, other_options = {})
|
284
|
-
other_options[:service] = service ||
|
301
|
+
other_options[:service] = service || test_session_context&.service
|
285
302
|
other_options[:type] = type
|
286
303
|
|
287
304
|
other_options
|
288
305
|
end
|
306
|
+
|
307
|
+
# one of:
|
308
|
+
# 1. Currrent test session from the Store::Process
|
309
|
+
# 2. Readonly copy of the remote test session (if test session was started by a parent process and local copy was created)
|
310
|
+
# 3. Remote test session as DRb::DRbObject link (in this case also local copy will be created)
|
311
|
+
def test_session_context
|
312
|
+
local_test_session = @process_context.active_test_session
|
313
|
+
return local_test_session if local_test_session
|
314
|
+
|
315
|
+
local_readonly_test_session = @process_context.readonly_test_session
|
316
|
+
return local_readonly_test_session if local_readonly_test_session
|
317
|
+
|
318
|
+
remote_test_session = @test_visibility_component.active_test_session
|
319
|
+
@process_context.set_readonly_test_session(remote_test_session)
|
320
|
+
|
321
|
+
remote_test_session
|
322
|
+
end
|
323
|
+
|
324
|
+
# works similar to test_session_context
|
325
|
+
def test_module_context
|
326
|
+
local_test_module = @process_context.active_test_module
|
327
|
+
return local_test_module if local_test_module
|
328
|
+
|
329
|
+
local_readonly_test_module = @process_context.readonly_test_module
|
330
|
+
return local_readonly_test_module if local_readonly_test_module
|
331
|
+
|
332
|
+
remote_test_module = @test_visibility_component.active_test_module
|
333
|
+
@process_context.set_readonly_test_module(remote_test_module)
|
334
|
+
|
335
|
+
remote_test_module
|
336
|
+
end
|
289
337
|
end
|
290
338
|
end
|
291
339
|
end
|
@@ -8,7 +8,9 @@ module Datadog
|
|
8
8
|
def configure(_, _)
|
9
9
|
end
|
10
10
|
|
11
|
-
def start_test_session(
|
11
|
+
def start_test_session(
|
12
|
+
service: nil, tags: {}, estimated_total_tests_count: 0, distributed: false, local_test_suites_mode: true
|
13
|
+
)
|
12
14
|
skip_tracing
|
13
15
|
end
|
14
16
|
|
@@ -63,6 +65,10 @@ module Datadog
|
|
63
65
|
def logical_test_session_name
|
64
66
|
end
|
65
67
|
|
68
|
+
def client_process?
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
66
72
|
private
|
67
73
|
|
68
74
|
def skip_tracing(block = nil)
|
@@ -1,11 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "../../readonly_test_session"
|
4
|
+
require_relative "../../readonly_test_module"
|
5
|
+
|
3
6
|
module Datadog
|
4
7
|
module CI
|
5
8
|
module TestVisibility
|
6
9
|
module Store
|
7
10
|
# This context is shared between threads and represents the current test session and test module.
|
8
|
-
class
|
11
|
+
class Process
|
12
|
+
attr_reader :readonly_test_session, :readonly_test_module
|
13
|
+
|
9
14
|
def initialize
|
10
15
|
# we are using Monitor instead of Mutex because it is reentrant
|
11
16
|
@mutex = Monitor.new
|
@@ -13,6 +18,11 @@ module Datadog
|
|
13
18
|
@test_session = nil
|
14
19
|
@test_module = nil
|
15
20
|
@test_suites = {}
|
21
|
+
|
22
|
+
# small copies of id, name and some tags: store them in the current process to set session/module context
|
23
|
+
# for any spans faster
|
24
|
+
@readonly_test_session = nil
|
25
|
+
@readonly_test_module = nil
|
16
26
|
end
|
17
27
|
|
18
28
|
def fetch_or_activate_test_suite(test_suite_name, &block)
|
@@ -53,12 +63,6 @@ module Datadog
|
|
53
63
|
@mutex.synchronize { @test_suites[test_suite_name] }
|
54
64
|
end
|
55
65
|
|
56
|
-
def service
|
57
|
-
@mutex.synchronize do
|
58
|
-
@test_session&.service
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
66
|
def stop_all_test_suites
|
63
67
|
@mutex.synchronize do
|
64
68
|
@test_suites.each_value(&:finish)
|
@@ -66,17 +70,6 @@ module Datadog
|
|
66
70
|
end
|
67
71
|
end
|
68
72
|
|
69
|
-
def inheritable_session_tags
|
70
|
-
@mutex.synchronize do
|
71
|
-
test_session = @test_session
|
72
|
-
if test_session
|
73
|
-
test_session.inheritable_tags
|
74
|
-
else
|
75
|
-
{}
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
73
|
def deactivate_test_session!
|
81
74
|
@mutex.synchronize { @test_session = nil }
|
82
75
|
end
|
@@ -88,6 +81,18 @@ module Datadog
|
|
88
81
|
def deactivate_test_suite!(test_suite_name)
|
89
82
|
@mutex.synchronize { @test_suites.delete(test_suite_name) }
|
90
83
|
end
|
84
|
+
|
85
|
+
def set_readonly_test_session(remote_test_session)
|
86
|
+
return if remote_test_session.nil?
|
87
|
+
|
88
|
+
@readonly_test_session = Datadog::CI::ReadonlyTestSession.new(remote_test_session)
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_readonly_test_module(remote_test_module)
|
92
|
+
return if remote_test_module.nil?
|
93
|
+
|
94
|
+
@readonly_test_module = Datadog::CI::ReadonlyTestModule.new(remote_test_module)
|
95
|
+
end
|
91
96
|
end
|
92
97
|
end
|
93
98
|
end
|
@@ -4,7 +4,6 @@ require "datadog/core/environment/identity"
|
|
4
4
|
require "datadog/core/telemetry/logging"
|
5
5
|
require "datadog/core/utils/only_once"
|
6
6
|
|
7
|
-
require_relative "capabilities"
|
8
7
|
require_relative "serializers/factories/test_level"
|
9
8
|
|
10
9
|
require_relative "../ext/app_types"
|
@@ -117,7 +116,7 @@ module Datadog
|
|
117
116
|
packer.write("library_version")
|
118
117
|
packer.write(Datadog::CI::VERSION::STRING)
|
119
118
|
|
120
|
-
library_capabilities_tags =
|
119
|
+
library_capabilities_tags = Ext::Test::LibraryCapabilities::CAPABILITY_VERSIONS
|
121
120
|
|
122
121
|
Ext::AppTypes::CI_SPAN_TYPES.each do |ci_span_type|
|
123
122
|
packer.write(ci_span_type)
|
@@ -10,11 +10,12 @@ module Datadog
|
|
10
10
|
class Agentless < Base
|
11
11
|
attr_reader :api_key
|
12
12
|
|
13
|
-
def initialize(api_key:, citestcycle_url:, api_url:, citestcov_url:)
|
13
|
+
def initialize(api_key:, citestcycle_url:, api_url:, citestcov_url:, logs_intake_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
17
|
@citestcov_http = build_http_client(citestcov_url, compress: true)
|
18
|
+
@logs_intake_http = build_http_client(logs_intake_url, compress: true)
|
18
19
|
end
|
19
20
|
|
20
21
|
def citestcycle_request(path:, payload:, headers: {}, verb: "post")
|
@@ -42,6 +43,12 @@ module Datadog
|
|
42
43
|
perform_request(@citestcov_http, path: path, payload: @citestcov_payload, headers: headers, verb: verb)
|
43
44
|
end
|
44
45
|
|
46
|
+
def logs_intake_request(path:, payload:, headers: {}, verb: "post")
|
47
|
+
super
|
48
|
+
|
49
|
+
perform_request(@logs_intake_http, path: path, payload: payload, headers: headers, verb: verb)
|
50
|
+
end
|
51
|
+
|
45
52
|
private
|
46
53
|
|
47
54
|
def perform_request(http_client, path:, payload:, headers:, verb:, accept_compressed_response: false)
|
@@ -38,6 +38,10 @@ module Datadog
|
|
38
38
|
].join("\r\n")
|
39
39
|
end
|
40
40
|
|
41
|
+
def logs_intake_request(path:, payload:, headers: {}, verb: "post")
|
42
|
+
headers[Ext::Transport::HEADER_CONTENT_TYPE] ||= Ext::Transport::CONTENT_TYPE_JSON
|
43
|
+
end
|
44
|
+
|
41
45
|
def headers_with_default(headers)
|
42
46
|
request_headers = default_headers
|
43
47
|
request_headers.merge!(headers)
|
@@ -27,11 +27,15 @@ module Datadog
|
|
27
27
|
citestcov_url = settings.ci.agentless_url ||
|
28
28
|
"https://#{Ext::Transport::TEST_COVERAGE_INTAKE_HOST_PREFIX}.#{dd_site}:443"
|
29
29
|
|
30
|
+
logs_intake_url = settings.ci.agentless_url ||
|
31
|
+
"https://#{Ext::Transport::LOGS_INTAKE_HOST_PREFIX}.#{dd_site}:443"
|
32
|
+
|
30
33
|
Agentless.new(
|
31
34
|
api_key: settings.api_key,
|
32
35
|
citestcycle_url: citestcycle_url,
|
33
36
|
api_url: api_url,
|
34
|
-
citestcov_url: citestcov_url
|
37
|
+
citestcov_url: citestcov_url,
|
38
|
+
logs_intake_url: logs_intake_url
|
35
39
|
)
|
36
40
|
end
|
37
41
|
|
@@ -46,6 +46,10 @@ module Datadog
|
|
46
46
|
perform_request(@agent_intake_http, path: path, payload: @citestcov_payload, headers: headers, verb: verb)
|
47
47
|
end
|
48
48
|
|
49
|
+
def logs_intake_request(path:, payload:, headers: {}, verb: "post")
|
50
|
+
raise NotImplementedError, "Logs intake is not supported in EVP proxy mode"
|
51
|
+
end
|
52
|
+
|
49
53
|
private
|
50
54
|
|
51
55
|
def perform_request(http_client, path:, payload:, headers:, verb:)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "tempfile"
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module CI
|
8
|
+
module Utils
|
9
|
+
# FileStorage module provides functionality for storing and retrieving arbitrary Ruby objects in a temp file
|
10
|
+
# to share them between processes.
|
11
|
+
module FileStorage
|
12
|
+
TEMP_DIR = File.join(Dir.tmpdir, "datadog-ci-storage")
|
13
|
+
|
14
|
+
def self.store(key, value)
|
15
|
+
ensure_temp_dir_exists
|
16
|
+
file_path = file_path_for(key)
|
17
|
+
|
18
|
+
File.binwrite(file_path, Marshal.dump(value))
|
19
|
+
|
20
|
+
true
|
21
|
+
rescue => e
|
22
|
+
Datadog.logger.error("Failed to store data for key '#{key}': #{e.class} - #{e.message}")
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.retrieve(key)
|
27
|
+
file_path = file_path_for(key)
|
28
|
+
return nil unless File.exist?(file_path)
|
29
|
+
|
30
|
+
Marshal.load(File.binread(file_path))
|
31
|
+
rescue => e
|
32
|
+
Datadog.logger.error("Failed to retrieve data for key '#{key}': #{e.class} - #{e.message}")
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.cleanup
|
37
|
+
return false unless Dir.exist?(TEMP_DIR)
|
38
|
+
|
39
|
+
FileUtils.rm_rf(TEMP_DIR)
|
40
|
+
true
|
41
|
+
rescue => e
|
42
|
+
Datadog.logger.error("Failed to cleanup storage directory: #{e.class} - #{e.message}")
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.ensure_temp_dir_exists
|
47
|
+
FileUtils.mkdir_p(TEMP_DIR) unless Dir.exist?(TEMP_DIR)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.file_path_for(key)
|
51
|
+
sanitized_key = key.to_s.gsub(/[^a-zA-Z0-9_-]/, "_")
|
52
|
+
File.join(TEMP_DIR, "dd-ci-#{sanitized_key}.dat")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "file_storage"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module CI
|
7
|
+
module Utils
|
8
|
+
# Module for components that need to persist and restore state
|
9
|
+
module Stateful
|
10
|
+
# Store component state
|
11
|
+
def store_component_state
|
12
|
+
state = serialize_state
|
13
|
+
|
14
|
+
res = Utils::FileStorage.store(storage_key, state)
|
15
|
+
Datadog.logger.debug { "Stored component state (key=#{storage_key}): #{res}" }
|
16
|
+
|
17
|
+
res
|
18
|
+
end
|
19
|
+
|
20
|
+
# Load component state
|
21
|
+
def load_component_state
|
22
|
+
test_visibility_component = Datadog.send(:components).test_visibility
|
23
|
+
return false unless test_visibility_component.client_process?
|
24
|
+
|
25
|
+
state = Utils::FileStorage.retrieve(storage_key)
|
26
|
+
unless state
|
27
|
+
Datadog.logger.debug { "No component state found in file storage (key=#{storage_key})" }
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
restore_state(state)
|
32
|
+
Datadog.logger.debug { "Loaded component state from file storage (key=#{storage_key})" }
|
33
|
+
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
# These methods must be implemented by including classes
|
38
|
+
def serialize_state
|
39
|
+
raise NotImplementedError, "Components must implement #serialize_state"
|
40
|
+
end
|
41
|
+
|
42
|
+
def restore_state(state)
|
43
|
+
raise NotImplementedError, "Components must implement #restore_state"
|
44
|
+
end
|
45
|
+
|
46
|
+
def storage_key
|
47
|
+
raise NotImplementedError, "Components must implement #storage_key"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/datadog/ci/version.rb
CHANGED
data/lib/datadog/ci.rb
CHANGED
@@ -415,19 +415,23 @@ end
|
|
415
415
|
|
416
416
|
# Integrations
|
417
417
|
|
418
|
-
# Test frameworks
|
418
|
+
# Test frameworks
|
419
419
|
require_relative "ci/contrib/cucumber/integration"
|
420
420
|
require_relative "ci/contrib/minitest/integration"
|
421
421
|
require_relative "ci/contrib/rspec/integration"
|
422
422
|
|
423
|
-
# Test runners
|
423
|
+
# Test runners
|
424
424
|
require_relative "ci/contrib/knapsack/integration"
|
425
425
|
require_relative "ci/contrib/ciqueue/integration"
|
426
|
+
require_relative "ci/contrib/parallel_tests/integration"
|
426
427
|
|
427
|
-
# Additional test libraries (auto instrumented
|
428
|
+
# Additional test libraries (auto instrumented on test session start)
|
428
429
|
require_relative "ci/contrib/selenium/integration"
|
429
430
|
require_relative "ci/contrib/cuprite/integration"
|
430
431
|
require_relative "ci/contrib/simplecov/integration"
|
432
|
+
require_relative "ci/contrib/activesupport/integration"
|
433
|
+
require_relative "ci/contrib/lograge/integration"
|
434
|
+
require_relative "ci/contrib/semantic_logger/integration"
|
431
435
|
|
432
436
|
# Configuration extensions
|
433
437
|
require_relative "ci/configuration/extensions"
|