diff --git a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle similarity index 91% rename from dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle rename to dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle index a8e4eff200..f93bacc3e2 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle @@ -28,7 +28,7 @@ dependencies { implementation deps.autoservice testCompile project(':dd-java-agent:testing') - + testCompile project(':dd-java-agent:instrumentation:mongo').sourceSets.test.output testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0' diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy similarity index 85% rename from dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy index 4f32eaba58..ef7d9fb116 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy @@ -5,18 +5,8 @@ import com.mongodb.ServerAddress import com.mongodb.client.MongoCollection import com.mongodb.client.MongoDatabase import datadog.opentracing.DDSpan -import datadog.trace.agent.test.AgentTestRunner import datadog.trace.agent.test.asserts.TraceAssert -import datadog.trace.agent.test.utils.PortUtils import datadog.trace.api.DDSpanTypes -import de.flapdoodle.embed.mongo.MongodExecutable -import de.flapdoodle.embed.mongo.MongodProcess -import de.flapdoodle.embed.mongo.MongodStarter -import de.flapdoodle.embed.mongo.config.IMongodConfig -import de.flapdoodle.embed.mongo.config.MongodConfigBuilder -import de.flapdoodle.embed.mongo.config.Net -import de.flapdoodle.embed.mongo.distribution.Version -import de.flapdoodle.embed.process.runtime.Network import io.opentracing.tag.Tags import org.bson.BsonDocument import org.bson.BsonString @@ -26,38 +16,18 @@ import spock.lang.Shared import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace -class MongoClientTest extends AgentTestRunner { +abstract class MongoClientTest extends MongoBaseTest { @Shared MongoClient client - @Shared - int port = PortUtils.randomOpenPort() - @Shared - MongodExecutable mongodExe - @Shared - MongodProcess mongod def setup() throws Exception { - final MongodStarter starter = MongodStarter.getDefaultInstance() - final IMongodConfig mongodConfig = - new MongodConfigBuilder() - .version(Version.Main.PRODUCTION) - .net(new Net("localhost", port, Network.localhostIsIPv6())) - .build() - - mongodExe = starter.prepare(mongodConfig) - mongod = mongodExe.start() - client = new MongoClient("localhost", port) } def cleanup() throws Exception { client?.close() client = null - mongod?.stop() - mongod = null - mongodExe?.stop() - mongodExe = null } def "test create collection"() { diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle similarity index 86% rename from dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle rename to dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle index 03579283ed..36b7fbe845 100644 --- a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle @@ -20,10 +20,9 @@ testSets { dependencies { // use mongo listener - compile(project(':dd-java-agent:instrumentation:mongo-3.1')) { + compile(project(':dd-java-agent:instrumentation:mongo:driver-3.1')) { transitive = false } - compileOnly group: 'org.mongodb', name: 'mongo-java-driver', version: '3.3.0' compileOnly group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' compile project(':dd-java-agent:agent-tooling') @@ -34,7 +33,7 @@ dependencies { implementation deps.autoservice testCompile project(':dd-java-agent:testing') - + testCompile project(':dd-java-agent:instrumentation:mongo').sourceSets.test.output testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java rename to dd-java-agent/instrumentation/mongo/driver-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy similarity index 67% rename from dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy rename to dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy index cb3c7debb0..2caa1f7044 100644 --- a/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy @@ -6,18 +6,8 @@ import com.mongodb.async.client.MongoDatabase import com.mongodb.client.result.DeleteResult import com.mongodb.client.result.UpdateResult import datadog.opentracing.DDSpan -import datadog.trace.agent.test.AgentTestRunner import datadog.trace.agent.test.asserts.TraceAssert -import datadog.trace.agent.test.utils.PortUtils import datadog.trace.api.DDSpanTypes -import de.flapdoodle.embed.mongo.MongodExecutable -import de.flapdoodle.embed.mongo.MongodProcess -import de.flapdoodle.embed.mongo.MongodStarter -import de.flapdoodle.embed.mongo.config.IMongodConfig -import de.flapdoodle.embed.mongo.config.MongodConfigBuilder -import de.flapdoodle.embed.mongo.config.Net -import de.flapdoodle.embed.mongo.distribution.Version -import de.flapdoodle.embed.process.runtime.Network import io.opentracing.tag.Tags import org.bson.BsonDocument import org.bson.BsonString @@ -31,38 +21,18 @@ import java.util.concurrent.CountDownLatch import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace @Timeout(10) -class MongoAsyncClientTest extends AgentTestRunner { +class MongoAsyncClientTest extends MongoBaseTest { @Shared MongoClient client - @Shared - int port = PortUtils.randomOpenPort() - @Shared - MongodExecutable mongodExe - @Shared - MongodProcess mongod def setup() throws Exception { - final MongodStarter starter = MongodStarter.getDefaultInstance() - final IMongodConfig mongodConfig = - new MongodConfigBuilder() - .version(Version.Main.PRODUCTION) - .net(new Net("localhost", port, Network.localhostIsIPv6())) - .build() - - mongodExe = starter.prepare(mongodConfig) - mongod = mongodExe.start() - client = MongoClients.create("mongodb://localhost:$port") } def cleanup() throws Exception { client?.close() client = null - mongod?.stop() - mongod = null - mongodExe?.stop() - mongodExe = null } def "test create collection"() { @@ -75,7 +45,11 @@ class MongoAsyncClientTest extends AgentTestRunner { then: assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }") + mongoSpan(it, 0) { + assert it == "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }" || + it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -96,7 +70,11 @@ class MongoAsyncClientTest extends AgentTestRunner { count.get() == 0 assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0) { + assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -127,10 +105,18 @@ class MongoAsyncClientTest extends AgentTestRunner { count.get() == 1 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }") + mongoSpan(it, 0) { + assert it == "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }" || + it == "{\"insert\": \"$collectionName\", \"ordered\": \"?\", \"\$db\": \"?\", \"documents\": [{\"_id\": \"?\", \"password\": \"?\"}]}" + true + } } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0) { + assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -170,10 +156,18 @@ class MongoAsyncClientTest extends AgentTestRunner { count.get() == 1 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }") + mongoSpan(it, 0) { + assert it == "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }" || + it == "{\"update\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"updates\": [{\"q\": {\"password\": \"?\"}, \"u\": {\"\$set\": {\"password\": \"?\"}}}]}" + true + } } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0) { + assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -211,10 +205,18 @@ class MongoAsyncClientTest extends AgentTestRunner { count.get() == 0 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }") + mongoSpan(it, 0) { + assert it == "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }" || + it == "{\"delete\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"deletes\": [{\"q\": {\"password\": \"?\"}, \"limit\": \"?\"}]}" + true + } } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0) { + assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -236,11 +238,11 @@ class MongoAsyncClientTest extends AgentTestRunner { } } - def mongoSpan(TraceAssert trace, int index, String statement, Object parentSpan = null, Throwable exception = null) { + def mongoSpan(TraceAssert trace, int index, Closure statementEval, Object parentSpan = null, Throwable exception = null) { trace.span(index) { serviceName "mongo" operationName "mongo.query" - resourceName statement + resourceName statementEval spanType DDSpanTypes.MONGO if (parentSpan == null) { parent() @@ -251,7 +253,7 @@ class MongoAsyncClientTest extends AgentTestRunner { "$Tags.COMPONENT.key" "java-mongo" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$Tags.DB_INSTANCE.key" "test_db" - "$Tags.DB_STATEMENT.key" statement + "$Tags.DB_STATEMENT.key" statementEval "$Tags.DB_TYPE.key" "mongo" "$Tags.PEER_HOSTNAME.key" "localhost" "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" diff --git a/dd-java-agent/instrumentation/mongo/mongo.gradle b/dd-java-agent/instrumentation/mongo/mongo.gradle new file mode 100644 index 0000000000..437c738bcd --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/mongo.gradle @@ -0,0 +1,28 @@ +apply from: "${rootDir}/gradle/java.gradle" + +dependencies { + testAnnotationProcessor deps.autoservice + testImplementation deps.autoservice + + testCompile project(':dd-java-agent:testing') + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' +} + +// Forcing strict test execution order (no parallel execution) to ensure proper mongo executable initialization. +List testTasks = [] +tasks.withType(Test) { Test testTask -> + testTasks.each { + testTask.shouldRunAfter(it) + } + testTasks.add(testTask) +} +subprojects { + afterEvaluate { + tasks.withType(Test) { Test testTask -> + testTasks.each { + testTask.shouldRunAfter(it) + } + testTasks.add(testTask) + } + } +} diff --git a/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy new file mode 100644 index 0000000000..0fede79b21 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy @@ -0,0 +1,54 @@ +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.utils.PortUtils +import de.flapdoodle.embed.mongo.MongodExecutable +import de.flapdoodle.embed.mongo.MongodProcess +import de.flapdoodle.embed.mongo.MongodStarter +import de.flapdoodle.embed.mongo.config.IMongodConfig +import de.flapdoodle.embed.mongo.config.MongodConfigBuilder +import de.flapdoodle.embed.mongo.config.Net +import de.flapdoodle.embed.mongo.distribution.Version +import de.flapdoodle.embed.process.runtime.Network +import spock.lang.Shared + +/** + * Testing needs to be in a centralized project. + * If tests in multiple different projects are using embedded mongo, + * they downloader is at risk of a race condition. + */ +class MongoBaseTest extends AgentTestRunner { + // https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo#executable-collision + private static final MongodStarter starter = MongodStarter.getDefaultInstance() + + @Shared + int port = PortUtils.randomOpenPort() + @Shared + MongodExecutable mongodExe + @Shared + MongodProcess mongod + + def setup() throws Exception { + final IMongodConfig mongodConfig = + new MongodConfigBuilder() + .version(Version.Main.PRODUCTION) + .net(new Net("localhost", port, Network.localhostIsIPv6())) + .build() + + mongodExe = starter.prepare(mongodConfig) + mongod = mongodExe.start() + } + + def cleanup() throws Exception { + mongod?.stop() + mongod = null + mongodExe?.stop() + mongodExe = null + } + + def "test port open"() { + when: + new Socket("localhost", port) + + then: + noExceptionThrown() + } +} diff --git a/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java b/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java new file mode 100644 index 0000000000..d830b1adf3 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java @@ -0,0 +1,12 @@ +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import net.bytebuddy.agent.builder.AgentBuilder; + +@AutoService(Instrumenter.class) +public class NoOpInstrumentation implements Instrumenter { + + @Override + public AgentBuilder instrument(final AgentBuilder agentBuilder) { + return agentBuilder; + } +} diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy index c5a9351885..eae9c17550 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy @@ -64,6 +64,11 @@ class SpanAssert { checked.resourceName = true } + def resourceName(Closure eval) { + assert eval(span.resourceName) + checked.resourceName = true + } + def resourceNameContains(String... resourceNameParts) { assertSpanNameContains(span.resourceName, resourceNameParts) checked.resourceName = true diff --git a/settings.gradle b/settings.gradle index d31e0b382d..0cd07e4c11 100644 --- a/settings.gradle +++ b/settings.gradle @@ -66,8 +66,9 @@ include ':dd-java-agent:instrumentation:jsp-2.3' include ':dd-java-agent:instrumentation:kafka-clients-0.11' include ':dd-java-agent:instrumentation:kafka-streams-0.11' include ':dd-java-agent:instrumentation:lettuce-5' -include ':dd-java-agent:instrumentation:mongo-3.1' -include ':dd-java-agent:instrumentation:mongo-async-3.3' +include ':dd-java-agent:instrumentation:mongo' +include ':dd-java-agent:instrumentation:mongo:driver-3.1' +include ':dd-java-agent:instrumentation:mongo:driver-async-3.3' include ':dd-java-agent:instrumentation:netty-4.0' include ':dd-java-agent:instrumentation:netty-4.1' include ':dd-java-agent:instrumentation:okhttp-3'