Skip to content

Commit b8bbd26

Browse files
committed
Define Feature Flagging configuration source contract
1 parent 0c91932 commit b8bbd26

10 files changed

Lines changed: 165 additions & 3 deletions

File tree

dd-smoke-tests/openfeature/src/test/groovy/datadog/smoketest/springboot/OpenFeatureProviderSmokeTest.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class OpenFeatureProviderSmokeTest extends AbstractServerSmokeTest {
4242
command.addAll(['-jar', springBootShadowJar, "--server.port=${httpPort}".toString()])
4343
final builder = new ProcessBuilder(command).directory(new File(buildDirectory))
4444
builder.environment().put('DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED', 'true')
45+
builder.environment().put('DD_FEATURE_FLAGS_CONFIGURATION_SOURCE', 'remote_config')
4546
return builder
4647
}
4748

dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public final class ConfigDefaults {
4949
static final boolean DEFAULT_INJECT_DATADOG_ATTRIBUTE = true;
5050
static final String DEFAULT_SITE = "datadoghq.com";
5151

52+
public static final String DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE = "agentless";
53+
public static final int DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS = 30;
54+
public static final int DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS = 2;
55+
5256
static final boolean DEFAULT_CODE_ORIGIN_FOR_SPANS_INTERFACE_SUPPORT = false;
5357
static final int DEFAULT_CODE_ORIGIN_MAX_USER_FRAMES = 8;
5458
static final boolean DEFAULT_TRACE_ENABLED = true;

dd-trace-api/src/main/java/datadog/trace/api/config/FeatureFlaggingConfig.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,15 @@
33
public class FeatureFlaggingConfig {
44

55
public static final String FLAGGING_PROVIDER_ENABLED = "experimental.flagging.provider.enabled";
6+
7+
public static final String FEATURE_FLAGS_CONFIGURATION_SOURCE =
8+
"feature.flags.configuration.source";
9+
public static final String FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL =
10+
"feature.flags.configuration.source.agentless.base.url";
11+
public static final String FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS =
12+
"feature.flags.configuration.source.agentless.poll.interval.seconds";
13+
public static final String FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS =
14+
"feature.flags.configuration.source.agentless.request.timeout.seconds";
15+
16+
private FeatureFlaggingConfig() {}
617
}

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

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@
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;
90+
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS;
91+
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS;
8992
import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_CLIENT_ERROR_STATUSES;
9093
import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_SERVER_ERROR_STATUSES;
9194
import static datadog.trace.api.ConfigDefaults.DEFAULT_HEALTH_METRICS_ENABLED;
@@ -361,6 +364,10 @@
361364
import static datadog.trace.api.config.DebuggerConfig.THIRD_PARTY_EXCLUDES;
362365
import static datadog.trace.api.config.DebuggerConfig.THIRD_PARTY_INCLUDES;
363366
import static datadog.trace.api.config.DebuggerConfig.THIRD_PARTY_SHADING_IDENTIFIERS;
367+
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
368+
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL;
369+
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS;
370+
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS;
364371
import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_LEVEL;
365372
import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_QUEUE_SIZE;
366373
import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_URL;
@@ -1213,6 +1220,11 @@ public static String getHostName() {
12131220

12141221
private final int remoteConfigMaxExtraServices;
12151222

1223+
private final String featureFlaggingConfigurationSource;
1224+
private final String featureFlaggingConfigurationSourceAgentlessBaseUrl;
1225+
private final int featureFlaggingConfigurationSourcePollIntervalSeconds;
1226+
private final int featureFlaggingConfigurationSourceRequestTimeoutSeconds;
1227+
12161228
private final boolean dbmInjectSqlBaseHash;
12171229
private final String dbmPropagationMode;
12181230
private final boolean dbmTracePreparedStatements;
@@ -2837,6 +2849,40 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())
28372849
configProvider.getInteger(
28382850
REMOTE_CONFIG_MAX_EXTRA_SERVICES, DEFAULT_REMOTE_CONFIG_MAX_EXTRA_SERVICES);
28392851

2852+
featureFlaggingConfigurationSource =
2853+
normalizeFeatureFlaggingConfigurationSource(
2854+
configProvider.getString(
2855+
FEATURE_FLAGS_CONFIGURATION_SOURCE, DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE));
2856+
featureFlaggingConfigurationSourceAgentlessBaseUrl =
2857+
configProvider.getStringNotEmpty(
2858+
FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL, null);
2859+
int configuredFeatureFlaggingPollIntervalSeconds =
2860+
configProvider.getInteger(
2861+
FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS,
2862+
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS);
2863+
if (configuredFeatureFlaggingPollIntervalSeconds <= 0) {
2864+
log.warn(
2865+
"Invalid Feature Flagging agentless poll interval: {}. The value must be positive",
2866+
configuredFeatureFlaggingPollIntervalSeconds);
2867+
configuredFeatureFlaggingPollIntervalSeconds =
2868+
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS;
2869+
}
2870+
featureFlaggingConfigurationSourcePollIntervalSeconds =
2871+
configuredFeatureFlaggingPollIntervalSeconds;
2872+
int configuredFeatureFlaggingRequestTimeoutSeconds =
2873+
configProvider.getInteger(
2874+
FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS,
2875+
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS);
2876+
if (configuredFeatureFlaggingRequestTimeoutSeconds <= 0) {
2877+
log.warn(
2878+
"Invalid Feature Flagging agentless request timeout: {}. The value must be positive",
2879+
configuredFeatureFlaggingRequestTimeoutSeconds);
2880+
configuredFeatureFlaggingRequestTimeoutSeconds =
2881+
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS;
2882+
}
2883+
featureFlaggingConfigurationSourceRequestTimeoutSeconds =
2884+
configuredFeatureFlaggingRequestTimeoutSeconds;
2885+
28402886
dynamicInstrumentationEnabled =
28412887
configProvider.getBoolean(
28422888
DYNAMIC_INSTRUMENTATION_ENABLED, DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED);
@@ -3749,6 +3795,14 @@ public boolean isInferredProxyPropagationEnabled() {
37493795
return traceInferredProxyEnabled;
37503796
}
37513797

3798+
private static String normalizeFeatureFlaggingConfigurationSource(final String source) {
3799+
if (source == null) {
3800+
return DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE;
3801+
}
3802+
final String normalized = source.trim().toLowerCase(Locale.ROOT);
3803+
return normalized.isEmpty() ? DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE : normalized;
3804+
}
3805+
37523806
public boolean isBaggageExtract() {
37533807
return tracePropagationStylesToExtract.contains(TracePropagationStyle.BAGGAGE);
37543808
}
@@ -4656,6 +4710,22 @@ public int getRemoteConfigMaxExtraServices() {
46564710
return remoteConfigMaxExtraServices;
46574711
}
46584712

4713+
public String getFeatureFlaggingConfigurationSource() {
4714+
return featureFlaggingConfigurationSource;
4715+
}
4716+
4717+
public String getFeatureFlaggingConfigurationSourceAgentlessBaseUrl() {
4718+
return featureFlaggingConfigurationSourceAgentlessBaseUrl;
4719+
}
4720+
4721+
public int getFeatureFlaggingConfigurationSourcePollIntervalSeconds() {
4722+
return featureFlaggingConfigurationSourcePollIntervalSeconds;
4723+
}
4724+
4725+
public int getFeatureFlaggingConfigurationSourceRequestTimeoutSeconds() {
4726+
return featureFlaggingConfigurationSourceRequestTimeoutSeconds;
4727+
}
4728+
46594729
public boolean isDynamicInstrumentationEnabled() {
46604730
return dynamicInstrumentationEnabled;
46614731
}
@@ -6488,6 +6558,14 @@ public String toString() {
64886558
+ remoteConfigMaxPayloadSize
64896559
+ ", remoteConfigIntegrityCheckEnabled="
64906560
+ remoteConfigIntegrityCheckEnabled
6561+
+ ", featureFlaggingConfigurationSource="
6562+
+ featureFlaggingConfigurationSource
6563+
+ ", featureFlaggingConfigurationSourceAgentlessBaseUrl="
6564+
+ featureFlaggingConfigurationSourceAgentlessBaseUrl
6565+
+ ", featureFlaggingConfigurationSourcePollIntervalSeconds="
6566+
+ featureFlaggingConfigurationSourcePollIntervalSeconds
6567+
+ ", featureFlaggingConfigurationSourceRequestTimeoutSeconds="
6568+
+ featureFlaggingConfigurationSourceRequestTimeoutSeconds
64916569
+ ", debuggerEnabled="
64926570
+ dynamicInstrumentationEnabled
64936571
+ ", debuggerUploadTimeout="

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package datadog.trace.api
22

33
import static datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_CLIENT_ERROR_STATUSES
44
import static datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_SERVER_ERROR_STATUSES
5+
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS
6+
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS
57
import static datadog.trace.api.ConfigDefaults.DEFAULT_PARTIAL_FLUSH_MIN_SPANS
68
import static datadog.trace.api.ConfigDefaults.DEFAULT_SERVICE_NAME
79
import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL
@@ -55,6 +57,8 @@ import static datadog.trace.api.config.GeneralConfig.TAGS
5557
import static datadog.trace.api.config.GeneralConfig.TRACER_METRICS_IGNORED_RESOURCES
5658
import static datadog.trace.api.config.GeneralConfig.TRACE_OTEL_SEMANTICS_ENABLED
5759
import static datadog.trace.api.config.GeneralConfig.VERSION
60+
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS
61+
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS
5862
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_CHECK_PERIOD
5963
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_ENABLED
6064
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_METRICS_CONFIGS
@@ -3478,4 +3482,32 @@ class ConfigTest extends DDSpecification {
34783482
"1" | true
34793483
"0" | false
34803484
}
3485+
3486+
def "agentless feature flag timing uses positive configured values"() {
3487+
setup:
3488+
Properties properties = new Properties()
3489+
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS, "60")
3490+
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS, "4")
3491+
3492+
when:
3493+
def config = new Config(ConfigProvider.withPropertiesOverride(properties))
3494+
3495+
then:
3496+
config.featureFlaggingConfigurationSourcePollIntervalSeconds == 60
3497+
config.featureFlaggingConfigurationSourceRequestTimeoutSeconds == 4
3498+
}
3499+
3500+
def "agentless feature flag timing falls back for non-positive values"() {
3501+
setup:
3502+
Properties properties = new Properties()
3503+
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS, "0")
3504+
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS, "-1")
3505+
3506+
when:
3507+
def config = new Config(ConfigProvider.withPropertiesOverride(properties))
3508+
3509+
then:
3510+
config.featureFlaggingConfigurationSourcePollIntervalSeconds == DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS
3511+
config.featureFlaggingConfigurationSourceRequestTimeoutSeconds == DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS
3512+
}
34813513
}

