From d6719ed5cd7c6c5c132fa802e94ffaf924980952 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 11 Jun 2019 10:54:17 -0700 Subject: [PATCH 1/6] Replace Mongo tests with updated tests. --- dd-java-agent/dd-java-agent.gradle | 7 - .../mongo-3.1/mongo-3.1.gradle | 5 +- .../groovy/MongoClientDecoratorTest.groovy | 45 --- .../src/test/groovy/MongoClientTest.groovy | 263 ++++++++++++++++++ .../mongo/MongoClientInstrumentationTest.java | 65 ----- .../mongo-async-3.3/mongo-async-3.3.gradle | 15 + .../test/groovy/MongoAsyncClientTest.groovy | 263 ++++++++++++++++++ .../MongoAsyncClientInstrumentationTest.java | 122 -------- .../MongoClientInstrumentationTest.java | 117 -------- 9 files changed, 543 insertions(+), 359 deletions(-) delete mode 100644 dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy create mode 100644 dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy delete mode 100644 dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java create mode 100644 dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy delete mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java delete mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java diff --git a/dd-java-agent/dd-java-agent.gradle b/dd-java-agent/dd-java-agent.gradle index 81ccc6669ad..f4086300939 100644 --- a/dd-java-agent/dd-java-agent.gradle +++ b/dd-java-agent/dd-java-agent.gradle @@ -97,13 +97,6 @@ dependencies { testCompile deps.opentracingMock testCompile deps.testLogging testCompile deps.guava - testCompile group: 'org.assertj', name: 'assertj-core', version: '2.9.+' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0' - - testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.4.2' - testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.4.2' - // run embedded mongodb for integration testing - testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' } tasks.withType(Test).configureEach { diff --git a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle b/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle index 5f1a0d2a0af..a8e4eff200f 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle +++ b/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle @@ -28,9 +28,8 @@ dependencies { implementation deps.autoservice testCompile project(':dd-java-agent:testing') - - testCompile group: 'org.assertj', name: 'assertj-core', version: '2.9.+' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0' + + 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' latestDepTestCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '+' diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy b/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy deleted file mode 100644 index 4ddc61f661f..00000000000 --- a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy +++ /dev/null @@ -1,45 +0,0 @@ -import datadog.trace.api.DDTags -import io.opentracing.Span -import io.opentracing.tag.Tags -import org.bson.BsonArray -import org.bson.BsonDocument -import org.bson.BsonString -import spock.lang.Shared -import spock.lang.Specification - -import static datadog.trace.instrumentation.mongo.MongoClientDecorator.DECORATE - -class MongoClientDecoratorTest extends Specification { - - @Shared - def query1, query2 - - def setupSpec() { - query1 = new BsonDocument("find", new BsonString("show")) - query1.put("stuff", new BsonString("secret")) - - - query2 = new BsonDocument("insert", new BsonString("table")) - def nestedDoc = new BsonDocument("count", new BsonString("show")) - nestedDoc.put("id", new BsonString("secret")) - query2.put("docs", new BsonArray(Arrays.asList(new BsonString("secret"), nestedDoc))) - } - - def "test query scrubbing"() { - setup: - def span = Mock(Span) - // all "secret" strings should be scrubbed out of these queries - - when: - DECORATE.onStatement(span, query) - - then: - 1 * span.setTag(Tags.DB_STATEMENT.key, expected) - 1 * span.setTag(DDTags.RESOURCE_NAME, expected) - 0 * _ - - where: - query << [query1, query2] - expected = query.toString().replaceAll("secret", "?") - } -} diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy b/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy new file mode 100644 index 00000000000..4f32eaba584 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy @@ -0,0 +1,263 @@ +import com.mongodb.MongoClient +import com.mongodb.MongoClientOptions +import com.mongodb.MongoTimeoutException +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 +import org.bson.Document +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 { + + @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"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + db.createCollection(collectionName) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test get collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + int count = db.getCollection(collectionName).count() + + then: + count == 0 + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test insert"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + collection.insertOne(new Document("password", "SECRET")) + + then: + collection.count() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test update"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + def coll = db.getCollection(collectionName) + coll.insertOne(new Document("password", "OLDPW")) + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = collection.updateOne( + new BsonDocument("password", new BsonString("OLDPW")), + new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW")))) + + then: + result.modifiedCount == 1 + collection.count() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test delete"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + def coll = db.getCollection(collectionName) + coll.insertOne(new Document("password", "SECRET")) + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET"))) + + then: + result.deletedCount == 1 + collection.count() == 0 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test error"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + collection.updateOne(new BsonDocument(), new BsonDocument()) + + then: + thrown(IllegalArgumentException) + // Unfortunately not caught by our instrumentation. + assertTraces(0) {} + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test client failure"() { + setup: + def options = MongoClientOptions.builder().serverSelectionTimeout(10).build() + def client = new MongoClient(new ServerAddress("localhost", UNUSABLE_PORT), [], options) + + when: + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + + then: + thrown(MongoTimeoutException) + // Unfortunately not caught by our instrumentation. + assertTraces(0) {} + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def mongoSpan(TraceAssert trace, int index, String statement, Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + serviceName "mongo" + operationName "mongo.query" + resourceName statement + spanType DDSpanTypes.MONGO + if (parentSpan == null) { + parent() + } else { + childOf((DDSpan) parentSpan) + } + tags { + "$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_TYPE.key" "mongo" + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" port + defaultTags() + } + } + } +} diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java b/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java deleted file mode 100644 index e3dd177876b..00000000000 --- a/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package datadog.trace.instrumentation.mongo; - -import static org.assertj.core.api.Java6Assertions.assertThat; - -import com.mongodb.ServerAddress; -import com.mongodb.connection.ClusterId; -import com.mongodb.connection.ConnectionDescription; -import com.mongodb.connection.ServerId; -import com.mongodb.event.CommandStartedEvent; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.api.DDSpanTypes; -import io.opentracing.tag.Tags; -import java.util.Arrays; -import java.util.List; -import org.bson.BsonArray; -import org.bson.BsonDocument; -import org.bson.BsonString; -import org.junit.Test; - -public class MongoClientInstrumentationTest { - - private static ConnectionDescription makeConnection() { - return new ConnectionDescription(new ServerId(new ClusterId(), new ServerAddress())); - } - - @Test - public void mongoSpan() { - final CommandStartedEvent cmd = - new CommandStartedEvent(1, makeConnection(), "databasename", "query", new BsonDocument()); - - final DDSpan span = new DDTracer().buildSpan("foo").start(); - MongoClientDecorator.DECORATE.afterStart(span); - MongoClientDecorator.DECORATE.onStatement(span, cmd.getCommand()); - - assertThat(span.context().getSpanType()).isEqualTo("mongodb"); - assertThat(span.context().getResourceName()) - .isEqualTo(span.context().getTags().get("db.statement")); - assertThat(span.getSpanType()).isEqualTo(DDSpanTypes.MONGO); - } - - @Test - public void queryScrubbing() { - // all "secret" strings should be scrubbed out of these queries - final BsonDocument query1 = new BsonDocument("find", new BsonString("show")); - query1.put("stuff", new BsonString("secret")); - final BsonDocument query2 = new BsonDocument("insert", new BsonString("table")); - final BsonDocument query2_1 = new BsonDocument("count", new BsonString("show")); - query2_1.put("id", new BsonString("secret")); - query2.put("docs", new BsonArray(Arrays.asList(new BsonString("secret"), query2_1))); - final List queries = Arrays.asList(query1, query2); - for (final BsonDocument query : queries) { - final CommandStartedEvent cmd = - new CommandStartedEvent(1, makeConnection(), "databasename", "query", query); - - final DDSpan span = new DDTracer().buildSpan("foo").start(); - MongoClientDecorator.DECORATE.afterStart(span); - MongoClientDecorator.DECORATE.onStatement(span, cmd.getCommand()); - - assertThat(span.getSpanType()).isEqualTo(DDSpanTypes.MONGO); - assertThat(span.getTags().get(Tags.DB_STATEMENT.getKey())) - .isEqualTo(query.toString().replaceAll("secret", "?")); - } - } -} diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle b/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle index df7028ecb5d..03579283edd 100644 --- a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle +++ b/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle @@ -10,6 +10,14 @@ muzzle { apply from: "${rootDir}/gradle/java.gradle" +apply plugin: 'org.unbroken-dome.test-sets' + +testSets { + latestDepTest { + dirName = 'test' + } +} + dependencies { // use mongo listener compile(project(':dd-java-agent:instrumentation:mongo-3.1')) { @@ -24,4 +32,11 @@ dependencies { compile deps.opentracing annotationProcessor deps.autoservice implementation deps.autoservice + + testCompile project(':dd-java-agent:testing') + + 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' + latestDepTestCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '+' } diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy new file mode 100644 index 00000000000..cb3c7debb0d --- /dev/null +++ b/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy @@ -0,0 +1,263 @@ +import com.mongodb.async.SingleResultCallback +import com.mongodb.async.client.MongoClient +import com.mongodb.async.client.MongoClients +import com.mongodb.async.client.MongoCollection +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 +import org.bson.Document +import spock.lang.Shared +import spock.lang.Timeout + +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CountDownLatch + +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace + +@Timeout(10) +class MongoAsyncClientTest extends AgentTestRunner { + + @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"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + db.createCollection(collectionName, toCallback {}) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test get collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + def count = new CompletableFuture() + db.getCollection(collectionName).count toCallback { count.complete(it) } + + then: + count.get() == 0 + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test insert"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def count = new CompletableFuture() + collection.insertOne(new Document("password", "SECRET"), toCallback { + collection.count toCallback { count.complete(it) } + }) + + then: + count.get() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test update"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + def coll = db.getCollection(collectionName) + def latch2 = new CountDownLatch(1) + coll.insertOne(new Document("password", "OLDPW"), toCallback { latch2.countDown() }) + latch2.await() + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = new CompletableFuture() + def count = new CompletableFuture() + collection.updateOne( + new BsonDocument("password", new BsonString("OLDPW")), + new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW"))), toCallback { + result.complete(it) + collection.count toCallback { count.complete(it) } + }) + + then: + result.get().modifiedCount == 1 + count.get() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test delete"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + def coll = db.getCollection(collectionName) + def latch2 = new CountDownLatch(1) + coll.insertOne(new Document("password", "SECRET"), toCallback { latch2.countDown() }) + latch2.await() + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = new CompletableFuture() + def count = new CompletableFuture() + collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")), toCallback { + result.complete(it) + collection.count toCallback { count.complete(it) } + }) + + then: + result.get().deletedCount == 1 + count.get() == 0 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + SingleResultCallback toCallback(Closure closure) { + return new SingleResultCallback() { + @Override + void onResult(Object result, Throwable t) { + if (t) { + closure.call(t) + } else { + closure.call(result) + } + } + } + } + + def mongoSpan(TraceAssert trace, int index, String statement, Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + serviceName "mongo" + operationName "mongo.query" + resourceName statement + spanType DDSpanTypes.MONGO + if (parentSpan == null) { + parent() + } else { + childOf((DDSpan) parentSpan) + } + tags { + "$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_TYPE.key" "mongo" + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" port + defaultTags() + } + } + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java deleted file mode 100644 index 86d2f58c9d0..00000000000 --- a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package datadog.trace.agent.integration; - -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_DB_NAME; -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_HOST; -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_PORT; - -import com.mongodb.async.SingleResultCallback; -import com.mongodb.async.client.MongoClient; -import com.mongodb.async.client.MongoClients; -import com.mongodb.async.client.MongoDatabase; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.agent.test.IntegrationTestUtils; -import datadog.trace.common.writer.ListWriter; -import io.opentracing.tag.Tags; -import java.util.concurrent.atomic.AtomicBoolean; -import org.bson.Document; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class MongoAsyncClientInstrumentationTest { - private static MongoClient client; - private static final ListWriter writer = new ListWriter(); - private static final DDTracer tracer = new DDTracer(writer); - - @BeforeClass - public static void setup() throws Exception { - IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer); - MongoClientInstrumentationTest.startLocalMongo(); - // Embeded Mongo uses HttpUrlConnection to download things so we have to clear traces before - // going to tests - writer.clear(); - - client = MongoClients.create("mongodb://" + MONGO_HOST + ":" + MONGO_PORT); - } - - @AfterClass - public static void destroy() throws Exception { - if (null != client) { - client.close(); - client = null; - } - MongoClientInstrumentationTest.stopLocalMongo(); - } - - @Test - public void asyncClientHasListener() { - Assert.assertEquals(1, client.getSettings().getCommandListeners().size()); - Assert.assertEquals( - "TracingCommandListener", - client.getSettings().getCommandListeners().get(0).getClass().getSimpleName()); - } - - @Test - public void insertOperation() throws Exception { - final MongoDatabase db = client.getDatabase(MONGO_DB_NAME); - final String collectionName = "asyncCollection"; - final AtomicBoolean done = new AtomicBoolean(false); - - db.createCollection( - collectionName, - new SingleResultCallback() { - @Override - public void onResult(final Void result, final Throwable t) { - done.set(true); - } - }); - while (!done.get()) { - Thread.sleep(1); - } - - db.getCollection(collectionName) - .insertOne( - new Document("foo", "bar"), - new SingleResultCallback() { - @Override - public void onResult(final Void result, final Throwable t) { - done.set(true); - } - }); - while (!done.get()) { - Thread.sleep(1); - } - - done.set(false); - db.getCollection(collectionName) - .count( - new SingleResultCallback() { - @Override - public void onResult(final Long result, final Throwable t) { - Assert.assertEquals(1, result.longValue()); - done.set(true); - } - }); - - while (!done.get()) { - Thread.sleep(1); - } - - // the final trace may still be reporting to the ListWriter, - // but we're only testing the first trace. - Assert.assertTrue(writer.size() >= 1); - - final String createCollectionQuery = - "{ \"create\" : \"asyncCollection\", \"autoIndexId\" : \"?\", \"capped\" : \"?\" }"; - final DDSpan trace0 = writer.get(0).get(0); - Assert.assertEquals("mongo.query", trace0.getOperationName()); - Assert.assertEquals(createCollectionQuery, trace0.getResourceName()); - Assert.assertEquals("mongodb", trace0.getType()); - Assert.assertEquals("mongo", trace0.getServiceName()); - - Assert.assertEquals("java-mongo", trace0.getTags().get(Tags.COMPONENT.getKey())); - Assert.assertEquals(createCollectionQuery, trace0.getTags().get(Tags.DB_STATEMENT.getKey())); - Assert.assertEquals(MONGO_DB_NAME, trace0.getTags().get(Tags.DB_INSTANCE.getKey())); - Assert.assertEquals(MONGO_HOST, trace0.getTags().get(Tags.PEER_HOSTNAME.getKey())); - Assert.assertEquals("127.0.0.1", trace0.getTags().get(Tags.PEER_HOST_IPV4.getKey())); - Assert.assertEquals(MONGO_PORT, trace0.getTags().get(Tags.PEER_PORT.getKey())); - Assert.assertEquals("mongo", trace0.getTags().get(Tags.DB_TYPE.getKey())); - } -} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java deleted file mode 100644 index 9942c6e5350..00000000000 --- a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package datadog.trace.agent.integration; - -import com.mongodb.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.agent.test.IntegrationTestUtils; -import datadog.trace.common.writer.ListWriter; -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 java.util.concurrent.TimeoutException; -import org.bson.Document; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class MongoClientInstrumentationTest { - public static final String MONGO_DB_NAME = "embedded"; - public static final String MONGO_HOST = "localhost"; - public static final int MONGO_PORT = 12345; - private static MongodExecutable mongodExe; - private static MongodProcess mongod; - - private static MongoClient client; - private static final ListWriter writer = new ListWriter(); - private static final DDTracer tracer = new DDTracer(writer); - - public static void startLocalMongo() throws Exception { - final MongodStarter starter = MongodStarter.getDefaultInstance(); - final IMongodConfig mongodConfig = - new MongodConfigBuilder() - .version(Version.Main.PRODUCTION) - .net(new Net(MONGO_HOST, MONGO_PORT, Network.localhostIsIPv6())) - .build(); - - mongodExe = starter.prepare(mongodConfig); - mongod = mongodExe.start(); - } - - public static void stopLocalMongo() throws Exception { - if (null != mongod) { - mongod.stop(); - mongod = null; - } - if (null != mongodExe) { - mongodExe.stop(); - mongodExe = null; - } - } - - @BeforeClass - public static void setup() throws Exception { - IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer); - startLocalMongo(); - // Embeded Mongo uses HttpUrlConnection to download things so we have to clear traces before - // going to tests - writer.clear(); - - client = new MongoClient(MONGO_HOST, MONGO_PORT); - } - - @AfterClass - public static void destroy() throws Exception { - if (null != client) { - client.close(); - client = null; - } - stopLocalMongo(); - } - - @Test - public void syncClientHasListener() { - Assert.assertEquals(1, client.getMongoClientOptions().getCommandListeners().size()); - Assert.assertEquals( - "TracingCommandListener", - client.getMongoClientOptions().getCommandListeners().get(0).getClass().getSimpleName()); - } - - @Test - public void insertOperation() throws TimeoutException, InterruptedException { - final MongoDatabase db = client.getDatabase(MONGO_DB_NAME); - final String collectionName = "testCollection"; - db.createCollection(collectionName); - final MongoCollection collection = db.getCollection(collectionName); - - collection.insertOne(new Document("foo", "bar")); - - Assert.assertEquals(1, collection.count()); - - Assert.assertEquals(3, writer.size()); - - final String createCollectionQuery = - "{ \"create\" : \"testCollection\", \"autoIndexId\" : \"?\", \"capped\" : \"?\" }"; - final DDSpan trace0 = writer.get(0).get(0); - Assert.assertEquals("mongo.query", trace0.getOperationName()); - Assert.assertEquals(createCollectionQuery, trace0.getResourceName()); - Assert.assertEquals("mongodb", trace0.getType()); - Assert.assertEquals("mongo", trace0.getServiceName()); - - Assert.assertEquals("java-mongo", trace0.getTags().get(Tags.COMPONENT.getKey())); - Assert.assertEquals(createCollectionQuery, trace0.getTags().get(Tags.DB_STATEMENT.getKey())); - Assert.assertEquals(MONGO_DB_NAME, trace0.getTags().get(Tags.DB_INSTANCE.getKey())); - Assert.assertEquals(MONGO_HOST, trace0.getTags().get(Tags.PEER_HOSTNAME.getKey())); - Assert.assertEquals("127.0.0.1", trace0.getTags().get(Tags.PEER_HOST_IPV4.getKey())); - Assert.assertEquals(MONGO_PORT, trace0.getTags().get(Tags.PEER_PORT.getKey())); - Assert.assertEquals("mongo", trace0.getTags().get(Tags.DB_TYPE.getKey())); - } -} From d26edd6c17a86340fc979edf5291a4f2afff445d Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Wed, 12 Jun 2019 14:46:11 -0700 Subject: [PATCH 2/6] Ensure gradle runs base mongo test first Otherwise there is a race condition between the projects initializing the mongo instance. --- .../driver-3.1/driver-3.1.gradle} | 2 +- .../mongo/MongoClientDecorator.java | 0 .../mongo/MongoClientInstrumentation.java | 0 .../mongo/TracingCommandListener.java | 0 .../src/test/groovy/MongoClientTest.groovy | 32 +------ .../driver-async-3.3/driver-async-3.3.gradle} | 5 +- .../MongoAsyncClientInstrumentation.java | 0 .../test/groovy/MongoAsyncClientTest.groovy | 86 ++++++++++--------- .../instrumentation/mongo/mongo.gradle | 28 ++++++ .../src/test/groovy/MongoBaseTest.groovy | 54 ++++++++++++ .../src/test/java/NoOpInstrumentation.java | 12 +++ .../agent/test/asserts/SpanAssert.groovy | 5 ++ settings.gradle | 5 +- 13 files changed, 150 insertions(+), 79 deletions(-) rename dd-java-agent/instrumentation/{mongo-3.1/mongo-3.1.gradle => mongo/driver-3.1/driver-3.1.gradle} (91%) rename dd-java-agent/instrumentation/{mongo-3.1 => mongo/driver-3.1}/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java (100%) rename dd-java-agent/instrumentation/{mongo-3.1 => mongo/driver-3.1}/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java (100%) rename dd-java-agent/instrumentation/{mongo-3.1 => mongo/driver-3.1}/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java (100%) rename dd-java-agent/instrumentation/{mongo-3.1 => mongo/driver-3.1}/src/test/groovy/MongoClientTest.groovy (85%) rename dd-java-agent/instrumentation/{mongo-async-3.3/mongo-async-3.3.gradle => mongo/driver-async-3.3/driver-async-3.3.gradle} (86%) rename dd-java-agent/instrumentation/{mongo-async-3.3 => mongo/driver-async-3.3}/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java (100%) rename dd-java-agent/instrumentation/{mongo-async-3.3 => mongo/driver-async-3.3}/src/test/groovy/MongoAsyncClientTest.groovy (67%) create mode 100644 dd-java-agent/instrumentation/mongo/mongo.gradle create mode 100644 dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy create mode 100644 dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java 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 a8e4eff200f..f93bacc3e2e 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 4f32eaba584..ef7d9fb1161 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 03579283edd..36b7fbe845e 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 cb3c7debb0d..2caa1f70442 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 00000000000..437c738bcd8 --- /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 00000000000..0fede79b21e --- /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 00000000000..d830b1adf33 --- /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 c5a9351885c..eae9c175501 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 d31e0b382df..0cd07e4c116 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' From 25305444d4a6e5d9340056630c7816b01e33289c Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Wed, 12 Jun 2019 17:40:17 -0700 Subject: [PATCH 3/6] Set minimum version and fix var name. --- .../instrumentation/mongo/driver-3.1/driver-3.1.gradle | 2 +- .../mongo/driver-async-3.3/driver-async-3.3.gradle | 8 +++++++- dd-java-agent/instrumentation/mongo/mongo.gradle | 2 +- .../mongo/src/test/groovy/MongoBaseTest.groovy | 4 ++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle index f93bacc3e2e..747c30ad60a 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle @@ -29,7 +29,7 @@ dependencies { 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: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0' latestDepTestCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '+' diff --git a/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle index 36b7fbe845e..0461926bfb0 100644 --- a/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle @@ -1,3 +1,9 @@ +// Set properties before any plugins get loaded +ext { + // Since we're using CompletableFutures in the test... + minJavaVersionForTests = JavaVersion.VERSION_1_8 +} + muzzle { pass { group = "org.mongodb" @@ -34,7 +40,7 @@ dependencies { 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: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' latestDepTestCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '+' diff --git a/dd-java-agent/instrumentation/mongo/mongo.gradle b/dd-java-agent/instrumentation/mongo/mongo.gradle index 437c738bcd8..f91b66b0cca 100644 --- a/dd-java-agent/instrumentation/mongo/mongo.gradle +++ b/dd-java-agent/instrumentation/mongo/mongo.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation deps.autoservice testCompile project(':dd-java-agent:testing') - testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' } // Forcing strict test execution order (no parallel execution) to ensure proper mongo executable initialization. diff --git a/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy index 0fede79b21e..c945733e356 100644 --- a/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy +++ b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy @@ -17,7 +17,7 @@ import spock.lang.Shared */ class MongoBaseTest extends AgentTestRunner { // https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo#executable-collision - private static final MongodStarter starter = MongodStarter.getDefaultInstance() + private static final MongodStarter STARTER = MongodStarter.getDefaultInstance() @Shared int port = PortUtils.randomOpenPort() @@ -33,7 +33,7 @@ class MongoBaseTest extends AgentTestRunner { .net(new Net("localhost", port, Network.localhostIsIPv6())) .build() - mongodExe = starter.prepare(mongodConfig) + mongodExe = STARTER.prepare(mongodConfig) mongod = mongodExe.start() } From 75b626a84d03e47d0b025e14d8e5ddd0265e3b4d Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Thu, 13 Jun 2019 10:59:36 -0700 Subject: [PATCH 4/6] fix tests --- .../src/test/groovy/MongoAsyncClientTest.groovy | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy index 2caa1f70442..aa0b80f1e17 100644 --- a/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy @@ -46,7 +46,7 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(1) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }" || + assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" || it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } @@ -71,7 +71,7 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(1) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } @@ -106,14 +106,14 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(2) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }" || + assert it.replaceAll(" ", "") == "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}" || it == "{\"insert\": \"$collectionName\", \"ordered\": \"?\", \"\$db\": \"?\", \"documents\": [{\"_id\": \"?\", \"password\": \"?\"}]}" true } } trace(1, 1) { mongoSpan(it, 0) { - assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } @@ -157,14 +157,14 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(2) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }" || + assert it.replaceAll(" ", "") == "{\"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) { - assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } @@ -206,14 +206,14 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(2) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }" || + assert it.replaceAll(" ", "") == "{\"delete\":\"?\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}" || it == "{\"delete\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"deletes\": [{\"q\": {\"password\": \"?\"}, \"limit\": \"?\"}]}" true } } trace(1, 1) { mongoSpan(it, 0) { - assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } From 3be6868981a20c802fbfdea5b131bdf9549a7d6b Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Thu, 13 Jun 2019 11:58:42 -0700 Subject: [PATCH 5/6] Update Mongo instance name and remove memcache instance name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For mongo, use description if set, fallback to db name. Memcache doesn’t have a good “instance name” that would work as service name, so clear it out for now. --- .../mongo/MongoClientDecorator.java | 34 ++++++++++------ .../src/test/groovy/MongoClientTest.groovy | 30 ++++++++++++-- .../test/groovy/MongoAsyncClientTest.groovy | 39 +++++++++++++++++-- .../spymemcached/MemcacheClientDecorator.java | 2 +- .../spymemcached/SpymemcachedTest.groovy | 1 - 5 files changed, 85 insertions(+), 21 deletions(-) diff --git a/dd-java-agent/instrumentation/mongo/driver-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 index 08474ce55b6..a5d4782cdc6 100644 --- a/dd-java-agent/instrumentation/mongo/driver-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 @@ -1,5 +1,9 @@ package datadog.trace.instrumentation.mongo; +import com.mongodb.connection.ClusterId; +import com.mongodb.connection.ConnectionDescription; +import com.mongodb.connection.ConnectionId; +import com.mongodb.connection.ServerId; import com.mongodb.event.CommandStartedEvent; import datadog.trace.agent.decorator.DatabaseClientDecorator; import datadog.trace.api.DDSpanTypes; @@ -48,19 +52,25 @@ protected String dbUser(final CommandStartedEvent event) { @Override protected String dbInstance(final CommandStartedEvent event) { + // Use description if set. + final ConnectionDescription connectionDescription = event.getConnectionDescription(); + if (connectionDescription != null) { + final ConnectionId connectionId = connectionDescription.getConnectionId(); + if (connectionId != null) { + final ServerId serverId = connectionId.getServerId(); + if (serverId != null) { + ClusterId clusterId = serverId.getClusterId(); + if (clusterId != null) { + String description = clusterId.getDescription(); + if (description != null) { + return description; + } + } + } + } + } + // Fallback to db name. return event.getDatabaseName(); - // This would be the "proper" db.instance: - // final ConnectionDescription connectionDescription = event.getConnectionDescription(); - // if (connectionDescription != null) { - // final ConnectionId connectionId = connectionDescription.getConnectionId(); - // if (connectionId != null) { - // final ServerId serverId = connectionId.getServerId(); - // if (serverId != null) { - // return serverId.toString(); - // } - // } - // } - // return null; } public Span onStatement(final Span span, final BsonDocument statement) { diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy index ef7d9fb1161..6ecb6ef3efd 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy @@ -16,13 +16,16 @@ import spock.lang.Shared import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace -abstract class MongoClientTest extends MongoBaseTest { +class MongoClientTest extends MongoBaseTest { @Shared MongoClient client def setup() throws Exception { - client = new MongoClient("localhost", port) + client = new MongoClient(new ServerAddress("localhost", port), + MongoClientOptions.builder() + .description("some-description") + .build()) } def cleanup() throws Exception { @@ -49,6 +52,25 @@ abstract class MongoClientTest extends MongoBaseTest { collectionName = "testCollection" } + def "test create collection no description"() { + setup: + MongoDatabase db = new MongoClient("localhost", port).getDatabase(dbName) + + when: + db.createCollection(collectionName) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }", dbName) + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + def "test get collection"() { setup: MongoDatabase db = client.getDatabase(dbName) @@ -206,7 +228,7 @@ abstract class MongoClientTest extends MongoBaseTest { collectionName = "testCollection" } - def mongoSpan(TraceAssert trace, int index, String statement, Object parentSpan = null, Throwable exception = null) { + def mongoSpan(TraceAssert trace, int index, String statement, String instance = "some-description", Object parentSpan = null, Throwable exception = null) { trace.span(index) { serviceName "mongo" operationName "mongo.query" @@ -220,7 +242,7 @@ abstract class MongoClientTest extends MongoBaseTest { tags { "$Tags.COMPONENT.key" "java-mongo" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT - "$Tags.DB_INSTANCE.key" "test_db" + "$Tags.DB_INSTANCE.key" instance "$Tags.DB_STATEMENT.key" statement "$Tags.DB_TYPE.key" "mongo" "$Tags.PEER_HOSTNAME.key" "localhost" diff --git a/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy index aa0b80f1e17..1ba2cdc099d 100644 --- a/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy @@ -1,10 +1,13 @@ +import com.mongodb.ConnectionString import com.mongodb.async.SingleResultCallback import com.mongodb.async.client.MongoClient +import com.mongodb.async.client.MongoClientSettings import com.mongodb.async.client.MongoClients import com.mongodb.async.client.MongoCollection import com.mongodb.async.client.MongoDatabase import com.mongodb.client.result.DeleteResult import com.mongodb.client.result.UpdateResult +import com.mongodb.connection.ClusterSettings import datadog.opentracing.DDSpan import datadog.trace.agent.test.asserts.TraceAssert import datadog.trace.api.DDSpanTypes @@ -27,7 +30,14 @@ class MongoAsyncClientTest extends MongoBaseTest { MongoClient client def setup() throws Exception { - client = MongoClients.create("mongodb://localhost:$port") + client = MongoClients.create( + MongoClientSettings.builder() + .clusterSettings( + ClusterSettings.builder() + .description("some-description") + .applyConnectionString(new ConnectionString("mongodb://localhost:$port")) + .build()) + .build()) } def cleanup() throws Exception { @@ -58,6 +68,29 @@ class MongoAsyncClientTest extends MongoBaseTest { collectionName = "testCollection" } + def "test create collection no description"() { + setup: + MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(dbName) + + when: + db.createCollection(collectionName, toCallback {}) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, { + assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" || + it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + }, dbName) + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + def "test get collection"() { setup: MongoDatabase db = client.getDatabase(dbName) @@ -238,7 +271,7 @@ class MongoAsyncClientTest extends MongoBaseTest { } } - def mongoSpan(TraceAssert trace, int index, Closure statementEval, Object parentSpan = null, Throwable exception = null) { + def mongoSpan(TraceAssert trace, int index, Closure statementEval, String instance = "some-description", Object parentSpan = null, Throwable exception = null) { trace.span(index) { serviceName "mongo" operationName "mongo.query" @@ -252,7 +285,7 @@ class MongoAsyncClientTest extends MongoBaseTest { tags { "$Tags.COMPONENT.key" "java-mongo" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT - "$Tags.DB_INSTANCE.key" "test_db" + "$Tags.DB_INSTANCE.key" instance "$Tags.DB_STATEMENT.key" statementEval "$Tags.DB_TYPE.key" "mongo" "$Tags.PEER_HOSTNAME.key" "localhost" diff --git a/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java b/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java index f5729f94c64..cbb51e6784d 100644 --- a/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java +++ b/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java @@ -41,7 +41,7 @@ protected String dbUser(final MemcachedConnection session) { @Override protected String dbInstance(final MemcachedConnection connection) { - return connection.connectionsStatus(); + return null; } public Span onOperation(final Span span, final String methodName) { diff --git a/dd-java-agent/instrumentation/spymemcached-2.12/src/test/groovy/datadog/trace/instrumentation/spymemcached/SpymemcachedTest.groovy b/dd-java-agent/instrumentation/spymemcached-2.12/src/test/groovy/datadog/trace/instrumentation/spymemcached/SpymemcachedTest.groovy index 826c2312ce6..2f02de169e2 100644 --- a/dd-java-agent/instrumentation/spymemcached-2.12/src/test/groovy/datadog/trace/instrumentation/spymemcached/SpymemcachedTest.groovy +++ b/dd-java-agent/instrumentation/spymemcached-2.12/src/test/groovy/datadog/trace/instrumentation/spymemcached/SpymemcachedTest.groovy @@ -635,7 +635,6 @@ class SpymemcachedTest extends AgentTestRunner { "${Tags.COMPONENT.key}" COMPONENT_NAME "${Tags.SPAN_KIND.key}" Tags.SPAN_KIND_CLIENT "${Tags.DB_TYPE.key}" CompletionListener.DB_TYPE - "$Tags.DB_INSTANCE.key" ~/Connection Status \{ \w*\/127.0.0.1:\d+ active: true, authed: true, last read: \d+ ms ago }/ if (error == "canceled") { "${CompletionListener.DB_COMMAND_CANCELLED}" true From 7e2f85d67438399ecbd68307422be97f62901bbd Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Thu, 13 Jun 2019 14:54:41 -0700 Subject: [PATCH 6/6] Fix latest test --- .../src/test/groovy/MongoClientTest.groovy | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy index 6ecb6ef3efd..66ded8bb60e 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy @@ -43,7 +43,7 @@ class MongoClientTest extends MongoBaseTest { then: assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }") + mongoSpan(it, 0, "{\"create\":\"$collectionName\",\"capped\":\"?\"}") } } @@ -62,7 +62,7 @@ class MongoClientTest extends MongoBaseTest { then: assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }", dbName) + mongoSpan(it, 0, "{\"create\":\"$collectionName\",\"capped\":\"?\"}", dbName) } } @@ -82,7 +82,7 @@ class MongoClientTest extends MongoBaseTest { count == 0 assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") } } @@ -108,10 +108,10 @@ class MongoClientTest extends MongoBaseTest { collection.count() == 1 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }") + mongoSpan(it, 0, "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}") } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") } } @@ -142,10 +142,10 @@ class MongoClientTest extends MongoBaseTest { collection.count() == 1 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }") + mongoSpan(it, 0, "{\"update\":\"?\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}") } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") } } @@ -174,10 +174,10 @@ class MongoClientTest extends MongoBaseTest { collection.count() == 0 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }") + mongoSpan(it, 0, "{\"delete\":\"?\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}") } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") } } @@ -232,7 +232,10 @@ class MongoClientTest extends MongoBaseTest { trace.span(index) { serviceName "mongo" operationName "mongo.query" - resourceName statement + resourceName { + assert it.replace(" ", "") == statement + return true + } spanType DDSpanTypes.MONGO if (parentSpan == null) { parent() @@ -243,7 +246,9 @@ class MongoClientTest extends MongoBaseTest { "$Tags.COMPONENT.key" "java-mongo" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$Tags.DB_INSTANCE.key" instance - "$Tags.DB_STATEMENT.key" statement + "$Tags.DB_STATEMENT.key" { + it.replace(" ", "") == statement + } "$Tags.DB_TYPE.key" "mongo" "$Tags.PEER_HOSTNAME.key" "localhost" "$Tags.PEER_HOST_IPV4.key" "127.0.0.1"