diff --git a/lib/instana/span_filtering/configuration.rb b/lib/instana/span_filtering/configuration.rb index 9b14f568..642a922c 100644 --- a/lib/instana/span_filtering/configuration.rb +++ b/lib/instana/span_filtering/configuration.rb @@ -83,16 +83,24 @@ def check_discovery def process_discovery_config(discovery) # Check if tracing configuration exists in the discovery response tracing_config = discovery['tracing'] - return unless tracing_config && tracing_config['filter'] + return unless tracing_config - filter_config = tracing_config['filter'] - @deactivated = filter_config['deactivate'] == true + # Process filter configuration + if tracing_config['filter'] + filter_config = tracing_config['filter'] + @deactivated = filter_config['deactivate'] == true - # Process include rules - process_rules(filter_config['include'], true) if filter_config['include'] + # Process include rules + process_rules(filter_config['include'], true) if filter_config['include'] + + # Process exclude rules + process_rules(filter_config['exclude'], false) if filter_config['exclude'] + end - # Process exclude rules - process_rules(filter_config['exclude'], false) if filter_config['exclude'] + # Process disable configuration + if tracing_config['disable'] + process_disable_config(tracing_config['disable']) + end # Return true to indicate successful processing true @@ -117,16 +125,24 @@ def load_from_yaml # Support both "tracing" and "com.instana.tracing" as top-level keys tracing_config = yaml_content['tracing'] || yaml_content['com.instana.tracing'] ::Instana.logger.warn(TRACING_CONFIG_WARNING) if yaml_content.key?('com.instana.tracing') - return unless tracing_config && tracing_config['filter'] + return unless tracing_config - filter_config = tracing_config['filter'] - @deactivated = filter_config['deactivate'] == true + # Process filter configuration + if tracing_config['filter'] + filter_config = tracing_config['filter'] + @deactivated = filter_config['deactivate'] == true - # Process include rules - process_rules(filter_config['include'], true) if filter_config['include'] + # Process include rules + process_rules(filter_config['include'], true) if filter_config['include'] - # Process exclude rules - process_rules(filter_config['exclude'], false) if filter_config['exclude'] + # Process exclude rules + process_rules(filter_config['exclude'], false) if filter_config['exclude'] + end + + # Process disable configuration + if tracing_config['disable'] + process_disable_config(tracing_config['disable']) + end rescue => e Instana.logger.warn("Failed to load span filtering configuration from YAML: #{e.message}") end @@ -149,6 +165,10 @@ def load_from_env_vars process_env_suppression(parts[3..].join('_'), value) end end + + return unless !ENV["INSTANA_TRACING_DISABLE"].nil? && !%w[True true 1].include?(ENV["INSTANA_TRACING_DISABLE"]) + + process_disable_config(ENV["INSTANA_TRACING_DISABLE"].split(',')) end # Process rules from YAML configuration @@ -205,6 +225,38 @@ def process_env_suppression(policy_name, value) suppression = %w[1 true True].include?(value) @exclude_rules[rule_index].suppression = suppression end + + # Process disable configuration from YAML or agent discovery + # @param disable_config [Array] The disable configuration array + def process_disable_config(disable_config) + return unless disable_config.is_a?(Array) + + disable_config.each do |item| + if item.is_a?(Hash) + item.each do |key, value| + if value == true + update_instana_config_for_disabled_technology(key) + end + end + elsif item.is_a?(String) + update_instana_config_for_disabled_technology(item) + end + end + end + + # Update Instana::Config for a disabled technology + # @param technology [String] The technology to disable + def update_instana_config_for_disabled_technology(technology) + tech_sym = technology.to_sym + + case tech_sym + when :redis + ::Instana.config[:redis][:enabled] = false + when :databases + # If databases category is disabled, also disable redis + ::Instana.config[:redis][:enabled] = false + end + end end end end diff --git a/test/instrumentation/redis_disable_test.rb b/test/instrumentation/redis_disable_test.rb new file mode 100644 index 00000000..ffc87ae4 --- /dev/null +++ b/test/instrumentation/redis_disable_test.rb @@ -0,0 +1,155 @@ +# (c) Copyright IBM Corp. 2025 + +require 'test_helper' + +class RedisDisableTest < Minitest::Test + def setup + @redis_url = if ENV.key?('REDIS_URL') + ENV['REDIS_URL'] + else + "redis://localhost:6379" + end + @redis_client = Redis.new(url: @redis_url) + + # Reset span filtering configuration before each test + ::Instana::SpanFiltering.reset + ::Instana::SpanFiltering.initialize + + # Reset Redis configuration + ::Instana.config[:redis] = { :enabled => true } + + clear_all! + end + + def teardown + # Reset span filtering configuration after each test + ::Instana::SpanFiltering.reset + ::Instana::SpanFiltering.initialize + + # Reset Redis configuration + ::Instana.config[:redis] = { :enabled => true } + end + + def test_redis_disabled_by_configuration + # Set Redis to be disabled via configuration + ::Instana.config[:redis][:enabled] = false + + # Execute Redis operation + Instana.tracer.in_span(:redis_test) do + @redis_client.set('hello', 'world') + end + + # Verify that only the parent span is reported (Redis span is disabled) + spans = ::Instana.processor.queued_spans + assert_equal 1, spans.length + assert_equal :sdk, spans[0][:n] + end + + def test_redis_disabled_via_databases_category + # Create a mock configuration that disables the databases category + config = ::Instana::SpanFiltering::Configuration.new + + # Simulate disabling databases category + config.send(:update_instana_config_for_disabled_technology, 'databases') + + # Execute Redis operation + Instana.tracer.in_span(:redis_test) do + @redis_client.set('hello', 'world') + end + + # Verify that only the parent span is reported (Redis span is disabled) + spans = ::Instana.processor.queued_spans + assert_equal 1, spans.length + assert_equal :sdk, spans[0][:n] + end + + def test_redis_disabled_via_yaml_config + # Create a test YAML configuration file + yaml_content = <<~YAML + tracing: + disable: + - redis + YAML + + File.write('test_redis_config.yaml', yaml_content) + ENV['INSTANA_CONFIG_PATH'] = 'test_redis_config.yaml' + + # Create a new configuration that should load from our YAML file + ::Instana::SpanFiltering::Configuration.new + + # Execute Redis operation + Instana.tracer.in_span(:redis_test) do + @redis_client.set('hello', 'world') + end + + # Verify that only the parent span is reported (Redis span is disabled) + spans = ::Instana.processor.queued_spans + assert_equal 1, spans.length + assert_equal :sdk, spans[0][:n] + ensure + # Remove test config file + File.unlink('test_redis_config.yaml') if File.exist?('test_redis_config.yaml') + ENV.delete('INSTANA_CONFIG_PATH') + end + + def test_redis_disabled_via_env_var + # Set environment variable to disable Redis + ENV['INSTANA_TRACING_DISABLE'] = 'redis' + + # Create a new configuration that should load from our environment variable + ::Instana::SpanFiltering::Configuration.new + + # Execute Redis operation + Instana.tracer.in_span(:redis_test) do + @redis_client.set('hello', 'world') + end + + # Verify that only the parent span is reported (Redis span is disabled) + spans = ::Instana.processor.queued_spans + assert_equal 1, spans.length + assert_equal :sdk, spans[0][:n] + ensure + ENV.delete('INSTANA_TRACING_DISABLE') + end + + def test_redis_disabled_via_databases_env_var + # Set environment variable to disable databases category + ENV['INSTANA_TRACING_DISABLE'] = 'databases' + + # Create a new configuration that should load from our environment variable + ::Instana::SpanFiltering::Configuration.new + + # Execute Redis operation + Instana.tracer.in_span(:redis_test) do + @redis_client.set('hello', 'world') + end + + # Verify that only the parent span is reported (Redis span is disabled) + spans = ::Instana.processor.queued_spans + assert_equal 1, spans.length + assert_equal :sdk, spans[0][:n] + ensure + ENV.delete('INSTANA_TRACING_DISABLE') + end + + def test_redis_not_disabled_by_default + # Execute Redis operation + Instana.tracer.in_span(:redis_test) do + @redis_client.set('hello', 'world') + end + + # Verify that both spans are reported (Redis span is not disabled) + spans = ::Instana.processor.queued_spans + assert_equal 2, spans.length + first_span, second_span = spans.to_a.reverse + assert_equal :sdk, first_span[:n] + assert_equal :redis, second_span[:n] + end + + private + + def clear_all! + ::Instana.processor.clear! + ::Instana.tracer.clear! + end +end diff --git a/test/instrumentation/redis_test.rb b/test/instrumentation/redis_test.rb index 7c7f8ae6..3a3d90d0 100644 --- a/test/instrumentation/redis_test.rb +++ b/test/instrumentation/redis_test.rb @@ -5,6 +5,7 @@ class RedisTest < Minitest::Test def setup + ::Instana.config[:redis] = { :enabled => true } if ENV.key?('REDIS_URL') @redis_url = ENV['REDIS_URL'] else diff --git a/test/span_filtering/configuration_test.rb b/test/span_filtering/configuration_test.rb index 6480bb5d..cbda4fe2 100644 --- a/test/span_filtering/configuration_test.rb +++ b/test/span_filtering/configuration_test.rb @@ -428,3 +428,241 @@ def running? end end end + +# Tests for Redis disabling configuration +class DisableConfigurationTest < Minitest::Test + def setup + # Clear any existing configuration + Instana::SpanFiltering.reset + + # Save original environment variables + @original_env = ENV.to_hash + + # Clear relevant environment variables + ENV.delete('INSTANA_CONFIG_PATH') + ENV.delete('INSTANA_TRACING_DISABLE') + + # Save original agent + @original_agent = ::Instana.agent + + # Reset Redis configuration + ::Instana.config[:redis] = { :enabled => true } + end + + def teardown + # Restore original environment variables + ENV.clear + @original_env.each { |k, v| ENV[k] = v } + + # Reset configuration + Instana::SpanFiltering.reset + + # Remove any test config files + File.unlink('test_config.yaml') if File.exist?('test_config.yaml') + + # Restore original agent + ::Instana.instance_variable_set(:@agent, @original_agent) + + # Reset Redis configuration + ::Instana.config[:redis] = { :enabled => true } + end + + def test_redis_disabled_via_yaml_string_format + # Create a test YAML configuration file with string format + yaml_content = <<~YAML + tracing: + disable: + - "redis" + YAML + + File.write('test_config.yaml', yaml_content) + ENV['INSTANA_CONFIG_PATH'] = 'test_config.yaml' + + Instana::SpanFiltering::Configuration.new + + refute ::Instana.config[:redis][:enabled], "Redis should be disabled via YAML string format" + end + + def test_redis_disabled_via_yaml_hash_format + # Create a test YAML configuration file with hash format + yaml_content = <<~YAML + tracing: + disable: + - redis: true + YAML + + File.write('test_config.yaml', yaml_content) + ENV['INSTANA_CONFIG_PATH'] = 'test_config.yaml' + + Instana::SpanFiltering::Configuration.new + + refute ::Instana.config[:redis][:enabled], "Redis should be disabled via YAML hash format" + end + + def test_redis_not_disabled_when_set_to_false + # Create a test YAML configuration file with hash format set to false + yaml_content = <<~YAML + tracing: + disable: + - redis: false + YAML + + File.write('test_config.yaml', yaml_content) + ENV['INSTANA_CONFIG_PATH'] = 'test_config.yaml' + + Instana::SpanFiltering::Configuration.new + + assert ::Instana.config[:redis][:enabled], "Redis should not be disabled when explicitly set to false" + end + + def test_redis_disabled_via_databases_category_yaml + # Create a test YAML configuration file with databases category + yaml_content = <<~YAML + tracing: + disable: + - databases: true + YAML + + File.write('test_config.yaml', yaml_content) + ENV['INSTANA_CONFIG_PATH'] = 'test_config.yaml' + + Instana::SpanFiltering::Configuration.new + + refute ::Instana.config[:redis][:enabled], "Redis should be disabled when databases category is disabled" + end + + def test_redis_disabled_via_env_var_specific + # Set environment variable to disable Redis specifically + ENV['INSTANA_TRACING_DISABLE'] = 'redis' + + Instana::SpanFiltering::Configuration.new + + refute ::Instana.config[:redis][:enabled], "Redis should be disabled via environment variable" + end + + def test_redis_disabled_via_env_var_multiple + # Set environment variable to disable multiple technologies including Redis + ENV['INSTANA_TRACING_DISABLE'] = 'http,redis,mysql' + + Instana::SpanFiltering::Configuration.new + + refute ::Instana.config[:redis][:enabled], "Redis should be disabled when specified in a comma-separated list" + end + + def test_redis_disabled_via_agent_discovery_string_format + # Create a mock agent with discovery value using string format + discovery_value = { + 'tracing' => { + 'disable' => [{'redis' => true}] + } + } + + mock_agent = Minitest::Mock.new + mock_agent.expect(:delegate, mock_agent) + mock_agent.expect(:discovery_value, discovery_value) + + ::Instana.instance_variable_set(:@agent, mock_agent) + + Instana::SpanFiltering::Configuration.new + + refute ::Instana.config[:redis][:enabled], "Redis should be disabled via agent discovery string format" + mock_agent.verify + end + + def test_redis_disabled_via_agent_discovery_hash_format + # Create a mock agent with discovery value using hash format + discovery_value = { + 'tracing' => { + 'disable' => [{'redis' => true}] + } + } + + mock_agent = Minitest::Mock.new + mock_agent.expect(:delegate, mock_agent) + mock_agent.expect(:discovery_value, discovery_value) + + ::Instana.instance_variable_set(:@agent, mock_agent) + + Instana::SpanFiltering::Configuration.new + + refute ::Instana.config[:redis][:enabled], "Redis should be disabled via agent discovery hash format" + mock_agent.verify + end + + def test_redis_disabled_via_agent_discovery_databases + # Create a mock agent with discovery value disabling databases category + discovery_value = { + 'tracing' => { + 'disable' => [{'databases' => true}] + } + } + + mock_agent = Minitest::Mock.new + mock_agent.expect(:delegate, mock_agent) + mock_agent.expect(:discovery_value, discovery_value) + + ::Instana.instance_variable_set(:@agent, mock_agent) + + Instana::SpanFiltering::Configuration.new + + refute ::Instana.config[:redis][:enabled], "Redis should be disabled when databases category is disabled via agent discovery" + mock_agent.verify + end + + def test_yaml_config_takes_precedence_over_agent_discovery + # Create a test YAML configuration file that doesn't disable Redis + yaml_content = <<~YAML + tracing: + filter: + include: + - name: include-all + attributes: + - key: type + values: ["*"] + match_type: strict + YAML + + File.write('test_config.yaml', yaml_content) + ENV['INSTANA_CONFIG_PATH'] = 'test_config.yaml' + + # Create a mock agent with discovery value that would disable Redis + discovery_value = { + 'tracing' => { + 'disable' => [{'redis' => true}] + } + } + + mock_agent = Minitest::Mock.new + mock_agent.expect(:delegate, mock_agent) + # This discovery value should not be used since YAML config is loaded first + mock_agent.expect(:discovery_value, discovery_value) + + ::Instana.instance_variable_set(:@agent, mock_agent) + + Instana::SpanFiltering::Configuration.new + + # Redis should not be disabled because YAML config takes precedence + # and doesn't have any disable directives + assert ::Instana.config[:redis][:enabled], "YAML config should take precedence over agent discovery" + end + + def test_env_var_takes_precedence_over_yaml_config + # Create a test YAML configuration file that doesn't disable Redis + yaml_content = <<~YAML + tracing: + disable: + - http: true + YAML + + File.write('test_config.yaml', yaml_content) + ENV['INSTANA_CONFIG_PATH'] = 'test_config.yaml' + + # Set environment variable to disable Redis + ENV['INSTANA_TRACING_DISABLE'] = 'redis' + + Instana::SpanFiltering::Configuration.new + + # Redis should be disabled because env var takes precedence + refute ::Instana.config[:redis][:enabled], "Environment variable should take precedence over YAML config" + end +end