diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cc3ff9d1277..def652a112c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,6 +29,15 @@ variables: GRADLE_VERSION: "8.14.4" # must match gradle-wrapper.properties MAVEN_REPOSITORY_PROXY: "https://depot-read-api-java.us1.ddbuild.io/magicmirror/magicmirror/@current/" GRADLE_PLUGIN_PROXY: "https://depot-read-api-java.us1.ddbuild.io/magicmirror/magicmirror/@current/" + ARM64_BUILDER_IMAGE_REPO: "ghcr.io/datadog/dd-trace-java-docker-build" + ARM64_BUILDER_IMAGE_TAG: "alexeyk_arm64-test-arm64-base" + WINDOWS_JDK8_VERSION: "8u482-b08" # keep pinned to Temurin GA patch releases and bump periodically + WINDOWS_JDK11_VERSION: "11.0.30+7" + WINDOWS_JDK17_VERSION: "17.0.18+8" + WINDOWS_JDK21_VERSION: "21.0.10+7" + WINDOWS_JDK25_VERSION: "25.0.2+10" + WINDOWS_BOOTSTRAP_CACHE_POLICY: pull + WINDOWS_DEPENDENCY_CACHE_POLICY: pull BUILDER_IMAGE_REPO: "registry.ddbuild.io/images/mirror/dd-trace-java-docker-build" # images are pinned in images/mirror.lock.yaml in the DataDog/images repo BUILDER_IMAGE_VERSION_PREFIX: "ci-" # use either an empty string (e.g. "") for latest images or a version followed by a hyphen (e.g. "ci-" or "123_merge-") REPO_NOTIFICATION_CHANNEL: "#apm-java-escalations" @@ -350,6 +359,18 @@ populate_dep_cache: - GRADLE_TARGET: ":smokeTest" CACHE_TYPE: "smoke" +populate_dep_cache_windows: + extends: .windows_base_test_job + when: manual + allow_failure: true + variables: + WINDOWS_BOOTSTRAP_CACHE_POLICY: pull-push + WINDOWS_DEPENDENCY_CACHE_POLICY: push + script: + - | + .\gradlew.bat :baseTest -PskipTests -PtestJvm=21 --stacktrace --no-daemon --parallel --max-workers=4 + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + publish-artifacts-to-s3: image: registry.ddbuild.io/images/mirror/amazon/aws-cli:2.4.29 stage: publish @@ -575,6 +596,156 @@ muzzle-dep-report: .restore_pretest_env: &restore_pretest_env - source pretest.env +.windows_test_cache: + cache: + - key: "windows-jdks-temurin-${WINDOWS_JDK8_VERSION}-${WINDOWS_JDK11_VERSION}-${WINDOWS_JDK17_VERSION}-${WINDOWS_JDK21_VERSION}-${WINDOWS_JDK25_VERSION}-x64" + paths: + - .tools/jdk-8/ + - .tools/jdk-11/ + - .tools/jdk-17/ + - .tools/jdk-21/ + - .tools/jdk-25/ + policy: $WINDOWS_BOOTSTRAP_CACHE_POLICY + unprotect: true + - key: + files: + - gradle/wrapper/gradle-wrapper.properties + prefix: "windows-gradle-wrapper" + paths: + - .gradle/wrapper/ + policy: $WINDOWS_BOOTSTRAP_CACHE_POLICY + unprotect: true + - key: + files: + - settings.gradle.kts + - "**/gradle.lockfile" + prefix: "windows-base-deps-jdk21" + fallback_keys: + - "windows-base-deps-jdk21-default" + paths: + - .gradle/caches/ + - .gradle/notifications/ + - .mvn/caches/ + policy: $WINDOWS_DEPENDENCY_CACHE_POLICY + unprotect: true + +.windows_base_test_job: + extends: .windows_test_cache + stage: tests + tags: [ "windows-v2:2025" ] + needs: [] + variables: + GIT_STRATEGY: none + GIT_SUBMODULE_STRATEGY: normal + GIT_SUBMODULE_DEPTH: 1 + SKIP_BUILDSCAN: "true" + TESTCONTAINERS_CHECKS_DISABLE: "true" + TESTCONTAINERS_RYUK_DISABLED: "true" + before_script: + - | + $ErrorActionPreference = "Stop" + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + + $toolsDir = Join-Path $env:CI_PROJECT_DIR ".tools" + $tmpDir = Join-Path $env:CI_PROJECT_DIR ".tmp" + $tmpDirJava = $tmpDir.Replace("\", "/") + $jdk8ArchiveVersion = $env:WINDOWS_JDK8_VERSION.Replace("-", "") + $jdk11ArchiveVersion = $env:WINDOWS_JDK11_VERSION.Replace("+", "_") + $jdk17ArchiveVersion = $env:WINDOWS_JDK17_VERSION.Replace("+", "_") + $jdk21ArchiveVersion = $env:WINDOWS_JDK21_VERSION.Replace("+", "_") + $jdk25ArchiveVersion = $env:WINDOWS_JDK25_VERSION.Replace("+", "_") + $jdk8Home = Join-Path $toolsDir "jdk-8" + $jdk11Home = Join-Path $toolsDir "jdk-11" + $jdk17Home = Join-Path $toolsDir "jdk-17" + $jdk21Home = Join-Path $toolsDir "jdk-21" + $jdk25Home = Join-Path $toolsDir "jdk-25" + + New-Item -ItemType Directory -Force -Path $toolsDir, $tmpDir, ".gradle", ".mvn\caches" | Out-Null + try { + New-Item -ItemType Directory -Force -Path "C:\tmp" | Out-Null + } catch { + Write-Host "Unable to create C:\tmp: $($_.Exception.Message)" + } + + git --version + git config --global --add safe.directory $env:CI_PROJECT_DIR + if (-not (Test-Path ".git")) { + git init . + git remote add origin $env:CI_REPOSITORY_URL + } else { + git remote remove origin 2>$null + git remote add origin $env:CI_REPOSITORY_URL + } + git config core.longpaths true + git fetch --depth 1 origin $env:CI_COMMIT_SHA + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + git checkout --force --detach FETCH_HEAD + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + @( + "DD_SERVICE", + "DD_ENTITY_ID", + "DD_SITE", + "DD_ENV", + "DD_DATACENTER", + "DD_PARTITION", + "DD_CLOUDPROVIDER" + ) | ForEach-Object { + if (Test-Path "Env:$_") { + Remove-Item "Env:$_" + } + } + + function Install-TemurinJdk([string]$jdkHome, [string]$jdkUrl, [string]$zipName) { + $jdkZip = Join-Path $env:TEMP $zipName + $jdkExtract = "$jdkHome-extract" + if (-not (Test-Path (Join-Path $jdkHome "bin\java.exe"))) { + if (Test-Path $jdkExtract) { Remove-Item $jdkExtract -Recurse -Force } + (New-Object System.Net.WebClient).DownloadFile($jdkUrl, $jdkZip) + Expand-Archive -Path $jdkZip -DestinationPath $jdkExtract -Force + if (Test-Path $jdkHome) { Remove-Item $jdkHome -Recurse -Force } + $expandedJdkDir = Get-ChildItem $jdkExtract -Directory | Select-Object -First 1 + Move-Item $expandedJdkDir.FullName $jdkHome + Remove-Item $jdkZip -Force + Remove-Item $jdkExtract -Recurse -Force + } + } + + Install-TemurinJdk $jdk8Home "https://github.com/adoptium/temurin8-binaries/releases/download/jdk$($env:WINDOWS_JDK8_VERSION)/OpenJDK8U-jdk_x64_windows_hotspot_$jdk8ArchiveVersion.zip" "temurin-jdk-$jdk8ArchiveVersion-windows-x64.zip" + Install-TemurinJdk $jdk11Home "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-$($env:WINDOWS_JDK11_VERSION)/OpenJDK11U-jdk_x64_windows_hotspot_$jdk11ArchiveVersion.zip" "temurin-jdk-$jdk11ArchiveVersion-windows-x64.zip" + Install-TemurinJdk $jdk17Home "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-$($env:WINDOWS_JDK17_VERSION)/OpenJDK17U-jdk_x64_windows_hotspot_$jdk17ArchiveVersion.zip" "temurin-jdk-$jdk17ArchiveVersion-windows-x64.zip" + Install-TemurinJdk $jdk21Home "https://github.com/adoptium/temurin21-binaries/releases/download/jdk-$($env:WINDOWS_JDK21_VERSION)/OpenJDK21U-jdk_x64_windows_hotspot_$jdk21ArchiveVersion.zip" "temurin-jdk-$jdk21ArchiveVersion-windows-x64.zip" + Install-TemurinJdk $jdk25Home "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-$($env:WINDOWS_JDK25_VERSION)/OpenJDK25U-jdk_x64_windows_hotspot_$jdk25ArchiveVersion.zip" "temurin-jdk-$jdk25ArchiveVersion-windows-x64.zip" + + $env:JAVA_HOME = $jdk21Home + $env:JAVA_8_HOME = $jdk8Home + $env:JAVA_11_HOME = $jdk11Home + $env:JAVA_17_HOME = $jdk17Home + $env:JAVA_21_HOME = $jdk21Home + $env:JAVA_25_HOME = $jdk25Home + $env:Path = "$($env:JAVA_HOME)\bin;$env:Path" + $env:GRADLE_USER_HOME = Join-Path $env:CI_PROJECT_DIR ".gradle" + $env:ORG_GRADLE_PROJECT_mavenRepositoryProxy = $env:MAVEN_REPOSITORY_PROXY + $env:ORG_GRADLE_PROJECT_gradlePluginProxy = $env:GRADLE_PLUGIN_PROXY + + if ($env:MAVEN_REPOSITORY_PROXY) { + (Get-Content ".mvn\wrapper\maven-wrapper.properties") ` + -replace "https://repo.maven.apache.org/maven2/", $env:MAVEN_REPOSITORY_PROXY ` + | Set-Content ".mvn\wrapper\maven-wrapper.properties" -Encoding ASCII + } + + Add-Content gradle.properties "org.gradle.java.installations.auto-detect=false" -Encoding ASCII + Add-Content gradle.properties "org.gradle.java.installations.auto-download=false" -Encoding ASCII + Add-Content gradle.properties "org.gradle.java.installations.fromEnv=JAVA_HOME,JAVA_8_HOME,JAVA_11_HOME,JAVA_17_HOME,JAVA_21_HOME,JAVA_25_HOME" -Encoding ASCII + Add-Content gradle.properties "org.gradle.jvmargs=-Xms1g -Xmx4g -Djava.util.prefs.userRoot=$tmpDirJava/java-prefs -Ddatadog.forkedMinHeapSize=128M -Ddatadog.forkedMaxHeapSize=1024M -XX:ErrorFile=$tmpDirJava/hs_err_pid%p.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$tmpDirJava" -Encoding ASCII + + & "$env:JAVA_8_HOME\bin\java.exe" -version + & "$env:JAVA_11_HOME\bin\java.exe" -version + & "$env:JAVA_17_HOME\bin\java.exe" -version + & "$env:JAVA_21_HOME\bin\java.exe" -version + & "$env:JAVA_25_HOME\bin\java.exe" -version + java -version + .\gradlew.bat --version + .test_job: extends: .gradle_build image: ${BUILDER_IMAGE_REPO}:${BUILDER_IMAGE_VERSION_PREFIX}$testJvm @@ -645,6 +816,96 @@ muzzle-dep-report: - scheduler_failure - data_integrity_failure +.test_job_arm64_poc: + image: ${ARM64_BUILDER_IMAGE_REPO}:${ARM64_BUILDER_IMAGE_TAG} + tags: [ "docker-in-docker:arm64" ] + stage: tests + needs: [] + cache: + - key: dependency-arm64-base + paths: + - .gradle/wrapper + - .gradle/caches + - .gradle/notifications + - .mvn/caches + policy: pull + unprotect: true + variables: + GRADLE_PARAMS: "-PskipFlakyTests" + GRADLE_WORKERS: 6 + GRADLE_MEMORY_MIN: 1G + GRADLE_MEMORY_MAX: 4G + KUBERNETES_CPU_REQUEST: 10 + KUBERNETES_MEMORY_REQUEST: 20Gi + KUBERNETES_MEMORY_LIMIT: 20Gi + TESTCONTAINERS_CHECKS_DISABLE: "true" + TESTCONTAINERS_RYUK_DISABLED: "true" + TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX: "registry.ddbuild.io/images/mirror/" + JETTY_AVAILABLE_PROCESSORS: 4 + GIT_SUBMODULE_STRATEGY: normal + GIT_SUBMODULE_DEPTH: 1 + before_script: + - git config --global --add safe.directory "$CI_PROJECT_DIR" + - export ORG_GRADLE_PROJECT_mavenRepositoryProxy=$MAVEN_REPOSITORY_PROXY + - export ORG_GRADLE_PROJECT_gradlePluginProxy=$GRADLE_PLUGIN_PROXY + - | + JAVA_HOMES=$(env | grep -E '^JAVA_[A-Z0-9_]+_HOME=' | sed 's/=.*//' | paste -sd,) + cat >> gradle.properties < elfBinaries() { @MethodSource("elfBinaries") void testElfBuildIdExtraction(String containerPath, String description) throws Exception { // TODO: check if arm64 can be supported too. - assumeFalse(OperatingSystem.architecture() == ARM64, "Skipping for arm64"); + assumeFalse(OperatingSystem.isArm64(), "Skipping for arm64"); Path localBinary = copyFromContainer(linuxContainer, containerPath); ElfBuildIdExtractor extractor = new ElfBuildIdExtractor(); diff --git a/dd-java-agent/agent-profiling/profiling-controller/src/main/java/com/datadog/profiling/controller/EnvironmentChecker.java b/dd-java-agent/agent-profiling/profiling-controller/src/main/java/com/datadog/profiling/controller/EnvironmentChecker.java index 62ca9ea29fe..62beaeba443 100644 --- a/dd-java-agent/agent-profiling/profiling-controller/src/main/java/com/datadog/profiling/controller/EnvironmentChecker.java +++ b/dd-java-agent/agent-profiling/profiling-controller/src/main/java/com/datadog/profiling/controller/EnvironmentChecker.java @@ -1,7 +1,5 @@ package com.datadog.profiling.controller; -import static datadog.environment.OperatingSystem.Architecture.ARM64; - import datadog.environment.JavaVirtualMachine; import datadog.environment.OperatingSystem; import datadog.environment.SystemProperties; @@ -243,11 +241,7 @@ private static boolean extractSoFromJar(Path target, StringBuilder sb) throws Ex .filter(e -> e.getName().contains("libjavaProfiler.so")) .filter( e -> - e.getName() - .contains( - OperatingSystem.architecture() == ARM64 - ? "/linux-arm64/" - : "/linux-x64/") + e.getName().contains(OperatingSystem.isArm64() ? "/linux-arm64/" : "/linux-x64/") && (!OperatingSystem.isMusl() || e.getName().contains("-musl"))) .findFirst() .map( diff --git a/dd-java-agent/instrumentation/aerospike-4.0/src/test/groovy/datadog/trace/instrumentation/aerospike4/AerospikeBaseTest.groovy b/dd-java-agent/instrumentation/aerospike-4.0/src/test/groovy/datadog/trace/instrumentation/aerospike4/AerospikeBaseTest.groovy index 57848379357..25353f6731d 100644 --- a/dd-java-agent/instrumentation/aerospike-4.0/src/test/groovy/datadog/trace/instrumentation/aerospike4/AerospikeBaseTest.groovy +++ b/dd-java-agent/instrumentation/aerospike-4.0/src/test/groovy/datadog/trace/instrumentation/aerospike4/AerospikeBaseTest.groovy @@ -1,5 +1,9 @@ package datadog.trace.instrumentation.aerospike4 +import static datadog.environment.OperatingSystem.arm64 +import static datadog.trace.agent.test.utils.PortUtils.waitForPortToOpen +import static java.util.concurrent.TimeUnit.SECONDS +import static org.testcontainers.containers.wait.strategy.Wait.forLogMessage import datadog.trace.agent.test.asserts.TraceAssert import datadog.trace.agent.test.naming.VersionedNamingTestBase @@ -9,11 +13,9 @@ import datadog.trace.core.DDSpan import org.testcontainers.containers.GenericContainer import spock.lang.Shared -import static datadog.trace.agent.test.utils.PortUtils.waitForPortToOpen -import static java.util.concurrent.TimeUnit.SECONDS -import static org.testcontainers.containers.wait.strategy.Wait.forLogMessage - abstract class AerospikeBaseTest extends VersionedNamingTestBase { + // arm64 supported only since `ce-6.2.0.2`: + private static final String AEROSPIKE_IMAGE = isArm64() ? 'aerospike:ce-6.2.0.2' : 'aerospike:5.5.0.9' @Shared def aerospike @@ -25,7 +27,7 @@ abstract class AerospikeBaseTest extends VersionedNamingTestBase { int aerospikePort = 3000 def setup() throws Exception { - aerospike = new GenericContainer('aerospike:5.5.0.9') + aerospike = new GenericContainer(AEROSPIKE_IMAGE) .withExposedPorts(3000) .waitingFor(forLogMessage(".*heartbeat-received.*\\n", 1)) diff --git a/dd-java-agent/instrumentation/google-pubsub-1.116/src/test/groovy/PubSubTest.groovy b/dd-java-agent/instrumentation/google-pubsub-1.116/src/test/groovy/PubSubTest.groovy index 289822df042..07189f86b37 100644 --- a/dd-java-agent/instrumentation/google-pubsub-1.116/src/test/groovy/PubSubTest.groovy +++ b/dd-java-agent/instrumentation/google-pubsub-1.116/src/test/groovy/PubSubTest.groovy @@ -1,4 +1,3 @@ - import static datadog.trace.agent.test.utils.TraceUtils.basicSpan import com.google.api.gax.core.NoCredentialsProvider @@ -34,13 +33,12 @@ import datadog.trace.core.datastreams.StatsGroup import datadog.trace.instrumentation.grpc.client.GrpcClientDecorator import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder +import java.nio.charset.StandardCharsets +import java.util.concurrent.CountDownLatch import org.testcontainers.containers.PubSubEmulatorContainer import org.testcontainers.utility.DockerImageName import spock.lang.Shared -import java.nio.charset.StandardCharsets -import java.util.concurrent.CountDownLatch - abstract class PubSubTest extends VersionedNamingTestBase { private static final String PROJECT_ID = "dd-trace-java" @@ -97,7 +95,7 @@ abstract class PubSubTest extends VersionedNamingTestBase { } def setupSpec() { - emulator = new PubSubEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:495.0.0-emulators")) + emulator = new PubSubEmulatorContainer(DockerImageName.parse("gcr.io/google.com/cloudsdktool/google-cloud-cli:495.0.0-emulators")) emulator.start() channel = ManagedChannelBuilder.forTarget(emulator.getEmulatorEndpoint()).usePlaintext().build() transportChannelProvider = FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)) diff --git a/dd-java-agent/instrumentation/java/java-concurrent/java-concurrent-1.8/src/test/groovy/executor/NettyExecutorInstrumentationTest.groovy b/dd-java-agent/instrumentation/java/java-concurrent/java-concurrent-1.8/src/test/groovy/executor/NettyExecutorInstrumentationTest.groovy index fc944416d94..9a9d75e597f 100644 --- a/dd-java-agent/instrumentation/java/java-concurrent/java-concurrent-1.8/src/test/groovy/executor/NettyExecutorInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/java/java-concurrent/java-concurrent-1.8/src/test/groovy/executor/NettyExecutorInstrumentationTest.groovy @@ -1,5 +1,9 @@ package executor +import static datadog.environment.OperatingSystem.arm64 +import static datadog.environment.OperatingSystem.linux +import static org.junit.jupiter.api.Assumptions.assumeTrue + import datadog.trace.agent.test.InstrumentationSpecification import datadog.trace.api.Trace import datadog.trace.core.DDSpan @@ -8,24 +12,18 @@ import io.netty.channel.epoll.EpollEventLoopGroup import io.netty.channel.local.LocalEventLoopGroup import io.netty.channel.nio.NioEventLoopGroup import io.netty.util.concurrent.DefaultEventExecutorGroup -import runnable.JavaAsyncChild -import spock.lang.Shared - import java.lang.reflect.InvocationTargetException import java.util.concurrent.Callable import java.util.concurrent.Future import java.util.concurrent.RejectedExecutionException import java.util.concurrent.TimeUnit - -import static org.junit.jupiter.api.Assumptions.assumeTrue +import runnable.JavaAsyncChild +import spock.lang.Shared class NettyExecutorInstrumentationTest extends InstrumentationSpecification { - - @Shared - boolean isLinux = System.getProperty("os.name").toLowerCase().contains("linux") - + // TODO: check if arm64 can be supported too. @Shared - EpollEventLoopGroup epollEventLoopGroup = isLinux ? new EpollEventLoopGroup(4) : null + EpollEventLoopGroup epollEventLoopGroup = (isLinux() && !isArm64()) ? new EpollEventLoopGroup(4) : null @Shared DefaultEventExecutorGroup defaultEventExecutorGroup = new DefaultEventExecutorGroup(4) @Shared @@ -279,6 +277,6 @@ class NettyExecutorInstrumentationTest extends InstrumentationSpecification { def epollExecutor() { // EPoll only works on linux - isLinux ? epollEventLoopGroup.next() : null + epollEventLoopGroup?.next() } } diff --git a/dd-java-agent/instrumentation/java/java-lang/java-lang-9.0/src/test/groovy/datadog/trace/instrumentation/java/lang/invoke/StringConcatFactoryCallSiteTest.groovy b/dd-java-agent/instrumentation/java/java-lang/java-lang-9.0/src/test/groovy/datadog/trace/instrumentation/java/lang/invoke/StringConcatFactoryCallSiteTest.groovy index efb7f32702a..aa664ec7c9e 100644 --- a/dd-java-agent/instrumentation/java/java-lang/java-lang-9.0/src/test/groovy/datadog/trace/instrumentation/java/lang/invoke/StringConcatFactoryCallSiteTest.groovy +++ b/dd-java-agent/instrumentation/java/java-lang/java-lang-9.0/src/test/groovy/datadog/trace/instrumentation/java/lang/invoke/StringConcatFactoryCallSiteTest.groovy @@ -147,7 +147,8 @@ class StringConcatFactoryCallSiteTest extends InstrumentationSpecification { setup: StringModule iastModule = Mock(StringModule) InstrumentationBridge.registerIastModule(iastModule) - final expected = '𠆢Hello𠆢\u0001𠆢World!.' + // 𠆢Hello... + final expected = '\uD840\uDDA2Hello\uD840\uDDA2\u0001\uD840\uDDA2World!.' when: final result = TestStringConcatFactorySuite.plusWithUtfConstants('Hello', 'World!') @@ -157,8 +158,8 @@ class StringConcatFactoryCallSiteTest extends InstrumentationSpecification { 1 * iastModule.onStringConcatFactory( expected, ['Hello', 'World!'] as String[], - '𠆢\u0001\u0002\u0001.', - ['𠆢\u0001𠆢'] as Object[], + '\uD840\uDDA2\u0001\u0002\u0001.', + ['\uD840\uDDA2\u0001\uD840\uDDA2'] as Object[], [-2, 0, -5, 1, -1] as int[]) 0 * _ } diff --git a/dd-java-agent/instrumentation/java/java-lang/java-lang-9.0/src/test/java/foo/bar/TestStringConcatFactorySuite.java b/dd-java-agent/instrumentation/java/java-lang/java-lang-9.0/src/test/java/foo/bar/TestStringConcatFactorySuite.java index 2bf8ce74c84..6c10ac73fb2 100644 --- a/dd-java-agent/instrumentation/java/java-lang/java-lang-9.0/src/test/java/foo/bar/TestStringConcatFactorySuite.java +++ b/dd-java-agent/instrumentation/java/java-lang/java-lang-9.0/src/test/java/foo/bar/TestStringConcatFactorySuite.java @@ -5,7 +5,6 @@ import org.slf4j.LoggerFactory; public abstract class TestStringConcatFactorySuite { - private static final Logger LOGGER = LoggerFactory.getLogger(TestStringConcatFactorySuite.class); private TestStringConcatFactorySuite() {} @@ -33,7 +32,8 @@ public static String plusWithConstantsAndTags(final String left, final String ri public static String plusWithUtfConstants(final String left, final String right) { LOGGER.debug("Before string plus {} {}", left, right); - final String result = "𠆢" + left + "𠆢\u0001𠆢" + right + "."; + // 𠆢 + left + ... + final String result = "\uD840\uDDA2" + left + "\uD840\uDDA2\u0001\uD840\uDDA2" + right + "."; LOGGER.debug("After string plus {}", result); return result; } @@ -50,7 +50,8 @@ public static String stringPlusWithMultipleObjects(final Object... target) { LOGGER.debug("Before string plus {}", Arrays.toString(target)); String result = ""; for (final Object item : target) { - result += item; + //noinspection StringConcatenationInLoop + result += item; // intentional `+` in loop for test. } LOGGER.debug("After string plus {}", result); return result; diff --git a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/datadog/trace/instrumentation/java/net/URLEncoderCallSiteTest.groovy b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/datadog/trace/instrumentation/java/net/URLEncoderCallSiteTest.groovy index 5e75f298b28..6dc6bd4cbd0 100644 --- a/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/datadog/trace/instrumentation/java/net/URLEncoderCallSiteTest.groovy +++ b/dd-java-agent/instrumentation/java/java-net/java-net-1.8/src/test/groovy/datadog/trace/instrumentation/java/net/URLEncoderCallSiteTest.groovy @@ -5,8 +5,13 @@ import datadog.trace.api.iast.InstrumentationBridge import datadog.trace.api.iast.propagation.CodecModule import datadog.trace.api.iast.propagation.PropagationModule import foo.bar.TestURLEncoderCallSiteSuite +import java.nio.charset.Charset class URLEncoderCallSiteTest extends InstrumentationSpecification { + // Explicit escape for non-ASCII `ståle` to make test independent of container settings. + private static final String NON_ASCII_QUERY = 'my test.asp?name=st\u00E5le&car=saab' + private static final String DEFAULT_CHARSET_ENCODED = + URLEncoder.encode(NON_ASCII_QUERY, Charset.defaultCharset().name()) @Override protected void configurePreAgent() { @@ -27,9 +32,9 @@ class URLEncoderCallSiteTest extends InstrumentationSpecification { 0 * _ where: - args | expected - ['my test.asp?name=ståle&car=saab'] | 'my+test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab' - ['my test.asp?name=ståle&car=saab', 'UTF-8'] | 'my+test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab' + args | expected + [NON_ASCII_QUERY] | DEFAULT_CHARSET_ENCODED + [NON_ASCII_QUERY, 'UTF-8'] | 'my+test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab' } void 'test encode with null args'() { diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy index 722685c95c8..6648b6ad531 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy @@ -1,7 +1,14 @@ +import static datadog.trace.agent.test.utils.TraceUtils.basicSpan +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace +import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE +import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_TRACE_PREPARED_STATEMENTS +import static org.junit.jupiter.api.Assumptions.assumeTrue + import com.mchange.v2.c3p0.ComboPooledDataSource import com.microsoft.sqlserver.jdbc.SQLServerException import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource +import datadog.environment.OperatingSystem import datadog.trace.agent.test.naming.VersionedNamingTestBase import datadog.trace.agent.test.utils.PortUtils import datadog.trace.api.Config @@ -9,15 +16,6 @@ import datadog.trace.api.DDSpanTypes import datadog.trace.api.naming.v1.DatabaseNamingV1 import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags import datadog.trace.bootstrap.instrumentation.api.Tags -import org.testcontainers.containers.MSSQLServerContainer -import org.testcontainers.containers.MySQLContainer -import org.testcontainers.containers.OracleContainer -import org.testcontainers.containers.PostgreSQLContainer -import org.testcontainers.utility.DockerImageName -import spock.lang.Requires -import spock.lang.Shared - -import javax.sql.DataSource import java.sql.CallableStatement import java.sql.Connection import java.sql.Driver @@ -27,16 +25,14 @@ import java.sql.Statement import java.sql.Types import java.time.Duration import java.util.concurrent.TimeUnit +import javax.sql.DataSource +import org.testcontainers.containers.MSSQLServerContainer +import org.testcontainers.containers.MySQLContainer +import org.testcontainers.containers.OracleContainer +import org.testcontainers.containers.PostgreSQLContainer +import org.testcontainers.utility.DockerImageName +import spock.lang.Shared -import static datadog.trace.agent.test.utils.TraceUtils.basicSpan -import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace -import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE -import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_TRACE_PREPARED_STATEMENTS - -// workaround for SSLHandShakeException on J9 only with Hikari/MySQL -@Requires({ - !System.getProperty("java.vendor").contains("IBM") -}) abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { static final String POSTGRESQL = "postgresql" static final String MYSQL = "mysql" @@ -96,6 +92,10 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { @Shared private Map> cpDatasources = new HashMap<>() + private static boolean dockerImageSupported(String db) { + return !(db == SQLSERVER && OperatingSystem.isArm64() && OperatingSystem.isLinux()) + } + def peerConnectionProps(String db){ def props = new Properties() props.setProperty("user", jdbcUserNames.get(db)) @@ -164,6 +164,11 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { def createDS(String connectionPoolName, String dbType, String jdbcUrl) { DataSource ds = null + + if (!dockerImageSupported(dbType)) { + return ds + } + if (connectionPoolName == "tomcat") { ds = createTomcatDS(dbType, jdbcUrl) } @@ -185,11 +190,14 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { } def setupSpec() { + // POSTGRESQL postgres = new PostgreSQLContainer("postgres:11.2") .withDatabaseName(dbName.get(POSTGRESQL)).withUsername(jdbcUserNames.get(POSTGRESQL)).withPassword(jdbcPasswords.get(POSTGRESQL)) postgres.start() PortUtils.waitForPortToOpen(postgres.getHost(), postgres.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), 5, TimeUnit.SECONDS) jdbcUrls.put(POSTGRESQL, "${postgres.getJdbcUrl()}") + + // MYSQL mysql = new MySQLContainer("mysql:8.0") .withDatabaseName(dbName.get(MYSQL)).withUsername(jdbcUserNames.get(MYSQL)).withPassword(jdbcPasswords.get(MYSQL)) // https://github.com/testcontainers/testcontainers-java/issues/914 @@ -197,11 +205,16 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { mysql.start() PortUtils.waitForPortToOpen(mysql.getHost(), mysql.getMappedPort(MySQLContainer.MYSQL_PORT), 5, TimeUnit.SECONDS) jdbcUrls.put(MYSQL, "${mysql.getJdbcUrl()}") - sqlserver = new MSSQLServerContainer(MSSQLServerContainer.IMAGE).acceptLicense().withPassword(jdbcPasswords.get(SQLSERVER)) - sqlserver.start() - PortUtils.waitForPortToOpen(sqlserver.getHost(), sqlserver.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT), 5, TimeUnit.SECONDS) - jdbcUrls.put(SQLSERVER, "${sqlserver.getJdbcUrl()};DatabaseName=${dbName.get(SQLSERVER)}") + // SQLSERVER + if (dockerImageSupported(SQLSERVER)) { + sqlserver = new MSSQLServerContainer(MSSQLServerContainer.IMAGE).acceptLicense().withPassword(jdbcPasswords.get(SQLSERVER)) + sqlserver.start() + PortUtils.waitForPortToOpen(sqlserver.getHost(), sqlserver.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT), 5, TimeUnit.SECONDS) + jdbcUrls.put(SQLSERVER, "${sqlserver.getJdbcUrl()};DatabaseName=${dbName.get(SQLSERVER)}") + } + + // ORACLE // Earlier Oracle version images (oracle-xe) don't work on arm64 DockerImageName oracleImage = DockerImageName.parse("gvenzl/oracle-free:23.5-slim-faststart").asCompatibleSubstituteFor("gvenzl/oracle-xe") oracle = new OracleContainer(oracleImage) @@ -226,8 +239,9 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { oracle?.close() } - def "basic statement with #connection.getClass().getCanonicalName() on #driver generates spans"() { + def "basic statement on #driver with #pool generates spans"() { setup: + Connection connection = setupConnection(pool, driver) injectSysConfig(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "$renameService") when: @@ -339,27 +353,28 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { connection.close() where: - driver | connection | renameService | query | operation | obfuscatedQuery | usingHikari - MYSQL | connectTo(driver, peerConnectionProps(driver)) | false | "SELECT 3" | "SELECT" | "SELECT ?" | false - POSTGRESQL | connectTo(driver, peerConnectionProps(driver)) | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false - SQLSERVER | connectTo(driver, peerConnectionProps(driver)) | false | "SELECT 3" | "SELECT" | "SELECT ?" | false - ORACLE | connectTo(driver, peerConnectionProps(driver)) | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false - MYSQL | cpDatasources.get("tomcat").get(driver).getConnection() | false | "SELECT 3" | "SELECT" | "SELECT ?" | false - POSTGRESQL | cpDatasources.get("tomcat").get(driver).getConnection() | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false - SQLSERVER | cpDatasources.get("tomcat").get(driver).getConnection() | false | "SELECT 3" | "SELECT" | "SELECT ?" | false - ORACLE | cpDatasources.get("tomcat").get(driver).getConnection() | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false - MYSQL | cpDatasources.get("hikari").get(driver).getConnection() | false | "SELECT 3" | "SELECT" | "SELECT ?" | true - POSTGRESQL | cpDatasources.get("hikari").get(driver).getConnection() | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | true - SQLSERVER | cpDatasources.get("hikari").get(driver).getConnection() | false | "SELECT 3" | "SELECT" | "SELECT ?" | true - ORACLE | cpDatasources.get("hikari").get(driver).getConnection() | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | true - MYSQL | cpDatasources.get("c3p0").get(driver).getConnection() | false | "SELECT 3" | "SELECT" | "SELECT ?" | false - POSTGRESQL | cpDatasources.get("c3p0").get(driver).getConnection() | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false - SQLSERVER | cpDatasources.get("c3p0").get(driver).getConnection() | false | "SELECT 3" | "SELECT" | "SELECT ?" | false - ORACLE | cpDatasources.get("c3p0").get(driver).getConnection() | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + driver | pool | renameService | query | operation | obfuscatedQuery | usingHikari + MYSQL | null | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + POSTGRESQL | null | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false + SQLSERVER | null | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + ORACLE | null | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + MYSQL | "tomcat" | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + POSTGRESQL | "tomcat" | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false + SQLSERVER | "tomcat" | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + ORACLE | "tomcat" | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + MYSQL | "hikari" | false | "SELECT 3" | "SELECT" | "SELECT ?" | true + POSTGRESQL | "hikari" | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | true + SQLSERVER | "hikari" | false | "SELECT 3" | "SELECT" | "SELECT ?" | true + ORACLE | "hikari" | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | true + MYSQL | "c3p0" | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + POSTGRESQL | "c3p0" | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false + SQLSERVER | "c3p0" | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + ORACLE | "c3p0" | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false } - def "prepared statement execute on #driver with #connection.getClass().getCanonicalName() generates a span"() { + def "prepared statement execute on #driver with #pool generates a span"() { setup: + Connection connection = setupConnection(pool, driver) PreparedStatement statement = connection.prepareStatement(query) when: @@ -475,28 +490,30 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { connection.close() where: - driver | connection | query | operation | obfuscatedQuery | usingHikari - MYSQL | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3" | "SELECT" | "SELECT ?" | false - POSTGRESQL | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false - SQLSERVER | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3" | "SELECT" | "SELECT ?" | false - ORACLE | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false - MYSQL | cpDatasources.get("tomcat").get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" | false - POSTGRESQL | cpDatasources.get("tomcat").get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false - SQLSERVER | cpDatasources.get("tomcat").get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" | false - ORACLE | cpDatasources.get("tomcat").get(driver).getConnection() | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false - MYSQL | cpDatasources.get("hikari").get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" | true - POSTGRESQL | cpDatasources.get("hikari").get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | true - SQLSERVER | cpDatasources.get("hikari").get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" | true - ORACLE | cpDatasources.get("hikari").get(driver).getConnection() | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | true - MYSQL | cpDatasources.get("c3p0").get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" | false - POSTGRESQL | cpDatasources.get("c3p0").get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false - SQLSERVER | cpDatasources.get("c3p0").get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" | false - ORACLE | cpDatasources.get("c3p0").get(driver).getConnection() | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + driver | pool | query | operation | obfuscatedQuery | usingHikari + MYSQL | null | "SELECT 3" | "SELECT" | "SELECT ?" | false + POSTGRESQL | null | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false + SQLSERVER | null | "SELECT 3" | "SELECT" | "SELECT ?" | false + ORACLE | null | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + MYSQL | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" | false + POSTGRESQL | "tomcat" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false + SQLSERVER | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" | false + ORACLE | "tomcat" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + MYSQL | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" | true + POSTGRESQL | "hikari" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | true + SQLSERVER | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" | true + ORACLE | "hikari" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | true + MYSQL | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" | false + POSTGRESQL | "c3p0" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false + SQLSERVER | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" | false + ORACLE | "c3p0" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false } - def "prepared statement query on #driver with #connection.getClass().getCanonicalName() generates a span"() { + def "prepared statement query on #driver with #pool generates a span"() { setup: + Connection connection = setupConnection(pool, driver) PreparedStatement statement = connection.prepareStatement(query) + when: ResultSet resultSet = runUnderTrace("parent") { return statement.executeQuery() @@ -590,7 +607,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { // since Connection.getClientInfo will not provide the username "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } "$Tags.DB_OPERATION" operation - if (conPoolType == "hikari") { + if (pool == "hikari") { "$Tags.DB_POOL_NAME" String } if (this.dbmTracePreparedStatements(driver)){ @@ -612,28 +629,30 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { connection.close() where: - driver | conPoolType | connection | query | operation | obfuscatedQuery - MYSQL | "" | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3" | "SELECT" | "SELECT ?" - POSTGRESQL | "" | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" - SQLSERVER | "" | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3" | "SELECT" | "SELECT ?" - ORACLE | "" | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" - MYSQL | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - POSTGRESQL | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" - SQLSERVER | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - ORACLE | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" - MYSQL | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - POSTGRESQL | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" - SQLSERVER | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - ORACLE | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" - MYSQL | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - POSTGRESQL | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" - SQLSERVER | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - ORACLE | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" + driver | pool | query | operation | obfuscatedQuery + MYSQL | null | "SELECT 3" | "SELECT" | "SELECT ?" + POSTGRESQL | null | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + SQLSERVER | null | "SELECT 3" | "SELECT" | "SELECT ?" + ORACLE | null | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" + MYSQL | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" + POSTGRESQL | "tomcat" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + SQLSERVER | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" + ORACLE | "tomcat" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" + MYSQL | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" + POSTGRESQL | "hikari" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + SQLSERVER | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" + ORACLE | "hikari" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" + MYSQL | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" + POSTGRESQL | "c3p0" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + SQLSERVER | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" + ORACLE | "c3p0" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" } - def "prepared call on #driver with #connection.getClass().getCanonicalName() generates a span"() { + def "prepared call on #driver with #pool generates a span"() { setup: + Connection connection = setupConnection(pool, driver) CallableStatement statement = connection.prepareCall(query) + when: ResultSet resultSet = runUnderTrace("parent") { return statement.executeQuery() @@ -721,7 +740,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { // since Connection.getClientInfo will not provide the username "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } "${Tags.DB_OPERATION}" operation - if (conPoolType == "hikari") { + if (pool == "hikari") { "$Tags.DB_POOL_NAME" String } if (this.dbmTracePreparedStatements(this.getDbType(driver))){ @@ -742,27 +761,28 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { connection.close() where: - driver | conPoolType | connection | query | operation | obfuscatedQuery - MYSQL | "" | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3" | "SELECT" | "SELECT ?" - POSTGRESQL | "" | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" - SQLSERVER | "" | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3" | "SELECT" | "SELECT ?" - ORACLE | "" | connectTo(driver, peerConnectionProps(driver)) | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" - MYSQL | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - POSTGRESQL | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" - SQLSERVER | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - ORACLE | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" - MYSQL | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - POSTGRESQL | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" - SQLSERVER | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - ORACLE | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" - MYSQL | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - POSTGRESQL | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" - SQLSERVER | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3" | "SELECT" | "SELECT ?" - ORACLE | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" + driver | pool | query | operation | obfuscatedQuery + MYSQL | null | "SELECT 3" | "SELECT" | "SELECT ?" + POSTGRESQL | null | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + SQLSERVER | null | "SELECT 3" | "SELECT" | "SELECT ?" + ORACLE | null | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" + MYSQL | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" + POSTGRESQL | "tomcat" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + SQLSERVER | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" + ORACLE | "tomcat" | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" + MYSQL | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" + POSTGRESQL | "hikari" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + SQLSERVER | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" + ORACLE | "hikari" | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" + MYSQL | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" + POSTGRESQL | "c3p0" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + SQLSERVER | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" + ORACLE | "c3p0" | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" } - def "statement update on #driver with #connection.getClass().getCanonicalName() generates a span"() { + def "statement update on #driver with #pool generates a span"() { setup: + Connection connection = setupConnection(pool, driver) Statement statement = connection.createStatement() def sql = connection.nativeSQL(query) @@ -801,7 +821,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { if (addDbmTag) { "$InstrumentationTags.DBM_TRACE_INJECTED" true } - if (conPoolType == "hikari") { + if (pool == "hikari") { "$Tags.DB_POOL_NAME" String } peerServiceFrom(Tags.DB_INSTANCE) @@ -878,28 +898,29 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { connection.close() where: - driver | conPoolType | connection | query | operation - MYSQL | "" | connectTo(driver, peerConnectionProps(driver)) | "CREATE TEMPORARY TABLE s_test_ (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - POSTGRESQL | "" | connectTo(driver, peerConnectionProps(driver)) | "CREATE TEMPORARY TABLE s_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - SQLSERVER | "" | connectTo(driver, peerConnectionProps(driver)) | "CREATE TABLE #s_test_ (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - ORACLE | "" | connectTo(driver, peerConnectionProps(driver)) | "CREATE GLOBAL TEMPORARY TABLE s_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - MYSQL | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - POSTGRESQL | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - SQLSERVER | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TABLE #s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - ORACLE | "tomcat" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE GLOBAL TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - MYSQL | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - POSTGRESQL | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - SQLSERVER | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TABLE #s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - ORACLE | "hikari" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE GLOBAL TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - MYSQL | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - POSTGRESQL | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - SQLSERVER | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE TABLE #s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" - ORACLE | "c3p0" | cpDatasources.get(conPoolType).get(driver).getConnection() | "CREATE GLOBAL TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + driver | pool | query | operation + MYSQL | null | "CREATE TEMPORARY TABLE s_test_ (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + POSTGRESQL | null | "CREATE TEMPORARY TABLE s_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + SQLSERVER | null | "CREATE TABLE #s_test_ (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + ORACLE | null | "CREATE GLOBAL TEMPORARY TABLE s_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + MYSQL | "tomcat" | "CREATE TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + POSTGRESQL | "tomcat" | "CREATE TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + SQLSERVER | "tomcat" | "CREATE TABLE #s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + ORACLE | "tomcat" | "CREATE GLOBAL TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + MYSQL | "hikari" | "CREATE TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + POSTGRESQL | "hikari" | "CREATE TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + SQLSERVER | "hikari" | "CREATE TABLE #s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + ORACLE | "hikari" | "CREATE GLOBAL TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + MYSQL | "c3p0" | "CREATE TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + POSTGRESQL | "c3p0" | "CREATE TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + SQLSERVER | "c3p0" | "CREATE TABLE #s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + ORACLE | "c3p0" | "CREATE GLOBAL TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" } - def "prepared procedure call with return value on #driver with #connection.getClass().getCanonicalName() does not hang"() { + def "prepared procedure call with return value on #driver with #pool does not hang"() { setup: + Connection connection = setupConnection(pool, driver) injectSysConfig("dd.dbm.propagation.mode", "full") CallableStatement upperProc = connection.prepareCall(query) @@ -918,19 +939,20 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { connection.close() where: - driver | connection | query - POSTGRESQL | cpDatasources.get("hikari").get(driver).getConnection() | "{ ? = call upper( ? ) }" - MYSQL | cpDatasources.get("hikari").get(driver).getConnection() | "{ ? = call upper( ? ) }" - POSTGRESQL | cpDatasources.get("tomcat").get(driver).getConnection() | "{ ? = call upper( ? ) }" - MYSQL | cpDatasources.get("tomcat").get(driver).getConnection() | "{ ? = call upper( ? ) }" - POSTGRESQL | cpDatasources.get("c3p0").get(driver).getConnection() | "{ ? = call upper( ? ) }" - MYSQL | cpDatasources.get("c3p0").get(driver).getConnection() | "{ ? = call upper( ? ) }" - POSTGRESQL | connectTo(driver, peerConnectionProps(driver)) | "{ ? = call upper( ? ) }" - MYSQL | connectTo(driver, peerConnectionProps(driver)) | "{ ? = call upper( ? ) }" + driver | pool | query + POSTGRESQL | "hikari" | "{ ? = call upper( ? ) }" + MYSQL | "hikari" | "{ ? = call upper( ? ) }" + POSTGRESQL | "tomcat" | "{ ? = call upper( ? ) }" + MYSQL | "tomcat" | "{ ? = call upper( ? ) }" + POSTGRESQL | "c3p0" | "{ ? = call upper( ? ) }" + MYSQL | "c3p0" | "{ ? = call upper( ? ) }" + POSTGRESQL | null | "{ ? = call upper( ? ) }" + MYSQL | null | "{ ? = call upper( ? ) }" } - def "prepared procedure call on #driver with #connection.getClass().getCanonicalName() does not hang"() { + def "prepared procedure call on #driver with #pool does not hang"() { setup: + Connection connection = setupConnection(pool, driver) String createSql if (driver == "postgresql") { @@ -993,22 +1015,21 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { connection.close() where: - driver | connection | query - POSTGRESQL | cpDatasources.get("hikari").get(driver).getConnection() | "CALL dummy(?)" - MYSQL | cpDatasources.get("hikari").get(driver).getConnection() | "CALL dummy(?)" - SQLSERVER | cpDatasources.get("hikari").get(driver).getConnection() | "{CALL dummy(?)}" - POSTGRESQL | cpDatasources.get("tomcat").get(driver).getConnection() | "CALL dummy(?)" - MYSQL | cpDatasources.get("tomcat").get(driver).getConnection() | "{CALL dummy(?)}" - SQLSERVER | cpDatasources.get("tomcat").get(driver).getConnection() | "{CALL dummy(?)}" - POSTGRESQL | cpDatasources.get("c3p0").get(driver).getConnection() | "CALL dummy(?)" - MYSQL | cpDatasources.get("c3p0").get(driver).getConnection() | "CALL dummy(?)" - SQLSERVER | cpDatasources.get("c3p0").get(driver).getConnection() | "{CALL dummy(?)}" - POSTGRESQL | connectTo(driver, peerConnectionProps(driver)) | "CALL dummy(?)" - MYSQL | connectTo(driver, peerConnectionProps(driver)) | "CALL dummy(?)" - SQLSERVER | connectTo(driver, peerConnectionProps(driver)) | "{CALL dummy(?)}" + driver | pool | query + POSTGRESQL | "hikari" | "CALL dummy(?)" + MYSQL | "hikari" | "CALL dummy(?)" + SQLSERVER | "hikari" | "{CALL dummy(?)}" + POSTGRESQL | "tomcat" | "CALL dummy(?)" + MYSQL | "tomcat" | "{CALL dummy(?)}" + SQLSERVER | "tomcat" | "{CALL dummy(?)}" + POSTGRESQL | "c3p0" | "CALL dummy(?)" + MYSQL | "c3p0" | "CALL dummy(?)" + SQLSERVER | "c3p0" | "{CALL dummy(?)}" + POSTGRESQL | null | "CALL dummy(?)" + MYSQL | null | "CALL dummy(?)" + SQLSERVER | null | "{CALL dummy(?)}" } - Driver driverFor(String db) { return newDriver(jdbcDriverClassNames.get(db)) } @@ -1017,6 +1038,16 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { return connect(jdbcDriverClassNames.get(db), jdbcUrls.get(db), properties) } + Connection setupConnection(String pool, String db) { + assumeTrue(dockerImageSupported(db)) + + def conn = pool ? cpDatasources.get(pool).get(db).getConnection() : connectTo(db, peerConnectionProps(db)) + + TEST_WRITER.clear() // Clear any traces that pool or db can emmit on connection creation. + + return conn + } + Driver newDriver(String driverClass) { return ((Driver) Class.forName(driverClass) .getDeclaredConstructor().newInstance())