Skip to content

Commit b32988c

Browse files
committed
feat(ffe): preserve configuration source semantics
1 parent 488b477 commit b32988c

14 files changed

Lines changed: 354 additions & 71 deletions

File tree

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ private enum AgentFeature {
135135
APP_LOGS_COLLECTION(GeneralConfig.APP_LOGS_COLLECTION_ENABLED, false),
136136
LLMOBS(LlmObsConfig.LLMOBS_ENABLED, false),
137137
LLMOBS_AGENTLESS(LlmObsConfig.LLMOBS_AGENTLESS_ENABLED, false),
138-
FEATURE_FLAGGING(FeatureFlaggingConfig.FLAGGING_PROVIDER_ENABLED, false);
138+
FEATURE_FLAGGING(FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED, true);
139139

140140
private final String configKey;
141141
private final String systemProp;
@@ -282,7 +282,7 @@ public static void start(
282282
agentlessLogSubmissionEnabled = isFeatureEnabled(AgentFeature.AGENTLESS_LOG_SUBMISSION);
283283
appLogsCollectionEnabled = isFeatureEnabled(AgentFeature.APP_LOGS_COLLECTION);
284284
llmObsEnabled = isFeatureEnabled(AgentFeature.LLMOBS);
285-
featureFlaggingEnabled = isFeatureEnabled(AgentFeature.FEATURE_FLAGGING);
285+
featureFlaggingEnabled = isFeatureFlaggingEnabled();
286286

287287
// setup writers when llmobs is enabled to accomodate apm and llmobs
288288
if (llmObsEnabled) {
@@ -1648,6 +1648,45 @@ private static boolean isFeatureEnabled(AgentFeature feature) {
16481648
}
16491649
}
16501650

1651+
private static boolean isFeatureFlaggingEnabled() {
1652+
final Boolean providerEnabled =
1653+
featureFlaggingBooleanSetting(FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED);
1654+
final String configurationSource =
1655+
featureFlaggingSetting(FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE);
1656+
final Boolean legacyProviderEnabled =
1657+
featureFlaggingBooleanSetting(FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED);
1658+
1659+
return !FeatureFlaggingConfig.CONFIGURATION_SOURCE_OFFLINE.equals(
1660+
FeatureFlaggingConfig.resolveConfigurationSource(
1661+
providerEnabled, configurationSource, legacyProviderEnabled));
1662+
}
1663+
1664+
@SuppressFBWarnings(
1665+
value = "NP_BOOLEAN_RETURN_NULL",
1666+
justification = "A null value preserves the distinction between absent and explicitly false")
1667+
private static Boolean featureFlaggingBooleanSetting(final String configKey) {
1668+
final String value = featureFlaggingSetting(configKey);
1669+
if (value == null) {
1670+
return null;
1671+
}
1672+
return Boolean.parseBoolean(value) || "1".equals(value);
1673+
}
1674+
1675+
private static String featureFlaggingSetting(final String configKey) {
1676+
final String systemProperty = propertyNameToSystemPropertyName(configKey);
1677+
String value = SystemProperties.get(systemProperty);
1678+
if (value == null) {
1679+
value = getStableConfig(FLEET, configKey);
1680+
}
1681+
if (value == null) {
1682+
value = ddGetEnv(systemProperty);
1683+
}
1684+
if (value == null) {
1685+
value = getStableConfig(LOCAL, configKey);
1686+
}
1687+
return value;
1688+
}
1689+
16511690
/**
16521691
* @see datadog.trace.api.ProductActivation#fromString(String)
16531692
*/

internal-api/src/main/java/datadog/trace/api/Config.java

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@
8686
import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_BODY_ENABLED;
8787
import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_PARAMS_ENABLED;
8888
import static datadog.trace.api.ConfigDefaults.DEFAULT_EXPERIMENTATAL_JEE_SPLIT_BY_DEPLOYMENT;
89-
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE;
9089
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS;
9190
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS;
9291
import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_CLIENT_ERROR_STATUSES;
@@ -726,10 +725,15 @@
726725
import static datadog.trace.api.config.TracerConfig.WRITER_BAGGAGE_INJECT;
727726
import static datadog.trace.api.config.TracerConfig.WRITER_LINKS_INJECT;
728727
import static datadog.trace.api.config.TracerConfig.WRITER_TYPE;
728+
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.CONFIGURATION_SOURCE_OFFLINE;
729+
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED;
729730
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
730731
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL;
731732
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS;
732733
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS;
734+
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED;
735+
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.isSupportedConfigurationSource;
736+
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.resolveConfigurationSource;
733737
import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;
734738
import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.OTLP_WRITER_TYPE;
735739
import static datadog.trace.util.CollectionUtils.tryMakeImmutableList;
@@ -1221,6 +1225,7 @@ public static String getHostName() {
12211225

12221226
private final int remoteConfigMaxExtraServices;
12231227

1228+
private final boolean featureFlaggingProviderEnabled;
12241229
private final String featureFlaggingConfigurationSource;
12251230
private final String featureFlaggingConfigurationSourceAgentlessBaseUrl;
12261231
private final int featureFlaggingConfigurationSourcePollIntervalSeconds;
@@ -2851,10 +2856,34 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())
28512856
configProvider.getInteger(
28522857
REMOTE_CONFIG_MAX_EXTRA_SERVICES, DEFAULT_REMOTE_CONFIG_MAX_EXTRA_SERVICES);
28532858

2854-
featureFlaggingConfigurationSource =
2855-
normalizeFeatureFlaggingConfigurationSource(
2856-
configProvider.getString(
2857-
FEATURE_FLAGS_CONFIGURATION_SOURCE, DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE));
2859+
final boolean configuredFeatureFlaggingProviderEnabled =
2860+
configProvider.getBoolean(FEATURE_FLAGS_ENABLED, true);
2861+
final Boolean legacyFeatureFlaggingProviderEnabled =
2862+
configProvider.getBoolean(EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED);
2863+
final String configuredFeatureFlaggingConfigurationSource =
2864+
configProvider.isSet(FEATURE_FLAGS_CONFIGURATION_SOURCE)
2865+
? configProvider.getString(FEATURE_FLAGS_CONFIGURATION_SOURCE)
2866+
: null;
2867+
final String resolvedFeatureFlaggingConfigurationSource =
2868+
resolveConfigurationSource(
2869+
configuredFeatureFlaggingProviderEnabled,
2870+
configuredFeatureFlaggingConfigurationSource,
2871+
legacyFeatureFlaggingProviderEnabled);
2872+
if (legacyFeatureFlaggingProviderEnabled != null) {
2873+
log.warn(
2874+
"Setting {} is deprecated. Use {} and {} instead.",
2875+
EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED,
2876+
FEATURE_FLAGS_ENABLED,
2877+
FEATURE_FLAGS_CONFIGURATION_SOURCE);
2878+
}
2879+
if (!isSupportedConfigurationSource(configuredFeatureFlaggingConfigurationSource)) {
2880+
log.warn(
2881+
"Unsupported Feature Flagging configuration source: {}. Disabling Feature Flagging",
2882+
configuredFeatureFlaggingConfigurationSource);
2883+
}
2884+
featureFlaggingProviderEnabled =
2885+
!CONFIGURATION_SOURCE_OFFLINE.equals(resolvedFeatureFlaggingConfigurationSource);
2886+
featureFlaggingConfigurationSource = resolvedFeatureFlaggingConfigurationSource;
28582887
featureFlaggingConfigurationSourceAgentlessBaseUrl =
28592888
configProvider.getStringNotEmpty(
28602889
FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL, null);
@@ -3797,28 +3826,6 @@ public boolean isInferredProxyPropagationEnabled() {
37973826
return traceInferredProxyEnabled;
37983827
}
37993828

3800-
private static String normalizeFeatureFlaggingConfigurationSource(final String source) {
3801-
if (source == null) {
3802-
return DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE;
3803-
}
3804-
final String normalized = source.trim().toLowerCase(Locale.ROOT);
3805-
if (normalized.isEmpty()) {
3806-
return DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE;
3807-
}
3808-
switch (normalized) {
3809-
case "agentless":
3810-
case "remote_config":
3811-
case "offline":
3812-
return normalized;
3813-
default:
3814-
log.warn(
3815-
"Unsupported Feature Flagging configuration source: {}. Defaulting to {}",
3816-
source,
3817-
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE);
3818-
return DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE;
3819-
}
3820-
}
3821-
38223829
public boolean isBaggageExtract() {
38233830
return tracePropagationStylesToExtract.contains(TracePropagationStyle.BAGGAGE);
38243831
}
@@ -4736,6 +4743,10 @@ public int getRemoteConfigMaxExtraServices() {
47364743
return remoteConfigMaxExtraServices;
47374744
}
47384745

4746+
public boolean isFeatureFlaggingProviderEnabled() {
4747+
return featureFlaggingProviderEnabled;
4748+
}
4749+
47394750
public String getFeatureFlaggingConfigurationSource() {
47404751
return featureFlaggingConfigurationSource;
47414752
}
@@ -6584,6 +6595,8 @@ public String toString() {
65846595
+ remoteConfigMaxPayloadSize
65856596
+ ", remoteConfigIntegrityCheckEnabled="
65866597
+ remoteConfigIntegrityCheckEnabled
6598+
+ ", featureFlaggingProviderEnabled="
6599+
+ featureFlaggingProviderEnabled
65876600
+ ", featureFlaggingConfigurationSource="
65886601
+ featureFlaggingConfigurationSource
65896602
+ ", featureFlaggingConfigurationSourceAgentlessBaseUrl="

internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ import static datadog.trace.api.config.GeneralConfig.TAGS
5757
import static datadog.trace.api.config.GeneralConfig.TRACER_METRICS_IGNORED_RESOURCES
5858
import static datadog.trace.api.config.GeneralConfig.TRACE_OTEL_SEMANTICS_ENABLED
5959
import static datadog.trace.api.config.GeneralConfig.VERSION
60+
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED
6061
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE
6162
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS
6263
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS
64+
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED
6365
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_CHECK_PERIOD
6466
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_ENABLED
6567
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_METRICS_CONFIGS
@@ -3517,7 +3519,39 @@ class ConfigTest extends DDSpecification {
35173519
"" | "agentless"
35183520
" " | "agentless"
35193521
" ReMoTe_ConFiG " | "remote_config"
3520-
"not-a-real-source" | "agentless"
3522+
"not-a-real-source" | "offline"
3523+
}
3524+
3525+
def "feature flag configuration applies migration precedence"() {
3526+
setup:
3527+
Properties properties = new Properties()
3528+
if (providerEnabled != null) {
3529+
properties.setProperty(FEATURE_FLAGS_ENABLED, providerEnabled.toString())
3530+
}
3531+
if (source != null) {
3532+
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE, source)
3533+
}
3534+
if (legacyProviderEnabled != null) {
3535+
properties.setProperty(EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED, legacyProviderEnabled.toString())
3536+
}
3537+
3538+
when:
3539+
def config = new Config(ConfigProvider.withPropertiesOverride(properties))
3540+
3541+
then:
3542+
config.featureFlaggingProviderEnabled == expectedEnabled
3543+
config.featureFlaggingConfigurationSource == expectedSource
3544+
3545+
where:
3546+
providerEnabled | source | legacyProviderEnabled | expectedEnabled | expectedSource
3547+
null | null | null | true | "agentless"
3548+
null | null | true | true | "remote_config"
3549+
null | null | false | false | "offline"
3550+
null | "agentless" | true | true | "agentless"
3551+
null | "remote_config" | false | true | "remote_config"
3552+
false | "agentless" | true | false | "offline"
3553+
true | null | false | false | "offline"
3554+
null | "offline" | null | false | "offline"
35213555
}
35223556
35233557
def "agentless feature flag timing falls back for non-positive values"() {

metadata/supported-configurations.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,14 @@
14971497
"aliases": []
14981498
}
14991499
],
1500+
"DD_FEATURE_FLAGS_ENABLED": [
1501+
{
1502+
"version": "A",
1503+
"type": "boolean",
1504+
"default": "true",
1505+
"aliases": []
1506+
}
1507+
],
15001508
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE": [
15011509
{
15021510
"version": "A",
@@ -11986,5 +11994,7 @@
1198611994
}
1198711995
]
1198811996
},
11989-
"deprecations": {}
11997+
"deprecations": {
11998+
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED": "DD_FEATURE_FLAGS_ENABLED"
11999+
}
1199012000
}

products/feature-flagging/feature-flagging-agent/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ dependencies {
1717
api(libs.slf4j)
1818
api(project(":products:feature-flagging:feature-flagging-lib"))
1919
api(project(":internal-api"))
20+
compileOnly(project(":products:feature-flagging:feature-flagging-config"))
2021

2122
testImplementation(libs.bundles.junit5)
2223
testImplementation(libs.bundles.mockito)

0 commit comments

Comments
 (0)