Skip to content

Commit

Permalink
Merge pull request #129 from DataDog/anmarchenko/itr_settings
Browse files Browse the repository at this point in the history
Remote library configuration support and ITR::Runner
  • Loading branch information
anmarchenko authored Mar 6, 2024
2 parents 8eb9540 + 06f6752 commit e016c07
Show file tree
Hide file tree
Showing 39 changed files with 1,684 additions and 632 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ Webmock accordingly.
```ruby
# when using agentless mode
# note to use the correct datadog site (e.g. datadoghq.eu, etc)
WebMock.disable_net_connect!(:allow => "citestcycle-intake.datadoghq.com")
WebMock.disable_net_connect!(:allow => /datadoghq.com/)

# when using agent
WebMock.disable_net_connect!(:allow_localhost => true)
Expand All @@ -199,7 +199,7 @@ VCR.configure do |config|

# when using agentless mode
# note to use the correct datadog site (e.g. datadoghq.eu, etc)
config.ignore_hosts "citestcycle-intake.datadoghq.com"
config.ignore_hosts "citestcycle-intake.datadoghq.com", "api.datadoghq.com"
end
```

Expand Down
24 changes: 20 additions & 4 deletions lib/datadog/ci/configuration/components.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# frozen_string_literal: true

require_relative "../ext/settings"
require_relative "../itr/runner"
require_relative "../test_visibility/flush"
require_relative "../test_visibility/recorder"
require_relative "../test_visibility/null_recorder"
require_relative "../test_visibility/serializers/factories/test_level"
require_relative "../test_visibility/serializers/factories/test_suite_level"
require_relative "../test_visibility/transport"
require_relative "../transport/api/builder"
require_relative "../transport/remote_settings_api"

module Datadog
module CI
Expand Down Expand Up @@ -59,13 +61,30 @@ def activate_ci!(settings)
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

settings.tracing.test_mode.writer_options = writer_options

itr = Datadog::CI::ITR::Runner.new(
enabled: settings.ci.enabled && settings.ci.itr_enabled
)

remote_settings_api = Transport::RemoteSettingsApi.new(
api: test_visibility_api,
dd_env: settings.env
)

# CI visibility recorder global instance
@ci_recorder = TestVisibility::Recorder.new(
test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility
test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility,
itr: itr,
remote_settings_api: remote_settings_api
)
end

Expand Down Expand Up @@ -95,9 +114,6 @@ def build_test_visibility_api(settings)
Datadog.logger.debug(
"Old agent version detected, no evp_proxy support. Forcing test level visibility mode"
)

# CI visibility is still enabled but in legacy test level visibility mode
settings.ci.force_test_level_visibility = true
end
end

Expand Down
6 changes: 6 additions & 0 deletions lib/datadog/ci/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ def self.add_settings!(base)
end
end

option :itr_enabled do |o|
o.type :bool
o.env CI::Ext::Settings::ENV_ITR_ENABLED
o.default false
end

define_method(:instrument) do |integration_name, options = {}, &block|
return unless enabled

Expand Down
1 change: 1 addition & 0 deletions lib/datadog/ci/ext/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Settings
ENV_AGENTLESS_URL = "DD_CIVISIBILITY_AGENTLESS_URL"
ENV_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED = "DD_CIVISIBILITY_EXPERIMENTAL_TEST_SUITE_LEVEL_VISIBILITY_ENABLED"
ENV_FORCE_TEST_LEVEL_VISIBILITY = "DD_CIVISIBILITY_FORCE_TEST_LEVEL_VISIBILITY"
ENV_ITR_ENABLED = "DD_CIVISIBILITY_ITR_ENABLED"

