diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 44dc2e54c8a..be8e2de9d59 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,6 +36,7 @@ stages: - shared-pipeline - benchmarks - tests + - tests-arm64 - test-summary - exploration-tests - ci-visibility-tests @@ -53,7 +54,7 @@ variables: 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/" 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-") + BUILDER_IMAGE_VERSION_PREFIX: "163_merge-" # 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" DEFAULT_TEST_JVMS: /^(8|11|17|21|25|tip)$/ # the latest "tip" version is 26 PROFILE_TESTS: @@ -163,6 +164,24 @@ default: echo -e "${TEXT_BOLD}${TEXT_YELLOW} Containers:${TEXT_CLEAR} https://app.datadoghq.com/containers?${TIME_PARAMS}query=image_name%3A%2A%2Fdatadog%2Fdd-trace-java-docker-build%20AND%20pod_name%3A${POD_NAME}&live=false" echo -e "${TEXT_BOLD}${TEXT_YELLOW} Processes:${TEXT_CLEAR} https://app.datadoghq.com/process?${TIME_PARAMS}query=image_name%3A%2A%2Fdatadog%2Fdd-trace-java-docker-build%20AND%20pod_name%3A${POD_NAME}&live=false" +.tier_m: + variables: &tier_m_variables + GRADLE_WORKERS: 4 + GRADLE_MEMORY_MIN: 1G + GRADLE_MEMORY_MAX: 5G + KUBERNETES_CPU_REQUEST: 6 + KUBERNETES_MEMORY_REQUEST: 16Gi + KUBERNETES_MEMORY_LIMIT: 16Gi + +.tier_l: + variables: &tier_l_variables + GRADLE_WORKERS: 6 + GRADLE_MEMORY_MIN: 1G + GRADLE_MEMORY_MAX: 6G + KUBERNETES_CPU_REQUEST: 10 + KUBERNETES_MEMORY_REQUEST: 20Gi + KUBERNETES_MEMORY_LIMIT: 20Gi + .gitlab_base_ref_params: &gitlab_base_ref_params - | export GIT_BASE_REF=$(.gitlab/find-gh-base-ref.sh) @@ -176,13 +195,8 @@ default: image: ${BUILDER_IMAGE_REPO}:${BUILDER_IMAGE_VERSION_PREFIX}base stage: build variables: + <<: *tier_m_variables MAVEN_OPTS: "-Xms256M -Xmx1024M" - GRADLE_WORKERS: 6 - GRADLE_MEMORY_MIN: 1G - GRADLE_MEMORY_MAX: 4G - KUBERNETES_CPU_REQUEST: 10 - KUBERNETES_MEMORY_REQUEST: 20Gi - KUBERNETES_MEMORY_LIMIT: 20Gi CACHE_TYPE: "lib" #default FF_USE_FASTZIP: "true" CACHE_COMPRESSION_LEVEL: "slowest" @@ -626,6 +640,7 @@ muzzle-dep-report: needs: [ build_tests ] stage: tests variables: + <<: *tier_m_variables GRADLE_PARAMS: "-PskipFlakyTests" CONTINUE_ON_FAILURE: "false" TESTCONTAINERS_CHECKS_DISABLE: "true" @@ -693,6 +708,122 @@ muzzle-dep-report: - scheduler_failure - data_integrity_failure +.test_job_arm64: + image: ${BUILDER_IMAGE_REPO}:${BUILDER_IMAGE_VERSION_PREFIX}$testJvm + tags: [ "docker-in-docker:arm64" ] + stage: tests-arm64 + needs: [] + variables: + <<: *tier_m_variables + # arm64 does not include JDK 7 or ibm8 (upstream images not published for arm64). + DEFAULT_TEST_JVMS: /^(8|11|17|21|25|tip)$/ + GRADLE_PARAMS: "-PskipFlakyTests" + 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 + rules: + - if: $testJvm =~ $DEFAULT_TEST_JVMS + when: manual + allow_failure: true + - if: '$NON_DEFAULT_JVMS == "true" && $testJvm != "ibm8" && $testJvm != "oracle8"' + when: manual + allow_failure: true + cache: + - key: dependency-$CACHE_TYPE + paths: + - .gradle/wrapper + - .gradle/caches + - .gradle/notifications + - .mvn/caches + policy: pull + fallback_keys: + - dependency-base + - dependency-lib + unprotect: true + before_script: + - git config --global --add safe.directory "$CI_PROJECT_DIR" + # Akka token added to SSM from https://account.akka.io/token + - export ORG_GRADLE_PROJECT_akkaRepositoryToken=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.akka_repo_token --with-decryption --query "Parameter.Value" --out text) + - 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 <()) } + val isLinuxArm64 = HostPlatform.isLinuxArm64() + tasks.configureEach { if (this is JavaForkOptions) { maxHeapSize = System.getProperty("datadog.forkedMaxHeapSize") @@ -91,6 +94,20 @@ allprojects { "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/tmp" ) + if (isLinuxArm64) { + // Disable CDS to avoid SIGSEGVs on Linux arm64. + jvmArgs("-Xshare:off") + } + } + } + + // Disable CDS to avoid SIGSEGVs on Linux arm64. + if (isLinuxArm64) { + tasks.withType().configureEach { + options.forkOptions.jvmArgs?.add("-Xshare:off") + } + tasks.withType().configureEach { + groovyOptions.forkOptions.jvmArgs?.add("-Xshare:off") } } } diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/HostPlatform.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/HostPlatform.kt new file mode 100644 index 00000000000..40837194ecd --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/HostPlatform.kt @@ -0,0 +1,21 @@ +package datadog.gradle.plugin + +import java.util.Locale + +object HostPlatform { + @JvmStatic + fun isLinuxArm64(): Boolean = isExpectedOs("linux") && isArm64() + + @JvmStatic + fun isMacArm64(): Boolean = isExpectedOs("mac") && isArm64() + + private fun isExpectedOs(expectedOs: String): Boolean { + val osName = System.getProperty("os.name", "").lowercase(Locale.ROOT) + return osName.contains(expectedOs) + } + + private fun isArm64(): Boolean { + val osArch = System.getProperty("os.arch", "").lowercase(Locale.ROOT) + return osArch.contains("aarch64") || osArch.contains("arm64") + } +} diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/instrument/InstrumentPostProcessingAction.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/instrument/InstrumentPostProcessingAction.kt index b8c3e300afd..839b41e0960 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/instrument/InstrumentPostProcessingAction.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/instrument/InstrumentPostProcessingAction.kt @@ -1,5 +1,6 @@ package datadog.gradle.plugin.instrument +import datadog.gradle.plugin.HostPlatform import datadog.gradle.plugin.instrument.BuildTimeInstrumentationPlugin.Companion.BUILD_TIME_INSTRUMENTATION_PLUGIN_CONFIGURATION import org.gradle.api.Action import org.gradle.api.Project @@ -73,6 +74,10 @@ abstract class InstrumentPostProcessingAction @Inject constructor( return workerExecutor.processIsolation { forkOptions { setExecutable(javaLauncher.executablePath.asFile.absolutePath) + if (HostPlatform.isLinuxArm64()) { + // Disable CDS to avoid SIGSEGVs on Linux arm64. + jvmArgs("-Xshare:off") + } } } } diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleTask.kt index 2d5d830ea3b..76917c6a6c7 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/muzzle/tasks/MuzzleTask.kt @@ -1,5 +1,6 @@ package datadog.gradle.plugin.muzzle.tasks +import datadog.gradle.plugin.HostPlatform import datadog.gradle.plugin.muzzle.MuzzleAction import datadog.gradle.plugin.muzzle.MuzzleDirective import datadog.gradle.plugin.muzzle.MuzzleExtension @@ -102,6 +103,10 @@ abstract class MuzzleTask @Inject constructor( if(javaLauncher.metadata.languageVersion > JavaLanguageVersion.of(9)) { jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") } + if (HostPlatform.isLinuxArm64()) { + // Disable CDS to avoid SIGSEGVs on Linux arm64. + jvmArgs("-Xshare:off") + } executable(javaLauncher.executablePath) } } diff --git a/buildSrc/src/main/kotlin/dd-trace-java.test-jvm-constraints.gradle.kts b/buildSrc/src/main/kotlin/dd-trace-java.test-jvm-constraints.gradle.kts index aa4724183b0..41868d093d1 100644 --- a/buildSrc/src/main/kotlin/dd-trace-java.test-jvm-constraints.gradle.kts +++ b/buildSrc/src/main/kotlin/dd-trace-java.test-jvm-constraints.gradle.kts @@ -1,3 +1,4 @@ +import datadog.gradle.plugin.HostPlatform import datadog.gradle.plugin.testJvmConstraints.ProvideJvmArgsOnJvmLauncherVersion import datadog.gradle.plugin.testJvmConstraints.TestJvmConstraintsExtension import datadog.gradle.plugin.testJvmConstraints.TestJvmConstraintsExtension.Companion.TEST_JVM_CONSTRAINTS @@ -79,6 +80,15 @@ private fun Test.configureTestJvm(extension: TestJvmConstraintsExtension) { ), extension.allowReflectiveAccessToJdk ) + + // Fix for Linux arm64 ByteBuddy error: + // "Could not self-attach to current VM using external process" + if (HostPlatform.isLinuxArm64()) { + conditionalJvmArgs( + JavaVersion.VERSION_1_9, + listOf("-Djdk.attach.allowAttachSelf=true") + ) + } } // Jacoco plugin is not applied on every project diff --git a/communication/src/test/java/datadog/communication/serialization/msgpack/MsgPackWriterTest.java b/communication/src/test/java/datadog/communication/serialization/msgpack/MsgPackWriterTest.java index 4a3f773fdc5..fca302dd3d1 100644 --- a/communication/src/test/java/datadog/communication/serialization/msgpack/MsgPackWriterTest.java +++ b/communication/src/test/java/datadog/communication/serialization/msgpack/MsgPackWriterTest.java @@ -33,8 +33,7 @@ import org.msgpack.core.MessageUnpacker; public class MsgPackWriterTest { - // Explicit escapes for non-ASCII chars to make test independent of container settings. - private static final String NON_ASCII_STRING = "foob\u00E1r_\u263a"; // foobár_☺ + private static final String NON_ASCII_STRING = "foobár_☺"; private static final byte[] NON_ASCII_BYTES = NON_ASCII_STRING.getBytes(UTF_8); private static final int NON_ASCII_BUFFER_CAPACITY = NON_ASCII_BYTES.length + 1; diff --git a/components/environment/src/main/java/datadog/environment/OperatingSystem.java b/components/environment/src/main/java/datadog/environment/OperatingSystem.java index 1d24d7680c9..7e64e6bdb33 100644 --- a/components/environment/src/main/java/datadog/environment/OperatingSystem.java +++ b/components/environment/src/main/java/datadog/environment/OperatingSystem.java @@ -213,5 +213,14 @@ static Architecture current() { String property = SystemProperties.getOrDefault(OS_ARCH_PROPERTY, "").toLowerCase(ROOT); return Architecture.of(property); } + + /** + * Checks whether the architecture is arm64. + * + * @return {@code true} if architecture is arm64, {@code false} otherwise. + */ + public boolean isArm64() { + return this == ARM64; + } } } diff --git a/components/json/src/test/java/datadog/json/JsonReaderTest.java b/components/json/src/test/java/datadog/json/JsonReaderTest.java index 15a635c148a..e716a625103 100644 --- a/components/json/src/test/java/datadog/json/JsonReaderTest.java +++ b/components/json/src/test/java/datadog/json/JsonReaderTest.java @@ -215,8 +215,7 @@ void testStringEscaping() { assertEquals("\n", reader.nextString()); assertEquals("\r", reader.nextString()); assertEquals("\t", reader.nextString()); - // Explicit escape for non-ASCII `É` to make test independent of container settings. - assertEquals("\u00C9", reader.nextString()); + assertEquals("É", reader.nextString()); reader.endArray(); } catch (IOException e) { fail("Failed to read escaped JSON strings", e); diff --git a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/buildid/BuildIdExtractorIntegrationTest.java b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/buildid/BuildIdExtractorIntegrationTest.java index a0cf46ea89b..14c14a6a97d 100644 --- a/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/buildid/BuildIdExtractorIntegrationTest.java +++ b/dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/buildid/BuildIdExtractorIntegrationTest.java @@ -1,12 +1,11 @@ package datadog.crashtracking.buildid; -import static datadog.environment.OperatingSystem.Architecture.ARM64; +import static datadog.environment.OperatingSystem.architecture; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeFalse; -import datadog.environment.OperatingSystem; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -138,7 +137,7 @@ private static Stream 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(architecture().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..be2553acade 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,6 +1,6 @@ package com.datadog.profiling.controller; -import static datadog.environment.OperatingSystem.Architecture.ARM64; +import static datadog.environment.OperatingSystem.architecture; import datadog.environment.JavaVirtualMachine; import datadog.environment.OperatingSystem; @@ -238,16 +238,13 @@ private static boolean checkLoadLibrary(Path target, StringBuilder sb) { @SuppressForbidden private static boolean extractSoFromJar(Path target, StringBuilder sb) throws Exception { URL jarUrl = EnvironmentChecker.class.getProtectionDomain().getCodeSource().getLocation(); + String linuxArchFolder = architecture().isArm64() ? "/linux-arm64/" : "/linux-x64/"; try (JarFile jarFile = new JarFile(new File(jarUrl.toURI()))) { return jarFile.stream() .filter(e -> e.getName().contains("libjavaProfiler.so")) .filter( e -> - e.getName() - .contains( - OperatingSystem.architecture() == ARM64 - ? "/linux-arm64/" - : "/linux-x64/") + e.getName().contains(linuxArchFolder) && (!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..746f57577c8 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,6 +1,10 @@ package datadog.trace.instrumentation.aerospike4 +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 com.github.dockerjava.api.model.Ulimit import datadog.trace.agent.test.asserts.TraceAssert import datadog.trace.agent.test.naming.VersionedNamingTestBase import datadog.trace.api.DDSpanTypes @@ -9,10 +13,6 @@ 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 { @Shared @@ -25,8 +25,14 @@ abstract class AerospikeBaseTest extends VersionedNamingTestBase { int aerospikePort = 3000 def setup() throws Exception { - aerospike = new GenericContainer('aerospike:5.5.0.9') + // Linux arm64 supported since `ce-6.2.0.2` + aerospike = new GenericContainer('aerospike:ce-6.2.0.2') .withExposedPorts(3000) + // proto-fd-max default is 15000, but container default is 1024. + // see: https://aerospike.com/docs/database/reference/config#service__proto-fd-max + .withCreateContainerCmdModifier({ cmd -> + cmd.getHostConfig().withUlimits([new Ulimit('nofile', 15000L, 15000L)] as Ulimit[]) + }) .waitingFor(forLogMessage(".*heartbeat-received.*\\n", 1)) aerospike.start() @@ -37,9 +43,7 @@ abstract class AerospikeBaseTest extends VersionedNamingTestBase { } def cleanup() throws Exception { - if (aerospike) { - aerospike.stop() - } + aerospike?.stop() } def aerospikeSpan(TraceAssert trace, int index, String methodName, Object parentSpan = null) { 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/build.gradle b/dd-java-agent/instrumentation/java/java-concurrent/java-concurrent-1.8/build.gradle index ee85bef77fa..83caaeb1324 100644 --- a/dd-java-agent/instrumentation/java/java-concurrent/java-concurrent-1.8/build.gradle +++ b/dd-java-agent/instrumentation/java/java-concurrent/java-concurrent-1.8/build.gradle @@ -21,4 +21,3 @@ dependencies { // TODO: Tomcat 10.0.10 has a copy of the JSR166 ThreadPoolExecutor so it needs special instrumentation latestDepTestImplementation group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '10.0.8' } - 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..727d8060c32 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.architecture +import static org.junit.jupiter.api.Assumptions.assumeTrue + +import datadog.environment.OperatingSystem import datadog.trace.agent.test.InstrumentationSpecification import datadog.trace.api.Trace import datadog.trace.core.DDSpan @@ -8,21 +12,23 @@ 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 runnable.JavaAsyncChild +import spock.lang.IgnoreIf +import spock.lang.Shared -import static org.junit.jupiter.api.Assumptions.assumeTrue - +// TODO: netty-all 4.1.9 only ships linux-x86_64 epoll native libraries. +@IgnoreIf({ + OperatingSystem.isLinux() && architecture().isArm64() +}) class NettyExecutorInstrumentationTest extends InstrumentationSpecification { @Shared - boolean isLinux = System.getProperty("os.name").toLowerCase().contains("linux") + boolean isLinux = OperatingSystem.isLinux() @Shared EpollEventLoopGroup epollEventLoopGroup = isLinux ? new EpollEventLoopGroup(4) : null 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 616b38a456d..4d780134a1f 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy @@ -1,12 +1,15 @@ +import static datadog.environment.OperatingSystem.architecture 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 @@ -31,50 +34,63 @@ import org.testcontainers.containers.PostgreSQLContainer import org.testcontainers.utility.DockerImageName import spock.lang.Shared -abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { - static final String POSTGRESQL = "postgresql" - static final String MYSQL = "mysql" - static final String SQLSERVER = "sqlserver" - static final String ORACLE = "oracle" +enum DbType { + POSTGRESQL("postgresql"), + MYSQL("mysql"), + SQLSERVER("sqlserver"), + ORACLE("oracle") + + private final String name + + DbType(String name) { + this.name = name + } + + @Override + String toString() { + return name + } +} +abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { @Shared - private Map dbName = [ - (POSTGRESQL): "jdbcUnitTest", - (MYSQL) : "jdbcUnitTest", - (SQLSERVER) : "master", - (ORACLE) : "freepdb1", + private Map dbName = [ + (DbType.POSTGRESQL): "jdbcUnitTest", + (DbType.MYSQL) : "jdbcUnitTest", + (DbType.SQLSERVER) : "master", + (DbType.ORACLE) : "freepdb1", ] @Shared - private Map jdbcUrls = [ - (POSTGRESQL) : "jdbc:postgresql://localhost:5432/" + dbName.get(POSTGRESQL), - (MYSQL) : "jdbc:mysql://localhost:3306/" + dbName.get(MYSQL), - (SQLSERVER) : "jdbc:sqlserver://localhost:1433/" + dbName.get(SQLSERVER), - (ORACLE) : "jdbc:oracle:thin:@//localhost:1521/" + dbName.get(ORACLE), + private Map jdbcUrls = [ + (DbType.POSTGRESQL) : "jdbc:postgresql://localhost:5432/" + dbName.get(DbType.POSTGRESQL), + (DbType.MYSQL) : "jdbc:mysql://localhost:3306/" + dbName.get(DbType.MYSQL), + (DbType.SQLSERVER) : "jdbc:sqlserver://localhost:1433/" + dbName.get(DbType.SQLSERVER), + (DbType.ORACLE) : "jdbc:oracle:thin:@//localhost:1521/" + dbName.get(DbType.ORACLE), ] @Shared - private Map jdbcDriverClassNames = [ - (POSTGRESQL): "org.postgresql.Driver", - (MYSQL) : "com.mysql.jdbc.Driver", - (SQLSERVER) : "com.microsoft.sqlserver.jdbc.SQLServerDriver", - (ORACLE) : "oracle.jdbc.OracleDriver", + private Map jdbcDriverClassNames = [ + (DbType.POSTGRESQL): "org.postgresql.Driver", + (DbType.MYSQL) : "com.mysql.jdbc.Driver", + (DbType.SQLSERVER) : "com.microsoft.sqlserver.jdbc.SQLServerDriver", + (DbType.ORACLE) : "oracle.jdbc.OracleDriver", ] @Shared - private Map jdbcUserNames = [ - (POSTGRESQL): "sa", - (MYSQL) : "sa", - (SQLSERVER) : "sa", - (ORACLE) : "testuser", + private Map jdbcUserNames = [ + (DbType.POSTGRESQL): "sa", + (DbType.MYSQL) : "sa", + (DbType.SQLSERVER) : "sa", + (DbType.ORACLE) : "testuser", ] @Shared - private Map jdbcPasswords = [ - (MYSQL) : "sa", - (POSTGRESQL): "sa", - (SQLSERVER) : "Datad0g_", - (ORACLE) : "testPassword", + private Map jdbcPasswords = [ + (DbType.MYSQL) : "sa", + (DbType.POSTGRESQL): "sa", + (DbType.SQLSERVER) : "Datad0g_", + (DbType.ORACLE) : "testPassword", ] @Shared @@ -88,31 +104,101 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { // JDBC Connection pool name (i.e. HikariCP) -> Map @Shared - private Map> cpDatasources = new HashMap<>() + private Map> cpDatasources = new HashMap<>() + + private static boolean dockerImageSupported(DbType db) { + // MS SQL Server has no arm64 images. + return !(db == DbType.SQLSERVER + && OperatingSystem.isLinux() + && architecture().isArm64()) + } - def peerConnectionProps(String db){ + def peerConnectionProps(DbType db){ def props = new Properties() props.setProperty("user", jdbcUserNames.get(db)) props.setProperty("password", jdbcPasswords.get(db)) return props } - protected getDbType(String dbType){ - return dbType + protected String getDbType(DbType dbType){ + return dbType.toString() } - def prepareConnectionPoolDatasources() { - String[] connectionPoolNames = ["tomcat", "hikari", "c3p0",] - connectionPoolNames.each { cpName -> - Map dbDSMapping = new HashMap<>() - jdbcUrls.each { dbType, jdbcUrl -> - dbDSMapping.put(dbType, createDS(cpName, dbType, jdbcUrl)) - } - cpDatasources.put(cpName, dbDSMapping) + private String jdbcUrlFor(DbType db) { + startContainer(db) + return jdbcUrls.get(db) + } + + private void startContainer(DbType db) { + switch (db) { + case DbType.POSTGRESQL: + startPostgres() + return + case DbType.MYSQL: + startMysql() + return + case DbType.SQLSERVER: + startSqlserver() + return + case DbType.ORACLE: + startOracle() + return + } + + throw new IllegalArgumentException("Unsupported database: ${db}") + } + + private void startPostgres() { + if (postgres != null) { + return + } + + postgres = new PostgreSQLContainer("postgres:11.2") + .withDatabaseName(dbName.get(DbType.POSTGRESQL)).withUsername(jdbcUserNames.get(DbType.POSTGRESQL)).withPassword(jdbcPasswords.get(DbType.POSTGRESQL)) + postgres.start() + PortUtils.waitForPortToOpen(postgres.getHost(), postgres.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), 5, TimeUnit.SECONDS) + jdbcUrls.put(DbType.POSTGRESQL, "${postgres.getJdbcUrl()}") + } + + private void startMysql() { + if (mysql != null) { + return + } + + mysql = new MySQLContainer("mysql:8.0") + .withDatabaseName(dbName.get(DbType.MYSQL)).withUsername(jdbcUserNames.get(DbType.MYSQL)).withPassword(jdbcPasswords.get(DbType.MYSQL)) + // https://github.com/testcontainers/testcontainers-java/issues/914 + mysql.addParameter("TC_MY_CNF", null) + mysql.start() + PortUtils.waitForPortToOpen(mysql.getHost(), mysql.getMappedPort(MySQLContainer.MYSQL_PORT), 5, TimeUnit.SECONDS) + jdbcUrls.put(DbType.MYSQL, "${mysql.getJdbcUrl()}") + } + + private void startSqlserver() { + if (sqlserver != null) { + return + } + + sqlserver = new MSSQLServerContainer(MSSQLServerContainer.IMAGE).acceptLicense().withPassword(jdbcPasswords.get(DbType.SQLSERVER)) + sqlserver.start() + PortUtils.waitForPortToOpen(sqlserver.getHost(), sqlserver.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT), 5, TimeUnit.SECONDS) + jdbcUrls.put(DbType.SQLSERVER, "${sqlserver.getJdbcUrl()};DatabaseName=${dbName.get(DbType.SQLSERVER)}") + } + + private void startOracle() { + if (oracle != null) { + return } + + // 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) + .withStartupTimeout(Duration.ofMinutes(5)).withUsername(jdbcUserNames.get(DbType.ORACLE)).withPassword(jdbcPasswords.get(DbType.ORACLE)) + oracle.start() + jdbcUrls.put(DbType.ORACLE, "${oracle.getJdbcUrl()}".replace("xepdb1", dbName.get(DbType.ORACLE))) } - def createTomcatDS(String dbType, String jdbcUrl) { + def createTomcatDS(DbType dbType, String jdbcUrl) { DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource() ds.setUrl(jdbcUrl) ds.setDriverClassName(jdbcDriverClassNames.get(dbType)) @@ -126,7 +212,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { return ds } - def createHikariDS(String dbType, String jdbcUrl) { + def createHikariDS(DbType dbType, String jdbcUrl) { HikariConfig config = new HikariConfig() config.setJdbcUrl(jdbcUrl) String username = jdbcUserNames.get(dbType) @@ -142,11 +228,10 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { return new HikariDataSource(config) } - def createC3P0DS(String dbType, String jdbcUrl) { + def createC3P0DS(DbType dbType, String jdbcUrl) { DataSource ds = new ComboPooledDataSource() ds.setDriverClass(jdbcDriverClassNames.get(dbType)) - def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl - ds.setJdbcUrl(jdbcUrlToSet) + ds.setJdbcUrl(jdbcUrl) String username = jdbcUserNames.get(dbType) if (username != null) { ds.setUser(username) @@ -156,8 +241,9 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { return ds } - def createDS(String connectionPoolName, String dbType, String jdbcUrl) { + def createDS(String connectionPoolName, DbType dbType, String jdbcUrl) { DataSource ds = null + if (connectionPoolName == "tomcat") { ds = createTomcatDS(dbType, jdbcUrl) } @@ -170,6 +256,21 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { return ds } + private DataSource dataSourceFor(String pool, DbType db) { + Map dbDatasources = cpDatasources.get(pool) + if (dbDatasources == null) { + dbDatasources = new HashMap<>() + cpDatasources.put(pool, dbDatasources) + } + + DataSource datasource = dbDatasources.get(db) + if (datasource == null) { + datasource = createDS(pool, db, jdbcUrlFor(db)) + dbDatasources.put(db, datasource) + } + return datasource + } + @Override void configurePreAgent() { super.configurePreAgent() @@ -178,40 +279,6 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { injectSysConfig("dd.integration.jdbc-datasource.enabled", "true") } - 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 - mysql.addParameter("TC_MY_CNF", null) - mysql.start() - PortUtils.waitForPortToOpen(mysql.getHost(), mysql.getMappedPort(MySQLContainer.MYSQL_PORT), 5, TimeUnit.SECONDS) - jdbcUrls.put(MYSQL, "${mysql.getJdbcUrl()}") - - // 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) - .withStartupTimeout(Duration.ofMinutes(5)).withUsername(jdbcUserNames.get(ORACLE)).withPassword(jdbcPasswords.get(ORACLE)) - oracle.start() - jdbcUrls.put(ORACLE, "${oracle.getJdbcUrl()}".replace("xepdb1", dbName.get(ORACLE))) - - prepareConnectionPoolDatasources() - } - def cleanupSpec() { cpDatasources.values().each { it.values().each { datasource -> @@ -242,7 +309,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { def addDbmTag = dbmTraceInjected() resultSet.next() resultSet.getInt(1) == 3 - if (driver == POSTGRESQL || driver == MYSQL || driver == ORACLE || !addDbmTag) { + if (driver == DbType.POSTGRESQL || driver == DbType.MYSQL || driver == DbType.ORACLE || !addDbmTag) { assertTraces(1) { trace(2) { basicSpan(it, "parent") @@ -341,22 +408,22 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { where: 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 + DbType.MYSQL | null | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.POSTGRESQL | null | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false + DbType.SQLSERVER | null | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.ORACLE | null | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + DbType.MYSQL | "tomcat" | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.POSTGRESQL | "tomcat" | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false + DbType.SQLSERVER | "tomcat" | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.ORACLE | "tomcat" | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + DbType.MYSQL | "hikari" | false | "SELECT 3" | "SELECT" | "SELECT ?" | true + DbType.POSTGRESQL | "hikari" | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | true + DbType.SQLSERVER | "hikari" | false | "SELECT 3" | "SELECT" | "SELECT ?" | true + DbType.ORACLE | "hikari" | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | true + DbType.MYSQL | "c3p0" | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.POSTGRESQL | "c3p0" | false | "SELECT 3 FROM pg_user" | "SELECT" | "SELECT ? FROM pg_user" | false + DbType.SQLSERVER | "c3p0" | false | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.ORACLE | "c3p0" | false | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false } def "prepared statement execute on #driver with #pool generates a span"() { @@ -375,7 +442,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { resultSet.next() resultSet.getInt(1) == 3 def addDbmTag = dbmTraceInjected() - if (driver == SQLSERVER && addDbmTag){ + if (driver == DbType.SQLSERVER && addDbmTag){ assertTraces(1) { trace(3) { basicSpan(it, "parent") @@ -458,9 +525,9 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { if (usingHikari) { "$Tags.DB_POOL_NAME" String } - if (this.dbmTracePreparedStatements(this.getDbType(driver))){ + if (this.dbmTracePreparedStatements(driver)){ "$InstrumentationTags.DBM_TRACE_INJECTED" true - if (driver == POSTGRESQL) { + if (driver == DbType.POSTGRESQL) { "$InstrumentationTags.INSTRUMENTATION_TIME_MS" Long } } @@ -478,22 +545,22 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { where: 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 + DbType.MYSQL | null | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.POSTGRESQL | null | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false + DbType.SQLSERVER | null | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.ORACLE | null | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + DbType.MYSQL | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.POSTGRESQL | "tomcat" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false + DbType.SQLSERVER | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.ORACLE | "tomcat" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false + DbType.MYSQL | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" | true + DbType.POSTGRESQL | "hikari" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | true + DbType.SQLSERVER | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" | true + DbType.ORACLE | "hikari" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | true + DbType.MYSQL | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.POSTGRESQL | "c3p0" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" | false + DbType.SQLSERVER | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" | false + DbType.ORACLE | "c3p0" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" | false } def "prepared statement query on #driver with #pool generates a span"() { @@ -512,7 +579,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { resultSet.getInt(1) == 3 def addDbmTag = dbmTraceInjected() - if (driver == SQLSERVER && addDbmTag){ + if (driver == DbType.SQLSERVER && addDbmTag){ assertTraces(1) { trace(3) { basicSpan(it, "parent") @@ -599,7 +666,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { } if (this.dbmTracePreparedStatements(driver)){ "$InstrumentationTags.DBM_TRACE_INJECTED" true - if (driver == POSTGRESQL) { + if (driver == DbType.POSTGRESQL) { "$InstrumentationTags.INSTRUMENTATION_TIME_MS" Long } } @@ -617,22 +684,22 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { where: 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" + DbType.MYSQL | null | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.POSTGRESQL | null | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + DbType.SQLSERVER | null | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.ORACLE | null | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" + DbType.MYSQL | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.POSTGRESQL | "tomcat" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + DbType.SQLSERVER | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.ORACLE | "tomcat" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" + DbType.MYSQL | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.POSTGRESQL | "hikari" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + DbType.SQLSERVER | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.ORACLE | "hikari" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" + DbType.MYSQL | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.POSTGRESQL | "c3p0" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + DbType.SQLSERVER | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.ORACLE | "c3p0" | "SELECT 3 FROM dual" | "SELECT" | "SELECT ? FROM dual" } def "prepared call on #driver with #pool generates a span"() { @@ -650,7 +717,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { resultSet.next() resultSet.getInt(1) == 3 def addDbmTag = dbmTraceInjected() - if (driver == SQLSERVER && addDbmTag){ + if (driver == DbType.SQLSERVER && addDbmTag){ assertTraces(1) { trace(3) { basicSpan(it, "parent") @@ -730,9 +797,9 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { if (pool == "hikari") { "$Tags.DB_POOL_NAME" String } - if (this.dbmTracePreparedStatements(this.getDbType(driver))){ + if (this.dbmTracePreparedStatements(driver)){ "$InstrumentationTags.DBM_TRACE_INJECTED" true - if (driver == POSTGRESQL) { + if (driver == DbType.POSTGRESQL) { "$InstrumentationTags.INSTRUMENTATION_TIME_MS" Long } } @@ -749,22 +816,22 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { where: 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" + DbType.MYSQL | null | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.POSTGRESQL | null | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + DbType.SQLSERVER | null | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.ORACLE | null | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" + DbType.MYSQL | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.POSTGRESQL | "tomcat" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + DbType.SQLSERVER | "tomcat" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.ORACLE | "tomcat" | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" + DbType.MYSQL | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.POSTGRESQL | "hikari" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + DbType.SQLSERVER | "hikari" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.ORACLE | "hikari" | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" + DbType.MYSQL | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.POSTGRESQL | "c3p0" | "SELECT 3 from pg_user" | "SELECT" | "SELECT ? from pg_user" + DbType.SQLSERVER | "c3p0" | "SELECT 3" | "SELECT" | "SELECT ?" + DbType.ORACLE | "c3p0" | "SELECT 3 from DUAL" | "SELECT" | "SELECT ? from DUAL" } def "statement update on #driver with #pool generates a span"() { @@ -782,7 +849,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { then: def addDbmTag = dbmTraceInjected() statement.updateCount == 0 - if (driver == POSTGRESQL || driver == MYSQL || driver == ORACLE || !dbmTraceInjected()) { + if (driver == DbType.POSTGRESQL || driver == DbType.MYSQL || driver == DbType.ORACLE || !dbmTraceInjected()) { assertTraces(1) { trace(2) { basicSpan(it, "parent") @@ -886,22 +953,22 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { where: 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" + DbType.MYSQL | null | "CREATE TEMPORARY TABLE s_test_ (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.POSTGRESQL | null | "CREATE TEMPORARY TABLE s_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.SQLSERVER | null | "CREATE TABLE #s_test_ (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.ORACLE | null | "CREATE GLOBAL TEMPORARY TABLE s_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.MYSQL | "tomcat" | "CREATE TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.POSTGRESQL | "tomcat" | "CREATE TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.SQLSERVER | "tomcat" | "CREATE TABLE #s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.ORACLE | "tomcat" | "CREATE GLOBAL TEMPORARY TABLE s_tomcat_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.MYSQL | "hikari" | "CREATE TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.POSTGRESQL | "hikari" | "CREATE TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.SQLSERVER | "hikari" | "CREATE TABLE #s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.ORACLE | "hikari" | "CREATE GLOBAL TEMPORARY TABLE s_hikari_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.MYSQL | "c3p0" | "CREATE TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.POSTGRESQL | "c3p0" | "CREATE TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.SQLSERVER | "c3p0" | "CREATE TABLE #s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" + DbType.ORACLE | "c3p0" | "CREATE GLOBAL TEMPORARY TABLE s_c3p0_test (id INTEGER not NULL, PRIMARY KEY ( id ))" | "CREATE" } @@ -927,14 +994,14 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { where: 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( ? ) }" + DbType.POSTGRESQL | "hikari" | "{ ? = call upper( ? ) }" + DbType.MYSQL | "hikari" | "{ ? = call upper( ? ) }" + DbType.POSTGRESQL | "tomcat" | "{ ? = call upper( ? ) }" + DbType.MYSQL | "tomcat" | "{ ? = call upper( ? ) }" + DbType.POSTGRESQL | "c3p0" | "{ ? = call upper( ? ) }" + DbType.MYSQL | "c3p0" | "{ ? = call upper( ? ) }" + DbType.POSTGRESQL | null | "{ ? = call upper( ? ) }" + DbType.MYSQL | null | "{ ? = call upper( ? ) }" } def "prepared procedure call on #driver with #pool does not hang"() { @@ -942,7 +1009,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { Connection connection = setupConnection(pool, driver) String createSql - if (driver == "postgresql") { + if (driver == DbType.POSTGRESQL) { createSql = """ CREATE OR REPLACE PROCEDURE dummy(inout res integer) @@ -951,7 +1018,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { SELECT 1; \$\$; """ - } else if (driver == "mysql") { + } else if (driver == DbType.MYSQL) { createSql = """ CREATE PROCEDURE IF NOT EXISTS dummy(inout res int) @@ -959,7 +1026,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { SELECT 1; END """ - } else if (driver == "sqlserver") { + } else if (driver == DbType.SQLSERVER) { createSql = """ CREATE PROCEDURE dummy @res integer output @@ -972,7 +1039,7 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { assert false } - if (driver.equals("postgresql") && connection.getMetaData().getDatabaseMajorVersion() <= 11) { + if (driver == DbType.POSTGRESQL && connection.getMetaData().getDatabaseMajorVersion() <= 11) { // Skip test for older versions of PG that don't support out on procedure return } @@ -1003,30 +1070,32 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { where: 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(?)}" + DbType.POSTGRESQL | "hikari" | "CALL dummy(?)" + DbType.MYSQL | "hikari" | "CALL dummy(?)" + DbType.SQLSERVER | "hikari" | "{CALL dummy(?)}" + DbType.POSTGRESQL | "tomcat" | "CALL dummy(?)" + DbType.MYSQL | "tomcat" | "{CALL dummy(?)}" + DbType.SQLSERVER | "tomcat" | "{CALL dummy(?)}" + DbType.POSTGRESQL | "c3p0" | "CALL dummy(?)" + DbType.MYSQL | "c3p0" | "CALL dummy(?)" + DbType.SQLSERVER | "c3p0" | "{CALL dummy(?)}" + DbType.POSTGRESQL | null | "CALL dummy(?)" + DbType.MYSQL | null | "CALL dummy(?)" + DbType.SQLSERVER | null | "{CALL dummy(?)}" } - Driver driverFor(String db) { + Driver driverFor(DbType db) { return newDriver(jdbcDriverClassNames.get(db)) } - Connection connectTo(String db, Properties properties) { - return connect(jdbcDriverClassNames.get(db), jdbcUrls.get(db), properties) + Connection connectTo(DbType db, Properties properties) { + return connect(jdbcDriverClassNames.get(db), jdbcUrlFor(db), properties) } - Connection setupConnection(String pool, String db) { - def conn = pool ? cpDatasources.get(pool).get(db).getConnection() : connectTo(db, peerConnectionProps(db)) + Connection setupConnection(String pool, DbType db) { + assumeTrue(dockerImageSupported(db)) + + def conn = pool ? dataSourceFor(pool, db).getConnection() : connectTo(db, peerConnectionProps(db)) // Clear any traces that pool or db can emmit on connection creation. TEST_WRITER.clear() @@ -1054,13 +1123,13 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { return null } - protected abstract String service(String dbType) + protected abstract String service(DbType dbType) protected abstract String operation(String dbType) protected abstract boolean dbmTraceInjected() - protected abstract boolean dbmTracePreparedStatements(String dbType) + protected abstract boolean dbmTracePreparedStatements(DbType dbType) } class RemoteJDBCInstrumentationV0Test extends RemoteJDBCInstrumentationTest { @@ -1071,8 +1140,8 @@ class RemoteJDBCInstrumentationV0Test extends RemoteJDBCInstrumentationTest { } @Override - protected String service(String dbType) { - return dbType + protected String service(DbType dbType) { + return dbType.toString() } @Override @@ -1086,7 +1155,7 @@ class RemoteJDBCInstrumentationV0Test extends RemoteJDBCInstrumentationTest { } @Override - protected boolean dbmTracePreparedStatements(String dbType) { + protected boolean dbmTracePreparedStatements(DbType dbType) { return false } } @@ -1099,7 +1168,7 @@ class RemoteJDBCInstrumentationV1ForkedTest extends RemoteJDBCInstrumentationTes } @Override - protected String service(String dbType) { + protected String service(DbType dbType) { return Config.get().getServiceName() } @@ -1114,14 +1183,14 @@ class RemoteJDBCInstrumentationV1ForkedTest extends RemoteJDBCInstrumentationTes } @Override - protected boolean dbmTracePreparedStatements(String dbType) { + protected boolean dbmTracePreparedStatements(DbType dbType) { return false } @Override - protected String getDbType(String dbType) { + protected String getDbType(DbType dbType) { final databaseNaming = new DatabaseNamingV1() - return databaseNaming.normalizedName(dbType) + return databaseNaming.normalizedName(dbType.toString()) } } @@ -1139,8 +1208,8 @@ class RemoteDBMTraceInjectedForkedTest extends RemoteJDBCInstrumentationTest { } @Override - protected boolean dbmTracePreparedStatements(String dbType){ - return dbType == ORACLE + protected boolean dbmTracePreparedStatements(DbType dbType){ + return dbType == DbType.ORACLE } @Override @@ -1149,7 +1218,7 @@ class RemoteDBMTraceInjectedForkedTest extends RemoteJDBCInstrumentationTest { } @Override - protected String service(String dbType) { + protected String service(DbType dbType) { return Config.get().getServiceName() } @@ -1159,16 +1228,16 @@ class RemoteDBMTraceInjectedForkedTest extends RemoteJDBCInstrumentationTest { } @Override - protected String getDbType(String dbType) { + protected String getDbType(DbType dbType) { final databaseNaming = new DatabaseNamingV1() - return databaseNaming.normalizedName(dbType) + return databaseNaming.normalizedName(dbType.toString()) } def "Oracle DBM comment contains instance name in dddbs and dddb, not generic type string"() { setup: // Use a query text unlikely to already be in v$sql cursor cache def markerQuery = "SELECT 1729 /* oracle-dbm-fix-verify */ FROM dual" - def conn = connectTo(ORACLE, peerConnectionProps(ORACLE)) + def conn = connectTo(DbType.ORACLE, peerConnectionProps(DbType.ORACLE)) when: def stmt = conn.createStatement() @@ -1222,7 +1291,7 @@ class RemoteDBMTraceInjectedForkedTestTracePreparedStatements extends RemoteJDBC } @Override - protected String service(String dbType) { + protected String service(DbType dbType) { return Config.get().getServiceName() } @@ -1232,13 +1301,13 @@ class RemoteDBMTraceInjectedForkedTestTracePreparedStatements extends RemoteJDBC } @Override - protected String getDbType(String dbType) { + protected String getDbType(DbType dbType) { final databaseNaming = new DatabaseNamingV1() - return databaseNaming.normalizedName(dbType) + return databaseNaming.normalizedName(dbType.toString()) } @Override - protected boolean dbmTracePreparedStatements(String dbType){ - return dbType == POSTGRESQL || dbType == ORACLE + protected boolean dbmTracePreparedStatements(DbType dbType){ + return dbType == DbType.POSTGRESQL || dbType == DbType.ORACLE } } diff --git a/dd-java-agent/instrumentation/protobuf-3.0/build.gradle b/dd-java-agent/instrumentation/protobuf-3.0/build.gradle index d411c3b12d3..cd5e818975f 100644 --- a/dd-java-agent/instrumentation/protobuf-3.0/build.gradle +++ b/dd-java-agent/instrumentation/protobuf-3.0/build.gradle @@ -1,3 +1,5 @@ +import datadog.gradle.plugin.HostPlatform + plugins { id 'com.google.protobuf' version '0.10.0' } @@ -14,13 +16,11 @@ muzzle { } } + protobuf { protoc { - def os = System.getProperty("os.name").toLowerCase() - def arch = System.getProperty("os.arch").toLowerCase() - - // There is no m1 support for protoc 3.0.0, so require Rosetta - if (os.contains("mac") && arch.contains("aarch64")) { + // There is no macOS arm64 support for protoc 3.0.0, so require Rosetta. + if (HostPlatform.isMacArm64()) { artifact = "com.google.protobuf:protoc:3.0.0:osx-x86_64" } else { artifact = "com.google.protobuf:protoc:3.0.0" @@ -28,6 +28,23 @@ protobuf { } } +// TODO: protobuf supports Linux arm64 since `3.12.0`, but it is not source-compatible with 3.0.0 +if (HostPlatform.isLinuxArm64()) { + tasks.matching { + it.name in [ + "extractTestProto", + "generateTestProto", + "compileTestJava", + "compileTestGroovy", + "processTestResources", + "testClasses", + "test" + ] + }.configureEach { + enabled = false + } +} + dependencies { compileOnly group: 'com.google.protobuf', name: 'protobuf-java', version: '3.0.0' diff --git a/dd-java-agent/instrumentation/restlet-2.2/src/latestDepTest/groovy/RestletTest.groovy b/dd-java-agent/instrumentation/restlet-2.2/src/latestDepTest/groovy/RestletTest.groovy index 3217e53394d..fe504f1a030 100644 --- a/dd-java-agent/instrumentation/restlet-2.2/src/latestDepTest/groovy/RestletTest.groovy +++ b/dd-java-agent/instrumentation/restlet-2.2/src/latestDepTest/groovy/RestletTest.groovy @@ -1,3 +1,6 @@ +import static datadog.environment.OperatingSystem.architecture + +import datadog.environment.OperatingSystem import org.restlet.Request import org.restlet.Response import org.restlet.data.Header @@ -6,6 +9,12 @@ import org.restlet.util.Series class RestletTest extends RestletTestBase { + @Override + boolean testParallelRequest() { + // TODO: Parallel processing is failing on Linux arm64. + return !(OperatingSystem.isLinux() && architecture().isArm64()) + } + @Override protected Filter createHeaderFilter() { return new ResponseHeaderFilter() diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java index 5faf29bbaa5..7bdc5ddfa24 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java @@ -1,5 +1,8 @@ package datadog.smoketest; +import static datadog.environment.OperatingSystem.architecture; + +import datadog.environment.OperatingSystem; import java.io.File; import java.nio.file.Path; import java.util.ArrayList; @@ -56,6 +59,10 @@ public static ProcessBuilder createProcessBuilder( List command = new ArrayList<>(); command.addAll(baseCommand); + if (OperatingSystem.isLinux() && architecture().isArm64()) { + // Disable CDS to avoid SIGSEGVs on Linux arm64. + command.add(1, "-Xshare:off"); + } command.addAll(additionalCommandParams); command.addAll(Arrays.asList("-cp", classpath, mainClassName)); command.addAll(Arrays.asList(params)); diff --git a/dd-smoke-tests/java9-modules/src/test/groovy/datadog/smoketest/Java9ModulesSmokeTest.groovy b/dd-smoke-tests/java9-modules/src/test/groovy/datadog/smoketest/Java9ModulesSmokeTest.groovy index da1c10bd5c8..8272215b3fb 100644 --- a/dd-smoke-tests/java9-modules/src/test/groovy/datadog/smoketest/Java9ModulesSmokeTest.groovy +++ b/dd-smoke-tests/java9-modules/src/test/groovy/datadog/smoketest/Java9ModulesSmokeTest.groovy @@ -1,7 +1,16 @@ package datadog.smoketest +import static datadog.environment.OperatingSystem.architecture import static java.util.concurrent.TimeUnit.SECONDS +import datadog.environment.JavaVirtualMachine +import datadog.environment.OperatingSystem +import spock.lang.IgnoreIf + +// TODO: OpenJ9 (Semeru) on Linux arm64 fails on this test. +@IgnoreIf({ + OperatingSystem.isLinux() && architecture().isArm64() && JavaVirtualMachine.isJ9() +}) class Java9ModulesSmokeTest extends AbstractSmokeTest { // Estimate for the amount of time instrumentation plus some extra private static final int TIMEOUT_SECS = 30 diff --git a/dd-smoke-tests/play-2.4/build.gradle b/dd-smoke-tests/play-2.4/build.gradle index 1a0de08b58b..d035365979a 100644 --- a/dd-smoke-tests/play-2.4/build.gradle +++ b/dd-smoke-tests/play-2.4/build.gradle @@ -10,6 +10,7 @@ testJvmConstraints { } apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-routes.gradle" +apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle" def playVer = "2.4.11" def scalaVer = System.getProperty("scala.binary.version", /* default = */ "2.11") diff --git a/dd-smoke-tests/play-2.5/build.gradle b/dd-smoke-tests/play-2.5/build.gradle index f0e71773b3a..d1084044680 100644 --- a/dd-smoke-tests/play-2.5/build.gradle +++ b/dd-smoke-tests/play-2.5/build.gradle @@ -4,6 +4,7 @@ plugins { apply from: "$rootDir/gradle/java.gradle" apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-routes.gradle" +apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle" testJvmConstraints { // TODO Java 17: This version of play doesn't support Java 17 diff --git a/dd-smoke-tests/play-2.6/build.gradle b/dd-smoke-tests/play-2.6/build.gradle index 2d95b942697..54272256723 100644 --- a/dd-smoke-tests/play-2.6/build.gradle +++ b/dd-smoke-tests/play-2.6/build.gradle @@ -4,6 +4,7 @@ plugins { apply from: "$rootDir/gradle/java.gradle" apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-routes.gradle" +apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle" testJvmConstraints { // TODO Java 17: This version of play doesn't support Java 17 diff --git a/dd-smoke-tests/play-2.7/build.gradle b/dd-smoke-tests/play-2.7/build.gradle index 32001a18e7e..a4a6c127de4 100644 --- a/dd-smoke-tests/play-2.7/build.gradle +++ b/dd-smoke-tests/play-2.7/build.gradle @@ -4,6 +4,7 @@ plugins { apply from: "$rootDir/gradle/java.gradle" apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-routes.gradle" +apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle" testJvmConstraints { // TODO Java 17: This version of play doesn't support Java 17 diff --git a/dd-smoke-tests/play-2.8-otel/build.gradle b/dd-smoke-tests/play-2.8-otel/build.gradle index 689d177c9dd..420e60e2899 100644 --- a/dd-smoke-tests/play-2.8-otel/build.gradle +++ b/dd-smoke-tests/play-2.8-otel/build.gradle @@ -4,6 +4,7 @@ plugins { apply from: "$rootDir/gradle/java.gradle" apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-routes.gradle" +apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle" def playVer = "2.8.15" def scalaVer = System.getProperty("scala.version", /* default = */ "2.13") diff --git a/dd-smoke-tests/play-2.8-split-routes/build.gradle b/dd-smoke-tests/play-2.8-split-routes/build.gradle index 838c9aee693..2721c99b3e0 100644 --- a/dd-smoke-tests/play-2.8-split-routes/build.gradle +++ b/dd-smoke-tests/play-2.8-split-routes/build.gradle @@ -4,6 +4,7 @@ plugins { apply from: "$rootDir/gradle/java.gradle" apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-routes.gradle" +apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle" def playVer = "2.8.15" def scalaVer = System.getProperty("scala.version", /* default = */ "2.13") diff --git a/dd-smoke-tests/play-2.8/build.gradle b/dd-smoke-tests/play-2.8/build.gradle index 3cf6e17d7f7..95cf5e8c82d 100644 --- a/dd-smoke-tests/play-2.8/build.gradle +++ b/dd-smoke-tests/play-2.8/build.gradle @@ -4,6 +4,7 @@ plugins { apply from: "$rootDir/gradle/java.gradle" apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-routes.gradle" +apply from: "$rootDir/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle" def playVer = "2.8.15" def scalaVer = System.getProperty("scala.version", /* default = */ "2.13") diff --git a/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle b/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle new file mode 100644 index 00000000000..1c3e27fabc0 --- /dev/null +++ b/dd-smoke-tests/play-common/fix-play-linux-arm64.gradle @@ -0,0 +1,61 @@ +import datadog.gradle.plugin.HostPlatform + +// Fix for JVM crashes (SIGSEGV / SIGABRT, exit 134) when the +// org.gradle.playframework `compilePlayRoutes` Worker Daemon starts up on Linux arm64. +// No-op on every other host/arch combination. +if (HostPlatform.isLinuxArm64()) { + tasks.named("compilePlayRoutes").configure { task -> + // Find the class in the hierarchy that declares the private `workerExecutor` field. + def workerExecutorField = null + def declaringClass = task.getClass() + while (declaringClass != null && workerExecutorField == null) { + try { + workerExecutorField = declaringClass.getDeclaredField("workerExecutor") + } catch (NoSuchFieldException ignored) { + declaringClass = declaringClass.getSuperclass() + } + } + if (workerExecutorField == null) { + throw new GradleException( + "compilePlayRoutes task class ${task.getClass().name} has no workerExecutor field; " + + "fix-play-linux-arm64 needs to be updated for this version of the play plugin.") + } + workerExecutorField.accessible = true + + def cl = declaringClass.getClassLoader() + def specClass = cl.loadClass('org.gradle.playframework.tools.internal.routes.DefaultRoutesCompileSpec') + def workActionClass = cl.loadClass('org.gradle.playframework.tasks.internal.RoutesCompileWorkAction') + def factoryClass = cl.loadClass('org.gradle.playframework.tools.internal.routes.RoutesCompilerFactory') + // The `platform` value at runtime is `PlayPlatform_Decorated` (Gradle's generated subclass). + // `Class.getMethod` requires an exact parameter-type match, so look up the undecorated type. + def platformClass = cl.loadClass('org.gradle.playframework.extensions.PlayPlatform') + + task.actions.clear() + task.doLast { + def spec = specClass.getConstructors()[0].newInstance( + task.source.files, + task.outputDirectory.get().asFile, + task.javaProject, + task.namespaceReverseRouter.get(), + task.generateReverseRoutes.get(), + task.injectedRoutesGenerator.get(), + task.additionalImports.get(), + projectDir + ) + + def workerExecutor = workerExecutorField.get(task) + def platform = task.platform.get() + def compiler = factoryClass.getMethod('create', platformClass).invoke(null, platform) + + workerExecutor.processIsolation { workerSpec -> + workerSpec.forkOptions { options -> + options.jvmArgs("-XX:MaxMetaspaceSize=256m", "-Xshare:off") + } + workerSpec.classpath.from(task.routesCompilerClasspath) + }.submit(workActionClass) { parameters -> + parameters.compiler.set(compiler) + parameters.spec.set(spec) + } + } + } +} diff --git a/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy b/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy index 25aba9b9660..ae1d2f6b6ef 100644 --- a/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy +++ b/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy @@ -1,5 +1,6 @@ package datadog.smoketest +import static datadog.environment.OperatingSystem.architecture import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer import static datadog.trace.api.ProtocolVersion.V0_4 import static datadog.trace.api.ProtocolVersion.V0_5 @@ -252,6 +253,11 @@ abstract class AbstractSmokeTest extends ProcessManager { // Unlike crash tracking smoke test, keep the default delay; otherwise, otherwise other tests will fail // ret += "-Ddd.dogstatsd.start-delay=0" } + + // Disable CDS to avoid SIGSEGVs on Linux arm64. + if (OperatingSystem.isLinux() && architecture().isArm64()) { + ret += "-Xshare:off" + } ret as String[] } diff --git a/dd-smoke-tests/websphere-jmx/src/test/groovy/datadog/smoketest/WebSphereJmxSmokeTest.groovy b/dd-smoke-tests/websphere-jmx/src/test/groovy/datadog/smoketest/WebSphereJmxSmokeTest.groovy index 28b72c111c7..dc3dfdd6efe 100644 --- a/dd-smoke-tests/websphere-jmx/src/test/groovy/datadog/smoketest/WebSphereJmxSmokeTest.groovy +++ b/dd-smoke-tests/websphere-jmx/src/test/groovy/datadog/smoketest/WebSphereJmxSmokeTest.groovy @@ -1,6 +1,8 @@ package datadog.smoketest +import static datadog.environment.OperatingSystem.architecture +import datadog.environment.OperatingSystem import java.time.Duration import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.BlockingQueue @@ -11,6 +13,7 @@ import org.testcontainers.containers.GenericContainer import org.testcontainers.containers.output.Slf4jLogConsumer import org.testcontainers.containers.wait.strategy.Wait import org.testcontainers.utility.MountableFile +import spock.lang.IgnoreIf import spock.lang.Shared /** @@ -22,6 +25,10 @@ import spock.lang.Shared * * Note that the websphere related metrics will only arrive if our instrumentation is applied. */ +// There is no arm64 docker image for IBM icr.io/appcafe/websphere-traditional. +@IgnoreIf({ + OperatingSystem.isLinux() && architecture().isArm64() +}) class WebSphereJmxSmokeTest extends AbstractSmokeTest { private static final Logger LOG = LoggerFactory.getLogger(WebSphereJmxSmokeTest)