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 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..2d37aec1da5 --- /dev/null +++ b/dd-java-agent/instrumentation/datadog/openfeature/dd-openfeature-1.56/build.gradle @@ -0,0 +1,26 @@ +import static org.gradle.api.JavaVersion.VERSION_11 + +plugins { + id 'dd-trace-java.module.instrumentation' +} + +// 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(VERSION_11) +} + +dependencies { + main_java11CompileOnly 'com.datadoghq:dd-openfeature:1.56.0' +} + +tasks.named("compileMain_java11Java", JavaCompile) { + 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 new file mode 100644 index 00000000000..64978a9d0e3 --- /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 +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: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 +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..a8b6ca8b059 --- /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,68 @@ +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. + */ +@SuppressWarnings("unused") +@AutoService(InstrumenterModule.class) +public class LegacyProviderInstrumentation extends InstrumenterModule + implements Instrumenter.ForSingleType, + Instrumenter.WithTypeStructure, + Instrumenter.HasMethodAdvice { + 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. + 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( + named("getProviderHooks").and(takesArguments(0)), + packageName + ".LegacyProviderAdvice$Hooks"); + 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..edb89952075 --- /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,60 @@ +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; +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 java.util.List; +import org.slf4j.LoggerFactory; + +/** Avoids linking legacy evaluators to the removed agent bootstrap API. */ +@SuppressWarnings("unused") +public class LegacyProviderAdvice { + public static class Initialize { + @OnMethodEnter(skipOn = OnNonDefaultValue.class) + public static boolean skipLegacyInitialization() { + return true; + } + + @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 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() { + return true; + } + + @OnMethodExit(suppress = Throwable.class) + public static void returnDefault( + @Argument(1) final Object defaultValue, + @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-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..4b48ed3201d --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/src/main/java/datadog/smoketest/openfeature/legacy/LegacyProviderApplication.java @@ -0,0 +1,48 @@ +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 { + 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"); + 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..59ddfdf0cd7 --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/AbstractLegacyProviderSmokeTest.java @@ -0,0 +1,50 @@ +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"); + assertLogged(app, "PROVIDER_HOOKS 0"); + 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/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 new file mode 100644 index 00000000000..4a789c0d51d --- /dev/null +++ b/dd-smoke-tests/openfeature-legacy/src/test/java/datadog/smoketest/openfeature/legacy/LegacyProviderSmokeTest.java @@ -0,0 +1,17 @@ +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") + .env("DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED", "true") + .build(); + + @Override + SmokeCliApp app() { + return APP; + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 5e49743d4af..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", @@ -344,6 +345,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",