# Source: https://docs.datadoghq.com/getting_started/site/
DD_SITE_ALLOWLIST = [
Expand Down
11 changes: 11 additions & 0 deletions lib/datadog/ci/ext/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ module Test
TAG_CODEOWNERS = "test.codeowners"
TAG_PARAMETERS = "test.parameters"

# ITR tags
TAG_ITR_TEST_SKIPPING_ENABLED = "test.itr.tests_skipping.enabled"
TAG_ITR_TEST_SKIPPING_TYPE = "test.itr.tests_skipping.type"

# Code coverage tags
TAG_CODE_COVERAGE_ENABLED = "test.code_coverage.enabled"

# those tags are special and used to correlate tests with the test sessions, suites, and modules
# they are transient and not sent to the backend
TAG_TEST_SESSION_ID = "_test.session_id"
Expand All @@ -43,6 +50,10 @@ module Test
TAG_SPAN_KIND = "span.kind"
SPAN_KIND_TEST = "test"

# could be either "test" or "suite" depending on whether we skip individual tests or whole suites
# we use test skipping for Ruby
ITR_TEST_SKIPPING_MODE = "test"

# test status as recognized by Datadog
module Status
PASS = "pass"
Expand Down
11 changes: 11 additions & 0 deletions lib/datadog/ci/ext/transport.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,18 @@ module Transport
TEST_VISIBILITY_INTAKE_HOST_PREFIX = "citestcycle-intake"
TEST_VISIBILITY_INTAKE_PATH = "/api/v2/citestcycle"

DD_API_HOST_PREFIX = "api"
DD_API_SETTINGS_PATH = "/api/v2/libraries/tests/services/setting"
DD_API_SETTINGS_TYPE = "ci_app_test_service_libraries_settings"
DD_API_SETTINGS_RESPONSE_DIG_KEYS = %w[data attributes].freeze
DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY = "itr_enabled"
DD_API_SETTINGS_RESPONSE_CODE_COVERAGE_KEY = "code_coverage"
DD_API_SETTINGS_RESPONSE_TESTS_SKIPPING_KEY = "tests_skipping"
DD_API_SETTINGS_RESPONSE_REQUIRE_GIT_KEY = "require_git"
DD_API_SETTINGS_RESPONSE_DEFAULT = {DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY => false}.freeze

CONTENT_TYPE_MESSAGEPACK = "application/msgpack"
CONTENT_TYPE_JSON = "application/json"
CONTENT_ENCODING_GZIP = "gzip"
end
end
Expand Down
67 changes: 67 additions & 0 deletions lib/datadog/ci/itr/runner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

require_relative "../ext/test"
require_relative "../ext/transport"

module Datadog
module CI
module ITR
# Intelligent test runner implementation
# Integrates with backend to provide test impact analysis data and
# skip tests that are not impacted by the changes
class Runner
def initialize(
enabled: false
)
@enabled = enabled
@test_skipping_enabled = false
@code_coverage_enabled = false

Datadog.logger.debug("ITR Runner initialized with enabled: #{@enabled}")
end

def configure(remote_configuration, test_session)
Datadog.logger.debug("Configuring ITR Runner with remote configuration: #{remote_configuration}")

@enabled = convert_to_bool(
remote_configuration.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY, false)
)
@test_skipping_enabled = @enabled && convert_to_bool(
remote_configuration.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_TESTS_SKIPPING_KEY, false)
)
@code_coverage_enabled = @enabled && convert_to_bool(
remote_configuration.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_CODE_COVERAGE_KEY, false)
)

test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_ENABLED, @test_skipping_enabled)
# currently we set this tag when ITR requires collecting code coverage
# this will change as soon as we implement total code coverage support in this library
test_session.set_tag(Ext::Test::TAG_CODE_COVERAGE_ENABLED, @code_coverage_enabled)

# we skip tests, not suites
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_TYPE, Ext::Test::ITR_TEST_SKIPPING_MODE)

Datadog.logger.debug("Configured ITR Runner with enabled: #{@enabled}, skipping_tests: #{@test_skipping_enabled}, code_coverage: #{@code_coverage_enabled}")
end

def enabled?
@enabled
end

def skipping_tests?
@test_skipping_enabled
end

def code_coverage?
@code_coverage_enabled
end

private

def convert_to_bool(value)
value.to_s == "true"
end
end
end
end
end
42 changes: 42 additions & 0 deletions lib/datadog/ci/span.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,48 @@ def set_tags(tags)
tracer_span.set_tags(tags)
end

# Returns the git repository URL extracted from the environment.
# @return [String] the repository URL.
def git_repository_url
tracer_span.get_tag(Ext::Git::TAG_REPOSITORY_URL)
end

# Returns the latest commit SHA extracted from the environment.
# @return [String] the commit SHA of the last commit.
def git_commit_sha
tracer_span.get_tag(Ext::Git::TAG_COMMIT_SHA)
end

# Returns the git branch name extracted from the environment.
# @return [String] the branch.
def git_branch
tracer_span.get_tag(Ext::Git::TAG_BRANCH)
end

# Returns the OS architecture extracted from the environment.
# @return [String] OS arch.
def os_architecture
tracer_span.get_tag(Ext::Test::TAG_OS_ARCHITECTURE)
end

# Returns the OS platform extracted from the environment.
# @return [String] OS platform.
def os_platform
tracer_span.get_tag(Ext::Test::TAG_OS_PLATFORM)
end

# Returns the runtime name extracted from the environment.
# @return [String] runtime name.
def runtime_name
tracer_span.get_tag(Ext::Test::TAG_RUNTIME_NAME)
end

