Ensure gradle runs base mongo test first

Otherwise there is a race condition between the projects initializing the mongo instance.
This commit is contained in:
Tyler Benson 2019-06-12 14:46:11 -07:00
parent d6719ed5cd
commit d26edd6c17
13 changed files with 150 additions and 79 deletions

View File

@ -28,7 +28,7 @@ dependencies {
implementation deps.autoservice implementation deps.autoservice
testCompile project(':dd-java-agent:testing') 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: '2.2.0'
testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0' testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0'

View File

@ -5,18 +5,8 @@ import com.mongodb.ServerAddress
import com.mongodb.client.MongoCollection import com.mongodb.client.MongoCollection
import com.mongodb.client.MongoDatabase import com.mongodb.client.MongoDatabase
import datadog.opentracing.DDSpan import datadog.opentracing.DDSpan
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.asserts.TraceAssert import datadog.trace.agent.test.asserts.TraceAssert
import datadog.trace.agent.test.utils.PortUtils
import datadog.trace.api.DDSpanTypes 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 io.opentracing.tag.Tags
import org.bson.BsonDocument import org.bson.BsonDocument
import org.bson.BsonString 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.PortUtils.UNUSABLE_PORT
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
class MongoClientTest extends AgentTestRunner { abstract class MongoClientTest extends MongoBaseTest {
@Shared @Shared
MongoClient client MongoClient client
@Shared
int port = PortUtils.randomOpenPort()
@Shared
MongodExecutable mongodExe
@Shared
MongodProcess mongod
def setup() throws Exception { 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) client = new MongoClient("localhost", port)
} }
def cleanup() throws Exception { def cleanup() throws Exception {
client?.close() client?.close()
client = null client = null
mongod?.stop()
mongod = null
mongodExe?.stop()
mongodExe = null
} }
def "test create collection"() { def "test create collection"() {

View File

@ -20,10 +20,9 @@ testSets {
dependencies { dependencies {
// use mongo listener // use mongo listener
compile(project(':dd-java-agent:instrumentation:mongo-3.1')) { compile(project(':dd-java-agent:instrumentation:mongo:driver-3.1')) {
transitive = false 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' compileOnly group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0'
compile project(':dd-java-agent:agent-tooling') compile project(':dd-java-agent:agent-tooling')
@ -34,7 +33,7 @@ dependencies {
implementation deps.autoservice implementation deps.autoservice
testCompile project(':dd-java-agent:testing') 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: '2.2.0'
testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0'

View File

@ -6,18 +6,8 @@ import com.mongodb.async.client.MongoDatabase
import com.mongodb.client.result.DeleteResult import com.mongodb.client.result.DeleteResult
import com.mongodb.client.result.UpdateResult import com.mongodb.client.result.UpdateResult
import datadog.opentracing.DDSpan import datadog.opentracing.DDSpan
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.asserts.TraceAssert import datadog.trace.agent.test.asserts.TraceAssert
import datadog.trace.agent.test.utils.PortUtils
import datadog.trace.api.DDSpanTypes 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 io.opentracing.tag.Tags
import org.bson.BsonDocument import org.bson.BsonDocument
import org.bson.BsonString import org.bson.BsonString
@ -31,38 +21,18 @@ import java.util.concurrent.CountDownLatch
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
@Timeout(10) @Timeout(10)
class MongoAsyncClientTest extends AgentTestRunner { class MongoAsyncClientTest extends MongoBaseTest {
@Shared @Shared
MongoClient client MongoClient client
@Shared
int port = PortUtils.randomOpenPort()
@Shared
MongodExecutable mongodExe
@Shared
MongodProcess mongod
def setup() throws Exception { 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") client = MongoClients.create("mongodb://localhost:$port")
} }
def cleanup() throws Exception { def cleanup() throws Exception {
client?.close() client?.close()
client = null client = null
mongod?.stop()
mongod = null
mongodExe?.stop()
mongodExe = null
} }
def "test create collection"() { def "test create collection"() {
@ -75,7 +45,11 @@ class MongoAsyncClientTest extends AgentTestRunner {
then: then:
assertTraces(1) { assertTraces(1) {
trace(0, 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 count.get() == 0
assertTraces(1) { assertTraces(1) {
trace(0, 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 count.get() == 1
assertTraces(2) { assertTraces(2) {
trace(0, 1) { 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) { 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 count.get() == 1
assertTraces(2) { assertTraces(2) {
trace(0, 1) { 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) { 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 count.get() == 0
assertTraces(2) { assertTraces(2) {
trace(0, 1) { 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) { 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<Boolean> statementEval, Object parentSpan = null, Throwable exception = null) {
trace.span(index) { trace.span(index) {
serviceName "mongo" serviceName "mongo"
operationName "mongo.query" operationName "mongo.query"
resourceName statement resourceName statementEval
spanType DDSpanTypes.MONGO spanType DDSpanTypes.MONGO
if (parentSpan == null) { if (parentSpan == null) {
parent() parent()
@ -251,7 +253,7 @@ class MongoAsyncClientTest extends AgentTestRunner {
"$Tags.COMPONENT.key" "java-mongo" "$Tags.COMPONENT.key" "java-mongo"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.DB_INSTANCE.key" "test_db" "$Tags.DB_INSTANCE.key" "test_db"
"$Tags.DB_STATEMENT.key" statement "$Tags.DB_STATEMENT.key" statementEval
"$Tags.DB_TYPE.key" "mongo" "$Tags.DB_TYPE.key" "mongo"
"$Tags.PEER_HOSTNAME.key" "localhost" "$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_HOST_IPV4.key" "127.0.0.1" "$Tags.PEER_HOST_IPV4.key" "127.0.0.1"

View File

@ -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<Test> 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)
}
}
}

View File

@ -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()
}
}

View File

@ -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;
}
}

View File

@ -64,6 +64,11 @@ class SpanAssert {
checked.resourceName = true checked.resourceName = true
} }
def resourceName(Closure<Boolean> eval) {
assert eval(span.resourceName)
checked.resourceName = true
}
def resourceNameContains(String... resourceNameParts) { def resourceNameContains(String... resourceNameParts) {
assertSpanNameContains(span.resourceName, resourceNameParts) assertSpanNameContains(span.resourceName, resourceNameParts)
checked.resourceName = true checked.resourceName = true

View File

@ -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-clients-0.11'
include ':dd-java-agent:instrumentation:kafka-streams-0.11' include ':dd-java-agent:instrumentation:kafka-streams-0.11'
include ':dd-java-agent:instrumentation:lettuce-5' include ':dd-java-agent:instrumentation:lettuce-5'
include ':dd-java-agent:instrumentation:mongo-3.1' include ':dd-java-agent:instrumentation:mongo'
include ':dd-java-agent:instrumentation:mongo-async-3.3' 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.0'
include ':dd-java-agent:instrumentation:netty-4.1' include ':dd-java-agent:instrumentation:netty-4.1'
include ':dd-java-agent:instrumentation:okhttp-3' include ':dd-java-agent:instrumentation:okhttp-3'