Skip to content

Commit

Permalink
Merge pull request #196 from DataDog/anmarchenko/refactor_configs_and…
Browse files Browse the repository at this point in the history
…_components

[SDTEST-172] Refactor configs and components
  • Loading branch information
anmarchenko authored Jul 2, 2024
2 parents fea0ea2 + f8f603b commit ef4a668
Show file tree
Hide file tree
Showing 57 changed files with 364 additions and 333 deletions.
4 changes: 2 additions & 2 deletions ext/datadog_cov/datadog_cov.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ void Init_datadog_cov(void)
{
VALUE mDatadog = rb_define_module("Datadog");
VALUE mCI = rb_define_module_under(mDatadog, "CI");
VALUE mITR = rb_define_module_under(mCI, "ITR");
VALUE mCoverage = rb_define_module_under(mITR, "Coverage");
VALUE mTestOptimisation = rb_define_module_under(mCI, "TestOptimisation");
VALUE mCoverage = rb_define_module_under(mTestOptimisation, "Coverage");
VALUE cDatadogCov = rb_define_class_under(mCoverage, "DDCov", rb_cObject);

rb_define_alloc_func(cDatadogCov, dd_cov_allocate);
Expand Down
30 changes: 15 additions & 15 deletions lib/datadog/ci.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class << self
# @return [Datadog::CI::TestSession] the active, running {Datadog::CI::TestSession}.
# @return [nil] if test suite level visibility is disabled or CI mode is disabled.
def start_test_session(service: Utils::Configuration.fetch_service_name("test"), tags: {})
recorder.start_test_session(service: service, tags: tags)
test_visibility.start_test_session(service: service, tags: tags)
end

# The active, unfinished test session.
Expand All @@ -61,7 +61,7 @@ def start_test_session(service: Utils::Configuration.fetch_service_name("test"),
# @return [Datadog::CI::TestSession] the active test session
# @return [nil] if no test session is active
def active_test_session
recorder.active_test_session
test_visibility.active_test_session
end

# Starts a {Datadog::CI::TestModule ci_test_module} that represents a single test module (for most Ruby test frameworks
Expand Down Expand Up @@ -93,7 +93,7 @@ def active_test_session
# @return [Datadog::CI::TestModule] the active, running {Datadog::CI::TestModule}.
# @return [nil] if test suite level visibility is disabled or CI mode is disabled.
def start_test_module(test_module_name, service: nil, tags: {})
recorder.start_test_module(test_module_name, service: service, tags: tags)
test_visibility.start_test_module(test_module_name, service: service, tags: tags)
end

# The active, unfinished test module.
Expand All @@ -116,7 +116,7 @@ def start_test_module(test_module_name, service: nil, tags: {})
# @return [Datadog::CI::TestModule] the active test module
# @return [nil] if no test module is active
def active_test_module
recorder.active_test_module
test_visibility.active_test_module
end

# Starts a {Datadog::CI::TestSuite ci_test_suite} that represents a single test suite.
Expand Down Expand Up @@ -145,7 +145,7 @@ def active_test_module
# @return [Datadog::CI::TestSuite] the active, running {Datadog::CI::TestSuite}.
# @return [nil] if test suite level visibility is disabled or CI mode is disabled.
def start_test_suite(test_suite_name, service: nil, tags: {})
recorder.start_test_suite(test_suite_name, service: service, tags: tags)
test_visibility.start_test_suite(test_suite_name, service: service, tags: tags)
end

# The active, unfinished test suite.
Expand All @@ -168,7 +168,7 @@ def start_test_suite(test_suite_name, service: nil, tags: {})
# @return [Datadog::CI::TestSuite] the active test suite
# @return [nil] if no test suite with given name is active
def active_test_suite(test_suite_name)
recorder.active_test_suite(test_suite_name)
test_visibility.active_test_suite(test_suite_name)
end

# Return a {Datadog::CI::Test ci_test} that will trace a test called `test_name`.
Expand Down Expand Up @@ -222,7 +222,7 @@ def active_test_suite(test_suite_name)
# @yieldparam [Datadog::CI::Test] ci_test the newly created and active [Datadog::CI::Test]
# @yieldparam [nil] if CI mode is disabled
def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
recorder.trace_test(test_name, test_suite_name, service: service, tags: tags, &block)
test_visibility.trace_test(test_name, test_suite_name, service: service, tags: tags, &block)
end

# Same as {.trace_test} but it does not accept a block.
Expand All @@ -248,7 +248,7 @@ def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
# @return [Datadog::CI::Test] the active, unfinished {Datadog::CI::Test}.
# @return [nil] if CI mode is disabled.
def start_test(test_name, test_suite_name, service: nil, tags: {})
recorder.trace_test(test_name, test_suite_name, service: service, tags: tags)
test_visibility.trace_test(test_name, test_suite_name, service: service, tags: tags)
end

# Trace any custom span inside a test. For example, you could trace:
Expand Down Expand Up @@ -300,7 +300,7 @@ def trace(span_name, type: "span", tags: {}, &block)
)
end

recorder.trace(span_name, type: type, tags: tags, &block)
test_visibility.trace(span_name, type: type, tags: tags, &block)
end

# The active, unfinished custom (i.e. not test/suite/module/session) span.
Expand All @@ -326,7 +326,7 @@ def trace(span_name, type: "span", tags: {}, &block)
# @return [Datadog::CI::Span] the active span
# @return [nil] if no span is active, or if the active span is not a custom span
def active_span
span = recorder.active_span
span = test_visibility.active_span
span if span && !Ext::AppTypes::CI_SPAN_TYPES.include?(span.type)
end

Expand All @@ -352,7 +352,7 @@ def active_span
# @return [Datadog::CI::Test] the active test
# @return [nil] if no test is active
def active_test
recorder.active_test
test_visibility.active_test
end

private
Expand All @@ -361,12 +361,12 @@ def components
Datadog.send(:components)
end

def recorder
components.ci_recorder
def test_visibility
components.test_visibility
end

def itr_runner
components.itr
def test_optimisation
components.test_optimisation
end
end
end
Expand Down
170 changes: 92 additions & 78 deletions lib/datadog/ci/configuration/components.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

require_relative "../ext/settings"
require_relative "../git/tree_uploader"
require_relative "../itr/runner"
require_relative "../itr/coverage/transport"
require_relative "../itr/coverage/writer"
require_relative "../test_optimisation/component"
require_relative "../test_optimisation/coverage/transport"
require_relative "../test_optimisation/coverage/writer"
require_relative "../test_visibility/component"
require_relative "../test_visibility/flush"
require_relative "../test_visibility/recorder"
require_relative "../test_visibility/null_recorder"
require_relative "../test_visibility/null_component"
require_relative "../test_visibility/serializers/factories/test_level"
require_relative "../test_visibility/serializers/factories/test_suite_level"
require_relative "../test_visibility/transport"
Expand All @@ -21,15 +21,15 @@ module CI
module Configuration
# Adds CI behavior to Datadog trace components
module Components
attr_reader :ci_recorder, :itr
attr_reader :test_visibility, :test_optimisation

def initialize(settings)
@test_optimisation = nil
@test_visibility = TestVisibility::NullComponent.new

# Activate CI mode if enabled
if settings.ci.enabled
activate_ci!(settings)
else
@itr = nil
@ci_recorder = TestVisibility::NullRecorder.new
end

super
Expand All @@ -38,8 +38,8 @@ def initialize(settings)
def shutdown!(replacement = nil)
super

@ci_recorder&.shutdown!
@itr&.shutdown!
@test_visibility&.shutdown!
@test_optimisation&.shutdown!
end

def activate_ci!(settings)
Expand All @@ -53,22 +53,21 @@ def activate_ci!(settings)
return
end

# Configure ddtrace library for CI visibility mode
# Builds test visibility API layer in agentless or EvP proxy mode
test_visibility_api = build_test_visibility_api(settings)
# bail out early if api is misconfigured
return unless settings.ci.enabled

# Configure datadog gem for test visibility mode

# Deactivate telemetry
settings.telemetry.enabled = false

# Deactivate remote configuration
# Test visibility uses its own remote settings
settings.remote.enabled = false

# do not use 128-bit trace ids for CI visibility
# they are used for OTEL compatibility in Datadog tracer
settings.tracing.trace_id_128_bit_generation_enabled = false

# Activate underlying tracing test mode
settings.tracing.test_mode.enabled = true

# Choose user defined TraceFlush or default to CI TraceFlush
settings.tracing.test_mode.trace_flush = settings.ci.trace_flush || CI::TestVisibility::Flush::Partial.new
# startup logs are useless for test visibility and create noise
settings.diagnostics.startup_logs.enabled = false

# When timecop is present, Time.now is mocked and .now_without_mock_time is added on Time to
# get the current time without the mock.
Expand All @@ -81,76 +80,42 @@ def activate_ci!(settings)
end
end

# startup logs are useless for CI visibility and create noise
settings.diagnostics.startup_logs.enabled = false

# transport creation
writer_options = settings.ci.writer_options
coverage_writer = nil
test_visibility_api = build_test_visibility_api(settings)
# Configure Datadog::Tracing module

if test_visibility_api
# setup writer for code coverage payloads
coverage_writer = ITR::Coverage::Writer.new(
transport: ITR::Coverage::Transport.new(api: test_visibility_api)
)

# configure tracing writer to send traces to CI visibility backend
writer_options[:transport] = TestVisibility::Transport.new(
api: test_visibility_api,
serializers_factory: serializers_factory(settings),
dd_env: settings.env
)
writer_options[:shutdown_timeout] = 60
writer_options[:buffer_size] = 10_000

settings.tracing.test_mode.async = true
else
# only legacy APM protocol is supported, so no test suite level visibility
settings.ci.force_test_level_visibility = true

# ITR is not supported with APM protocol
settings.ci.itr_enabled = false
end
# No need not use 128-bit trace ids for test visibility,
# they are used for OTEL compatibility in Datadog tracer
settings.tracing.trace_id_128_bit_generation_enabled = false

settings.tracing.test_mode.writer_options = writer_options
# Activate underlying tracing test mode with async worker
settings.tracing.test_mode.enabled = true
settings.tracing.test_mode.async = true
settings.tracing.test_mode.trace_flush = settings.ci.trace_flush || CI::TestVisibility::Flush::Partial.new

custom_configuration_tags = Utils::TestRun.custom_configuration(settings.tags)
trace_writer_options = settings.ci.writer_options
trace_writer_options[:shutdown_timeout] = 60
trace_writer_options[:buffer_size] = 10_000
tracing_transport = build_tracing_transport(settings, test_visibility_api)
trace_writer_options[:transport] = tracing_transport if tracing_transport

remote_settings_api = Transport::RemoteSettingsApi.new(
api: test_visibility_api,
dd_env: settings.env,
config_tags: custom_configuration_tags
)
settings.tracing.test_mode.writer_options = trace_writer_options

itr = ITR::Runner.new(
# @type ivar @test_optimisation: Datadog::CI::TestOptimisation::Component
@test_optimisation = TestOptimisation::Component.new(
api: test_visibility_api,
dd_env: settings.env,
config_tags: custom_configuration_tags,
coverage_writer: coverage_writer,
config_tags: custom_configuration(settings),
coverage_writer: build_coverage_writer(settings, test_visibility_api),
enabled: settings.ci.enabled && settings.ci.itr_enabled,
bundle_location: settings.ci.itr_code_coverage_excluded_bundle_path,
use_single_threaded_coverage: settings.ci.itr_code_coverage_use_single_threaded_mode
)

git_tree_uploader = Git::TreeUploader.new(api: test_visibility_api)
git_tree_upload_worker = if settings.ci.git_metadata_upload_enabled
Worker.new do |repository_url|
git_tree_uploader.call(repository_url)
end
else
DummyWorker.new
end

# CI visibility recorder global instance
@ci_recorder = TestVisibility::Recorder.new(
@test_visibility = TestVisibility::Component.new(
test_optimisation: @test_optimisation,
test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility,
itr: itr,
remote_settings_api: remote_settings_api,
git_tree_upload_worker: git_tree_upload_worker
remote_settings_api: build_remote_settings_client(settings, test_visibility_api),
git_tree_upload_worker: build_git_upload_worker(settings, test_visibility_api)
)

@itr = itr
end

def build_test_visibility_api(settings)
Expand Down Expand Up @@ -179,12 +144,61 @@ def build_test_visibility_api(settings)
Datadog.logger.debug(
"Old agent version detected, no evp_proxy support. Forcing test level visibility mode"
)

# only legacy APM protocol is supported, so no test suite level visibility
settings.ci.force_test_level_visibility = true

# ITR is not supported with APM protocol
settings.ci.itr_enabled = false
end
end

api
end

def build_tracing_transport(settings, api)
return nil if api.nil?

TestVisibility::Transport.new(
api: api,
serializers_factory: serializers_factory(settings),
dd_env: settings.env
)
end

def build_coverage_writer(settings, api)
return nil if api.nil?

TestOptimisation::Coverage::Writer.new(
transport: TestOptimisation::Coverage::Transport.new(api: api)
)
end

def build_git_upload_worker(settings, api)
if settings.ci.git_metadata_upload_enabled
git_tree_uploader = Git::TreeUploader.new(api: api)
Worker.new do |repository_url|
git_tree_uploader.call(repository_url)
end
else
DummyWorker.new
end
end

def build_remote_settings_client(settings, api)
Transport::RemoteSettingsApi.new(
api: api,
dd_env: settings.env,
config_tags: custom_configuration(settings)
)
end

# fetch custom tags provided by the user in DD_TAGS env var
# with prefix test.configuration.
def custom_configuration(settings)
@custom_configuration ||= Utils::TestRun.custom_configuration(settings.tags)
end

def serializers_factory(settings)
if settings.ci.force_test_level_visibility
TestVisibility::Serializers::Factories::TestLevel
Expand Down
6 changes: 3 additions & 3 deletions lib/datadog/ci/span.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ def to_s

private

# provides access to global CI recorder for CI models to deactivate themselves
def recorder
Datadog.send(:components).ci_recorder
# provides access to the test visibility component for CI models to deactivate themselves
def test_visibility
Datadog.send(:components).test_visibility
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/datadog/ci/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def name
# Finishes the current test.
# @return [void]
def finish
recorder.deactivate_test
test_visibility.deactivate_test

super
end
Expand Down
Loading

0 comments on commit ef4a668

Please sign in to comment.