# Returns the runtime version extracted from the environment.
# @return [String] runtime version.
def runtime_version
tracer_span.get_tag(Ext::Test::TAG_RUNTIME_VERSION)
end

def set_environment_runtime_tags
tracer_span.set_tag(Ext::Test::TAG_OS_ARCHITECTURE, ::RbConfig::CONFIG["host_cpu"])
tracer_span.set_tag(Ext::Test::TAG_OS_PLATFORM, ::RbConfig::CONFIG["host_os"])
Expand Down
8 changes: 8 additions & 0 deletions lib/datadog/ci/test_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ def name
get_tag(Ext::Test::TAG_COMMAND)
end

def skipping_tests?
get_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_ENABLED) == "true"
end

def code_coverage?
get_tag(Ext::Test::TAG_CODE_COVERAGE_ENABLED) == "true"
end

# Return the test session tags that could be inherited by sub-spans
# @return [Hash] the tags to be inherited by sub-spans.
def inheritable_tags
Expand Down
24 changes: 22 additions & 2 deletions lib/datadog/ci/test_visibility/recorder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,19 @@ class Recorder
attr_reader :environment_tags, :test_suite_level_visibility_enabled

def initialize(
test_suite_level_visibility_enabled: false,
itr:, remote_settings_api:, test_suite_level_visibility_enabled: false,
codeowners: Codeowners::Parser.new(Utils::Git.root).parse
)
@test_suite_level_visibility_enabled = test_suite_level_visibility_enabled

@environment_tags = Ext::Environment.tags(ENV).freeze
@local_context = Context::Local.new
@global_context = Context::Global.new

@codeowners = codeowners

@itr = itr
@remote_settings_api = remote_settings_api
end

def start_test_session(service: nil, tags: {})
Expand All @@ -49,7 +53,11 @@ def start_test_session(service: nil, tags: {})
)
set_session_context(tags, tracer_span)

build_test_session(tracer_span, tags)
test_session = build_test_session(tracer_span, tags)

configure_library(test_session)

test_session
end
end

Expand Down Expand Up @@ -175,8 +183,20 @@ def deactivate_test_suite(test_suite_name)
@global_context.deactivate_test_suite!(test_suite_name)
end

def itr_enabled?
@itr.enabled?
end

private

def configure_library(test_session)
# this will change when EFD is implemented
return unless itr_enabled?

remote_configuration = @remote_settings_api.fetch_library_settings(test_session)
@itr.configure(remote_configuration.payload, test_session)
end

def skip_tracing(block = nil)
block.call(nil) if block
end
Expand Down
6 changes: 1 addition & 5 deletions lib/datadog/ci/test_visibility/transport.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ def send_traces(traces)

response = send_payload(encoded_payload)

Datadog.logger.debug do
"Received server response: #{response.inspect}"
end

responses << response
end

Expand All @@ -68,7 +64,7 @@ def send_traces(traces)
private

def send_payload(encoded_payload)
api.request(
api.citestcycle_request(
path: Datadog::CI::Ext::Transport::TEST_VISIBILITY_INTAKE_PATH,
payload: encoded_payload
)
Expand Down
39 changes: 36 additions & 3 deletions lib/datadog/ci/transport/api/agentless.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,48 @@ module Api
class Agentless < Base
attr_reader :api_key

def initialize(api_key:, http:)
def initialize(api_key:, citestcycle_url:, api_url:)
@api_key = api_key
@citestcycle_http = build_http_client(citestcycle_url, compress: true)
@api_http = build_http_client(api_url, compress: false)
end

def citestcycle_request(path:, payload:, headers: {}, verb: "post")
super

perform_request(@citestcycle_http, path: path, payload: payload, headers: headers, verb: verb)
end

def api_request(path:, payload:, headers: {}, verb: "post")
super

super(http: http)
perform_request(@api_http, path: path, payload: payload, headers: headers, verb: verb)
end

private

def headers
def perform_request(http_client, path:, payload:, headers:, verb:)
http_client.request(
path: path,
payload: payload,
headers: headers_with_default(headers),
verb: verb
)
end

def build_http_client(url, compress:)
uri = URI.parse(url)
raise "Invalid agentless mode URL: #{url}" if uri.host.nil?

Datadog::CI::Transport::HTTP.new(
host: uri.host,
port: uri.port,
ssl: uri.scheme == "https" || uri.port == 443,
compress: compress
)
end

def default_headers
headers = super
headers[Ext::Transport::HEADER_DD_API_KEY] = api_key
headers
Expand Down
Loading

0 comments on commit e016c07

Please sign in to comment.