Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@
/dd-openfeature/ @DataDog/feature-flagging-and-experimentation-sdk
/dd-smoke-tests/openfeature/ @DataDog/feature-flagging-and-experimentation-sdk
/dd-smoke-tests/openfeature-legacy/ @DataDog/feature-flagging-and-experimentation-sdk
/dd-trace-api/**/config/FeatureFlaggingConfig* @DataDog/feature-flagging-and-experimentation-sdk
/products/feature-flagging/ @DataDog/feature-flagging-and-experimentation-sdk

# @DataDog/profiling-java
Expand Down
1 change: 0 additions & 1 deletion dd-java-agent/agent-bootstrap/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ dependencies {
api project(':dd-java-agent:agent-debugger:debugger-bootstrap')
api project(':components:environment')
api project(':components:json')
api project(':products:feature-flagging:feature-flagging-config')
api project(':products:metrics:metrics-agent')
api libs.instrument.java
api libs.slf4j
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import datadog.trace.api.config.CrashTrackingConfig;
import datadog.trace.api.config.CwsConfig;
import datadog.trace.api.config.DebuggerConfig;
import datadog.trace.api.config.FeatureFlaggingConfig;
import datadog.trace.api.config.GeneralConfig;
import datadog.trace.api.config.IastConfig;
import datadog.trace.api.config.JmxFetchConfig;
Expand All @@ -44,7 +45,6 @@
import datadog.trace.api.config.TraceInstrumentationConfig;
import datadog.trace.api.config.TracerConfig;
import datadog.trace.api.config.UsmConfig;
import datadog.trace.api.featureflag.config.FeatureFlaggingConfig;
import datadog.trace.api.gateway.RequestContextSlot;
import datadog.trace.api.gateway.SubscriptionService;
import datadog.trace.api.git.EmbeddedGitInfoBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package datadog.trace.instrumentation.openfeature;

import static datadog.trace.api.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED;
import static java.util.Locale.ROOT;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.datadog.openfeature.internal.bridge.v1.AgentBridge;
import com.datadog.openfeature.internal.config.Settings;
import datadog.trace.api.InstrumenterConfig;
import datadog.trace.test.junit.utils.config.WithConfigExtension;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.extension.ExtendWith;
import org.tabletest.junit.TableTest;

/**
* Guards against drift between the agent ({@link InstrumenterConfig}) and the SDK ({@link
* Settings}) resolution of the Feature Flags product switch and configuration source. Both sides
* read the same raw settings, keyed by the agent setting names.
*/
@ExtendWith(WithConfigExtension.class)
class ConfigurationResolutionParityTest {
@TableTest({
"Scenario | Enabled | Source | Legacy | Expected ",
"defaults | | | | agentless ",
"enabled | true | | | agentless ",
"enabled as number | 1 | | | agentless ",
"disabled | false | | | ",
"disabled as number | 0 | | | ",
"disabled wins over source | false | remote_config | | ",
"remote config source | | remote_config | | remote_config",
"agentless source | | agentless | | agentless ",
"source is case insensitive | | Remote_Config | | remote_config",
"padded source is unsupported | | ' agentless ' | | ",
"unsupported source | | offline | | ",
"legacy enabled | | | true | remote_config",
"legacy disabled | | | false | ",
"source wins over legacy | | agentless | true | agentless ",
"source wins over legacy disabled | | remote_config | false | remote_config",
"enabled with legacy disabled | true | | false | ",
"uppercase boolean | TRUE | | TRUE | remote_config",
"invalid boolean reads as false | maybe | | | ",
"invalid legacy reads as false | | | maybe | ",
"padded enabled reads as false | ' true ' | | | ",
"padded number reads as false | ' 1 ' | | | ",
"padded legacy reads as false | | | ' true ' | "
})
void agentAndSdkResolveTheSameConfiguration(
final String enabled, final String source, final String legacy, final String expected) {
final Map<String, String> settings = new HashMap<>();
putIfNotNull(settings, FEATURE_FLAGS_ENABLED, enabled);
putIfNotNull(settings, FEATURE_FLAGS_CONFIGURATION_SOURCE, source);
putIfNotNull(settings, EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED, legacy);

assertEquals(expected, agentSource(settings), "agent resolution");
assertEquals(expected, sdkSource(settings), "SDK resolution");
}

/** Resolves the source like the agent, through {@link InstrumenterConfig}. */
private static String agentSource(final Map<String, String> settings) {
settings.forEach(WithConfigExtension::injectSysConfig);
final InstrumenterConfig config = InstrumenterConfig.get();
return config.isFeatureFlaggingEnabled()
? config.getFeatureFlaggingConfigurationSource().name().toLowerCase(ROOT)
: null;
}

/**
* Resolves the source like the SDK. Missing settings fall back to the system properties injected
* for the agent, which hold the same values.
*/
private static String sdkSource(final Map<String, String> settings) {
final AgentBridge bridge = mock(AgentBridge.class);
when(bridge.setting(anyString()))
.thenAnswer(invocation -> settings.get(invocation.<String>getArgument(0)));
return Settings.load(bridge).configurationSource();
}

private static void putIfNotNull(
final Map<String, String> map, final String key, final String value) {
if (value != null) {
map.put(key, value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package datadog.trace.api.config;

/** Constant with names of configuration options for Feature Flags. */
public final class FeatureFlaggingConfig {

public static final String FEATURE_FLAGS_ENABLED = "feature.flags.enabled";

/** Use {@link #FEATURE_FLAGS_ENABLED} and {@link #FEATURE_FLAGS_CONFIGURATION_SOURCE}. */
@Deprecated
public static final String EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED =
"experimental.flagging.provider.enabled";

public static final String FEATURE_FLAGS_CONFIGURATION_SOURCE =
"feature.flags.configuration.source";

private FeatureFlaggingConfig() {}
}
1 change: 0 additions & 1 deletion gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ final class CachedData {
filter.exclude(filter.project(':components:environment'))
filter.exclude(filter.project(':components:json'))
filter.exclude(filter.project(':products:feature-flagging:feature-flagging-bootstrap'))
filter.exclude(filter.project(':products:feature-flagging:feature-flagging-config'))
filter.exclude(filter.project(':products:metrics:metrics-agent'))
filter.exclude(filter.project(':products:metrics:metrics-api'))
filter.exclude(filter.project(':products:metrics:metrics-lib'))
Expand Down
1 change: 0 additions & 1 deletion internal-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ dependencies {
api(project(":components:context"))
api(project(":components:environment"))
api(project(":components:json"))
implementation(project(":products:feature-flagging:feature-flagging-config"))
api(project(":utils:config-utils"))
api(project(":utils:time-utils"))

Expand Down
11 changes: 5 additions & 6 deletions internal-api/src/main/java/datadog/trace/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@
import static datadog.trace.api.config.DebuggerConfig.THIRD_PARTY_EXCLUDES;
import static datadog.trace.api.config.DebuggerConfig.THIRD_PARTY_INCLUDES;
import static datadog.trace.api.config.DebuggerConfig.THIRD_PARTY_SHADING_IDENTIFIERS;
import static datadog.trace.api.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED;
import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_LEVEL;
import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_QUEUE_SIZE;
import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL;
Expand Down Expand Up @@ -742,9 +745,6 @@
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_ENABLED;
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;
Expand Down Expand Up @@ -4888,9 +4888,8 @@ public boolean isFeatureFlaggingProviderEnabled() {
return instrumenterConfig.isFeatureFlaggingEnabled();
}

public String getFeatureFlaggingConfigurationSource() {
final FeatureFlaggingSource source = instrumenterConfig.getFeatureFlaggingConfigurationSource();
return source == FeatureFlaggingSource.UNSET ? null : source.name().toLowerCase(Locale.ROOT);
public FeatureFlaggingSource getFeatureFlaggingConfigurationSource() {
return instrumenterConfig.getFeatureFlaggingConfigurationSource();
}

public boolean isDynamicInstrumentationEnabled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
import static datadog.trace.api.config.AppSecConfig.APPSEC_ENABLED;
import static datadog.trace.api.config.AppSecConfig.APPSEC_RASP_ENABLED;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_ENABLED;
import static datadog.trace.api.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED;
import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_ENABLED;
import static datadog.trace.api.config.GeneralConfig.APP_LOGS_COLLECTION_ENABLED;
import static datadog.trace.api.config.GeneralConfig.DATA_JOBS_ENABLED;
Expand Down Expand Up @@ -103,9 +106,6 @@
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;
Expand Down
45 changes: 23 additions & 22 deletions internal-api/src/test/java/datadog/trace/api/ConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_TIMEOUT;
import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_VERIFY_BYTECODE;
import static datadog.trace.api.config.DebuggerConfig.EXCEPTION_REPLAY_ENABLED;
import static datadog.trace.api.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED;
import static datadog.trace.api.config.GeneralConfig.API_KEY;
import static datadog.trace.api.config.GeneralConfig.API_KEY_FILE;
import static datadog.trace.api.config.GeneralConfig.CONFIGURATION_FILE;
Expand Down Expand Up @@ -166,9 +169,6 @@
import static datadog.trace.api.config.TracerConfig.TRACE_X_DATADOG_TAGS_MAX_LENGTH;
import static datadog.trace.api.config.TracerConfig.WRITER_TYPE;
import static datadog.trace.api.env.FixedCapturedEnvironment.useFixedEnv;
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.bootstrap.config.provider.ConfigProvider.withPropertiesOverride;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
Expand Down Expand Up @@ -3679,15 +3679,16 @@ void traceResourceRenamingAlwaysSimplifiedEndpointWithValue(String value, boolea

@TableTest({
"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 "
"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 featureFlagConfigurationSourceParsesValue(String value, String expected) throws Exception {
void featureFlagConfigurationSourceParsesValue(String value, FeatureFlaggingSource expected)
throws Exception {
Properties properties = new Properties();
if (value != null) {
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE, value);
Expand All @@ -3700,23 +3701,23 @@ void featureFlagConfigurationSourceParsesValue(String value, String expected) th

@TableTest({
"scenario | providerEnabled | source | legacyProviderEnabled | expectedEnabled | expectedSource",
"nothing set | | | | true | agentless ",
"provider enabled | true | | | true | agentless ",
"legacy enabled | | | true | true | remote_config ",
"legacy disabled | | | false | false | ",
"source wins over legacy enabled | | agentless | true | true | agentless ",
"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 | invalid ",
"offline source | | offline | true | false | invalid "
"nothing set | | | | true | AGENTLESS ",
"provider enabled | true | | | true | AGENTLESS ",
"legacy enabled | | | true | true | REMOTE_CONFIG ",
"legacy disabled | | | false | false | UNSET ",
"source wins over legacy enabled | | agentless | true | true | AGENTLESS ",
"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 | UNSET ",
"unknown source | | not-a-source | | false | INVALID ",
"offline source | | offline | true | false | INVALID "
})
void featureFlagConfigurationAppliesMigrationPrecedence(
Boolean providerEnabled,
String source,
Boolean legacyProviderEnabled,
boolean expectedEnabled,
String expectedSource)
FeatureFlaggingSource expectedSource)
throws Exception {
Properties properties = new Properties();
if (providerEnabled != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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 datadog.trace.api.config.FeatureFlaggingConfig.EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_ENABLED;
import static org.junit.jupiter.api.Assertions.assertEquals;

import datadog.trace.bootstrap.config.provider.ConfigProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ 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)
testImplementation(project(":products:feature-flagging:feature-flagging-config"))
testImplementation(project(":utils:test-utils"))
testRuntimeOnly(project(":dd-trace-core"))
}
Expand Down
Loading
Loading