diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index 522b7a0e117..d9888f23df4 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java @@ -135,7 +135,8 @@ private enum AgentFeature { AGENTLESS_LOG_SUBMISSION(GeneralConfig.AGENTLESS_LOG_SUBMISSION_ENABLED, false), APP_LOGS_COLLECTION(GeneralConfig.APP_LOGS_COLLECTION_ENABLED, false), LLMOBS(LlmObsConfig.LLMOBS_ENABLED, false), - LLMOBS_AGENTLESS(LlmObsConfig.LLMOBS_AGENTLESS_ENABLED, false); + LLMOBS_AGENTLESS(LlmObsConfig.LLMOBS_AGENTLESS_ENABLED, false), + FEATURE_FLAGS(FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED, true); private final String configKey; private final String systemProp; @@ -284,7 +285,7 @@ public static void start( agentlessLogSubmissionEnabled = isFeatureEnabled(AgentFeature.AGENTLESS_LOG_SUBMISSION); appLogsCollectionEnabled = isFeatureEnabled(AgentFeature.APP_LOGS_COLLECTION); llmObsEnabled = isFeatureEnabled(AgentFeature.LLMOBS); - featureFlaggingEnabled = isFeatureFlaggingEnabled(); + featureFlaggingEnabled = isFeatureEnabled(AgentFeature.FEATURE_FLAGS); // setup writers when llmobs is enabled to accomodate apm and llmobs if (llmObsEnabled) { @@ -1796,45 +1797,6 @@ private static boolean isFeatureEnabled(AgentFeature feature) { } } - private static boolean isFeatureFlaggingEnabled() { - final Boolean providerEnabled = - featureFlaggingBooleanSetting(FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED); - final String configurationSource = - featureFlaggingSetting(FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE); - final Boolean legacyProviderEnabled = - featureFlaggingBooleanSetting(FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED); - - return FeatureFlaggingConfig.resolveConfiguration( - providerEnabled, configurationSource, legacyProviderEnabled) - .isEnabled(); - } - - @SuppressFBWarnings( - value = "NP_BOOLEAN_RETURN_NULL", - justification = "A null value preserves the distinction between absent and explicitly false") - private static Boolean featureFlaggingBooleanSetting(final String configKey) { - final String value = featureFlaggingSetting(configKey); - if (value == null) { - return null; - } - return Boolean.parseBoolean(value) || "1".equals(value); - } - - private static String featureFlaggingSetting(final String configKey) { - final String systemProperty = propertyNameToSystemPropertyName(configKey); - String value = SystemProperties.get(systemProperty); - if (value == null) { - value = getStableConfig(FLEET, configKey); - } - if (value == null) { - value = ddGetEnv(systemProperty); - } - if (value == null) { - value = getStableConfig(LOCAL, configKey); - } - return value; - } - /** * @see datadog.trace.api.ProductActivation#fromString(String) */ diff --git a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java index 1dae022b06c..63068a7ceb0 100644 --- a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java +++ b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java @@ -347,6 +347,9 @@ public static Set getEnabledSystems() { if (cfg.isLlmObsEnabled()) { enabledSystems.add(InstrumenterModule.TargetSystem.LLMOBS); } + if (cfg.isFeatureFlaggingEnabled()) { + enabledSystems.add(InstrumenterModule.TargetSystem.FEATURE_FLAGS); + } return enabledSystems; } diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterModule.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterModule.java index 6baeca12b62..ab2c4d78637 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterModule.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterModule.java @@ -44,6 +44,7 @@ public abstract class InstrumenterModule implements Instrumenter { *
  • {@link TargetSystem#USM usm} *
  • {@link TargetSystem#CONTEXT_TRACKING context-tracking} *
  • {@link TargetSystem#RASP rasp} + *
  • {@link TargetSystem#FEATURE_FLAGS feature-flags} * */ public enum TargetSystem { @@ -56,6 +57,7 @@ public enum TargetSystem { LLMOBS, CONTEXT_TRACKING, RASP, + FEATURE_FLAGS, } private static final Logger log = LoggerFactory.getLogger(InstrumenterModule.class); diff --git a/dd-java-agent/instrumentation/graal/graal-native-image-20.0/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java b/dd-java-agent/instrumentation/graal/graal-native-image-20.0/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java index 8c8c6c06cb0..13998669551 100644 --- a/dd-java-agent/instrumentation/graal/graal-native-image-20.0/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java +++ b/dd-java-agent/instrumentation/graal/graal-native-image-20.0/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java @@ -85,6 +85,7 @@ public static void onEnter(@Advice.Argument(value = 0, readOnly = false) String[ + "datadog.trace.api.config.OtlpConfig$Protocol:build_time," + "datadog.trace.api.config.OtlpConfig$Temporality:build_time," + "datadog.trace.api.EventTracker:build_time," + + "datadog.trace.api.FeatureFlaggingSource:build_time," + "datadog.trace.api.InstrumenterConfig:build_time," + "datadog.trace.api.Functions:build_time," + "datadog.trace.api.Functions$NewInstance:build_time," diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index b1707b77a71..3471750c347 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -750,8 +750,6 @@ import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS; import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS; import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED; -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.isSupportedConfigurationSource; -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.resolveConfiguration; import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY; import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.MULTI_WRITER_TYPE; import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.OTLP_WRITER_TYPE; @@ -769,7 +767,6 @@ import datadog.trace.api.config.OtlpConfig; import datadog.trace.api.config.ProfilingConfig; import datadog.trace.api.config.TracerConfig; -import datadog.trace.api.featureflag.config.FeatureFlaggingConfig; import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.IastDetectionMode; import datadog.trace.api.iast.telemetry.Verbosity; @@ -1258,8 +1255,6 @@ public static String getHostName() { private final int remoteConfigMaxExtraServices; - private final boolean featureFlaggingProviderEnabled; - private final String featureFlaggingConfigurationSource; private final String featureFlaggingConfigurationSourceAgentlessBaseUrl; private final int featureFlaggingConfigurationSourcePollIntervalSeconds; private final int featureFlaggingConfigurationSourceRequestTimeoutSeconds; @@ -2951,33 +2946,19 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) configProvider.getInteger( REMOTE_CONFIG_MAX_EXTRA_SERVICES, DEFAULT_REMOTE_CONFIG_MAX_EXTRA_SERVICES); - final Boolean configuredFeatureFlaggingProviderEnabled = - configProvider.getBoolean(FEATURE_FLAGS_ENABLED); - final Boolean legacyFeatureFlaggingProviderEnabled = - configProvider.getBoolean(EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED); - final String configuredFeatureFlaggingConfigurationSource = - configProvider.isSet(FEATURE_FLAGS_CONFIGURATION_SOURCE) - ? configProvider.getString(FEATURE_FLAGS_CONFIGURATION_SOURCE) - : null; - final FeatureFlaggingConfig.Resolution resolvedFeatureFlaggingConfiguration = - resolveConfiguration( - configuredFeatureFlaggingProviderEnabled, - configuredFeatureFlaggingConfigurationSource, - legacyFeatureFlaggingProviderEnabled); - if (legacyFeatureFlaggingProviderEnabled != null) { + if (configProvider.getBoolean(EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED) != null) { log.warn( "Setting {} is deprecated. Use {} and {} instead.", EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED, FEATURE_FLAGS_ENABLED, FEATURE_FLAGS_CONFIGURATION_SOURCE); } - if (!isSupportedConfigurationSource(configuredFeatureFlaggingConfigurationSource)) { + if (instrumenterConfig.getFeatureFlaggingConfigurationSource() + == FeatureFlaggingSource.INVALID) { log.warn( "Unsupported Feature Flagging configuration source; provider disabled: '{}'", - resolvedFeatureFlaggingConfiguration.getSource()); + configProvider.getString(FEATURE_FLAGS_CONFIGURATION_SOURCE)); } - featureFlaggingProviderEnabled = resolvedFeatureFlaggingConfiguration.isEnabled(); - featureFlaggingConfigurationSource = resolvedFeatureFlaggingConfiguration.getSource(); featureFlaggingConfigurationSourceAgentlessBaseUrl = configProvider.getStringNotEmpty( FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL, null); @@ -4940,11 +4921,12 @@ public int getRemoteConfigMaxExtraServices() { } public boolean isFeatureFlaggingProviderEnabled() { - return featureFlaggingProviderEnabled; + return instrumenterConfig.isFeatureFlaggingEnabled(); } public String getFeatureFlaggingConfigurationSource() { - return featureFlaggingConfigurationSource; + final FeatureFlaggingSource source = instrumenterConfig.getFeatureFlaggingConfigurationSource(); + return source == FeatureFlaggingSource.UNSET ? null : source.name().toLowerCase(Locale.ROOT); } public String getFeatureFlaggingConfigurationSourceAgentlessBaseUrl() { @@ -6916,9 +6898,9 @@ public String toString() { + ", remoteConfigIntegrityCheckEnabled=" + remoteConfigIntegrityCheckEnabled + ", featureFlaggingProviderEnabled=" - + featureFlaggingProviderEnabled + + isFeatureFlaggingProviderEnabled() + ", featureFlaggingConfigurationSource=" - + featureFlaggingConfigurationSource + + getFeatureFlaggingConfigurationSource() + ", featureFlaggingConfigurationSourceAgentlessBaseUrl=" + featureFlaggingConfigurationSourceAgentlessBaseUrl + ", featureFlaggingConfigurationSourcePollIntervalSeconds=" diff --git a/internal-api/src/main/java/datadog/trace/api/FeatureFlaggingSource.java b/internal-api/src/main/java/datadog/trace/api/FeatureFlaggingSource.java new file mode 100644 index 00000000000..bf8c9921822 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/FeatureFlaggingSource.java @@ -0,0 +1,13 @@ +package datadog.trace.api; + +/** The Feature Flags configuration source. */ +public enum FeatureFlaggingSource { + /** No source is configured. */ + UNSET, + /** The configured source is not supported. */ + INVALID, + /** The flag configuration is polled from the Datadog CDN. */ + AGENTLESS, + /** The flag configuration is received through Remote Configuration. */ + REMOTE_CONFIG +} diff --git a/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java b/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java index 76b65d43036..3c92d01665b 100644 --- a/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java +++ b/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java @@ -29,6 +29,10 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_OTEL_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_USM_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_WEBSOCKET_MESSAGES_ENABLED; +import static datadog.trace.api.FeatureFlaggingSource.AGENTLESS; +import static datadog.trace.api.FeatureFlaggingSource.INVALID; +import static datadog.trace.api.FeatureFlaggingSource.REMOTE_CONFIG; +import static datadog.trace.api.FeatureFlaggingSource.UNSET; import static datadog.trace.api.config.AppSecConfig.API_SECURITY_ENDPOINT_COLLECTION_ENABLED; import static datadog.trace.api.config.AppSecConfig.APPSEC_ENABLED; import static datadog.trace.api.config.AppSecConfig.APPSEC_RASP_ENABLED; @@ -98,8 +102,12 @@ import static datadog.trace.api.config.TraceInstrumentationConfig.UNSAFE_CLASS_INJECTION; import static datadog.trace.api.config.TraceInstrumentationConfig.VISITOR_CLASS_PARSING; import static datadog.trace.api.config.UsmConfig.USM_ENABLED; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED; import static datadog.trace.util.CollectionUtils.tryMakeImmutableList; import static datadog.trace.util.CollectionUtils.tryMakeImmutableSet; +import static java.lang.Boolean.FALSE; import static java.util.Collections.singletonList; import datadog.environment.JavaVirtualMachine; @@ -175,6 +183,8 @@ public class InstrumenterConfig { private final boolean usmEnabled; private final boolean telemetryEnabled; private final boolean llmObsEnabled; + private final boolean featureFlaggingEnabled; + private final FeatureFlaggingSource featureFlaggingConfigurationSource; private final String traceExtensionsPath; @@ -276,6 +286,21 @@ private InstrumenterConfig() { CODE_ORIGIN_FOR_SPANS_INTERFACE_SUPPORT, DEFAULT_CODE_ORIGIN_FOR_SPANS_INTERFACE_SUPPORT); traceEnabled = configProvider.getBoolean(TRACE_ENABLED, DEFAULT_TRACE_ENABLED); + // The product switch wins, then an explicit source, then the deprecated experimental toggle. + final Boolean featureFlagsEnabled = configProvider.getBoolean(FEATURE_FLAGS_ENABLED); + final FeatureFlaggingSource featureFlagsSource = featureFlaggingSource(configProvider); + final Boolean legacyFeatureFlagsEnabled = + configProvider.getBoolean(EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED); + if (FALSE.equals(featureFlagsEnabled) || featureFlagsSource != UNSET) { + featureFlaggingEnabled = !FALSE.equals(featureFlagsEnabled) && featureFlagsSource != INVALID; + featureFlaggingConfigurationSource = featureFlagsSource; + } else if (legacyFeatureFlagsEnabled != null) { + featureFlaggingEnabled = legacyFeatureFlagsEnabled; + featureFlaggingConfigurationSource = legacyFeatureFlagsEnabled ? REMOTE_CONFIG : UNSET; + } else { + featureFlaggingEnabled = true; + featureFlaggingConfigurationSource = AGENTLESS; + } traceOtelEnabled = configProvider.getBoolean(TRACE_OTEL_ENABLED, DEFAULT_TRACE_OTEL_ENABLED); metricsOtelEnabled = configProvider.getBoolean(METRICS_OTEL_ENABLED, DEFAULT_METRICS_OTEL_ENABLED); @@ -491,6 +516,30 @@ public boolean isTraceEnabled() { return traceEnabled; } + public boolean isFeatureFlaggingEnabled() { + return featureFlaggingEnabled; + } + + /** + * @return the Feature Flags configuration source, {@link FeatureFlaggingSource#UNSET} when none + * is configured or {@link FeatureFlaggingSource#INVALID} when unsupported. + */ + public FeatureFlaggingSource getFeatureFlaggingConfigurationSource() { + return featureFlaggingConfigurationSource; + } + + private static FeatureFlaggingSource featureFlaggingSource(final ConfigProvider configProvider) { + final String value = configProvider.getString(FEATURE_FLAGS_CONFIGURATION_SOURCE); + if (value == null || value.trim().isEmpty()) { + return UNSET; + } + final FeatureFlaggingSource source = + configProvider.getEnum( + FEATURE_FLAGS_CONFIGURATION_SOURCE, FeatureFlaggingSource.class, INVALID); + // UNSET is not a valid value to configure. + return source == UNSET ? INVALID : source; + } + public boolean isTraceOtelEnabled() { return traceOtelEnabled; } diff --git a/internal-api/src/test/java/datadog/trace/api/ConfigTest.java b/internal-api/src/test/java/datadog/trace/api/ConfigTest.java index 526e15a1a00..bbea22a73fa 100644 --- a/internal-api/src/test/java/datadog/trace/api/ConfigTest.java +++ b/internal-api/src/test/java/datadog/trace/api/ConfigTest.java @@ -3667,16 +3667,16 @@ void agentlessFeatureFlagTimingUsesPositiveConfiguredValues() throws Exception { } @TableTest({ - "scenario | value | expected ", - "not set | | agentless ", - "empty | '' | agentless ", - "blank | ' ' | agentless ", - "mixed case padded | ' ReMoTe_ConFiG ' | remote_config ", - "unknown source | not-a-real-source | not-a-real-source", - "padded uppercase | ' OFFLINE ' | offline " + "scenario | value | expected ", + "not set | | agentless ", + "empty | '' | agentless ", + "blank | ' ' | agentless ", + "case insensitive | ReMoTe_ConFiG | remote_config", + "padded | ' remote_config ' | invalid ", + "unknown source | not-a-real-source | invalid ", + "unset is not a value | unset | invalid " }) - void featureFlagConfigurationSourceNormalizesValue(String value, String expected) - throws Exception { + void featureFlagConfigurationSourceParsesValue(String value, String expected) throws Exception { Properties properties = new Properties(); if (value != null) { properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE, value); @@ -3697,8 +3697,8 @@ void featureFlagConfigurationSourceNormalizesValue(String value, String expected "source wins over legacy disabled | | remote_config | false | true | remote_config ", "provider disabled wins over legacy | false | agentless | true | false | agentless ", "legacy disabled wins over provider | true | | false | false | ", - "unknown source | | not-a-source | | false | not-a-source ", - "offline source | | offline | true | false | offline " + "unknown source | | not-a-source | | false | invalid ", + "offline source | | offline | true | false | invalid " }) void featureFlagConfigurationAppliesMigrationPrecedence( Boolean providerEnabled, diff --git a/internal-api/src/test/java/datadog/trace/api/InstrumenterConfigFeatureFlaggingTest.java b/internal-api/src/test/java/datadog/trace/api/InstrumenterConfigFeatureFlaggingTest.java new file mode 100644 index 00000000000..5a10341d0dc --- /dev/null +++ b/internal-api/src/test/java/datadog/trace/api/InstrumenterConfigFeatureFlaggingTest.java @@ -0,0 +1,51 @@ +package datadog.trace.api; + +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import datadog.trace.bootstrap.config.provider.ConfigProvider; +import java.util.Properties; +import org.tabletest.junit.TableTest; + +class InstrumenterConfigFeatureFlaggingTest { + + @TableTest({ + "scenario | enabled | source | legacy | resolvedEnabled | resolvedSource", + "defaults | | | | true | AGENTLESS ", + "blank source | | ' ' | | true | AGENTLESS ", + "legacy enabled | | | 'true' | true | REMOTE_CONFIG ", + "legacy disabled | | | 'false' | false | UNSET ", + "source wins over legacy | | 'agentless' | 'true' | true | AGENTLESS ", + "case insensitive source | | 'Remote_Config' | 'false' | true | REMOTE_CONFIG ", + "disabled wins over source | 'false' | 'agentless' | 'true' | false | AGENTLESS ", + "disabled without source | 'false' | | | false | UNSET ", + "unsupported source | | 'offline' | 'true' | false | INVALID ", + "padded source | | ' agentless ' | | false | INVALID ", + "unset is not a value | | 'unset' | | false | INVALID " + }) + void resolvesFeatureFlagging( + final String enabled, + final String source, + final String legacy, + final boolean resolvedEnabled, + final FeatureFlaggingSource resolvedSource) { + final Properties properties = new Properties(); + if (enabled != null) { + properties.setProperty(FEATURE_FLAGS_ENABLED, enabled); + } + if (source != null) { + properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE, source); + } + if (legacy != null) { + properties.setProperty(EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED, legacy); + } + + final InstrumenterConfig config = + new InstrumenterConfig(ConfigProvider.withPropertiesOverride(properties)); + + assertEquals(resolvedEnabled, config.isFeatureFlaggingEnabled()); + assertEquals(resolvedSource, config.getFeatureFlaggingConfigurationSource()); + } +} diff --git a/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfig.java b/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfig.java index 1edfd5bcb26..c772051e389 100644 --- a/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfig.java +++ b/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfig.java @@ -5,12 +5,6 @@ public class FeatureFlaggingConfig { public static final String CONFIGURATION_SOURCE_AGENTLESS = "agentless"; public static final String CONFIGURATION_SOURCE_REMOTE_CONFIG = "remote_config"; - private static final Resolution DISABLED_RESOLUTION = new Resolution(false, null); - private static final Resolution AGENTLESS_CONFIGURATION = - new Resolution(true, CONFIGURATION_SOURCE_AGENTLESS); - private static final Resolution REMOTE_CONFIG_CONFIGURATION = - new Resolution(true, CONFIGURATION_SOURCE_REMOTE_CONFIG); - public static final String FEATURE_FLAGS_ENABLED = "feature.flags.enabled"; public static final String EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED = "experimental.flagging.provider.enabled"; @@ -39,62 +33,4 @@ public class FeatureFlaggingConfig { "feature.flags.configuration.source.agentless.poll.interval.seconds"; public static final String FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS = "feature.flags.configuration.source.agentless.request.timeout.seconds"; - - public static Resolution resolveConfiguration( - final Boolean providerEnabled, - final String explicitSource, - final Boolean legacyProviderEnabled) { - final String normalizedSource = normalizeConfigurationSource(explicitSource); - if (Boolean.FALSE.equals(providerEnabled)) { - return new Resolution(false, normalizedSource); - } - if (normalizedSource != null) { - if (CONFIGURATION_SOURCE_AGENTLESS.equals(normalizedSource)) { - return AGENTLESS_CONFIGURATION; - } - if (CONFIGURATION_SOURCE_REMOTE_CONFIG.equals(normalizedSource)) { - return REMOTE_CONFIG_CONFIGURATION; - } - return new Resolution(false, normalizedSource); - } - if (legacyProviderEnabled != null) { - return legacyProviderEnabled ? REMOTE_CONFIG_CONFIGURATION : DISABLED_RESOLUTION; - } - return AGENTLESS_CONFIGURATION; - } - - public static boolean isSupportedConfigurationSource(final String source) { - final String normalizedSource = normalizeConfigurationSource(source); - return normalizedSource == null - || CONFIGURATION_SOURCE_AGENTLESS.equals(normalizedSource) - || CONFIGURATION_SOURCE_REMOTE_CONFIG.equals(normalizedSource); - } - - private static String normalizeConfigurationSource(final String source) { - if (source == null) { - return null; - } - final String normalized = source.trim().toLowerCase(java.util.Locale.ROOT); - return normalized.isEmpty() ? null : normalized; - } - - public static final class Resolution { - private final boolean enabled; - private final String source; - - private Resolution(final boolean enabled, final String source) { - this.enabled = enabled; - this.source = source; - } - - public boolean isEnabled() { - return enabled; - } - - public String getSource() { - return source; - } - } - - private FeatureFlaggingConfig() {} } diff --git a/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigTest.java b/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigTest.java deleted file mode 100644 index 16283869d71..00000000000 --- a/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package datadog.trace.api.featureflag.config; - -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.CONFIGURATION_SOURCE_AGENTLESS; -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.CONFIGURATION_SOURCE_REMOTE_CONFIG; -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.isSupportedConfigurationSource; -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.resolveConfiguration; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class FeatureFlaggingConfigTest { - - @Test - void appliesConfigurationPrecedence() { - assertResolution(true, CONFIGURATION_SOURCE_AGENTLESS, null, null, null); - assertResolution(true, CONFIGURATION_SOURCE_AGENTLESS, null, " ", null); - assertResolution(true, CONFIGURATION_SOURCE_REMOTE_CONFIG, null, null, true); - assertResolution(false, null, null, null, false); - assertResolution(true, CONFIGURATION_SOURCE_AGENTLESS, null, "agentless", true); - assertResolution(true, CONFIGURATION_SOURCE_REMOTE_CONFIG, null, " remote_CONFIG ", false); - assertResolution(false, CONFIGURATION_SOURCE_AGENTLESS, false, "agentless", true); - assertResolution(false, "invalid", null, "invalid", null); - assertResolution(false, "offline", null, " OFFLINE ", true); - } - - @Test - void recognizesSupportedExplicitSources() { - assertTrue(isSupportedConfigurationSource(null)); - assertTrue(isSupportedConfigurationSource(" ")); - assertTrue(isSupportedConfigurationSource("agentless")); - assertTrue(isSupportedConfigurationSource(" REMOTE_CONFIG ")); - assertFalse(isSupportedConfigurationSource("invalid")); - assertFalse(isSupportedConfigurationSource("offline")); - } - - private static void assertResolution( - final boolean enabled, - final String source, - final Boolean providerEnabled, - final String explicitSource, - final Boolean legacyProviderEnabled) { - final FeatureFlaggingConfig.Resolution resolution = - resolveConfiguration(providerEnabled, explicitSource, legacyProviderEnabled); - - assertEquals(enabled, resolution.isEnabled()); - if (source == null) { - assertNull(resolution.getSource()); - } else { - assertEquals(source, resolution.getSource()); - } - } -}