metadata/supported-configurations.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,38 @@
14971497
"aliases": []
14981498
}
14991499
],
1500+
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE": [
1501+
{
1502+
"version": "A",
1503+
"type": "string",
1504+
"default": "agentless",
1505+
"aliases": []
1506+
}
1507+
],
1508+
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL": [
1509+
{
1510+
"version": "A",
1511+
"type": "string",
1512+
"default": null,
1513+
"aliases": []
1514+
}
1515+
],
1516+
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS": [
1517+
{
1518+
"version": "A",
1519+
"type": "int",
1520+
"default": "30",
1521+
"aliases": []
1522+
}
1523+
],
1524+
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS": [
1525+
{
1526+
"version": "A",
1527+
"type": "int",
1528+
"default": "2",
1529+
"aliases": []
1530+
}
1531+
],
15001532
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED": [
15011533
{
15021534
"version": "A",

products/feature-flagging/feature-flagging-agent/src/main/java/com/datadog/featureflag/FeatureFlaggingSystem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class FeatureFlaggingSystem {
99

1010
private static final Logger LOGGER = LoggerFactory.getLogger(FeatureFlaggingSystem.class);
1111

12-
private static volatile RemoteConfigService CONFIG_SERVICE;
12+
private static volatile ConfigurationSourceService CONFIG_SERVICE;
1313
private static volatile ExposureWriter EXPOSURE_WRITER;
1414

1515
private FeatureFlaggingSystem() {}

products/feature-flagging/feature-flagging-agent/src/test/java/com/datadog/featureflag/FeatureFlaggingSystemTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.datadog.featureflag;
22

3+
import static datadog.trace.api.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
34
import static datadog.trace.api.config.RemoteConfigConfig.REMOTE_CONFIGURATION_ENABLED;
45
import static org.junit.jupiter.api.Assertions.assertThrows;
56
import static org.mockito.ArgumentMatchers.any;
@@ -23,6 +24,8 @@
2324
class FeatureFlaggingSystemTest {
2425

2526
@Test
27+
@WithConfig(key = FEATURE_FLAGS_CONFIGURATION_SOURCE, value = "remote_config")
28+
@WithConfig(key = REMOTE_CONFIGURATION_ENABLED, value = "true")
2629
void testFeatureFlagSystemInitialization() {
2730
ConfigurationPoller poller = mock(ConfigurationPoller.class);
2831
DDAgentFeaturesDiscovery discovery = mock(DDAgentFeaturesDiscovery.class);
@@ -48,6 +51,7 @@ void testFeatureFlagSystemInitialization() {
4851
}
4952

5053
@Test
54+
@WithConfig(key = FEATURE_FLAGS_CONFIGURATION_SOURCE, value = "remote_config")
5155
@WithConfig(key = REMOTE_CONFIGURATION_ENABLED, value = "false")
5256
void testThatRemoteConfigIsRequired() {
5357
SharedCommunicationObjects sharedCommunicationObjects = mock(SharedCommunicationObjects.class);

products/feature-flagging/feature-flagging-lib/src/main/java/com/datadog/featureflag/RemoteConfigService.java renamed to products/feature-flagging/feature-flagging-lib/src/main/java/com/datadog/featureflag/ConfigurationSourceService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.io.Closeable;
44

5-
public interface RemoteConfigService extends Closeable {
5+
public interface ConfigurationSourceService extends Closeable {
66

77
void init();
88

products/feature-flagging/feature-flagging-lib/src/main/java/com/datadog/featureflag/RemoteConfigServiceImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import okio.Okio;
3333

3434
public class RemoteConfigServiceImpl
35-
implements RemoteConfigService, ConfigurationChangesTypedListener<ServerConfiguration> {
35+
implements ConfigurationSourceService, ConfigurationChangesTypedListener<ServerConfiguration> {
3636

3737
private final ConfigurationPoller configurationPoller;
3838

0 commit comments

Comments
 (0)