From b11330b31b5d1f92be79cdbea134bea51ed81ae3 Mon Sep 17 00:00:00 2001 From: Bruce Bujon Date: Wed, 21 Oct 2026 13:47:21 +0200 Subject: [PATCH 1/5] feat(ffe): Keep released dd-openfeature SDKs working with the agent Released providers, from 1.56 to 1.68, link to the agent bootstrap API the bridge replaced: make them return the caller defaults instead of failing. Co-Authored-By: Claude Opus 5.5 --- .../bytebuddy/matcher/ignored_class_name.trie | 2 + .../dd-openfeature-1.56/build.gradle | 55 ++++++++ .../dd-openfeature-1.56/gradle.lockfile | 127 ++++++++++++++++++ .../LegacyProviderInstrumentation.java | 59 ++++++++ .../openfeature/LegacyProviderAdvice.java | 45 +++++++ .../LegacyProviderDisabledForkedTest.java | 6 + .../LegacyProviderInstrumentationTest.java | 98 ++++++++++++++ settings.gradle.kts | 1 + 8 files changed, 393 insertions(+) create mode 100644 dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle create mode 100644 dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/gradle.lockfile create mode 100644 dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java create mode 100644 dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java create mode 100644 dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderDisabledForkedTest.java create mode 100644 dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentationTest.java diff --git a/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/ignored_class_name.trie b/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/ignored_class_name.trie index bf582679d58..7a57990c9f5 100644 --- a/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/ignored_class_name.trie +++ b/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/ignored_class_name.trie @@ -36,6 +36,8 @@ 1 datadog.opentracing.* 1 datadog.slf4j.* 1 datadog.trace.* +# Legacy OpenFeature providers need compatibility advice after their bootstrap API was removed. +0 datadog.trace.api.openfeature.Provider # remove this once https://github.com/raphw/byte-buddy/issues/558 is fixed 0 datadog.trace.bootstrap.instrumentation.java.concurrent.RunnableWrapper 1 datadog.trace.core.* diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle new file mode 100644 index 00000000000..958ce006ea3 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle @@ -0,0 +1,55 @@ +plugins { + id 'dd-trace-java.module.instrumentation' + id 'idea' +} + +idea { + module { + jdkName = '11' + } +} + +// The providers released before the SDK bridge, linked to the agent bootstrap API it removed. +muzzle { + pass { + group = 'com.datadoghq' + module = 'dd-openfeature' + versions = '[1.56.0,1.69.0)' + } +} + +tracerJava { + addSourceSetFor(JavaVersion.VERSION_11) +} + +testJvmConstraints { + minJavaVersion = JavaVersion.VERSION_11 +} + +// Load a released provider in its own classloader, as an application does. +def legacySdkDependency = configurations.dependencyScope('legacySdkDependency') +def legacySdk = configurations.resolvable('legacySdk') { + extendsFrom legacySdkDependency.get() + transitive = false +}.get() + +dependencies { + add('legacySdkDependency', 'com.datadoghq:dd-openfeature:1.67.0') + main_java11CompileOnly 'com.datadoghq:dd-openfeature:1.56.0' + + testImplementation 'dev.openfeature:sdk:1.20.1' +} + +tasks.named("compileMain_java11Java", JavaCompile) { + configureCompiler(it, JavaVersion.VERSION_11) +} + +tasks.named("compileTestJava", JavaCompile) { + configureCompiler(it, JavaVersion.VERSION_11) +} + +tasks.named('processTestResources', ProcessResources) { + from(legacySdk) { + into 'legacy-sdk' + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/gradle.lockfile b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/gradle.lockfile new file mode 100644 index 00000000000..09b1cf44139 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/gradle.lockfile @@ -0,0 +1,127 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +# To regenerate this file, run: ./gradlew :dd-java-agent:instrumentation:datadog:openfeature:dd-openfeature-1.56:dependencies --write-locks +cafe.cryptography:curve25519-elisabeth:0.1.0=testRuntimeClasspath +cafe.cryptography:ed25519-elisabeth:0.1.0=testRuntimeClasspath +ch.qos.logback:logback-classic:1.2.13=testCompileClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.2.13=testCompileClasspath,testRuntimeClasspath +com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq.okhttp3:okhttp:3.12.15=testCompileClasspath,testRuntimeClasspath +com.datadoghq.okio:okio:1.17.6=testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-instrument-java:0.0.6=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-openfeature:1.56.0=main_java11CompileClasspath +com.datadoghq:dd-openfeature:1.67.0=legacySdk +com.datadoghq:java-dogstatsd-client:4.4.5=testRuntimeClasspath +com.datadoghq:sketches-java:0.8.3=testRuntimeClasspath +com.github.javaparser:javaparser-core:3.25.6=codenarc +com.github.jnr:jffi:1.3.15=testRuntimeClasspath +com.github.jnr:jnr-a64asm:1.0.0=testRuntimeClasspath +com.github.jnr:jnr-constants:0.10.4=testRuntimeClasspath +com.github.jnr:jnr-enxio:0.32.20=testRuntimeClasspath +com.github.jnr:jnr-ffi:2.2.19=testRuntimeClasspath +com.github.jnr:jnr-posix:3.1.22=testRuntimeClasspath +com.github.jnr:jnr-unixsocket:0.38.25=testRuntimeClasspath +com.github.jnr:jnr-x86asm:1.0.2=testRuntimeClasspath +com.github.spotbugs:spotbugs-annotations:4.10.4=compileClasspath,spotbugs +com.github.spotbugs:spotbugs:4.10.4=spotbugs +com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs +com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,testAnnotationProcessor,testCompileClasspath +com.google.auto.service:auto-service:1.1.1=annotationProcessor,testAnnotationProcessor +com.google.auto:auto-common:1.2.1=annotationProcessor,testAnnotationProcessor +com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +com.google.code.gson:gson:2.14.0=spotbugs +com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,testAnnotationProcessor +com.google.errorprone:error_prone_annotations:2.47.0=testCompileClasspath,testRuntimeClasspath +com.google.errorprone:error_prone_annotations:2.48.0=spotbugs +com.google.guava:failureaccess:1.0.1=annotationProcessor,testAnnotationProcessor +com.google.guava:failureaccess:1.0.3=testCompileClasspath,testRuntimeClasspath +com.google.guava:guava:32.0.1-jre=annotationProcessor,testAnnotationProcessor +com.google.guava:guava:33.6.0-jre=testCompileClasspath,testRuntimeClasspath +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,testAnnotationProcessor +com.google.j2objc:j2objc-annotations:3.1=testCompileClasspath,testRuntimeClasspath +com.google.re2j:re2j:1.8=testRuntimeClasspath +com.squareup.moshi:moshi:1.11.0=testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:logging-interceptor:3.12.12=testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp:3.12.12=testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio:1.17.5=testCompileClasspath,testRuntimeClasspath +com.thoughtworks.qdox:qdox:1.12.1=codenarc +commons-fileupload:commons-fileupload:1.5=testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.11.0=testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.21.0=spotbugs +de.thetaphi:forbiddenapis:3.11=compileClasspath,testCompileClasspath,testRuntimeClasspath +dev.openfeature:sdk:1.18.2=main_java11CompileClasspath +dev.openfeature:sdk:1.20.1=testCompileClasspath,testRuntimeClasspath +io.leangen.geantyref:geantyref:1.3.16=testRuntimeClasspath +io.sqreen:libsqreen:17.5.0=testRuntimeClasspath +javax.servlet:javax.servlet-api:3.1.0=testCompileClasspath,testRuntimeClasspath +jaxen:jaxen:2.0.6=spotbugs +junit:junit:4.13.2=testRuntimeClasspath +net.bytebuddy:byte-buddy-agent:1.18.12=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.18.12=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.java.dev.jna:jna-platform:5.8.0=testRuntimeClasspath +net.java.dev.jna:jna:5.8.0=testRuntimeClasspath +net.sf.saxon:Saxon-HE:12.10=spotbugs +org.apache.ant:ant-antlr:1.10.14=codenarc +org.apache.ant:ant-junit:1.10.14=codenarc +org.apache.bcel:bcel:6.12.0=spotbugs +org.apache.commons:commons-lang3:3.20.0=spotbugs +org.apache.commons:commons-text:1.15.0=spotbugs +org.apache.logging.log4j:log4j-api:2.26.1=spotbugs +org.apache.logging.log4j:log4j-core:2.26.1=spotbugs +org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testRuntimeClasspath +org.checkerframework:checker-qual:3.33.0=annotationProcessor,testAnnotationProcessor +org.codehaus.groovy:groovy-ant:3.0.23=codenarc +org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc +org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc +org.codehaus.groovy:groovy-json:3.0.23=codenarc +org.codehaus.groovy:groovy-json:3.0.25=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-templates:3.0.23=codenarc +org.codehaus.groovy:groovy-xml:3.0.23=codenarc +org.codehaus.groovy:groovy:3.0.23=codenarc +org.codehaus.groovy:groovy:3.0.25=testCompileClasspath,testRuntimeClasspath +org.codenarc:CodeNarc:3.7.0=codenarc +org.dom4j:dom4j:2.2.0=spotbugs +org.gmetrics:GMetrics:2.1.0=codenarc +org.hamcrest:hamcrest-core:1.3=testRuntimeClasspath +org.hamcrest:hamcrest:3.0=testCompileClasspath,testRuntimeClasspath +org.jctools:jctools-core-jdk11:4.0.6=testRuntimeClasspath +org.jctools:jctools-core:4.0.6=testRuntimeClasspath +org.jspecify:jspecify:1.0.0=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:5.14.1=testRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-commons:1.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:1.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-launcher:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-runner:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-suite-api:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-suite-commons:1.14.1=testRuntimeClasspath +org.junit:junit-bom:5.14.1=testCompileClasspath,testRuntimeClasspath +org.mockito:mockito-core:4.4.0=testRuntimeClasspath +org.objenesis:objenesis:3.3=testCompileClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-analysis:9.10.1=spotbugs +org.ow2.asm:asm-analysis:9.7.1=testRuntimeClasspath +org.ow2.asm:asm-commons:9.10.1=spotbugs,testRuntimeClasspath +org.ow2.asm:asm-tree:9.10.1=spotbugs,testRuntimeClasspath +org.ow2.asm:asm-util:9.10.1=spotbugs +org.ow2.asm:asm-util:9.7.1=testRuntimeClasspath +org.ow2.asm:asm:9.10.1=buildTimeInstrumentationPlugin,compileClasspath,main_java11CompileClasspath,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +org.slf4j:jcl-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:jul-to-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:log4j-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,compileClasspath,main_java11RuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath +org.slf4j:slf4j-api:2.0.17=main_java11CompileClasspath,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.18=spotbugs +org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j +org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testRuntimeClasspath +org.spockframework:spock-bom:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath +org.spockframework:spock-core:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-junit:1.2.2=testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-parser:1.2.1=testCompileClasspath,testRuntimeClasspath +org.xmlresolver:xmlresolver:5.3.3=spotbugs +empty=main_java11AnnotationProcessor,spotbugsPlugins diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java new file mode 100644 index 00000000000..c7e6743de60 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java @@ -0,0 +1,59 @@ +package datadog.trace.instrumentation.openfeature; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresMethod; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import java.util.Set; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +/** + * Keeps legacy providers usable as default-value providers after their bootstrap API is removed. + */ +@AutoService(InstrumenterModule.class) +public class LegacyProviderInstrumentation extends InstrumenterModule + implements Instrumenter.ForSingleType, + Instrumenter.WithTypeStructure, + Instrumenter.HasMethodAdvice { + public LegacyProviderInstrumentation() { + super("openfeature"); + } + + @Override + public boolean isApplicable(final Set enabledSystems) { + // Application safety must not depend on whether Feature Flags is enabled. + return true; + } + + @Override + public String instrumentedType() { + return "datadog.trace.api.openfeature.Provider"; + } + + @Override + public ElementMatcher structureMatcher() { + // The current SDK's deprecated alias has no legacy evaluator to replace. + return declaresMethod(named("loadEvaluatorClass").and(takesArguments(0))); + } + + @Override + public void methodAdvice(final MethodTransformer transformer) { + transformer.applyAdvice( + named("initialize").and(takesArguments(1)), + packageName + ".LegacyProviderAdvice$Initialize"); + transformer.applyAdvice( + namedOneOf( + "getBooleanEvaluation", + "getStringEvaluation", + "getIntegerEvaluation", + "getDoubleEvaluation", + "getObjectEvaluation") + .and(takesArguments(3)), + packageName + ".LegacyProviderAdvice$Evaluation"); + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java new file mode 100644 index 00000000000..d9e9127b842 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java @@ -0,0 +1,45 @@ +package datadog.trace.instrumentation.openfeature; + +import dev.openfeature.sdk.ErrorCode; +import dev.openfeature.sdk.ProviderEvaluation; +import dev.openfeature.sdk.Reason; +import net.bytebuddy.asm.Advice; +import org.slf4j.LoggerFactory; + +/** Avoids linking legacy evaluators to the removed agent bootstrap API. */ +public class LegacyProviderAdvice { + public static class Initialize { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + public static boolean skipLegacyInitialization() { + return true; + } + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void warn() { + LoggerFactory.getLogger("datadog.trace.api.openfeature.Provider") + .warn( + "This dd-openfeature version is incompatible with the Datadog Java agent;" + + " returning caller defaults. Upgrade dd-openfeature to enable Feature Flags."); + } + } + + public static class Evaluation { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + public static boolean skipLegacyEvaluation() { + return true; + } + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void returnDefault( + @Advice.Argument(1) final Object defaultValue, + @Advice.Return(readOnly = false) ProviderEvaluation evaluation) { + evaluation = + ProviderEvaluation.builder() + .value(defaultValue) + .reason(Reason.ERROR.name()) + .errorCode(ErrorCode.PROVIDER_NOT_READY) + .errorMessage("Upgrade dd-openfeature to enable Feature Flags with this Java agent") + .build(); + } + } +} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderDisabledForkedTest.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderDisabledForkedTest.java new file mode 100644 index 00000000000..17a2d4abadb --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderDisabledForkedTest.java @@ -0,0 +1,6 @@ +package datadog.trace.instrumentation.openfeature; + +import datadog.trace.test.junit.utils.config.WithConfig; + +@WithConfig(key = "feature.flags.enabled", value = "false") +class LegacyProviderDisabledForkedTest extends LegacyProviderInstrumentationTest {} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentationTest.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentationTest.java new file mode 100644 index 00000000000..46a39b2f14b --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentationTest.java @@ -0,0 +1,98 @@ +package datadog.trace.instrumentation.openfeature; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import datadog.trace.agent.test.AbstractInstrumentationTest; +import dev.openfeature.sdk.Client; +import dev.openfeature.sdk.ErrorCode; +import dev.openfeature.sdk.FeatureProvider; +import dev.openfeature.sdk.FlagEvaluationDetails; +import dev.openfeature.sdk.ImmutableStructure; +import dev.openfeature.sdk.OpenFeatureAPI; +import dev.openfeature.sdk.ProviderState; +import dev.openfeature.sdk.Value; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +class LegacyProviderInstrumentationTest extends AbstractInstrumentationTest { + @Test + @Timeout(10) + void initializesTheReleasedProviderWithoutLoadingItsLegacyEvaluator() throws Exception { + final URL jar = getClass().getResource("/legacy-sdk/dd-openfeature-1.67.0.jar"); + assertNotNull(jar); + final OpenFeatureAPI api = OpenFeatureAPI.getInstance(); + try (final LegacySdkLoader loader = new LegacySdkLoader(jar, getClass().getClassLoader())) { + try { + final FeatureProvider provider = + (FeatureProvider) + loader + .loadClass("datadog.trace.api.openfeature.Provider") + .getConstructor() + .newInstance(); + assertDoesNotThrow(() -> api.setProviderAndWait(provider)); + final Client client = api.getClient(); + assertEquals(ProviderState.READY, client.getProviderState()); + final Value object = new Value(new ImmutableStructure(Map.of("fallback", new Value(true)))); + final List> evaluations = + List.of( + client.getBooleanDetails("boolean", true), + client.getStringDetails("string", "fallback"), + client.getIntegerDetails("integer", -1), + client.getDoubleDetails("double", -1.0), + client.getObjectDetails("object", object)); + final List defaults = List.of(true, "fallback", -1, -1.0, object); + for (int i = 0; i < evaluations.size(); i++) { + final FlagEvaluationDetails evaluation = evaluations.get(i); + assertEquals(defaults.get(i), evaluation.getValue()); + assertEquals(ErrorCode.PROVIDER_NOT_READY, evaluation.getErrorCode()); + assertEquals("ERROR", evaluation.getReason()); + assertNull(evaluation.getVariant()); + } + assertFalse(loader.evaluatorRequested, "the removed bootstrap API must never be linked"); + } finally { + assertDoesNotThrow(api::shutdown); + } + } + } + + private static final class LegacySdkLoader extends URLClassLoader { + private boolean evaluatorRequested; + + LegacySdkLoader(final URL jar, final ClassLoader parent) { + super(new URL[] {jar}, parent); + } + + @Override + public URL getResource(final String name) { + // Agent type matching must see the same SDK bytecode that this loader defines. + return name.startsWith("datadog/trace/api/openfeature/") + ? findResource(name) + : super.getResource(name); + } + + @Override + protected synchronized Class loadClass(final String name, final boolean resolve) + throws ClassNotFoundException { + if (!name.startsWith("datadog.trace.api.openfeature.")) { + return super.loadClass(name, resolve); + } + evaluatorRequested |= name.endsWith(".DDEvaluator"); + Class type = findLoadedClass(name); + if (type == null) { + type = findClass(name); + } + if (resolve) { + resolveClass(type); + } + return type; + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 5e49743d4af..b0be629574f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -344,6 +344,7 @@ include( ":dd-java-agent:instrumentation:cxf-2.1", ":dd-java-agent:instrumentation:datadog:asm:iast-instrumenter", ":dd-java-agent:instrumentation:datadog:dynamic-instrumentation:span-origin", + ":dd-java-agent:instrumentation:datadog:openfeature:dd-openfeature-1.56", ":dd-java-agent:instrumentation:datadog:openfeature:dd-openfeature-1.69", ":dd-java-agent:instrumentation:datadog:profiling:enable-wallclock-profiling", ":dd-java-agent:instrumentation:datadog:profiling:exception-profiling", From f2da46a39a2dc963fbf9117dcc0d8c60c90ea52f Mon Sep 17 00:00:00 2001 From: Bruce Bujon Date: Thu, 22 Oct 2026 12:56:47 +0200 Subject: [PATCH 2/5] test(ffe): Check released providers with a smoke test Replace the legacy provider instrumentation tests, which loaded a released provider in a dedicated class loader, with a smoke test running an application built with the released dd-openfeature 1.67.1, with and without Feature Flags enabled. Drop the IDEA module configuration and use the static VERSION_11 import in the legacy provider instrumentation build. Co-Authored-By: Claude Opus 5.5 --- .../dd-openfeature-1.56/build.gradle | 37 +---- .../dd-openfeature-1.56/gradle.lockfile | 4 +- .../LegacyProviderDisabledForkedTest.java | 6 - .../LegacyProviderInstrumentationTest.java | 98 -------------- .../openfeature-legacy/build.gradle | 46 +++++++ .../openfeature-legacy/gradle.lockfile | 126 ++++++++++++++++++ .../legacy/LegacyProviderApplication.java | 46 +++++++ .../AbstractLegacyProviderSmokeTest.java | 49 +++++++ ...ProviderFeatureFlagsDisabledSmokeTest.java | 18 +++ .../legacy/LegacyProviderSmokeTest.java | 13 ++ settings.gradle.kts | 1 + 11 files changed, 305 insertions(+), 139 deletions(-) delete mode 100644 dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderDisabledForkedTest.java delete mode 100644 dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentationTest.java create mode 100644 dd-smoke-tests/openfeature-legacy/build.gradle create mode 100644 dd-smoke-tests/openfeature-legacy/gradle.lockfile create mode 100644 dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java create mode 100644 dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java create mode 100644 dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderFeatureFlagsDisabledSmokeTest.java create mode 100644 dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle index 958ce006ea3..2d37aec1da5 100644 --- a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle @@ -1,12 +1,7 @@ +import static org.gradle.api.JavaVersion.VERSION_11 + plugins { id 'dd-trace-java.module.instrumentation' - id 'idea' -} - -idea { - module { - jdkName = '11' - } } // The providers released before the SDK bridge, linked to the agent bootstrap API it removed. @@ -19,37 +14,13 @@ muzzle { } tracerJava { - addSourceSetFor(JavaVersion.VERSION_11) + addSourceSetFor(VERSION_11) } -testJvmConstraints { - minJavaVersion = JavaVersion.VERSION_11 -} - -// Load a released provider in its own classloader, as an application does. -def legacySdkDependency = configurations.dependencyScope('legacySdkDependency') -def legacySdk = configurations.resolvable('legacySdk') { - extendsFrom legacySdkDependency.get() - transitive = false -}.get() - dependencies { - add('legacySdkDependency', 'com.datadoghq:dd-openfeature:1.67.0') main_java11CompileOnly 'com.datadoghq:dd-openfeature:1.56.0' - - testImplementation 'dev.openfeature:sdk:1.20.1' } tasks.named("compileMain_java11Java", JavaCompile) { - configureCompiler(it, JavaVersion.VERSION_11) -} - -tasks.named("compileTestJava", JavaCompile) { - configureCompiler(it, JavaVersion.VERSION_11) -} - -tasks.named('processTestResources', ProcessResources) { - from(legacySdk) { - into 'legacy-sdk' - } + configureCompiler(it, VERSION_11) } diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/gradle.lockfile b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/gradle.lockfile index 09b1cf44139..64978a9d0e3 100644 --- a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/gradle.lockfile +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/gradle.lockfile @@ -53,7 +53,6 @@ commons-io:commons-io:2.11.0=testCompileClasspath,testRuntimeClasspath commons-io:commons-io:2.21.0=spotbugs de.thetaphi:forbiddenapis:3.11=compileClasspath,testCompileClasspath,testRuntimeClasspath dev.openfeature:sdk:1.18.2=main_java11CompileClasspath -dev.openfeature:sdk:1.20.1=testCompileClasspath,testRuntimeClasspath io.leangen.geantyref:geantyref:1.3.16=testRuntimeClasspath io.sqreen:libsqreen:17.5.0=testRuntimeClasspath javax.servlet:javax.servlet-api:3.1.0=testCompileClasspath,testRuntimeClasspath @@ -115,7 +114,8 @@ org.slf4j:jcl-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath org.slf4j:log4j-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,compileClasspath,main_java11RuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath -org.slf4j:slf4j-api:2.0.17=main_java11CompileClasspath,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:1.7.32=testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.17=main_java11CompileClasspath,spotbugsSlf4j org.slf4j:slf4j-api:2.0.18=spotbugs org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,main_java11RuntimeClasspath,muzzleTooling,runtimeClasspath,testRuntimeClasspath diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderDisabledForkedTest.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderDisabledForkedTest.java deleted file mode 100644 index 17a2d4abadb..00000000000 --- a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderDisabledForkedTest.java +++ /dev/null @@ -1,6 +0,0 @@ -package datadog.trace.instrumentation.openfeature; - -import datadog.trace.test.junit.utils.config.WithConfig; - -@WithConfig(key = "feature.flags.enabled", value = "false") -class LegacyProviderDisabledForkedTest extends LegacyProviderInstrumentationTest {} diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentationTest.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentationTest.java deleted file mode 100644 index 46a39b2f14b..00000000000 --- a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/test/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentationTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package datadog.trace.instrumentation.openfeature; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -import datadog.trace.agent.test.AbstractInstrumentationTest; -import dev.openfeature.sdk.Client; -import dev.openfeature.sdk.ErrorCode; -import dev.openfeature.sdk.FeatureProvider; -import dev.openfeature.sdk.FlagEvaluationDetails; -import dev.openfeature.sdk.ImmutableStructure; -import dev.openfeature.sdk.OpenFeatureAPI; -import dev.openfeature.sdk.ProviderState; -import dev.openfeature.sdk.Value; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -class LegacyProviderInstrumentationTest extends AbstractInstrumentationTest { - @Test - @Timeout(10) - void initializesTheReleasedProviderWithoutLoadingItsLegacyEvaluator() throws Exception { - final URL jar = getClass().getResource("/legacy-sdk/dd-openfeature-1.67.0.jar"); - assertNotNull(jar); - final OpenFeatureAPI api = OpenFeatureAPI.getInstance(); - try (final LegacySdkLoader loader = new LegacySdkLoader(jar, getClass().getClassLoader())) { - try { - final FeatureProvider provider = - (FeatureProvider) - loader - .loadClass("datadog.trace.api.openfeature.Provider") - .getConstructor() - .newInstance(); - assertDoesNotThrow(() -> api.setProviderAndWait(provider)); - final Client client = api.getClient(); - assertEquals(ProviderState.READY, client.getProviderState()); - final Value object = new Value(new ImmutableStructure(Map.of("fallback", new Value(true)))); - final List> evaluations = - List.of( - client.getBooleanDetails("boolean", true), - client.getStringDetails("string", "fallback"), - client.getIntegerDetails("integer", -1), - client.getDoubleDetails("double", -1.0), - client.getObjectDetails("object", object)); - final List defaults = List.of(true, "fallback", -1, -1.0, object); - for (int i = 0; i < evaluations.size(); i++) { - final FlagEvaluationDetails evaluation = evaluations.get(i); - assertEquals(defaults.get(i), evaluation.getValue()); - assertEquals(ErrorCode.PROVIDER_NOT_READY, evaluation.getErrorCode()); - assertEquals("ERROR", evaluation.getReason()); - assertNull(evaluation.getVariant()); - } - assertFalse(loader.evaluatorRequested, "the removed bootstrap API must never be linked"); - } finally { - assertDoesNotThrow(api::shutdown); - } - } - } - - private static final class LegacySdkLoader extends URLClassLoader { - private boolean evaluatorRequested; - - LegacySdkLoader(final URL jar, final ClassLoader parent) { - super(new URL[] {jar}, parent); - } - - @Override - public URL getResource(final String name) { - // Agent type matching must see the same SDK bytecode that this loader defines. - return name.startsWith("datadog/trace/api/openfeature/") - ? findResource(name) - : super.getResource(name); - } - - @Override - protected synchronized Class loadClass(final String name, final boolean resolve) - throws ClassNotFoundException { - if (!name.startsWith("datadog.trace.api.openfeature.")) { - return super.loadClass(name, resolve); - } - evaluatorRequested |= name.endsWith(".DDEvaluator"); - Class type = findLoadedClass(name); - if (type == null) { - type = findClass(name); - } - if (resolve) { - resolveClass(type); - } - return type; - } - } -} diff --git a/dd-smoke-tests/openfeature-legacy/build.gradle b/dd-smoke-tests/openfeature-legacy/build.gradle new file mode 100644 index 00000000000..a40801abc0e --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/build.gradle @@ -0,0 +1,46 @@ +import static org.gradle.api.JavaVersion.VERSION_11 + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + id 'application' + id 'com.gradleup.shadow' + id 'dd-trace-java.module.smoke-test' +} + +description = 'Released dd-openfeature provider Smoke Tests.' + +testJvmConstraints { + minJavaVersion = VERSION_11 +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + +tasks.withType(JavaCompile).configureEach { + configureCompiler(it, VERSION_11) +} + +application { + mainClass = 'datadog.smoketest.openfeature.legacy.LegacyProviderApplication' +} + +dependencies { + // A provider released before the SDK bridge, linked to the agent bootstrap API it removed. + implementation 'com.datadoghq:dd-openfeature:1.67.1' + testImplementation project(':dd-smoke-tests') +} + +tasks.withType(Test).configureEach { + def shadowJarTask = tasks.named('shadowJar', ShadowJar) + dependsOn shadowJarTask + jvmArgumentProviders.add(new CommandLineArgumentProvider() { + @Override + Iterable asArguments() { + return shadowJarTask.map { ["-Ddatadog.smoketest.shadowJar.path=${it.archiveFile.get()}"] }.get() + } + }) +} diff --git a/dd-smoke-tests/openfeature-legacy/gradle.lockfile b/dd-smoke-tests/openfeature-legacy/gradle.lockfile new file mode 100644 index 00000000000..faad5eeeb27 --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/gradle.lockfile @@ -0,0 +1,126 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +# To regenerate this file, run: ./gradlew :dd-smoke-tests:openfeature-legacy:dependencies --write-locks +cafe.cryptography:curve25519-elisabeth:0.1.0=testRuntimeClasspath +cafe.cryptography:ed25519-elisabeth:0.1.0=testRuntimeClasspath +ch.qos.logback:logback-classic:1.2.13=testCompileClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.2.13=testCompileClasspath,testRuntimeClasspath +com.blogspot.mydailyjava:weak-lock-free:0.17=testCompileClasspath,testRuntimeClasspath +com.datadoghq.okhttp3:okhttp:3.12.15=testCompileClasspath,testRuntimeClasspath +com.datadoghq.okio:okio:1.17.6=testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-instrument-java:0.0.6=testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-javac-plugin-client:0.2.2=testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-openfeature:1.67.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:java-dogstatsd-client:4.4.5=testRuntimeClasspath +com.datadoghq:sketches-java:0.8.3=testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.10.3=testCompileClasspath,testRuntimeClasspath +com.github.docker-java:docker-java-api:3.4.2=testCompileClasspath,testRuntimeClasspath +com.github.docker-java:docker-java-transport-zerodep:3.4.2=testCompileClasspath,testRuntimeClasspath +com.github.docker-java:docker-java-transport:3.4.2=testCompileClasspath,testRuntimeClasspath +com.github.javaparser:javaparser-core:3.25.6=codenarc +com.github.jnr:jffi:1.3.15=testRuntimeClasspath +com.github.jnr:jnr-a64asm:1.0.0=testRuntimeClasspath +com.github.jnr:jnr-constants:0.10.4=testRuntimeClasspath +com.github.jnr:jnr-enxio:0.32.20=testRuntimeClasspath +com.github.jnr:jnr-ffi:2.2.19=testRuntimeClasspath +com.github.jnr:jnr-posix:3.1.22=testRuntimeClasspath +com.github.jnr:jnr-unixsocket:0.38.25=testRuntimeClasspath +com.github.jnr:jnr-x86asm:1.0.2=testRuntimeClasspath +com.github.spotbugs:spotbugs-annotations:4.10.4=compileClasspath,spotbugs +com.github.spotbugs:spotbugs:4.10.4=spotbugs +com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs +com.google.code.findbugs:jsr305:3.0.2=compileClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +com.google.code.gson:gson:2.14.0=spotbugs +com.google.errorprone:error_prone_annotations:2.47.0=testCompileClasspath,testRuntimeClasspath +com.google.errorprone:error_prone_annotations:2.48.0=spotbugs +com.google.guava:failureaccess:1.0.3=testCompileClasspath,testRuntimeClasspath +com.google.guava:guava:33.6.0-jre=testCompileClasspath,testRuntimeClasspath +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=testCompileClasspath,testRuntimeClasspath +com.google.j2objc:j2objc-annotations:3.1=testCompileClasspath,testRuntimeClasspath +com.google.re2j:re2j:1.8=testRuntimeClasspath +com.squareup.moshi:moshi:1.11.0=testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:logging-interceptor:3.12.12=testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp:3.12.12=testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio:1.17.5=testCompileClasspath,testRuntimeClasspath +com.thoughtworks.qdox:qdox:1.12.1=codenarc +commons-fileupload:commons-fileupload:1.5=testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.11.0=testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.21.0=spotbugs +de.thetaphi:forbiddenapis:3.11=compileClasspath,testCompileClasspath,testRuntimeClasspath +dev.openfeature:sdk:1.20.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.leangen.geantyref:geantyref:1.3.16=testRuntimeClasspath +io.sqreen:libsqreen:17.5.0=testRuntimeClasspath +javax.servlet:javax.servlet-api:3.1.0=testCompileClasspath,testRuntimeClasspath +jaxen:jaxen:2.0.6=spotbugs +junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy-agent:1.18.12=testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.18.12=testCompileClasspath,testRuntimeClasspath +net.java.dev.jna:jna-platform:5.8.0=testRuntimeClasspath +net.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath +net.sf.saxon:Saxon-HE:12.10=spotbugs +org.apache.ant:ant-antlr:1.10.14=codenarc +org.apache.ant:ant-junit:1.10.14=codenarc +org.apache.bcel:bcel:6.12.0=spotbugs +org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath +org.apache.commons:commons-lang3:3.20.0=spotbugs +org.apache.commons:commons-text:1.15.0=spotbugs +org.apache.logging.log4j:log4j-api:2.26.1=spotbugs +org.apache.logging.log4j:log4j-core:2.26.1=spotbugs +org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-ant:3.0.23=codenarc +org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc +org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc +org.codehaus.groovy:groovy-json:3.0.23=codenarc +org.codehaus.groovy:groovy-json:3.0.25=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-templates:3.0.23=codenarc +org.codehaus.groovy:groovy-xml:3.0.23=codenarc +org.codehaus.groovy:groovy:3.0.23=codenarc +org.codehaus.groovy:groovy:3.0.25=testCompileClasspath,testRuntimeClasspath +org.codenarc:CodeNarc:3.7.0=codenarc +org.dom4j:dom4j:2.2.0=spotbugs +org.gmetrics:GMetrics:2.1.0=codenarc +org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath +org.hamcrest:hamcrest:3.0=testCompileClasspath,testRuntimeClasspath +org.jctools:jctools-core-jdk11:4.0.6=testRuntimeClasspath +org.jctools:jctools-core:4.0.6=testRuntimeClasspath +org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath +org.jspecify:jspecify:1.0.0=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:5.14.1=testRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter:5.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-commons:1.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:1.14.1=testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-launcher:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-runner:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-suite-api:1.14.1=testRuntimeClasspath +org.junit.platform:junit-platform-suite-commons:1.14.1=testRuntimeClasspath +org.junit:junit-bom:5.14.1=testCompileClasspath,testRuntimeClasspath +org.mockito:mockito-core:4.4.0=testRuntimeClasspath +org.msgpack:msgpack-core:0.8.24=testRuntimeClasspath +org.objenesis:objenesis:3.3=testCompileClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-analysis:9.10.1=spotbugs +org.ow2.asm:asm-analysis:9.7.1=testRuntimeClasspath +org.ow2.asm:asm-commons:9.10.1=spotbugs +org.ow2.asm:asm-commons:9.7.1=testRuntimeClasspath +org.ow2.asm:asm-tree:9.10.1=spotbugs +org.ow2.asm:asm-tree:9.7.1=testRuntimeClasspath +org.ow2.asm:asm-util:9.10.1=spotbugs +org.ow2.asm:asm-util:9.7.1=testRuntimeClasspath +org.ow2.asm:asm:9.10.1=spotbugs,testCompileClasspath,testRuntimeClasspath +org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath +org.slf4j:jcl-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:jul-to-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:log4j-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.17=compileClasspath,runtimeClasspath,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.18=spotbugs +org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j +org.spockframework:spock-bom:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath +org.spockframework:spock-core:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-junit:1.2.2=testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-parser:1.2.1=testCompileClasspath,testRuntimeClasspath +org.testcontainers:testcontainers:1.21.4=testCompileClasspath,testRuntimeClasspath +org.xmlresolver:xmlresolver:5.3.3=spotbugs +empty=annotationProcessor,shadow,spotbugsPlugins,testAnnotationProcessor diff --git a/dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java b/dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java new file mode 100644 index 00000000000..9fbb06071b0 --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java @@ -0,0 +1,46 @@ +package datadog.smoketest.openfeature.legacy; + +import datadog.trace.api.openfeature.Provider; +import dev.openfeature.sdk.Client; +import dev.openfeature.sdk.FlagEvaluationDetails; +import dev.openfeature.sdk.ImmutableStructure; +import dev.openfeature.sdk.OpenFeatureAPI; +import dev.openfeature.sdk.Value; +import java.util.Map; + +/** Registers a released provider and reports how it evaluates flags. */ +public final class LegacyProviderApplication { + private LegacyProviderApplication() {} + + public static void main(final String[] args) { + final OpenFeatureAPI api = OpenFeatureAPI.getInstance(); + try { + api.setProviderAndWait(new Provider()); + final Client client = api.getClient(); + System.out.println("PROVIDER " + client.getProviderState()); + final Value object = new Value(new ImmutableStructure(Map.of("fallback", new Value(true)))); + report("boolean", client.getBooleanDetails("boolean", true), true); + report("string", client.getStringDetails("string", "fallback"), "fallback"); + report("integer", client.getIntegerDetails("integer", -1), -1); + report("double", client.getDoubleDetails("double", -1.0), -1.0); + report("object", client.getObjectDetails("object", object), object); + } finally { + api.shutdown(); + } + } + + private static void report( + final String type, final FlagEvaluationDetails details, final Object defaultValue) { + System.out.println( + "EVALUATION " + + type + + " default=" + + defaultValue.equals(details.getValue()) + + " reason=" + + details.getReason() + + " error=" + + details.getErrorCode() + + " variant=" + + details.getVariant()); + } +} diff --git a/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java new file mode 100644 index 00000000000..3ad8f05d962 --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java @@ -0,0 +1,49 @@ +package datadog.smoketest.openfeature.legacy; + +import static datadog.smoketest.backend.AgentBackend.testAgent; +import static java.util.Arrays.asList; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.smoketest.SmokeCliApp; +import org.junit.jupiter.api.Test; + +/** Checks a released provider returns the caller defaults instead of linking the removed API. */ +abstract class AbstractLegacyProviderSmokeTest { + static SmokeCliApp.Builder legacyProviderApp(final String name) { + return SmokeCliApp.named(name) + .jar(System.getProperty("datadog.smoketest.shadowJar.path")) + .backend(testAgent()) + // The evaluations report the ERROR reason of the caller defaults. + .allowedErrorLogs("EVALUATION ") + // The app may exit before sending telemetry. + .skipTelemetryCheck(); + } + + abstract SmokeCliApp app(); + + @Test + void returnsTheCallerDefaults() { + final SmokeCliApp app = app(); + app.assertCompletesWithValue(30, SECONDS, 0); + + assertLogged(app, "PROVIDER READY"); + for (final String type : asList("boolean", "string", "integer", "double", "object")) { + assertLogged( + app, + "EVALUATION " + + type + + " default=true reason=ERROR error=PROVIDER_NOT_READY variant=null"); + } + assertTrue( + app.waitForLogLine( + line -> + line.contains("WARN datadog.trace.api.openfeature.Provider") + && line.contains("incompatible with the Datadog Java agent")), + "the agent warns to upgrade the provider"); + } + + private static void assertLogged(final SmokeCliApp app, final String expected) { + assertTrue(app.waitForLogLine(expected::equals), "the app logged: " + expected); + } +} diff --git a/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderFeatureFlagsDisabledSmokeTest.java b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderFeatureFlagsDisabledSmokeTest.java new file mode 100644 index 00000000000..2224741745a --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderFeatureFlagsDisabledSmokeTest.java @@ -0,0 +1,18 @@ +package datadog.smoketest.openfeature.legacy; + +import datadog.smoketest.SmokeCliApp; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** The agent keeps released providers safe even with Feature Flags disabled. */ +class LegacyProviderFeatureFlagsDisabledSmokeTest extends AbstractLegacyProviderSmokeTest { + @RegisterExtension + static final SmokeCliApp APP = + legacyProviderApp("openfeature-legacy-disabled") + .env("DD_FEATURE_FLAGS_ENABLED", "false") + .build(); + + @Override + SmokeCliApp app() { + return APP; + } +} diff --git a/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java new file mode 100644 index 00000000000..af60dcbdd4d --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java @@ -0,0 +1,13 @@ +package datadog.smoketest.openfeature.legacy; + +import datadog.smoketest.SmokeCliApp; +import org.junit.jupiter.api.extension.RegisterExtension; + +class LegacyProviderSmokeTest extends AbstractLegacyProviderSmokeTest { + @RegisterExtension static final SmokeCliApp APP = legacyProviderApp("openfeature-legacy").build(); + + @Override + SmokeCliApp app() { + return APP; + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index b0be629574f..b05284e5d42 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -214,6 +214,7 @@ include( ":dd-smoke-tests:log-injection", ":dd-smoke-tests:maven", ":dd-smoke-tests:openfeature", + ":dd-smoke-tests:openfeature-legacy", ":dd-smoke-tests:opentracing", ":dd-smoke-tests:opentelemetry", ":dd-smoke-tests:osgi", From 77fc7b22c53166844e963e5c72882182c20fb6ee Mon Sep 17 00:00:00 2001 From: Bruce Bujon Date: Thu, 22 Oct 2026 13:07:49 +0200 Subject: [PATCH 3/5] style(ffe): Follow the instrumentation conventions Mark the instrumentation module and advice classes as unused, as they are only referenced through AutoService and advice class names, and import the Advice annotations statically. Co-Authored-By: Claude Opus 5.5 --- .../LegacyProviderInstrumentation.java | 1 + .../openfeature/LegacyProviderAdvice.java | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java index c7e6743de60..e85423b0a1e 100644 --- a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java @@ -15,6 +15,7 @@ /** * Keeps legacy providers usable as default-value providers after their bootstrap API is removed. */ +@SuppressWarnings("unused") @AutoService(InstrumenterModule.class) public class LegacyProviderInstrumentation extends InstrumenterModule implements Instrumenter.ForSingleType, diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java index d9e9127b842..6fb3d2e426a 100644 --- a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java @@ -1,20 +1,26 @@ package datadog.trace.instrumentation.openfeature; +import static net.bytebuddy.asm.Advice.Argument; +import static net.bytebuddy.asm.Advice.OnMethodEnter; +import static net.bytebuddy.asm.Advice.OnMethodExit; +import static net.bytebuddy.asm.Advice.OnNonDefaultValue; +import static net.bytebuddy.asm.Advice.Return; + import dev.openfeature.sdk.ErrorCode; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.Reason; -import net.bytebuddy.asm.Advice; import org.slf4j.LoggerFactory; /** Avoids linking legacy evaluators to the removed agent bootstrap API. */ +@SuppressWarnings("unused") public class LegacyProviderAdvice { public static class Initialize { - @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + @OnMethodEnter(skipOn = OnNonDefaultValue.class) public static boolean skipLegacyInitialization() { return true; } - @Advice.OnMethodExit(suppress = Throwable.class) + @OnMethodExit(suppress = Throwable.class) public static void warn() { LoggerFactory.getLogger("datadog.trace.api.openfeature.Provider") .warn( @@ -24,15 +30,15 @@ public static void warn() { } public static class Evaluation { - @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + @OnMethodEnter(skipOn = OnNonDefaultValue.class) public static boolean skipLegacyEvaluation() { return true; } - @Advice.OnMethodExit(suppress = Throwable.class) + @OnMethodExit(suppress = Throwable.class) public static void returnDefault( - @Advice.Argument(1) final Object defaultValue, - @Advice.Return(readOnly = false) ProviderEvaluation evaluation) { + @Argument(1) final Object defaultValue, + @Return(readOnly = false) ProviderEvaluation evaluation) { evaluation = ProviderEvaluation.builder() .value(defaultValue) From de0798de9741a16c0a95eee53235b23984820f98 Mon Sep 17 00:00:00 2001 From: Bruce Bujon Date: Thu, 22 Oct 2026 13:28:16 +0200 Subject: [PATCH 4/5] chore(ffe): Own the released provider smoke tests Co-Authored-By: Claude Opus 5.5 --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f7dec0f13c9..a7ce61e0f94 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -402,6 +402,7 @@ /dd-java-agent/instrumentation/datadog/openfeature/ @DataDog/feature-flagging-and-experimentation-sdk /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 /products/feature-flagging/ @DataDog/feature-flagging-and-experimentation-sdk # @DataDog/profiling-java From 75cccf31cb15cab6c8d1eb75b4143d79891da10b Mon Sep 17 00:00:00 2001 From: Sarah Chen Date: Thu, 22 Oct 2026 17:10:27 -0500 Subject: [PATCH 5/5] fix(ffe): Keep legacy OpenFeature fallback safe --- .../LegacyProviderInstrumentation.java | 8 ++++++++ .../openfeature/LegacyProviderAdvice.java | 9 +++++++++ .../legacy/LegacyProviderApplication.java | 4 +++- .../legacy/AbstractLegacyProviderSmokeTest.java | 1 + ...cyProviderIntegrationsDisabledSmokeTest.java | 17 +++++++++++++++++ .../legacy/LegacyProviderSmokeTest.java | 6 +++++- 6 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderIntegrationsDisabledSmokeTest.java diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java index e85423b0a1e..a8b6ca8b059 100644 --- a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java/datadog/trace/instrumentation/openfeature/LegacyProviderInstrumentation.java @@ -25,6 +25,11 @@ public LegacyProviderInstrumentation() { super("openfeature"); } + @Override + protected boolean defaultEnabled() { + return true; + } + @Override public boolean isApplicable(final Set enabledSystems) { // Application safety must not depend on whether Feature Flags is enabled. @@ -47,6 +52,9 @@ public void methodAdvice(final MethodTransformer transformer) { transformer.applyAdvice( named("initialize").and(takesArguments(1)), packageName + ".LegacyProviderAdvice$Initialize"); + transformer.applyAdvice( + named("getProviderHooks").and(takesArguments(0)), + packageName + ".LegacyProviderAdvice$Hooks"); transformer.applyAdvice( namedOneOf( "getBooleanEvaluation", diff --git a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java index 6fb3d2e426a..edb89952075 100644 --- a/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/src/main/java11/datadog/trace/instrumentation/openfeature/LegacyProviderAdvice.java @@ -1,5 +1,6 @@ package datadog.trace.instrumentation.openfeature; +import static java.util.Collections.emptyList; import static net.bytebuddy.asm.Advice.Argument; import static net.bytebuddy.asm.Advice.OnMethodEnter; import static net.bytebuddy.asm.Advice.OnMethodExit; @@ -9,6 +10,7 @@ import dev.openfeature.sdk.ErrorCode; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.Reason; +import java.util.List; import org.slf4j.LoggerFactory; /** Avoids linking legacy evaluators to the removed agent bootstrap API. */ @@ -29,6 +31,13 @@ public static void warn() { } } + public static class Hooks { + @OnMethodExit(suppress = Throwable.class) + public static void returnNoHooks(@Return(readOnly = false) List hooks) { + hooks = emptyList(); + } + } + public static class Evaluation { @OnMethodEnter(skipOn = OnNonDefaultValue.class) public static boolean skipLegacyEvaluation() { diff --git a/dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java b/dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java index 9fbb06071b0..4b48ed3201d 100644 --- a/dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java +++ b/dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java @@ -15,9 +15,11 @@ private LegacyProviderApplication() {} public static void main(final String[] args) { final OpenFeatureAPI api = OpenFeatureAPI.getInstance(); try { - api.setProviderAndWait(new Provider()); + final Provider provider = new Provider(); + api.setProviderAndWait(provider); final Client client = api.getClient(); System.out.println("PROVIDER " + client.getProviderState()); + System.out.println("PROVIDER_HOOKS " + provider.getProviderHooks().size()); final Value object = new Value(new ImmutableStructure(Map.of("fallback", new Value(true)))); report("boolean", client.getBooleanDetails("boolean", true), true); report("string", client.getStringDetails("string", "fallback"), "fallback"); diff --git a/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java index 3ad8f05d962..59ddfdf0cd7 100644 --- a/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java +++ b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java @@ -28,6 +28,7 @@ void returnsTheCallerDefaults() { app.assertCompletesWithValue(30, SECONDS, 0); assertLogged(app, "PROVIDER READY"); + assertLogged(app, "PROVIDER_HOOKS 0"); for (final String type : asList("boolean", "string", "integer", "double", "object")) { assertLogged( app, diff --git a/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderIntegrationsDisabledSmokeTest.java b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderIntegrationsDisabledSmokeTest.java new file mode 100644 index 00000000000..da78f1554a2 --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderIntegrationsDisabledSmokeTest.java @@ -0,0 +1,17 @@ +package datadog.smoketest.openfeature.legacy; + +import datadog.smoketest.SmokeCliApp; +import org.junit.jupiter.api.extension.RegisterExtension; + +class LegacyProviderIntegrationsDisabledSmokeTest extends AbstractLegacyProviderSmokeTest { + @RegisterExtension + static final SmokeCliApp APP = + legacyProviderApp("openfeature-legacy-integrations-disabled") + .env("DD_INTEGRATIONS_ENABLED", "false") + .build(); + + @Override + SmokeCliApp app() { + return APP; + } +} diff --git a/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java index af60dcbdd4d..4a789c0d51d 100644 --- a/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java +++ b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java @@ -4,7 +4,11 @@ import org.junit.jupiter.api.extension.RegisterExtension; class LegacyProviderSmokeTest extends AbstractLegacyProviderSmokeTest { - @RegisterExtension static final SmokeCliApp APP = legacyProviderApp("openfeature-legacy").build(); + @RegisterExtension + static final SmokeCliApp APP = + legacyProviderApp("openfeature-legacy") + .env("DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED", "true") + .build(); @Override SmokeCliApp app() {