From b595d4177973583aa2f11ad9689d6147efeb2248 Mon Sep 17 00:00:00 2001 From: Ivo Anjo Date: Thu, 2 Jan 2025 16:15:19 +0000 Subject: [PATCH 1/5] Enable type checking for AgentSettingsResolver/AgentSettings Steep doesn't seem to be a big fan of Structs so I just went ahead and turned the `AgentSettings` into a regular class that's equivalent to the struct we had before. (In particular, I decided to still keep every field as optional). Ideally this would be a `Data` class, but we're far from dropping support for Rubies that don't have it. --- Steepfile | 1 - .../configuration/agent_settings_resolver.rb | 22 ++++----- .../configuration/agent_settings_resolver.rbs | 46 +++++++++++++------ sig/datadog/core/configuration/ext.rbs | 1 + 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/Steepfile b/Steepfile index 24d49808692..d2cce9c3ef6 100644 --- a/Steepfile +++ b/Steepfile @@ -80,7 +80,6 @@ target :datadog do ignore 'lib/datadog/core/buffer/thread_safe.rb' ignore 'lib/datadog/core/chunker.rb' ignore 'lib/datadog/core/configuration.rb' - ignore 'lib/datadog/core/configuration/agent_settings_resolver.rb' ignore 'lib/datadog/core/configuration/base.rb' ignore 'lib/datadog/core/configuration/components.rb' ignore 'lib/datadog/core/configuration/dependency_resolver.rb' diff --git a/lib/datadog/core/configuration/agent_settings_resolver.rb b/lib/datadog/core/configuration/agent_settings_resolver.rb index 7d7c048f553..3d407ce1afb 100644 --- a/lib/datadog/core/configuration/agent_settings_resolver.rb +++ b/lib/datadog/core/configuration/agent_settings_resolver.rb @@ -19,17 +19,17 @@ module Configuration # Whenever there is a conflict (different configurations are provided in different orders), it MUST warn the users # about it and pick a value based on the following priority: code > environment variable > defaults. class AgentSettingsResolver - AgentSettings = Struct.new( - :adapter, - :ssl, - :hostname, - :port, - :uds_path, - :timeout_seconds, - keyword_init: true - ) do - def initialize(*) - super + # Immutable container for the resulting settings + class AgentSettings + attr_reader :adapter, :ssl, :hostname, :port, :uds_path, :timeout_seconds + + def initialize(adapter: nil, ssl: nil, hostname: nil, port: nil, uds_path: nil, timeout_seconds: nil) + @adapter = adapter + @ssl = ssl + @hostname = hostname + @port = port + @uds_path = uds_path + @timeout_seconds = timeout_seconds freeze end end diff --git a/sig/datadog/core/configuration/agent_settings_resolver.rbs b/sig/datadog/core/configuration/agent_settings_resolver.rbs index 040a9aa960a..a4ea2830ddb 100644 --- a/sig/datadog/core/configuration/agent_settings_resolver.rbs +++ b/sig/datadog/core/configuration/agent_settings_resolver.rbs @@ -2,9 +2,8 @@ module Datadog module Core module Configuration class AgentSettingsResolver - class AgentSettings < ::Struct[untyped] + class AgentSettings def initialize: (adapter: untyped, ssl: untyped, hostname: untyped, port: untyped, uds_path: untyped, timeout_seconds: untyped) -> void - def merge: (**::Hash[untyped, untyped] member_values) -> AgentSettingsResolver attr_reader adapter: untyped attr_reader ssl: untyped @@ -14,6 +13,17 @@ module Datadog attr_reader timeout_seconds: untyped end + @settings: untyped + @logger: untyped + @configured_hostname: untyped + @configured_port: untyped + @configured_ssl: untyped + @configured_timeout_seconds: untyped + @configured_uds_path: untyped + @uds_fallback: untyped + @mixed_http_and_uds: untyped + @parsed_url: untyped + def self.call: (untyped settings, ?logger: untyped) -> untyped private @@ -38,33 +48,31 @@ module Datadog def configured_uds_path: () -> untyped - def try_parsing_as_integer: (value: untyped, friendly_name: untyped) -> untyped + def parsed_url_ssl?: () -> (nil | untyped) - def try_parsing_as_boolean: (value: untyped, friendly_name: untyped) -> untyped + def try_parsing_as_integer: (value: untyped, friendly_name: untyped) -> untyped - def ssl?: () -> bool + def ssl?: () -> (false | untyped) def hostname: () -> untyped def port: () -> untyped - def uds_path: () -> untyped - def timeout_seconds: () -> untyped - def uds_fallback: () -> untyped + def parsed_url_uds_path: () -> (nil | untyped) - def should_use_uds_fallback?: () -> untyped + def uds_path: () -> (nil | untyped) - def should_use_uds?: () -> bool + def uds_fallback: () -> untyped - def can_use_uds?: () -> bool + def should_use_uds?: () -> untyped - def parsed_url: () -> untyped + def mixed_http_and_uds: () -> untyped - def parsed_url_ssl?: () -> untyped + def can_use_uds?: () -> untyped - def parsed_url_uds_path: () -> untyped + def parsed_url: () -> untyped def pick_from: (*untyped configurations_in_priority_order) -> untyped @@ -72,7 +80,17 @@ module Datadog def log_warning: (untyped message) -> (untyped | nil) + def http_scheme?: (untyped uri) -> untyped + + def parsed_http_url: () -> (untyped | nil) + + def unix_scheme?: (untyped uri) -> untyped + class DetectedConfiguration + @friendly_name: untyped + + @value: untyped + attr_reader friendly_name: untyped attr_reader value: untyped diff --git a/sig/datadog/core/configuration/ext.rbs b/sig/datadog/core/configuration/ext.rbs index 71b7acc0951..77f9eae8265 100644 --- a/sig/datadog/core/configuration/ext.rbs +++ b/sig/datadog/core/configuration/ext.rbs @@ -15,6 +15,7 @@ module Datadog end module Agent + ENV_DEFAULT_HOST: 'DD_AGENT_HOST' ENV_DEFAULT_PORT: 'DD_TRACE_AGENT_PORT' ENV_DEFAULT_URL: 'DD_TRACE_AGENT_URL' ENV_DEFAULT_TIMEOUT_SECONDS: 'DD_TRACE_AGENT_TIMEOUT_SECONDS' From 6ada623a264d8556414b496050983cf2a286ab4b Mon Sep 17 00:00:00 2001 From: Ivo Anjo Date: Thu, 2 Jan 2025 16:23:53 +0000 Subject: [PATCH 2/5] Move url building behavior from `AgentBaseUrl` to `AgentSettings` This is preparation to also share this behavior with profiling. --- .../configuration/agent_settings_resolver.rb | 18 ++++++ .../core/crashtracking/agent_base_url.rb | 14 +---- .../configuration/agent_settings_resolver.rbs | 7 +++ .../agent_settings_resolver_spec.rb | 57 +++++++++++++++++++ .../core/crashtracking/agent_base_url_spec.rb | 25 ++------ 5 files changed, 88 insertions(+), 33 deletions(-) diff --git a/lib/datadog/core/configuration/agent_settings_resolver.rb b/lib/datadog/core/configuration/agent_settings_resolver.rb index 3d407ce1afb..424f47c331e 100644 --- a/lib/datadog/core/configuration/agent_settings_resolver.rb +++ b/lib/datadog/core/configuration/agent_settings_resolver.rb @@ -32,8 +32,26 @@ def initialize(adapter: nil, ssl: nil, hostname: nil, port: nil, uds_path: nil, @timeout_seconds = timeout_seconds freeze end + + def url + case adapter + when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER + hostname = self.hostname + hostname = "[#{hostname}]" if hostname =~ IPV6_REGEXP + "#{ssl ? 'https' : 'http'}://#{hostname}:#{port}/" + when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER + "unix://#{uds_path}" + else + raise ArgumentError, "Unexpected adapter: #{adapter}" + end + end end + # IPv6 regular expression from + # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses + # Does not match IPv4 addresses. + IPV6_REGEXP = /\A(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\z)/.freeze # rubocop:disable Layout/LineLength + def self.call(settings, logger: Datadog.logger) new(settings, logger: logger).send(:call) end diff --git a/lib/datadog/core/crashtracking/agent_base_url.rb b/lib/datadog/core/crashtracking/agent_base_url.rb index 74b59a1cda5..20a9a55129b 100644 --- a/lib/datadog/core/crashtracking/agent_base_url.rb +++ b/lib/datadog/core/crashtracking/agent_base_url.rb @@ -7,20 +7,8 @@ module Core module Crashtracking # This module provides a method to resolve the base URL of the agent module AgentBaseUrl - # IPv6 regular expression from - # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses - # Does not match IPv4 addresses. - IPV6_REGEXP = /\A(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\z)/.freeze # rubocop:disable Layout/LineLength - def self.resolve(agent_settings) - case agent_settings.adapter - when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER - hostname = agent_settings.hostname - hostname = "[#{hostname}]" if hostname =~ IPV6_REGEXP - "#{agent_settings.ssl ? 'https' : 'http'}://#{hostname}:#{agent_settings.port}/" - when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER - "unix://#{agent_settings.uds_path}" - end + agent_settings.url end end end diff --git a/sig/datadog/core/configuration/agent_settings_resolver.rbs b/sig/datadog/core/configuration/agent_settings_resolver.rbs index a4ea2830ddb..e3a2472abf4 100644 --- a/sig/datadog/core/configuration/agent_settings_resolver.rbs +++ b/sig/datadog/core/configuration/agent_settings_resolver.rbs @@ -11,6 +11,8 @@ module Datadog attr_reader port: untyped attr_reader uds_path: untyped attr_reader timeout_seconds: untyped + + def url: () -> ::String end @settings: untyped @@ -24,6 +26,11 @@ module Datadog @mixed_http_and_uds: untyped @parsed_url: untyped + # IPv6 regular expression from + # https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses + # Does not match IPv4 addresses. + IPV6_REGEXP: ::Regexp + def self.call: (untyped settings, ?logger: untyped) -> untyped private diff --git a/spec/datadog/core/configuration/agent_settings_resolver_spec.rb b/spec/datadog/core/configuration/agent_settings_resolver_spec.rb index ef2a6766ea1..c057e520739 100644 --- a/spec/datadog/core/configuration/agent_settings_resolver_spec.rb +++ b/spec/datadog/core/configuration/agent_settings_resolver_spec.rb @@ -803,4 +803,61 @@ end end end + + describe 'url' do + context 'when using HTTP adapter' do + before do + datadog_settings.agent.host = 'example.com' + datadog_settings.agent.port = 8080 + end + + context 'when SSL is enabled' do + before { datadog_settings.agent.use_ssl = true } + + it 'returns the correct base URL' do + expect(resolver.url).to eq('https://example.com:8080/') + end + end + + context 'when SSL is disabled' do + before { datadog_settings.agent.use_ssl = false } + + it 'returns the correct base URL' do + expect(resolver.url).to eq('http://example.com:8080/') + end + end + + context 'when hostname is an IPv4 address' do + before { datadog_settings.agent.host = '1.2.3.4' } + + it 'returns the correct base URL' do + expect(resolver.url).to eq('http://1.2.3.4:8080/') + end + end + + context 'when hostname is an IPv6 address' do + before { datadog_settings.agent.host = '1234:1234::1' } + + it 'returns the correct base URL' do + expect(resolver.url).to eq('http://[1234:1234::1]:8080/') + end + end + end + + context 'when using UnixSocket adapter' do + before { datadog_settings.agent.uds_path = '/var/run/datadog.sock' } + + it 'returns the correct base URL' do + expect(resolver.url).to eq('unix:///var/run/datadog.sock') + end + end + + context 'when using an unknown adapter' do + it 'raises an exception' do + agent_settings = Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new(adapter: :unknown) + + expect { agent_settings.url }.to raise_error(ArgumentError, /Unexpected adapter/) + end + end + end end diff --git a/spec/datadog/core/crashtracking/agent_base_url_spec.rb b/spec/datadog/core/crashtracking/agent_base_url_spec.rb index 407b74daf26..4a8145208cf 100644 --- a/spec/datadog/core/crashtracking/agent_base_url_spec.rb +++ b/spec/datadog/core/crashtracking/agent_base_url_spec.rb @@ -8,8 +8,7 @@ context 'when using HTTP adapter' do context 'when SSL is enabled' do let(:agent_settings) do - instance_double( - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings, + Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( adapter: Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER, ssl: true, hostname: 'example.com', @@ -24,8 +23,7 @@ context 'when SSL is disabled' do let(:agent_settings) do - instance_double( - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings, + Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( adapter: Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER, ssl: false, hostname: 'example.com', @@ -40,8 +38,7 @@ context 'when hostname is an IPv4 address' do let(:agent_settings) do - instance_double( - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings, + Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( adapter: Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER, ssl: false, hostname: '1.2.3.4', @@ -56,8 +53,7 @@ context 'when hostname is an IPv6 address' do let(:agent_settings) do - instance_double( - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings, + Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( adapter: Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER, ssl: false, hostname: '1234:1234::1', @@ -73,8 +69,7 @@ context 'when using UnixSocket adapter' do let(:agent_settings) do - instance_double( - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings, + Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( adapter: Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER, uds_path: '/var/run/datadog.sock' ) @@ -84,15 +79,5 @@ expect(described_class.resolve(agent_settings)).to eq('unix:///var/run/datadog.sock') end end - - context 'when using unknownm adapter' do - let(:agent_settings) do - instance_double(Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings, adapter: 'unknown') - end - - it 'returns nil' do - expect(described_class.resolve(agent_settings)).to be_nil - end - end end end From 65c92d284f8eb34039d515746c9894c21a403f76 Mon Sep 17 00:00:00 2001 From: Ivo Anjo Date: Thu, 2 Jan 2025 17:28:45 +0000 Subject: [PATCH 3/5] Refactor crashtracking to use `AgentSettings#url` The behavior from the old `AgentBaseUrl` is now contained in `AgentSettings` so we can clean up the extra logic. --- .../core/crashtracking/agent_base_url.rb | 16 ---- lib/datadog/core/crashtracking/component.rb | 4 +- .../core/crashtracking/agent_base_url.rbs | 10 --- .../core/crashtracking/agent_base_url_spec.rb | 83 ------------------- .../core/crashtracking/component_spec.rb | 34 ++------ 5 files changed, 8 insertions(+), 139 deletions(-) delete mode 100644 lib/datadog/core/crashtracking/agent_base_url.rb delete mode 100644 sig/datadog/core/crashtracking/agent_base_url.rbs delete mode 100644 spec/datadog/core/crashtracking/agent_base_url_spec.rb diff --git a/lib/datadog/core/crashtracking/agent_base_url.rb b/lib/datadog/core/crashtracking/agent_base_url.rb deleted file mode 100644 index 20a9a55129b..00000000000 --- a/lib/datadog/core/crashtracking/agent_base_url.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -require_relative '../configuration/ext' - -module Datadog - module Core - module Crashtracking - # This module provides a method to resolve the base URL of the agent - module AgentBaseUrl - def self.resolve(agent_settings) - agent_settings.url - end - end - end - end -end diff --git a/lib/datadog/core/crashtracking/component.rb b/lib/datadog/core/crashtracking/component.rb index ee04e1c5cc8..460a7974bc5 100644 --- a/lib/datadog/core/crashtracking/component.rb +++ b/lib/datadog/core/crashtracking/component.rb @@ -3,7 +3,6 @@ require 'libdatadog' require_relative 'tag_builder' -require_relative 'agent_base_url' require_relative '../utils/only_once' require_relative '../utils/at_fork_monkey_patch' @@ -31,8 +30,7 @@ class Component def self.build(settings, agent_settings, logger:) tags = TagBuilder.call(settings) - agent_base_url = AgentBaseUrl.resolve(agent_settings) - logger.warn('Missing agent base URL; cannot enable crash tracking') unless agent_base_url + agent_base_url = agent_settings.url ld_library_path = ::Libdatadog.ld_library_path logger.warn('Missing ld_library_path; cannot enable crash tracking') unless ld_library_path diff --git a/sig/datadog/core/crashtracking/agent_base_url.rbs b/sig/datadog/core/crashtracking/agent_base_url.rbs deleted file mode 100644 index 3a2c77f8e85..00000000000 --- a/sig/datadog/core/crashtracking/agent_base_url.rbs +++ /dev/null @@ -1,10 +0,0 @@ -module Datadog - module Core - module Crashtracking - module AgentBaseUrl - IPV6_REGEXP: Regexp - def self.resolve: (Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings) -> ::String? - end - end - end -end diff --git a/spec/datadog/core/crashtracking/agent_base_url_spec.rb b/spec/datadog/core/crashtracking/agent_base_url_spec.rb deleted file mode 100644 index 4a8145208cf..00000000000 --- a/spec/datadog/core/crashtracking/agent_base_url_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'datadog/core/crashtracking/agent_base_url' - -RSpec.describe Datadog::Core::Crashtracking::AgentBaseUrl do - describe '.resolve' do - context 'when using HTTP adapter' do - context 'when SSL is enabled' do - let(:agent_settings) do - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( - adapter: Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER, - ssl: true, - hostname: 'example.com', - port: 8080 - ) - end - - it 'returns the correct base URL' do - expect(described_class.resolve(agent_settings)).to eq('https://example.com:8080/') - end - end - - context 'when SSL is disabled' do - let(:agent_settings) do - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( - adapter: Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER, - ssl: false, - hostname: 'example.com', - port: 8080 - ) - end - - it 'returns the correct base URL' do - expect(described_class.resolve(agent_settings)).to eq('http://example.com:8080/') - end - end - - context 'when hostname is an IPv4 address' do - let(:agent_settings) do - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( - adapter: Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER, - ssl: false, - hostname: '1.2.3.4', - port: 8080 - ) - end - - it 'returns the correct base URL' do - expect(described_class.resolve(agent_settings)).to eq('http://1.2.3.4:8080/') - end - end - - context 'when hostname is an IPv6 address' do - let(:agent_settings) do - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( - adapter: Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER, - ssl: false, - hostname: '1234:1234::1', - port: 8080 - ) - end - - it 'returns the correct base URL' do - expect(described_class.resolve(agent_settings)).to eq('http://[1234:1234::1]:8080/') - end - end - end - - context 'when using UnixSocket adapter' do - let(:agent_settings) do - Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings.new( - adapter: Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER, - uds_path: '/var/run/datadog.sock' - ) - end - - it 'returns the correct base URL' do - expect(described_class.resolve(agent_settings)).to eq('unix:///var/run/datadog.sock') - end - end - end -end diff --git a/spec/datadog/core/crashtracking/component_spec.rb b/spec/datadog/core/crashtracking/component_spec.rb index 034bd5f441e..fcbef942d43 100644 --- a/spec/datadog/core/crashtracking/component_spec.rb +++ b/spec/datadog/core/crashtracking/component_spec.rb @@ -9,7 +9,9 @@ describe '.build' do let(:settings) { Datadog::Core::Configuration::Settings.new } - let(:agent_settings) { double('agent_settings') } + let(:agent_settings) do + instance_double(Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings) + end let(:tags) { { 'tag1' => 'value1' } } let(:agent_base_url) { 'agent_base_url' } let(:ld_library_path) { 'ld_library_path' } @@ -19,8 +21,7 @@ it 'creates a new instance of Component and starts it' do expect(Datadog::Core::Crashtracking::TagBuilder).to receive(:call).with(settings) .and_return(tags) - expect(Datadog::Core::Crashtracking::AgentBaseUrl).to receive(:resolve).with(agent_settings) - .and_return(agent_base_url) + expect(agent_settings).to receive(:url).and_return(agent_base_url) expect(::Libdatadog).to receive(:ld_library_path) .and_return(ld_library_path) expect(::Libdatadog).to receive(:path_to_crashtracking_receiver_binary) @@ -42,32 +43,13 @@ end end - context 'when missing `agent_base_url`' do - let(:agent_base_url) { nil } - - it 'returns nil' do - expect(Datadog::Core::Crashtracking::TagBuilder).to receive(:call).with(settings) - .and_return(tags) - expect(Datadog::Core::Crashtracking::AgentBaseUrl).to receive(:resolve).with(agent_settings) - .and_return(agent_base_url) - expect(::Libdatadog).to receive(:ld_library_path) - .and_return(ld_library_path) - expect(::Libdatadog).to receive(:path_to_crashtracking_receiver_binary) - .and_return(path_to_crashtracking_receiver_binary) - expect(logger).to receive(:warn).with(/cannot enable crash tracking/) - - expect(described_class.build(settings, agent_settings, logger: logger)).to be_nil - end - end - context 'when missing `ld_library_path`' do let(:ld_library_path) { nil } it 'returns nil' do expect(Datadog::Core::Crashtracking::TagBuilder).to receive(:call).with(settings) .and_return(tags) - expect(Datadog::Core::Crashtracking::AgentBaseUrl).to receive(:resolve).with(agent_settings) - .and_return(agent_base_url) + expect(agent_settings).to receive(:url).and_return(agent_base_url) expect(::Libdatadog).to receive(:ld_library_path) .and_return(ld_library_path) expect(::Libdatadog).to receive(:path_to_crashtracking_receiver_binary) @@ -84,8 +66,7 @@ it 'returns nil' do expect(Datadog::Core::Crashtracking::TagBuilder).to receive(:call).with(settings) .and_return(tags) - expect(Datadog::Core::Crashtracking::AgentBaseUrl).to receive(:resolve).with(agent_settings) - .and_return(agent_base_url) + expect(agent_settings).to receive(:url).and_return(agent_base_url) expect(::Libdatadog).to receive(:ld_library_path) .and_return(ld_library_path) expect(::Libdatadog).to receive(:path_to_crashtracking_receiver_binary) @@ -102,8 +83,7 @@ it 'returns an instance of Component that failed to start' do expect(Datadog::Core::Crashtracking::TagBuilder).to receive(:call).with(settings) .and_return(tags) - expect(Datadog::Core::Crashtracking::AgentBaseUrl).to receive(:resolve).with(agent_settings) - .and_return(agent_base_url) + expect(agent_settings).to receive(:url).and_return(agent_base_url) expect(::Libdatadog).to receive(:ld_library_path) .and_return(ld_library_path) expect(::Libdatadog).to receive(:path_to_crashtracking_receiver_binary) From e4f5e19d9007907c3c17e6f2c5bb871a950e6288 Mon Sep 17 00:00:00 2001 From: Ivo Anjo Date: Thu, 2 Jan 2025 17:32:49 +0000 Subject: [PATCH 4/5] [PROF-11078] Fix profiling exception when agent url is an ipv6 address **What does this PR do?** This PR builds atop #4237 and fixes a similar-ish issue in the profiler caused by the same mishandling of ipv6 addresses. In particular, when provided with an ipv6 address in the agent url, the profiler would fail with an exception: ``` $ env DD_AGENT_HOST=2001:db8:1::2 DD_PROFILING_ENABLED=true \ bundle exec ddprofrb exec ruby -e "sleep 2" dd-trace-rb/lib/datadog/profiling/http_transport.rb:27:in `initialize': Failed to initialize transport: invalid authority (ArgumentError) ``` **Motivation:** Luckily we didn't have any customers using this, as it fails immediately and loudly, but it's still a bug on a configuration that should be supported. **Additional Notes:** Since we had similar buggy logic copy-pasted in crashtracking and profiling (crashtracking had been fixed in #4237) I chose to extract out the relevant logic into the `AgentSettings` class, so that both can reuse it. **How to test the change?** I've added unit test coverage for this issue to profiling, and the snippet above can be used to end-to-end test it's working fine. Here's how it looks on my machine now: ``` E, [2025-01-02T17:32:32.398756 #359317] ERROR -- datadog: [datadog] (dd-trace-rb/lib/datadog/profiling/http_transport.rb:68:in `export') Failed to report profiling data (agent: http://[2001:db8:1::2]:8126/): failed ddog_prof_Exporter_send: error trying to connect: tcp connect error: Network is unreachable (os error 101): tcp connect error: Network is unreachable (os error 101): Network is unreachable (os error 101) ``` E.g. we correctly try to connect to the dummy address, and fail :) (Note: The error message is a bit ugly AND repeats itself a bit. That's being tracked separately in https://github.com/DataDog/libdatadog/issues/283 ) --- lib/datadog/profiling/http_transport.rb | 27 +------------------ sig/datadog/profiling/http_transport.rbs | 4 --- spec/datadog/profiling/http_transport_spec.rb | 13 +++++++++ 3 files changed, 14 insertions(+), 30 deletions(-) diff --git a/lib/datadog/profiling/http_transport.rb b/lib/datadog/profiling/http_transport.rb index 2c89c6548b7..9de76899494 100644 --- a/lib/datadog/profiling/http_transport.rb +++ b/lib/datadog/profiling/http_transport.rb @@ -13,13 +13,11 @@ class HttpTransport def initialize(agent_settings:, site:, api_key:, upload_timeout_seconds:) @upload_timeout_milliseconds = (upload_timeout_seconds * 1_000).to_i - validate_agent_settings(agent_settings) - @exporter_configuration = if agentless?(site, api_key) [:agentless, site, api_key].freeze else - [:agent, base_url_from(agent_settings)].freeze + [:agent, agent_settings.url].freeze end status, result = validate_exporter(exporter_configuration) @@ -75,29 +73,6 @@ def export(flush) private - def base_url_from(agent_settings) - case agent_settings.adapter - when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER - "#{agent_settings.ssl ? "https" : "http"}://#{agent_settings.hostname}:#{agent_settings.port}/" - when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER - "unix://#{agent_settings.uds_path}" - else - raise ArgumentError, "Unexpected adapter: #{agent_settings.adapter}" - end - end - - def validate_agent_settings(agent_settings) - supported_adapters = [ - Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER, - Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER - ] - unless supported_adapters.include?(agent_settings.adapter) - raise ArgumentError, - "Unsupported transport configuration for profiling: Adapter #{agent_settings.adapter} " \ - " is not supported" - end - end - def agentless?(site, api_key) site && api_key && Core::Environment::VariableHelpers.env_to_bool(Profiling::Ext::ENV_AGENTLESS, false) end diff --git a/sig/datadog/profiling/http_transport.rbs b/sig/datadog/profiling/http_transport.rbs index 8c58ea8180e..af663a225b7 100644 --- a/sig/datadog/profiling/http_transport.rbs +++ b/sig/datadog/profiling/http_transport.rbs @@ -19,10 +19,6 @@ module Datadog private - def base_url_from: (Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings agent_settings) -> ::String - - def validate_agent_settings: (Datadog::Core::Configuration::AgentSettingsResolver::AgentSettings agent_settings) -> void - def agentless?: (::String? site, ::String? api_key) -> bool def validate_exporter: (exporter_configuration_array exporter_configuration) -> [:ok | :error, ::String?] diff --git a/spec/datadog/profiling/http_transport_spec.rb b/spec/datadog/profiling/http_transport_spec.rb index 44423984fab..0cd98ba09e3 100644 --- a/spec/datadog/profiling/http_transport_spec.rb +++ b/spec/datadog/profiling/http_transport_spec.rb @@ -127,6 +127,19 @@ http_transport end end + + context "when hostname is an ipv6 address" do + let(:hostname) { "1234:1234::1" } + + it "provides the correct ipv6 address-safe url to the exporter" do + expect(described_class) + .to receive(:_native_validate_exporter) + .with([:agent, "http://[1234:1234::1]:12345/"]) + .and_return([:ok, nil]) + + http_transport + end + end end context "when additionally site and api_key are provided" do From 8aba599f784aefd64a1a7bfbec3d315820bbe285 Mon Sep 17 00:00:00 2001 From: Ivo Anjo Date: Thu, 2 Jan 2025 17:56:59 +0000 Subject: [PATCH 5/5] Implement `==` for new `AgentSettings` class Forgot this one, some of our tests relied on it! --- .../core/configuration/agent_settings_resolver.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/datadog/core/configuration/agent_settings_resolver.rb b/lib/datadog/core/configuration/agent_settings_resolver.rb index 424f47c331e..5ced057274a 100644 --- a/lib/datadog/core/configuration/agent_settings_resolver.rb +++ b/lib/datadog/core/configuration/agent_settings_resolver.rb @@ -45,6 +45,16 @@ def url raise ArgumentError, "Unexpected adapter: #{adapter}" end end + + def ==(other) + self.class == other.class && + adapter == other.adapter && + ssl == other.ssl && + hostname == other.hostname && + port == other.port && + uds_path == other.uds_path && + timeout_seconds == other.timeout_seconds + end end # IPv6 regular expression from