From 5bd54ec2cf53fe570624f46b0a16289c7797be58 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Fri, 17 Jul 2026 20:27:56 -0400 Subject: [PATCH 1/6] feat(ffe): preserve legacy source selection --- .../java/datadog/trace/bootstrap/Agent.java | 41 ++++++++- .../main/java/datadog/trace/api/Config.java | 66 +++++++++------ .../datadog/trace/api/ConfigTest.groovy | 36 +++++++- metadata/supported-configurations.json | 14 +++- .../feature-flagging-config/build.gradle.kts | 4 + .../config/FeatureFlaggingConfig.java | 4 +- .../FeatureFlaggingConfigurationSource.java | 83 +++++++++++++++++++ ...eatureFlaggingConfigurationSourceTest.java | 24 ++++++ 8 files changed, 239 insertions(+), 33 deletions(-) create mode 100644 products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSource.java create mode 100644 products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSourceTest.java 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 18e9128f5e0..dbdfd4af496 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 @@ -44,6 +44,7 @@ import datadog.trace.api.config.TracerConfig; import datadog.trace.api.config.UsmConfig; import datadog.trace.api.featureflag.config.FeatureFlaggingConfig; +import datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource; import datadog.trace.api.gateway.RequestContextSlot; import datadog.trace.api.gateway.SubscriptionService; import datadog.trace.api.git.EmbeddedGitInfoBuilder; @@ -135,7 +136,7 @@ private enum AgentFeature { APP_LOGS_COLLECTION(GeneralConfig.APP_LOGS_COLLECTION_ENABLED, false), LLMOBS(LlmObsConfig.LLMOBS_ENABLED, false), LLMOBS_AGENTLESS(LlmObsConfig.LLMOBS_AGENTLESS_ENABLED, false), - FEATURE_FLAGGING(FeatureFlaggingConfig.FLAGGING_PROVIDER_ENABLED, false); + FEATURE_FLAGGING(FeatureFlaggingConfig.FLAGGING_PROVIDER_ENABLED, true); private final String configKey; private final String systemProp; @@ -282,7 +283,7 @@ public static void start( agentlessLogSubmissionEnabled = isFeatureEnabled(AgentFeature.AGENTLESS_LOG_SUBMISSION); appLogsCollectionEnabled = isFeatureEnabled(AgentFeature.APP_LOGS_COLLECTION); llmObsEnabled = isFeatureEnabled(AgentFeature.LLMOBS); - featureFlaggingEnabled = isFeatureEnabled(AgentFeature.FEATURE_FLAGGING); + featureFlaggingEnabled = isFeatureFlaggingEnabled(); // setup writers when llmobs is enabled to accomodate apm and llmobs if (llmObsEnabled) { @@ -1648,6 +1649,42 @@ private static boolean isFeatureEnabled(AgentFeature feature) { } } + private static boolean isFeatureFlaggingEnabled() { + final Boolean providerEnabled = + featureFlaggingBooleanSetting(FeatureFlaggingConfig.FLAGGING_PROVIDER_ENABLED); + final String configurationSource = + featureFlaggingSetting(FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE); + final Boolean legacyProviderEnabled = + featureFlaggingBooleanSetting(FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED); + + return FeatureFlaggingConfigurationSource.resolve( + providerEnabled, configurationSource, legacyProviderEnabled) + != FeatureFlaggingConfigurationSource.OFFLINE; + } + + 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/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 368e666c701..c9dad49505a 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -86,7 +86,6 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_BODY_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_PARAMS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_EXPERIMENTATAL_JEE_SPLIT_BY_DEPLOYMENT; -import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE; import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS; import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS; import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_CLIENT_ERROR_STATUSES; @@ -726,10 +725,12 @@ import static datadog.trace.api.config.TracerConfig.WRITER_BAGGAGE_INJECT; import static datadog.trace.api.config.TracerConfig.WRITER_LINKS_INJECT; import static datadog.trace.api.config.TracerConfig.WRITER_TYPE; +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_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL; 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.FLAGGING_PROVIDER_ENABLED; import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY; import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.OTLP_WRITER_TYPE; import static datadog.trace.util.CollectionUtils.tryMakeImmutableList; @@ -746,6 +747,7 @@ import datadog.trace.api.config.OtlpConfig; import datadog.trace.api.config.ProfilingConfig; import datadog.trace.api.config.TracerConfig; +import datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource; import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.IastDetectionMode; import datadog.trace.api.iast.telemetry.Verbosity; @@ -1221,6 +1223,7 @@ public static String getHostName() { private final int remoteConfigMaxExtraServices; + private final boolean featureFlaggingProviderEnabled; private final String featureFlaggingConfigurationSource; private final String featureFlaggingConfigurationSourceAgentlessBaseUrl; private final int featureFlaggingConfigurationSourcePollIntervalSeconds; @@ -2851,10 +2854,35 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) configProvider.getInteger( REMOTE_CONFIG_MAX_EXTRA_SERVICES, DEFAULT_REMOTE_CONFIG_MAX_EXTRA_SERVICES); - featureFlaggingConfigurationSource = - normalizeFeatureFlaggingConfigurationSource( - configProvider.getString( - FEATURE_FLAGS_CONFIGURATION_SOURCE, DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE)); + final boolean configuredFeatureFlaggingProviderEnabled = + configProvider.getBoolean(FLAGGING_PROVIDER_ENABLED, true); + 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 FeatureFlaggingConfigurationSource resolvedFeatureFlaggingConfigurationSource = + FeatureFlaggingConfigurationSource.resolve( + configuredFeatureFlaggingProviderEnabled, + configuredFeatureFlaggingConfigurationSource, + legacyFeatureFlaggingProviderEnabled); + if (legacyFeatureFlaggingProviderEnabled != null) { + log.warn( + "Setting {} is deprecated. Use {} and {} instead.", + EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED, + FLAGGING_PROVIDER_ENABLED, + FEATURE_FLAGS_CONFIGURATION_SOURCE); + } + if (!FeatureFlaggingConfigurationSource.isSupportedExplicitSource( + configuredFeatureFlaggingConfigurationSource)) { + log.warn( + "Unsupported Feature Flagging configuration source: {}. Disabling Feature Flagging", + configuredFeatureFlaggingConfigurationSource); + } + featureFlaggingProviderEnabled = + resolvedFeatureFlaggingConfigurationSource != FeatureFlaggingConfigurationSource.OFFLINE; + featureFlaggingConfigurationSource = resolvedFeatureFlaggingConfigurationSource.value(); featureFlaggingConfigurationSourceAgentlessBaseUrl = configProvider.getStringNotEmpty( FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL, null); @@ -3797,28 +3825,6 @@ public boolean isInferredProxyPropagationEnabled() { return traceInferredProxyEnabled; } - private static String normalizeFeatureFlaggingConfigurationSource(final String source) { - if (source == null) { - return DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE; - } - final String normalized = source.trim().toLowerCase(Locale.ROOT); - if (normalized.isEmpty()) { - return DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE; - } - switch (normalized) { - case "agentless": - case "remote_config": - case "offline": - return normalized; - default: - log.warn( - "Unsupported Feature Flagging configuration source: {}. Defaulting to {}", - source, - DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE); - return DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE; - } - } - public boolean isBaggageExtract() { return tracePropagationStylesToExtract.contains(TracePropagationStyle.BAGGAGE); } @@ -4736,6 +4742,10 @@ public int getRemoteConfigMaxExtraServices() { return remoteConfigMaxExtraServices; } + public boolean isFeatureFlaggingProviderEnabled() { + return featureFlaggingProviderEnabled; + } + public String getFeatureFlaggingConfigurationSource() { return featureFlaggingConfigurationSource; } @@ -6584,6 +6594,8 @@ public String toString() { + remoteConfigMaxPayloadSize + ", remoteConfigIntegrityCheckEnabled=" + remoteConfigIntegrityCheckEnabled + + ", featureFlaggingProviderEnabled=" + + featureFlaggingProviderEnabled + ", featureFlaggingConfigurationSource=" + featureFlaggingConfigurationSource + ", featureFlaggingConfigurationSourceAgentlessBaseUrl=" diff --git a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy index 7227597108b..2050e25c284 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy @@ -57,9 +57,11 @@ import static datadog.trace.api.config.GeneralConfig.TAGS import static datadog.trace.api.config.GeneralConfig.TRACER_METRICS_IGNORED_RESOURCES import static datadog.trace.api.config.GeneralConfig.TRACE_OTEL_SEMANTICS_ENABLED import static datadog.trace.api.config.GeneralConfig.VERSION +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_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.FLAGGING_PROVIDER_ENABLED import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_CHECK_PERIOD import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_ENABLED import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_METRICS_CONFIGS @@ -3517,7 +3519,39 @@ class ConfigTest extends DDSpecification { "" | "agentless" " " | "agentless" " ReMoTe_ConFiG " | "remote_config" - "not-a-real-source" | "agentless" + "not-a-real-source" | "offline" + } + + def "feature flag configuration applies migration precedence"() { + setup: + Properties properties = new Properties() + if (providerEnabled != null) { + properties.setProperty(FLAGGING_PROVIDER_ENABLED, providerEnabled.toString()) + } + if (source != null) { + properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE, source) + } + if (legacyProviderEnabled != null) { + properties.setProperty(EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED, legacyProviderEnabled.toString()) + } + + when: + def config = new Config(ConfigProvider.withPropertiesOverride(properties)) + + then: + config.featureFlaggingProviderEnabled == expectedEnabled + config.featureFlaggingConfigurationSource == expectedSource + + where: + providerEnabled | source | legacyProviderEnabled | expectedEnabled | expectedSource + null | null | null | true | "agentless" + null | null | true | true | "remote_config" + null | null | false | false | "offline" + null | "agentless" | true | true | "agentless" + null | "remote_config" | false | true | "remote_config" + false | "agentless" | true | false | "offline" + true | null | false | false | "offline" + null | "offline" | null | false | "offline" } def "agentless feature flag timing falls back for non-positive values"() { diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 74a0bb8e67e..0e45f97cb0a 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1497,6 +1497,14 @@ "aliases": [] } ], + "DD_FEATURE_FLAGGING_PROVIDER_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "true", + "aliases": [] + } + ], "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE": [ { "version": "A", @@ -1533,7 +1541,7 @@ { "version": "A", "type": "boolean", - "default": "false", + "default": null, "aliases": [] } ], @@ -11986,5 +11994,7 @@ } ] }, - "deprecations": {} + "deprecations": { + "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED": "DD_FEATURE_FLAGGING_PROVIDER_ENABLED" + } } diff --git a/products/feature-flagging/feature-flagging-config/build.gradle.kts b/products/feature-flagging/feature-flagging-config/build.gradle.kts index 14109d8dfd9..1998ca33fef 100644 --- a/products/feature-flagging/feature-flagging-config/build.gradle.kts +++ b/products/feature-flagging/feature-flagging-config/build.gradle.kts @@ -10,3 +10,7 @@ extra["excludedClassesCoverage"] = listOf( // Constants-only holder — no executable logic to cover. "datadog.trace.api.featureflag.config.FeatureFlaggingConfig", ) + +dependencies { + testImplementation(libs.bundles.junit5) +} 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 379f6470c5b..686bf9e3a25 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 @@ -2,7 +2,9 @@ public class FeatureFlaggingConfig { - public static final String FLAGGING_PROVIDER_ENABLED = "experimental.flagging.provider.enabled"; + public static final String FLAGGING_PROVIDER_ENABLED = "feature.flagging.provider.enabled"; + public static final String EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED = + "experimental.flagging.provider.enabled"; /** * Opt-in gate for APM span enrichment with feature-flag evaluation metadata. DISTINCT from {@link diff --git a/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSource.java b/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSource.java new file mode 100644 index 00000000000..753cb959d91 --- /dev/null +++ b/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSource.java @@ -0,0 +1,83 @@ +package datadog.trace.api.featureflag.config; + +import java.util.Locale; + +/** Resolves Feature Flagging provider and delivery-source configuration. */ +public enum FeatureFlaggingConfigurationSource { + AGENTLESS("agentless"), + REMOTE_CONFIG("remote_config"), + OFFLINE("offline"); + + private final String value; + + FeatureFlaggingConfigurationSource(final String value) { + this.value = value; + } + + public String value() { + return value; + } + + /** + * Applies the migration-window precedence rules. Unsupported explicit sources fail closed. + * + * @param providerEnabled stable provider kill switch, or {@code null} when absent + * @param explicitSource explicitly configured delivery source, or {@code null} when absent + * @param legacyProviderEnabled legacy provider setting, or {@code null} when absent + */ + public static FeatureFlaggingConfigurationSource resolve( + final Boolean providerEnabled, + final String explicitSource, + final Boolean legacyProviderEnabled) { + if (Boolean.FALSE.equals(providerEnabled)) { + return OFFLINE; + } + + final String normalized = normalize(explicitSource); + if (normalized != null) { + for (final FeatureFlaggingConfigurationSource source : values()) { + if (source.value.equals(normalized)) { + return source; + } + } + return OFFLINE; + } + + if (legacyProviderEnabled != null) { + return legacyProviderEnabled ? REMOTE_CONFIG : OFFLINE; + } + return AGENTLESS; + } + + public static boolean isSupportedExplicitSource(final String source) { + final String normalized = normalize(source); + if (normalized == null) { + return true; + } + for (final FeatureFlaggingConfigurationSource configurationSource : values()) { + if (configurationSource.value.equals(normalized)) { + return true; + } + } + return false; + } + + public static FeatureFlaggingConfigurationSource fromResolved(final String source) { + final String normalized = normalize(source); + for (final FeatureFlaggingConfigurationSource configurationSource : values()) { + if (configurationSource.value.equals(normalized)) { + return configurationSource; + } + } + throw new IllegalArgumentException( + "Unsupported Feature Flagging configuration source: " + source); + } + + private static String normalize(final String source) { + if (source == null) { + return null; + } + final String normalized = source.trim().toLowerCase(Locale.ROOT); + return normalized.isEmpty() ? null : normalized; + } +} diff --git a/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSourceTest.java b/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSourceTest.java new file mode 100644 index 00000000000..53b48f554eb --- /dev/null +++ b/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSourceTest.java @@ -0,0 +1,24 @@ +package datadog.trace.api.featureflag.config; + +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource.AGENTLESS; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource.OFFLINE; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource.REMOTE_CONFIG; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class FeatureFlaggingConfigurationSourceTest { + + @Test + void appliesConfigurationPrecedence() { + assertEquals(AGENTLESS, FeatureFlaggingConfigurationSource.resolve(null, null, null)); + assertEquals(REMOTE_CONFIG, FeatureFlaggingConfigurationSource.resolve(null, null, true)); + assertEquals(OFFLINE, FeatureFlaggingConfigurationSource.resolve(null, null, false)); + assertEquals(AGENTLESS, FeatureFlaggingConfigurationSource.resolve(null, "agentless", true)); + assertEquals( + REMOTE_CONFIG, FeatureFlaggingConfigurationSource.resolve(null, " remote_CONFIG ", false)); + assertEquals(OFFLINE, FeatureFlaggingConfigurationSource.resolve(false, "agentless", true)); + assertEquals(OFFLINE, FeatureFlaggingConfigurationSource.resolve(null, "invalid", null)); + assertEquals(OFFLINE, FeatureFlaggingConfigurationSource.resolve(null, "offline", null)); + } +} From 88d966a33a9bac7e798268a0899a55fd73a2b8a0 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Fri, 17 Jul 2026 20:27:57 -0400 Subject: [PATCH 2/6] feat(ffe): defer agentless delivery until provider access --- .../feature-flagging-agent/build.gradle.kts | 1 + .../featureflag/FeatureFlaggingSystem.java | 86 +++++++++++++------ .../FeatureFlaggingSystemTest.java | 21 ++++- .../trace/api/openfeature/DDEvaluator.java | 1 + .../api/openfeature/DDEvaluatorTest.java | 17 ++++ .../featureflag/FeatureFlaggingGateway.java | 18 ++++ .../FeatureFlaggingGatewayTest.java | 13 +++ 7 files changed, 129 insertions(+), 28 deletions(-) diff --git a/products/feature-flagging/feature-flagging-agent/build.gradle.kts b/products/feature-flagging/feature-flagging-agent/build.gradle.kts index 0c008e373a3..d2dc4baea2b 100644 --- a/products/feature-flagging/feature-flagging-agent/build.gradle.kts +++ b/products/feature-flagging/feature-flagging-agent/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { api(libs.slf4j) api(project(":products:feature-flagging:feature-flagging-lib")) api(project(":internal-api")) + compileOnly(project(":products:feature-flagging:feature-flagging-config")) testImplementation(libs.bundles.junit5) testImplementation(libs.bundles.mockito) diff --git a/products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java b/products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java index 5c9cb20540e..8c0f98cd49b 100644 --- a/products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java +++ b/products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java @@ -2,6 +2,8 @@ import datadog.communication.ddagent.SharedCommunicationObjects; import datadog.trace.api.Config; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; +import datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,16 +14,61 @@ public class FeatureFlaggingSystem { private static volatile ConfigurationSourceService CONFIG_SERVICE; private static volatile ExposureWriter EXPOSURE_WRITER; private static volatile SpanEnrichmentWriter SPAN_ENRICHMENT_WRITER; + private static volatile FeatureFlaggingGateway.ActivationListener ACTIVATION_LISTENER; + private static volatile boolean STARTED; private FeatureFlaggingSystem() {} public static synchronized void start(final SharedCommunicationObjects sco) { - if (CONFIG_SERVICE != null || EXPOSURE_WRITER != null) { + if (STARTED) { LOGGER.debug("Feature Flagging system already started"); return; } LOGGER.debug("Feature Flagging system starting"); final Config config = Config.get(); + final FeatureFlaggingConfigurationSource source = + FeatureFlaggingConfigurationSource.fromResolved( + config.getFeatureFlaggingConfigurationSource()); + STARTED = true; + + if (source == FeatureFlaggingConfigurationSource.OFFLINE) { + LOGGER.debug("Feature Flagging system disabled"); + return; + } + if (source == FeatureFlaggingConfigurationSource.AGENTLESS) { + final FeatureFlaggingGateway.ActivationListener activationListener = + () -> activateAgentless(sco, config); + ACTIVATION_LISTENER = activationListener; + FeatureFlaggingGateway.addActivationListener(activationListener); + LOGGER.debug("Feature Flagging system awaiting application provider activation"); + return; + } + + try { + initializeSystem(sco, config); + } catch (final RuntimeException | Error e) { + STARTED = false; + throw e; + } + } + + private static synchronized void activateAgentless( + final SharedCommunicationObjects sco, final Config config) { + final FeatureFlaggingGateway.ActivationListener activationListener = ACTIVATION_LISTENER; + if (!STARTED || activationListener == null) { + return; + } + ACTIVATION_LISTENER = null; + FeatureFlaggingGateway.removeActivationListener(activationListener); + try { + initializeSystem(sco, config); + } catch (final RuntimeException | Error e) { + STARTED = false; + throw e; + } + } + + private static void initializeSystem(final SharedCommunicationObjects sco, final Config config) { final ConfigurationSourceService configService = createConfigurationSourceService(sco, config); final ExposureWriter exposureWriter = new ExposureWriterImpl(sco, config); initialize(configService, exposureWriter); @@ -60,16 +107,17 @@ static void initialize( static ConfigurationSourceService createConfigurationSourceService( final SharedCommunicationObjects sco, final Config config) { - final ConfigurationSource configurationSource = - ConfigurationSource.from(config.getFeatureFlaggingConfigurationSource()); + final FeatureFlaggingConfigurationSource configurationSource = + FeatureFlaggingConfigurationSource.fromResolved( + config.getFeatureFlaggingConfigurationSource()); - if (configurationSource == ConfigurationSource.REMOTE_CONFIG) { + if (configurationSource == FeatureFlaggingConfigurationSource.REMOTE_CONFIG) { if (!config.isRemoteConfigEnabled()) { throw new IllegalStateException("Feature Flagging system started without RC"); } return new RemoteConfigServiceImpl(sco, config); } - if (configurationSource == ConfigurationSource.AGENTLESS) { + if (configurationSource == FeatureFlaggingConfigurationSource.AGENTLESS) { return new AgentlessConfigurationSource(config); } LOGGER.debug( @@ -78,12 +126,18 @@ static ConfigurationSourceService createConfigurationSourceService( } public static synchronized void stop() { + final FeatureFlaggingGateway.ActivationListener activationListener = ACTIVATION_LISTENER; final SpanEnrichmentWriter spanEnrichmentWriter = SPAN_ENRICHMENT_WRITER; final ExposureWriter exposureWriter = EXPOSURE_WRITER; final ConfigurationSourceService configService = CONFIG_SERVICE; + STARTED = false; + ACTIVATION_LISTENER = null; SPAN_ENRICHMENT_WRITER = null; EXPOSURE_WRITER = null; CONFIG_SERVICE = null; + if (activationListener != null) { + FeatureFlaggingGateway.removeActivationListener(activationListener); + } try { if (spanEnrichmentWriter != null) { spanEnrichmentWriter.close(); @@ -102,25 +156,7 @@ public static synchronized void stop() { LOGGER.debug("Feature Flagging system stopped"); } - private enum ConfigurationSource { - AGENTLESS("agentless"), - REMOTE_CONFIG("remote_config"), - OFFLINE("offline"); - - private final String value; - - ConfigurationSource(final String value) { - this.value = value; - } - - private static ConfigurationSource from(final String value) { - for (final ConfigurationSource source : values()) { - if (source.value.equals(value)) { - return source; - } - } - throw new IllegalArgumentException( - "Unsupported Feature Flagging configuration source: " + value); - } + static boolean isAwaitingApplicationActivation() { + return ACTIVATION_LISTENER != null; } } diff --git a/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java b/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java index 523736d13db..cb766751d24 100644 --- a/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java +++ b/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java @@ -3,9 +3,11 @@ import static datadog.trace.api.config.RemoteConfigConfig.REMOTE_CONFIGURATION_ENABLED; import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; @@ -27,6 +29,20 @@ class FeatureFlaggingSystemTest { + @Test + @WithConfig(key = FEATURE_FLAGS_CONFIGURATION_SOURCE, value = "agentless") + void agentlessStartWaitsForApplicationProviderActivation() { + try { + FeatureFlaggingSystem.start(sharedCommunicationObjects()); + + assertTrue(FeatureFlaggingSystem.isAwaitingApplicationActivation()); + } finally { + FeatureFlaggingSystem.stop(); + } + + assertFalse(FeatureFlaggingSystem.isAwaitingApplicationActivation()); + } + @Test @WithConfig(key = FEATURE_FLAGS_CONFIGURATION_SOURCE, value = "remote_config") @WithConfig(key = REMOTE_CONFIGURATION_ENABLED, value = "true") @@ -97,9 +113,8 @@ void explicitRemoteConfigUsesRemoteConfigService() { @Test @WithConfig(key = FEATURE_FLAGS_CONFIGURATION_SOURCE, value = "invalid") - void invalidConfigurationSourceUsesAgentlessDefault() { - assertInstanceOf( - AgentlessConfigurationSource.class, + void invalidConfigurationSourceFailsClosed() { + assertNull( FeatureFlaggingSystem.createConfigurationSourceService( sharedCommunicationObjects(), Config.get())); } diff --git a/products/feature-flagging/feature-flagging-api/src/main/java/datadog/trace/api/openfeature/DDEvaluator.java b/products/feature-flagging/feature-flagging-api/src/main/java/datadog/trace/api/openfeature/DDEvaluator.java index 654fb6e8f6c..6d55c54a5b3 100644 --- a/products/feature-flagging/feature-flagging-api/src/main/java/datadog/trace/api/openfeature/DDEvaluator.java +++ b/products/feature-flagging/feature-flagging-api/src/main/java/datadog/trace/api/openfeature/DDEvaluator.java @@ -67,6 +67,7 @@ public DDEvaluator(final Runnable configCallback) { @Override public boolean initialize( final long timeout, final TimeUnit unit, final EvaluationContext context) throws Exception { + FeatureFlaggingGateway.activate(); FeatureFlaggingGateway.addConfigListener(this); return initializationLatch.await(timeout, unit) || hasConfiguration(); } diff --git a/products/feature-flagging/feature-flagging-api/src/test/java/datadog/trace/api/openfeature/DDEvaluatorTest.java b/products/feature-flagging/feature-flagging-api/src/test/java/datadog/trace/api/openfeature/DDEvaluatorTest.java index 3421fe3454e..cac510732db 100644 --- a/products/feature-flagging/feature-flagging-api/src/test/java/datadog/trace/api/openfeature/DDEvaluatorTest.java +++ b/products/feature-flagging/feature-flagging-api/src/test/java/datadog/trace/api/openfeature/DDEvaluatorTest.java @@ -23,6 +23,7 @@ import com.squareup.moshi.JsonWriter; import com.squareup.moshi.Moshi; import com.squareup.moshi.Types; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; import datadog.trace.api.featureflag.ufc.v1.Flag; import datadog.trace.api.featureflag.ufc.v1.ServerConfiguration; import dev.openfeature.sdk.ErrorCode; @@ -145,6 +146,22 @@ public void testInitializeTimesOutWithoutConfig() throws Exception { } } + @Test + public void testInitializeSignalsApplicationProviderActivation() throws Exception { + final FeatureFlaggingGateway.ActivationListener listener = + mock(FeatureFlaggingGateway.ActivationListener.class); + final DDEvaluator evaluator = new DDEvaluator(mock(Runnable.class)); + FeatureFlaggingGateway.addActivationListener(listener); + try { + evaluator.initialize(1, MILLISECONDS, mock(EvaluationContext.class)); + + verify(listener).activate(); + } finally { + evaluator.shutdown(); + FeatureFlaggingGateway.removeActivationListener(listener); + } + } + @Test public void testInitializeWaitsForNonNullConfig() throws Exception { final DDEvaluator evaluator = new DDEvaluator(mock(Runnable.class)); diff --git a/products/feature-flagging/feature-flagging-bootstrap/src/main/java/datadog/trace/api/featureflag/FeatureFlaggingGateway.java b/products/feature-flagging/feature-flagging-bootstrap/src/main/java/datadog/trace/api/featureflag/FeatureFlaggingGateway.java index 2704b4be341..c8f5625c855 100644 --- a/products/feature-flagging/feature-flagging-bootstrap/src/main/java/datadog/trace/api/featureflag/FeatureFlaggingGateway.java +++ b/products/feature-flagging/feature-flagging-bootstrap/src/main/java/datadog/trace/api/featureflag/FeatureFlaggingGateway.java @@ -11,11 +11,16 @@ public abstract class FeatureFlaggingGateway { public interface ConfigListener extends Consumer {} + public interface ActivationListener { + void activate(); + } + public interface ExposureListener extends Consumer {} public interface SpanEnrichmentListener extends Consumer {} private static final List CONFIG_LISTENERS = new CopyOnWriteArrayList<>(); + private static final List ACTIVATION_LISTENERS = new CopyOnWriteArrayList<>(); private static final List EXPOSURE_LISTENERS = new CopyOnWriteArrayList<>(); private static final List SPAN_ENRICHMENT_LISTENERS = new CopyOnWriteArrayList<>(); @@ -42,6 +47,19 @@ public static void dispatch(final ServerConfiguration config) { CONFIG_LISTENERS.forEach(listener -> listener.accept(config)); } + public static void addActivationListener(final ActivationListener listener) { + ACTIVATION_LISTENERS.add(listener); + } + + public static void removeActivationListener(final ActivationListener listener) { + ACTIVATION_LISTENERS.remove(listener); + } + + /** Signals that application code initialized the Datadog OpenFeature provider. */ + public static void activate() { + ACTIVATION_LISTENERS.forEach(ActivationListener::activate); + } + public static void addExposureListener(final ExposureListener listener) { EXPOSURE_LISTENERS.add(listener); } diff --git a/products/feature-flagging/feature-flagging-bootstrap/src/test/java/datadog/trace/api/featureflag/FeatureFlaggingGatewayTest.java b/products/feature-flagging/feature-flagging-bootstrap/src/test/java/datadog/trace/api/featureflag/FeatureFlaggingGatewayTest.java index 3af2ed5add8..daaaf8d7001 100644 --- a/products/feature-flagging/feature-flagging-bootstrap/src/test/java/datadog/trace/api/featureflag/FeatureFlaggingGatewayTest.java +++ b/products/feature-flagging/feature-flagging-bootstrap/src/test/java/datadog/trace/api/featureflag/FeatureFlaggingGatewayTest.java @@ -13,6 +13,7 @@ class FeatureFlaggingGatewayTest { private FeatureFlaggingGateway.ConfigListener configListener; + private FeatureFlaggingGateway.ActivationListener activationListener; private FeatureFlaggingGateway.ExposureListener exposureListener; private FeatureFlaggingGateway.SpanEnrichmentListener spanEnrichmentListener; private ServerConfiguration firstConfiguration; @@ -23,6 +24,7 @@ class FeatureFlaggingGatewayTest { @BeforeEach void setUp() { configListener = mock(FeatureFlaggingGateway.ConfigListener.class); + activationListener = mock(FeatureFlaggingGateway.ActivationListener.class); exposureListener = mock(FeatureFlaggingGateway.ExposureListener.class); spanEnrichmentListener = mock(FeatureFlaggingGateway.SpanEnrichmentListener.class); firstConfiguration = mock(ServerConfiguration.class); @@ -34,10 +36,21 @@ void setUp() { @AfterEach void tearDown() { FeatureFlaggingGateway.removeConfigListener(configListener); + FeatureFlaggingGateway.removeActivationListener(activationListener); FeatureFlaggingGateway.removeExposureListener(exposureListener); FeatureFlaggingGateway.removeSpanEnrichmentListener(spanEnrichmentListener); } + @Test + void testProviderActivationListener() { + FeatureFlaggingGateway.addActivationListener(activationListener); + + FeatureFlaggingGateway.activate(); + + verify(activationListener).activate(); + verifyNoMoreInteractions(activationListener); + } + @Test void testAttachingAConfigListener() { clearCurrentServerConfiguration(); From 191543e087051391de9779e77c1b8603bacaf61c Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Fri, 17 Jul 2026 21:10:55 -0400 Subject: [PATCH 3/6] fix(ffe): keep source resolution within agent budget --- .../java/datadog/trace/bootstrap/Agent.java | 7 +- .../main/java/datadog/trace/api/Config.java | 15 ++-- metadata/supported-configurations.json | 2 +- .../featureflag/FeatureFlaggingSystem.java | 21 +++-- .../FeatureFlaggingSystemTest.java | 8 +- .../feature-flagging-config/build.gradle.kts | 7 +- .../config/FeatureFlaggingConfig.java | 39 +++++++++ .../FeatureFlaggingConfigurationSource.java | 83 ------------------- .../config/FeatureFlaggingConfigTest.java | 42 ++++++++++ ...eatureFlaggingConfigurationSourceTest.java | 24 ------ 10 files changed, 107 insertions(+), 141 deletions(-) delete mode 100644 products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSource.java create mode 100644 products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigTest.java delete mode 100644 products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSourceTest.java 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 dbdfd4af496..4b152a78a08 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 @@ -44,7 +44,6 @@ import datadog.trace.api.config.TracerConfig; import datadog.trace.api.config.UsmConfig; import datadog.trace.api.featureflag.config.FeatureFlaggingConfig; -import datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource; import datadog.trace.api.gateway.RequestContextSlot; import datadog.trace.api.gateway.SubscriptionService; import datadog.trace.api.git.EmbeddedGitInfoBuilder; @@ -1657,9 +1656,9 @@ private static boolean isFeatureFlaggingEnabled() { final Boolean legacyProviderEnabled = featureFlaggingBooleanSetting(FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED); - return FeatureFlaggingConfigurationSource.resolve( - providerEnabled, configurationSource, legacyProviderEnabled) - != FeatureFlaggingConfigurationSource.OFFLINE; + return !FeatureFlaggingConfig.CONFIGURATION_SOURCE_OFFLINE.equals( + FeatureFlaggingConfig.resolveConfigurationSource( + providerEnabled, configurationSource, legacyProviderEnabled)); } private static Boolean featureFlaggingBooleanSetting(final String configKey) { 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 c9dad49505a..495d8e4d7af 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -725,12 +725,15 @@ import static datadog.trace.api.config.TracerConfig.WRITER_BAGGAGE_INJECT; import static datadog.trace.api.config.TracerConfig.WRITER_LINKS_INJECT; import static datadog.trace.api.config.TracerConfig.WRITER_TYPE; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.CONFIGURATION_SOURCE_OFFLINE; 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_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL; 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.FLAGGING_PROVIDER_ENABLED; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.isSupportedConfigurationSource; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.resolveConfigurationSource; import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY; import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.OTLP_WRITER_TYPE; import static datadog.trace.util.CollectionUtils.tryMakeImmutableList; @@ -747,7 +750,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.FeatureFlaggingConfigurationSource; import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.IastDetectionMode; import datadog.trace.api.iast.telemetry.Verbosity; @@ -2862,8 +2864,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) configProvider.isSet(FEATURE_FLAGS_CONFIGURATION_SOURCE) ? configProvider.getString(FEATURE_FLAGS_CONFIGURATION_SOURCE) : null; - final FeatureFlaggingConfigurationSource resolvedFeatureFlaggingConfigurationSource = - FeatureFlaggingConfigurationSource.resolve( + final String resolvedFeatureFlaggingConfigurationSource = + resolveConfigurationSource( configuredFeatureFlaggingProviderEnabled, configuredFeatureFlaggingConfigurationSource, legacyFeatureFlaggingProviderEnabled); @@ -2874,15 +2876,14 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) FLAGGING_PROVIDER_ENABLED, FEATURE_FLAGS_CONFIGURATION_SOURCE); } - if (!FeatureFlaggingConfigurationSource.isSupportedExplicitSource( - configuredFeatureFlaggingConfigurationSource)) { + if (!isSupportedConfigurationSource(configuredFeatureFlaggingConfigurationSource)) { log.warn( "Unsupported Feature Flagging configuration source: {}. Disabling Feature Flagging", configuredFeatureFlaggingConfigurationSource); } featureFlaggingProviderEnabled = - resolvedFeatureFlaggingConfigurationSource != FeatureFlaggingConfigurationSource.OFFLINE; - featureFlaggingConfigurationSource = resolvedFeatureFlaggingConfigurationSource.value(); + !CONFIGURATION_SOURCE_OFFLINE.equals(resolvedFeatureFlaggingConfigurationSource); + featureFlaggingConfigurationSource = resolvedFeatureFlaggingConfigurationSource; featureFlaggingConfigurationSourceAgentlessBaseUrl = configProvider.getStringNotEmpty( FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL, null); diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 0e45f97cb0a..06b1ee53677 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1541,7 +1541,7 @@ { "version": "A", "type": "boolean", - "default": null, + "default": "false", "aliases": [] } ], diff --git a/products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java b/products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java index 8c0f98cd49b..5bf2ff2858d 100644 --- a/products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java +++ b/products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java @@ -1,9 +1,12 @@ package com.datadog.featureflag; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.CONFIGURATION_SOURCE_AGENTLESS; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.CONFIGURATION_SOURCE_OFFLINE; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.CONFIGURATION_SOURCE_REMOTE_CONFIG; + import datadog.communication.ddagent.SharedCommunicationObjects; import datadog.trace.api.Config; import datadog.trace.api.featureflag.FeatureFlaggingGateway; -import datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,16 +29,14 @@ public static synchronized void start(final SharedCommunicationObjects sco) { } LOGGER.debug("Feature Flagging system starting"); final Config config = Config.get(); - final FeatureFlaggingConfigurationSource source = - FeatureFlaggingConfigurationSource.fromResolved( - config.getFeatureFlaggingConfigurationSource()); + final String source = config.getFeatureFlaggingConfigurationSource(); STARTED = true; - if (source == FeatureFlaggingConfigurationSource.OFFLINE) { + if (CONFIGURATION_SOURCE_OFFLINE.equals(source)) { LOGGER.debug("Feature Flagging system disabled"); return; } - if (source == FeatureFlaggingConfigurationSource.AGENTLESS) { + if (CONFIGURATION_SOURCE_AGENTLESS.equals(source)) { final FeatureFlaggingGateway.ActivationListener activationListener = () -> activateAgentless(sco, config); ACTIVATION_LISTENER = activationListener; @@ -107,17 +108,15 @@ static void initialize( static ConfigurationSourceService createConfigurationSourceService( final SharedCommunicationObjects sco, final Config config) { - final FeatureFlaggingConfigurationSource configurationSource = - FeatureFlaggingConfigurationSource.fromResolved( - config.getFeatureFlaggingConfigurationSource()); + final String configurationSource = config.getFeatureFlaggingConfigurationSource(); - if (configurationSource == FeatureFlaggingConfigurationSource.REMOTE_CONFIG) { + if (CONFIGURATION_SOURCE_REMOTE_CONFIG.equals(configurationSource)) { if (!config.isRemoteConfigEnabled()) { throw new IllegalStateException("Feature Flagging system started without RC"); } return new RemoteConfigServiceImpl(sco, config); } - if (configurationSource == FeatureFlaggingConfigurationSource.AGENTLESS) { + if (CONFIGURATION_SOURCE_AGENTLESS.equals(configurationSource)) { return new AgentlessConfigurationSource(config); } LOGGER.debug( diff --git a/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java b/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java index cb766751d24..27f703ff9ee 100644 --- a/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java +++ b/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java @@ -124,11 +124,9 @@ void rejectsUnsupportedNormalizedConfigurationSource() { Config config = mock(Config.class); when(config.getFeatureFlaggingConfigurationSource()).thenReturn("invalid"); - assertThrows( - IllegalArgumentException.class, - () -> - FeatureFlaggingSystem.createConfigurationSourceService( - sharedCommunicationObjects(), config)); + assertNull( + FeatureFlaggingSystem.createConfigurationSourceService( + sharedCommunicationObjects(), config)); } @Test diff --git a/products/feature-flagging/feature-flagging-config/build.gradle.kts b/products/feature-flagging/feature-flagging-config/build.gradle.kts index 1998ca33fef..97dd3aa9f0d 100644 --- a/products/feature-flagging/feature-flagging-config/build.gradle.kts +++ b/products/feature-flagging/feature-flagging-config/build.gradle.kts @@ -4,12 +4,7 @@ plugins { apply(from = "$rootDir/gradle/java.gradle") -description = "Feature flagging configuration keys (compile-time constants)" - -extra["excludedClassesCoverage"] = listOf( - // Constants-only holder — no executable logic to cover. - "datadog.trace.api.featureflag.config.FeatureFlaggingConfig", -) +description = "Feature flagging configuration keys and source resolution" dependencies { testImplementation(libs.bundles.junit5) 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 686bf9e3a25..fe1cc5b8a07 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 @@ -2,6 +2,10 @@ public class FeatureFlaggingConfig { + public static final String CONFIGURATION_SOURCE_AGENTLESS = "agentless"; + public static final String CONFIGURATION_SOURCE_OFFLINE = "offline"; + public static final String CONFIGURATION_SOURCE_REMOTE_CONFIG = "remote_config"; + public static final String FLAGGING_PROVIDER_ENABLED = "feature.flagging.provider.enabled"; public static final String EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED = "experimental.flagging.provider.enabled"; @@ -23,5 +27,40 @@ public class FeatureFlaggingConfig { public static final String FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS = "feature.flags.configuration.source.agentless.request.timeout.seconds"; + public static String resolveConfigurationSource( + final Boolean providerEnabled, + final String explicitSource, + final Boolean legacyProviderEnabled) { + if (Boolean.FALSE.equals(providerEnabled)) { + return CONFIGURATION_SOURCE_OFFLINE; + } + if (explicitSource != null && !explicitSource.trim().isEmpty()) { + final String source = explicitSource.trim(); + if (CONFIGURATION_SOURCE_AGENTLESS.equalsIgnoreCase(source)) { + return CONFIGURATION_SOURCE_AGENTLESS; + } + if (CONFIGURATION_SOURCE_REMOTE_CONFIG.equalsIgnoreCase(source)) { + return CONFIGURATION_SOURCE_REMOTE_CONFIG; + } + return CONFIGURATION_SOURCE_OFFLINE; + } + if (legacyProviderEnabled != null) { + return legacyProviderEnabled + ? CONFIGURATION_SOURCE_REMOTE_CONFIG + : CONFIGURATION_SOURCE_OFFLINE; + } + return CONFIGURATION_SOURCE_AGENTLESS; + } + + public static boolean isSupportedConfigurationSource(final String source) { + if (source == null || source.trim().isEmpty()) { + return true; + } + final String normalized = source.trim(); + return CONFIGURATION_SOURCE_AGENTLESS.equalsIgnoreCase(normalized) + || CONFIGURATION_SOURCE_REMOTE_CONFIG.equalsIgnoreCase(normalized) + || CONFIGURATION_SOURCE_OFFLINE.equalsIgnoreCase(normalized); + } + private FeatureFlaggingConfig() {} } diff --git a/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSource.java b/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSource.java deleted file mode 100644 index 753cb959d91..00000000000 --- a/products/feature-flagging/feature-flagging-config/src/main/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSource.java +++ /dev/null @@ -1,83 +0,0 @@ -package datadog.trace.api.featureflag.config; - -import java.util.Locale; - -/** Resolves Feature Flagging provider and delivery-source configuration. */ -public enum FeatureFlaggingConfigurationSource { - AGENTLESS("agentless"), - REMOTE_CONFIG("remote_config"), - OFFLINE("offline"); - - private final String value; - - FeatureFlaggingConfigurationSource(final String value) { - this.value = value; - } - - public String value() { - return value; - } - - /** - * Applies the migration-window precedence rules. Unsupported explicit sources fail closed. - * - * @param providerEnabled stable provider kill switch, or {@code null} when absent - * @param explicitSource explicitly configured delivery source, or {@code null} when absent - * @param legacyProviderEnabled legacy provider setting, or {@code null} when absent - */ - public static FeatureFlaggingConfigurationSource resolve( - final Boolean providerEnabled, - final String explicitSource, - final Boolean legacyProviderEnabled) { - if (Boolean.FALSE.equals(providerEnabled)) { - return OFFLINE; - } - - final String normalized = normalize(explicitSource); - if (normalized != null) { - for (final FeatureFlaggingConfigurationSource source : values()) { - if (source.value.equals(normalized)) { - return source; - } - } - return OFFLINE; - } - - if (legacyProviderEnabled != null) { - return legacyProviderEnabled ? REMOTE_CONFIG : OFFLINE; - } - return AGENTLESS; - } - - public static boolean isSupportedExplicitSource(final String source) { - final String normalized = normalize(source); - if (normalized == null) { - return true; - } - for (final FeatureFlaggingConfigurationSource configurationSource : values()) { - if (configurationSource.value.equals(normalized)) { - return true; - } - } - return false; - } - - public static FeatureFlaggingConfigurationSource fromResolved(final String source) { - final String normalized = normalize(source); - for (final FeatureFlaggingConfigurationSource configurationSource : values()) { - if (configurationSource.value.equals(normalized)) { - return configurationSource; - } - } - throw new IllegalArgumentException( - "Unsupported Feature Flagging configuration source: " + source); - } - - private static String normalize(final String source) { - if (source == null) { - return null; - } - final String normalized = source.trim().toLowerCase(Locale.ROOT); - return normalized.isEmpty() ? null : normalized; - } -} 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 new file mode 100644 index 00000000000..74d45fe7eed --- /dev/null +++ b/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigTest.java @@ -0,0 +1,42 @@ +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_OFFLINE; +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.resolveConfigurationSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class FeatureFlaggingConfigTest { + + @Test + void appliesConfigurationPrecedence() { + assertEquals(CONFIGURATION_SOURCE_AGENTLESS, resolveConfigurationSource(null, null, null)); + assertEquals(CONFIGURATION_SOURCE_AGENTLESS, resolveConfigurationSource(null, " ", null)); + assertEquals(CONFIGURATION_SOURCE_REMOTE_CONFIG, resolveConfigurationSource(null, null, true)); + assertEquals(CONFIGURATION_SOURCE_OFFLINE, resolveConfigurationSource(null, null, false)); + assertEquals( + CONFIGURATION_SOURCE_AGENTLESS, resolveConfigurationSource(null, "agentless", true)); + assertEquals( + CONFIGURATION_SOURCE_REMOTE_CONFIG, + resolveConfigurationSource(null, " remote_CONFIG ", false)); + assertEquals( + CONFIGURATION_SOURCE_OFFLINE, resolveConfigurationSource(false, "agentless", true)); + assertEquals(CONFIGURATION_SOURCE_OFFLINE, resolveConfigurationSource(null, "invalid", null)); + assertEquals(CONFIGURATION_SOURCE_OFFLINE, resolveConfigurationSource(null, "offline", null)); + } + + @Test + void recognizesSupportedExplicitSources() { + assertTrue(isSupportedConfigurationSource(null)); + assertTrue(isSupportedConfigurationSource(" ")); + assertTrue(isSupportedConfigurationSource("agentless")); + assertTrue(isSupportedConfigurationSource(" REMOTE_CONFIG ")); + assertTrue(isSupportedConfigurationSource("OFFLINE")); + assertFalse(isSupportedConfigurationSource("invalid")); + } +} diff --git a/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSourceTest.java b/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSourceTest.java deleted file mode 100644 index 53b48f554eb..00000000000 --- a/products/feature-flagging/feature-flagging-config/src/test/java/datadog/trace/api/featureflag/config/FeatureFlaggingConfigurationSourceTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package datadog.trace.api.featureflag.config; - -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource.AGENTLESS; -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource.OFFLINE; -import static datadog.trace.api.featureflag.config.FeatureFlaggingConfigurationSource.REMOTE_CONFIG; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -class FeatureFlaggingConfigurationSourceTest { - - @Test - void appliesConfigurationPrecedence() { - assertEquals(AGENTLESS, FeatureFlaggingConfigurationSource.resolve(null, null, null)); - assertEquals(REMOTE_CONFIG, FeatureFlaggingConfigurationSource.resolve(null, null, true)); - assertEquals(OFFLINE, FeatureFlaggingConfigurationSource.resolve(null, null, false)); - assertEquals(AGENTLESS, FeatureFlaggingConfigurationSource.resolve(null, "agentless", true)); - assertEquals( - REMOTE_CONFIG, FeatureFlaggingConfigurationSource.resolve(null, " remote_CONFIG ", false)); - assertEquals(OFFLINE, FeatureFlaggingConfigurationSource.resolve(false, "agentless", true)); - assertEquals(OFFLINE, FeatureFlaggingConfigurationSource.resolve(null, "invalid", null)); - assertEquals(OFFLINE, FeatureFlaggingConfigurationSource.resolve(null, "offline", null)); - } -} From 5588830996eeb6a1c60abd00d2f335a646cb84c3 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Fri, 17 Jul 2026 21:11:11 -0400 Subject: [PATCH 4/6] test(ffe): cover lazy agentless activation --- .../com/datadog/featureflag/FeatureFlaggingSystemTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java b/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java index 27f703ff9ee..2f5c72bb8ab 100644 --- a/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java +++ b/products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java @@ -2,6 +2,7 @@ import static datadog.trace.api.config.RemoteConfigConfig.REMOTE_CONFIGURATION_ENABLED; import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE; +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -22,6 +23,7 @@ import datadog.remoteconfig.ConfigurationPoller; import datadog.remoteconfig.Product; import datadog.trace.api.Config; +import datadog.trace.api.featureflag.FeatureFlaggingGateway; import datadog.trace.junit.utils.config.WithConfig; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -31,11 +33,16 @@ class FeatureFlaggingSystemTest { @Test @WithConfig(key = FEATURE_FLAGS_CONFIGURATION_SOURCE, value = "agentless") + @WithConfig( + key = FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL, + value = "http://127.0.0.1:1") void agentlessStartWaitsForApplicationProviderActivation() { try { FeatureFlaggingSystem.start(sharedCommunicationObjects()); assertTrue(FeatureFlaggingSystem.isAwaitingApplicationActivation()); + FeatureFlaggingGateway.activate(); + assertFalse(FeatureFlaggingSystem.isAwaitingApplicationActivation()); } finally { FeatureFlaggingSystem.stop(); } From 6e99dbb2a5cbd14bd990a0dccf28b70117648b5e Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Fri, 17 Jul 2026 21:40:52 -0400 Subject: [PATCH 5/6] fix(ffe): document nullable bootstrap setting --- .../src/main/java/datadog/trace/bootstrap/Agent.java | 3 +++ 1 file changed, 3 insertions(+) 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 4b152a78a08..717449e13bb 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 @@ -1661,6 +1661,9 @@ private static boolean isFeatureFlaggingEnabled() { providerEnabled, configurationSource, legacyProviderEnabled)); } + @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) { From c5bae851402cb98e6d23f4e9f6f4e442a2f2ad01 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Fri, 17 Jul 2026 22:54:10 -0400 Subject: [PATCH 6/6] fix(ffe): use canonical feature flags kill switch --- .../src/main/java/datadog/trace/bootstrap/Agent.java | 4 ++-- internal-api/src/main/java/datadog/trace/api/Config.java | 6 +++--- .../src/test/groovy/datadog/trace/api/ConfigTest.groovy | 4 ++-- metadata/supported-configurations.json | 4 ++-- .../trace/api/featureflag/config/FeatureFlaggingConfig.java | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) 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 717449e13bb..8e69a8aade8 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,7 @@ private enum AgentFeature { APP_LOGS_COLLECTION(GeneralConfig.APP_LOGS_COLLECTION_ENABLED, false), LLMOBS(LlmObsConfig.LLMOBS_ENABLED, false), LLMOBS_AGENTLESS(LlmObsConfig.LLMOBS_AGENTLESS_ENABLED, false), - FEATURE_FLAGGING(FeatureFlaggingConfig.FLAGGING_PROVIDER_ENABLED, true); + FEATURE_FLAGGING(FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED, true); private final String configKey; private final String systemProp; @@ -1650,7 +1650,7 @@ private static boolean isFeatureEnabled(AgentFeature feature) { private static boolean isFeatureFlaggingEnabled() { final Boolean providerEnabled = - featureFlaggingBooleanSetting(FeatureFlaggingConfig.FLAGGING_PROVIDER_ENABLED); + featureFlaggingBooleanSetting(FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED); final String configurationSource = featureFlaggingSetting(FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE); final Boolean legacyProviderEnabled = 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 495d8e4d7af..7acc7c56bb8 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -731,7 +731,7 @@ import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL; 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.FLAGGING_PROVIDER_ENABLED; +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.resolveConfigurationSource; import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY; @@ -2857,7 +2857,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) REMOTE_CONFIG_MAX_EXTRA_SERVICES, DEFAULT_REMOTE_CONFIG_MAX_EXTRA_SERVICES); final boolean configuredFeatureFlaggingProviderEnabled = - configProvider.getBoolean(FLAGGING_PROVIDER_ENABLED, true); + configProvider.getBoolean(FEATURE_FLAGS_ENABLED, true); final Boolean legacyFeatureFlaggingProviderEnabled = configProvider.getBoolean(EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED); final String configuredFeatureFlaggingConfigurationSource = @@ -2873,7 +2873,7 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) log.warn( "Setting {} is deprecated. Use {} and {} instead.", EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED, - FLAGGING_PROVIDER_ENABLED, + FEATURE_FLAGS_ENABLED, FEATURE_FLAGS_CONFIGURATION_SOURCE); } if (!isSupportedConfigurationSource(configuredFeatureFlaggingConfigurationSource)) { diff --git a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy index 2050e25c284..65672636ce3 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy @@ -61,7 +61,7 @@ import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.EXPERIM import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE 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.FLAGGING_PROVIDER_ENABLED +import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_CHECK_PERIOD import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_ENABLED import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_METRICS_CONFIGS @@ -3526,7 +3526,7 @@ class ConfigTest extends DDSpecification { setup: Properties properties = new Properties() if (providerEnabled != null) { - properties.setProperty(FLAGGING_PROVIDER_ENABLED, providerEnabled.toString()) + properties.setProperty(FEATURE_FLAGS_ENABLED, providerEnabled.toString()) } if (source != null) { properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE, source) diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 06b1ee53677..cb163a223c9 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1497,7 +1497,7 @@ "aliases": [] } ], - "DD_FEATURE_FLAGGING_PROVIDER_ENABLED": [ + "DD_FEATURE_FLAGS_ENABLED": [ { "version": "A", "type": "boolean", @@ -11995,6 +11995,6 @@ ] }, "deprecations": { - "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED": "DD_FEATURE_FLAGGING_PROVIDER_ENABLED" + "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED": "DD_FEATURE_FLAGS_ENABLED" } } 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 fe1cc5b8a07..88079188cf3 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 @@ -6,13 +6,13 @@ public class FeatureFlaggingConfig { public static final String CONFIGURATION_SOURCE_OFFLINE = "offline"; public static final String CONFIGURATION_SOURCE_REMOTE_CONFIG = "remote_config"; - public static final String FLAGGING_PROVIDER_ENABLED = "feature.flagging.provider.enabled"; + public static final String FEATURE_FLAGS_ENABLED = "feature.flags.enabled"; public static final String EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED = "experimental.flagging.provider.enabled"; /** * Opt-in gate for APM span enrichment with feature-flag evaluation metadata. DISTINCT from {@link - * #FLAGGING_PROVIDER_ENABLED} and OFF by default — enabling the provider does not enable span + * #FEATURE_FLAGS_ENABLED} and OFF by default — enabling the provider does not enable span * enrichment. */ public static final String EXPERIMENTAL_SPAN_ENRICHMENT_ENABLED =