diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/elasticsearch-transport-2.gradle b/dd-java-agent/instrumentation/elasticsearch-transport-2/elasticsearch-transport-2.gradle index be88bc1d6c..e363335d19 100644 --- a/dd-java-agent/instrumentation/elasticsearch-transport-2/elasticsearch-transport-2.gradle +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/elasticsearch-transport-2.gradle @@ -14,9 +14,7 @@ testJava8Only += '**/*Test.class' apply plugin: 'org.unbroken-dome.test-sets' testSets { - latestDepTest { - dirName = 'test' - } + latestDepTest } dependencies { @@ -43,6 +41,6 @@ dependencies { testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0' - latestDepTestCompile group: 'org.elasticsearch', name: 'elasticsearch', version: '2.+' - latestDepTestCompile group: 'org.springframework.data', name: 'spring-data-elasticsearch', version: '2.+' + latestDepTestCompile group: 'org.elasticsearch', name: 'elasticsearch', version: '2.4.6' + latestDepTestCompile group: 'org.springframework.data', name: 'spring-data-elasticsearch', version: '2.1.15.RELEASE' } diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/Elasticsearch2NodeClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/Elasticsearch2NodeClientTest.groovy new file mode 100644 index 0000000000..e6eb9cd609 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/Elasticsearch2NodeClientTest.groovy @@ -0,0 +1,293 @@ +import com.anotherchrisberry.spock.extensions.retry.RetryOnFailure +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.TestUtils +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.DDTags +import io.opentracing.tag.Tags +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest +import org.elasticsearch.common.io.FileSystemUtils +import org.elasticsearch.common.settings.Settings +import org.elasticsearch.index.IndexNotFoundException +import org.elasticsearch.node.Node +import org.elasticsearch.node.NodeBuilder +import spock.lang.Shared + +import static datadog.trace.agent.test.TestUtils.runUnderTrace +import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces + +class Elasticsearch2NodeClientTest extends AgentTestRunner { + public static final long TIMEOUT = 10000; // 10 seconds + + @Shared + int httpPort + @Shared + int tcpPort + @Shared + Node testNode + @Shared + File esWorkingDir + + def client = testNode.client() + + def setupSpec() { + httpPort = TestUtils.randomOpenPort() + tcpPort = TestUtils.randomOpenPort() + + esWorkingDir = File.createTempDir("test-es-working-dir-", "") + esWorkingDir.deleteOnExit() + println "ES work dir: $esWorkingDir" + + def settings = Settings.builder() + .put("path.home", esWorkingDir.path) + // Since we use listeners to close spans this should make our span closing deterministic which is good for tests + .put("threadpool.listener.size", 1) + .put("http.port", httpPort) + .put("transport.tcp.port", tcpPort) + .build() + testNode = NodeBuilder.newInstance().local(true).clusterName("test-cluster").settings(settings).build() + testNode.start() + runUnderTrace("setup") { + // this may potentially create multiple requests and therefore multiple spans, so we wrap this call + // into a top level trace to get exactly one trace in the result. + testNode.client().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + } + TEST_WRITER.waitForTraces(1) + } + + def cleanupSpec() { + testNode?.close() + if (esWorkingDir != null) { + FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath()) + esWorkingDir.delete() + } + } + + @RetryOnFailure + def "test elasticsearch status"() { + setup: + def result = client.admin().cluster().health(new ClusterHealthRequest(new String[0])) + + def status = result.get().status + + expect: + status.name() == "GREEN" + + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + } + } + + @RetryOnFailure + def "test elasticsearch error"() { + when: + client.prepareGet(indexName, indexType, id).get() + + then: + thrown IndexNotFoundException + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + errored true + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + errorTags IndexNotFoundException, "no such index" + defaultTags() + } + } + } + } + + where: + indexName = "invalid-index" + indexType = "test-type" + id = "1" + } + + def "test elasticsearch get"() { + setup: + assert TEST_WRITER == [] + def indexResult = client.admin().indices().prepareCreate(indexName).get() + TEST_WRITER.waitForTraces(1) + + expect: + indexResult.acknowledged + TEST_WRITER.size() == 1 + + when: + client.admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + def emptyResult = client.prepareGet(indexName, indexType, id).get() + + then: + !emptyResult.isExists() + emptyResult.id == id + emptyResult.type == indexType + emptyResult.index == indexName + + when: + def createResult = client.prepareIndex(indexName, indexType, id).setSource([:]).get() + + then: + createResult.id == id + createResult.type == indexType + createResult.index == indexName + + when: + def result = client.prepareGet(indexName, indexType, id).get() + + then: + result.isExists() + result.id == id + result.type == indexType + result.index == indexName + + and: + // IndexAction and PutMappingAction run in separate threads and order in which + // these spans are closed is not defined. So we force the order if it is wrong. + if (TEST_WRITER[3][0].resourceName == "IndexAction") { + def tmp = TEST_WRITER[3] + TEST_WRITER[3] = TEST_WRITER[4] + TEST_WRITER[4] = tmp + } + assertTraces(TEST_WRITER, 6) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "CreateIndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "CreateIndexAction" + "elasticsearch.request" "CreateIndexRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version"(-1) + defaultTags() + } + } + } + trace(3, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "PutMappingAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "PutMappingAction" + "elasticsearch.request" "PutMappingRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(4, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" indexType + defaultTags() + } + } + } + trace(5, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version" 1 + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + indexType = "test-type" + id = "1" + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/Elasticsearch2TransportClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/Elasticsearch2TransportClientTest.groovy new file mode 100644 index 0000000000..c02814edb8 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/Elasticsearch2TransportClientTest.groovy @@ -0,0 +1,322 @@ +import com.anotherchrisberry.spock.extensions.retry.RetryOnFailure +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.TestUtils +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.DDTags +import io.opentracing.tag.Tags +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest +import org.elasticsearch.client.transport.TransportClient +import org.elasticsearch.common.io.FileSystemUtils +import org.elasticsearch.common.settings.Settings +import org.elasticsearch.common.transport.InetSocketTransportAddress +import org.elasticsearch.index.IndexNotFoundException +import org.elasticsearch.node.Node +import org.elasticsearch.node.NodeBuilder +import org.elasticsearch.transport.RemoteTransportException +import spock.lang.Shared + +import static datadog.trace.agent.test.TestUtils.runUnderTrace +import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces + +class Elasticsearch2TransportClientTest extends AgentTestRunner { + public static final long TIMEOUT = 10000; // 10 seconds + + @Shared + int httpPort + @Shared + int tcpPort + @Shared + Node testNode + @Shared + File esWorkingDir + + @Shared + TransportClient client + + def setupSpec() { + httpPort = TestUtils.randomOpenPort() + tcpPort = TestUtils.randomOpenPort() + + esWorkingDir = File.createTempDir("test-es-working-dir-", "") + esWorkingDir.deleteOnExit() + println "ES work dir: $esWorkingDir" + + def settings = Settings.builder() + .put("path.home", esWorkingDir.path) + .put("http.port", httpPort) + .put("transport.tcp.port", tcpPort) + .build() + testNode = NodeBuilder.newInstance().clusterName("test-cluster").settings(settings).build() + testNode.start() + + client = TransportClient.builder().settings( + Settings.builder() + // Since we use listeners to close spans this should make our span closing deterministic which is good for tests + .put("threadpool.listener.size", 1) + .put("cluster.name", "test-cluster") + .build() + ).build() + client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), tcpPort)) + runUnderTrace("setup") { + // this may potentially create multiple requests and therefore multiple spans, so we wrap this call + // into a top level trace to get exactly one trace in the result. + client.admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + } + TEST_WRITER.waitForTraces(1) + } + + def cleanupSpec() { + testNode?.close() + if (esWorkingDir != null) { + FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath()) + esWorkingDir.delete() + } + } + + @RetryOnFailure + def "test elasticsearch status"() { + setup: + def result = client.admin().cluster().health(new ClusterHealthRequest(new String[0])) + + def status = result.get().status + + expect: + status.name() == "GREEN" + + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + } + } + + @RetryOnFailure + def "test elasticsearch error"() { + when: + client.prepareGet(indexName, indexType, id).get() + + then: + thrown IndexNotFoundException + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + errored true + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + errorTags RemoteTransportException, String + defaultTags() + } + } + } + } + + where: + indexName = "invalid-index" + indexType = "test-type" + id = "1" + } + + def "test elasticsearch get"() { + setup: + assert TEST_WRITER == [] + def indexResult = client.admin().indices().prepareCreate(indexName).get() + TEST_WRITER.waitForTraces(1) + + expect: + indexResult.acknowledged + TEST_WRITER.size() == 1 + + when: + client.admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + def emptyResult = client.prepareGet(indexName, indexType, id).get() + + then: + !emptyResult.isExists() + emptyResult.id == id + emptyResult.type == indexType + emptyResult.index == indexName + + when: + def createResult = client.prepareIndex(indexName, indexType, id).setSource([:]).get() + + then: + createResult.id == id + createResult.type == indexType + createResult.index == indexName + + when: + def result = client.prepareGet(indexName, indexType, id).get() + + then: + result.isExists() + result.id == id + result.type == indexType + result.index == indexName + + and: + // IndexAction and PutMappingAction run in separate threads and order in which + // these spans are closed is not defined. So we force the order if it is wrong. + if (TEST_WRITER[3][0].resourceName == "IndexAction") { + def tmp = TEST_WRITER[3] + TEST_WRITER[3] = TEST_WRITER[4] + TEST_WRITER[4] = tmp + } + assertTraces(TEST_WRITER, 6) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "CreateIndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "CreateIndexAction" + "elasticsearch.request" "CreateIndexRequest" + "elasticsearch.request.indices" indexName + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version"(-1) + defaultTags() + } + } + } + trace(3, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "PutMappingAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "PutMappingAction" + "elasticsearch.request" "PutMappingRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(4, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" indexType + defaultTags() + } + } + } + trace(5, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version" 1 + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + indexType = "test-type" + id = "1" + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Config.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Config.groovy new file mode 100644 index 0000000000..9cd62c70d4 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Config.groovy @@ -0,0 +1,49 @@ +package springdata + +import org.elasticsearch.common.io.FileSystemUtils +import org.elasticsearch.common.settings.Settings +import org.elasticsearch.node.NodeBuilder +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.data.elasticsearch.core.ElasticsearchOperations +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate +import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories + +@Configuration +@EnableElasticsearchRepositories(basePackages = "springdata") +@ComponentScan(basePackages = "springdata") +class Config { + + @Bean + NodeBuilder nodeBuilder() { + return new NodeBuilder() + } + + @Bean + ElasticsearchOperations elasticsearchTemplate() { + + def tmpDir = File.createTempFile("test-es-working-dir-", "") + tmpDir.delete() + tmpDir.mkdir() + tmpDir.deleteOnExit() + + System.addShutdownHook { + if (tmpDir != null) { + FileSystemUtils.deleteSubDirectories(tmpDir.toPath()) + tmpDir.delete() + } + } + + final Settings.Builder elasticsearchSettings = + Settings.settingsBuilder() + .put("http.enabled", "false") + .put("path.data", tmpDir.toString()) + .put("path.home", tmpDir.toString()) + + println "ES work dir: $tmpDir" + + return new ElasticsearchTemplate(nodeBuilder().local(true) + .settings(elasticsearchSettings.build()).node().client()) + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Doc.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Doc.groovy new file mode 100644 index 0000000000..b00adf02c8 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Doc.groovy @@ -0,0 +1,29 @@ +package springdata + +import groovy.transform.EqualsAndHashCode +import org.springframework.data.annotation.Id +import org.springframework.data.elasticsearch.annotations.Document + +@Document(indexName = "test-index") +@EqualsAndHashCode +class Doc { + @Id + private String id = "1" + private String data = "some data" + + String getId() { + return id + } + + void setId(String id) { + this.id = id + } + + String getData() { + return data + } + + void setData(String data) { + this.data = data + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/DocRepository.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/DocRepository.groovy new file mode 100644 index 0000000000..ab41d9513b --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/DocRepository.groovy @@ -0,0 +1,5 @@ +package springdata + +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository + +interface DocRepository extends ElasticsearchRepository {} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Elasticsearch2SpringRepositoryTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Elasticsearch2SpringRepositoryTest.groovy new file mode 100644 index 0000000000..effcba68c7 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Elasticsearch2SpringRepositoryTest.groovy @@ -0,0 +1,293 @@ +package springdata + +import com.anotherchrisberry.spock.extensions.retry.RetryOnFailure +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.DDTags +import io.opentracing.tag.Tags +import org.springframework.context.ApplicationContext +import org.springframework.context.annotation.AnnotationConfigApplicationContext +import spock.lang.Shared + +import static datadog.trace.agent.test.TestUtils.runUnderTrace +import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces + +@RetryOnFailure +class Elasticsearch2SpringRepositoryTest extends AgentTestRunner { + @Shared + ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config) + + @Shared + DocRepository repo = applicationContext.getBean(DocRepository) + + def setup() { + TEST_WRITER.clear() + runUnderTrace("delete") { + repo.deleteAll() + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + } + + def "test empty repo"() { + when: + def result = repo.findAll() + + then: + !result.iterator().hasNext() + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + errored false + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.search.types" "doc" + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + } + + def "test CRUD"() { + when: + def doc = new Doc() + + then: + repo.index(doc) == doc + + and: + assertTraces(TEST_WRITER, 3) { + trace(0, 1) { + span(0) { + resourceName "PutMappingAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "PutMappingAction" + "elasticsearch.request" "PutMappingRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" "doc" + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.shard.broadcast.failed" 0 + "elasticsearch.shard.broadcast.successful" 5 + "elasticsearch.shard.broadcast.total" 10 + defaultTags() + } + } + } + } + TEST_WRITER.clear() + + and: + repo.findOne("1") == doc + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" "doc" + "elasticsearch.id" "1" + "elasticsearch.version" 1 + defaultTags() + } + } + } + } + TEST_WRITER.clear() + + when: + doc.data = "other data" + + then: + repo.index(doc) == doc + repo.findOne("1") == doc + + and: + assertTraces(TEST_WRITER, 3) { + trace(0, 1) { + span(0) { + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" "doc" + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.shard.broadcast.failed" 0 + "elasticsearch.shard.broadcast.successful" 5 + "elasticsearch.shard.broadcast.total" 10 + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" "doc" + "elasticsearch.id" "1" + "elasticsearch.version" 2 + defaultTags() + } + } + } + } + TEST_WRITER.clear() + + when: + repo.delete("1") + + then: + !repo.findAll().iterator().hasNext() + + and: + assertTraces(TEST_WRITER, 3) { + trace(0, 1) { + span(0) { + resourceName "DeleteAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "DeleteAction" + "elasticsearch.request" "DeleteRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" "doc" + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.shard.broadcast.failed" 0 + "elasticsearch.shard.broadcast.successful" 5 + "elasticsearch.shard.broadcast.total" 10 + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.search.types" "doc" + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Elasticsearch2SpringTemplateTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Elasticsearch2SpringTemplateTest.groovy new file mode 100644 index 0000000000..cf2071d362 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/latestDepTest/groovy/springdata/Elasticsearch2SpringTemplateTest.groovy @@ -0,0 +1,349 @@ +package springdata + +import com.anotherchrisberry.spock.extensions.retry.RetryOnFailure +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.TestUtils +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.DDTags +import io.opentracing.tag.Tags +import org.elasticsearch.action.search.SearchResponse +import org.elasticsearch.common.io.FileSystemUtils +import org.elasticsearch.common.settings.Settings +import org.elasticsearch.index.IndexNotFoundException +import org.elasticsearch.node.Node +import org.elasticsearch.node.NodeBuilder +import org.elasticsearch.search.aggregations.bucket.nested.InternalNested +import org.elasticsearch.search.aggregations.bucket.terms.Terms +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate +import org.springframework.data.elasticsearch.core.ResultsExtractor +import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder +import spock.lang.Shared + +import java.util.concurrent.atomic.AtomicLong + +import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces + +class Elasticsearch2SpringTemplateTest extends AgentTestRunner { + public static final long TIMEOUT = 10000; // 10 seconds + + @Shared + int httpPort + @Shared + int tcpPort + @Shared + Node testNode + @Shared + File esWorkingDir + + @Shared + ElasticsearchTemplate template + + def setupSpec() { + httpPort = TestUtils.randomOpenPort() + tcpPort = TestUtils.randomOpenPort() + + esWorkingDir = File.createTempDir("test-es-working-dir-", "") + esWorkingDir.deleteOnExit() + println "ES work dir: $esWorkingDir" + + def settings = Settings.builder() + .put("path.home", esWorkingDir.path) + // Since we use listeners to close spans this should make our span closing deterministic which is good for tests + .put("threadpool.listener.size", 1) + .put("http.port", httpPort) + .put("transport.tcp.port", tcpPort) + .build() + testNode = NodeBuilder.newInstance().local(true).clusterName("test-cluster").settings(settings).build() + testNode.start() + + template = new ElasticsearchTemplate(testNode.client()) + } + + def cleanupSpec() { + testNode?.close() + if (esWorkingDir != null) { + FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath()) + esWorkingDir.delete() + } + } + + @RetryOnFailure + def "test elasticsearch error"() { + when: + template.refresh(indexName) + + then: + thrown IndexNotFoundException + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + errored true + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + errorTags IndexNotFoundException, "no such index" + defaultTags() + } + } + } + } + + where: + indexName = "invalid-index" + } + + def "test elasticsearch get"() { + expect: + template.createIndex(indexName) + template.getClient().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + + when: + NativeSearchQuery query = new NativeSearchQueryBuilder() + .withIndices(indexName) + .withTypes(indexType) + .withIds([id]) + .build() + + then: + template.queryForIds(query) == [] + + when: + def result = template.index(IndexQueryBuilder.newInstance() + .withObject(new Doc()) + .withIndexName(indexName) + .withType(indexType) + .withId(id) + .build()) + template.refresh(Doc) + + then: + result == id + template.queryForList(query, Doc) == [new Doc()] + + and: + // IndexAction and PutMappingAction run in separate threads and order in which + // these spans are closed is not defined. So we force the order if it is wrong. + if (TEST_WRITER[3][0].resourceName == "IndexAction") { + def tmp = TEST_WRITER[3] + TEST_WRITER[3] = TEST_WRITER[4] + TEST_WRITER[4] = tmp + } + assertTraces(TEST_WRITER, 7) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "CreateIndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "CreateIndexAction" + "elasticsearch.request" "CreateIndexRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.search.types" indexType + defaultTags() + } + } + } + trace(3, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "PutMappingAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "PutMappingAction" + "elasticsearch.request" "PutMappingRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(4, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" indexType + defaultTags() + } + } + } + trace(5, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.shard.broadcast.failed" 0 + "elasticsearch.shard.broadcast.successful" 5 + "elasticsearch.shard.broadcast.total" 10 + defaultTags() + } + } + } + trace(6, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.search.types" indexType + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + indexType = "test-type" + id = "1" + } + + def "test results extractor"() { + setup: + template.createIndex(indexName) + testNode.client().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + template.index(IndexQueryBuilder.newInstance() + .withObject(new Doc(id: 1, data: "doc a")) + .withIndexName(indexName) + .withId("a") + .build()) + template.index(IndexQueryBuilder.newInstance() + .withObject(new Doc(id: 2, data: "doc b")) + .withIndexName(indexName) + .withId("b") + .build()) + template.refresh(indexName) + TEST_WRITER.waitForTraces(6) + TEST_WRITER.clear() + + and: + def query = new NativeSearchQueryBuilder().withIndices(indexName).build() + def hits = new AtomicLong() + List> results = [] + def bucketTags = [:] + + when: + template.query(query, new ResultsExtractor() { + + @Override + Doc extract(SearchResponse response) { + hits.addAndGet(response.getHits().totalHits()) + results.addAll(response.hits.collect { it.source }) + if (response.getAggregations() != null) { + InternalNested internalNested = response.getAggregations().get("tag") + if (internalNested != null) { + Terms terms = internalNested.getAggregations().get("count_agg") + Collection buckets = terms.getBuckets() + for (Terms.Bucket bucket : buckets) { + bucketTags.put(Integer.valueOf(bucket.getKeyAsString()), bucket.getDocCount()) + } + } + } + return null + } + }) + + then: + hits.get() == 2 + results[0] == [id: "2", data: "doc b"] + results[1] == [id: "1", data: "doc a"] + bucketTags == [:] + + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + } + + where: + indexName = "test-index-extract" + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/test/groovy/springdata/Elasticsearch2SpringRepositoryTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/test/groovy/springdata/Elasticsearch2SpringRepositoryTest.groovy index 85e156f661..184f6b68cd 100644 --- a/dd-java-agent/instrumentation/elasticsearch-transport-2/src/test/groovy/springdata/Elasticsearch2SpringRepositoryTest.groovy +++ b/dd-java-agent/instrumentation/elasticsearch-transport-2/src/test/groovy/springdata/Elasticsearch2SpringRepositoryTest.groovy @@ -9,6 +9,7 @@ import org.springframework.context.ApplicationContext import org.springframework.context.annotation.AnnotationConfigApplicationContext import spock.lang.Shared +import static datadog.trace.agent.test.TestUtils.runUnderTrace import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces @RetryOnFailure @@ -20,8 +21,11 @@ class Elasticsearch2SpringRepositoryTest extends AgentTestRunner { DocRepository repo = applicationContext.getBean(DocRepository) def setup() { - repo.deleteAll() - TEST_WRITER.waitForTraces(4) + TEST_WRITER.clear() + runUnderTrace("delete") { + repo.deleteAll() + } + TEST_WRITER.waitForTraces(1) TEST_WRITER.clear() } diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/elasticsearch-transport-5.3.gradle b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/elasticsearch-transport-5.3.gradle index 610b749b9f..36dbf825fd 100644 --- a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/elasticsearch-transport-5.3.gradle +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/elasticsearch-transport-5.3.gradle @@ -43,6 +43,10 @@ dependencies { testCompile group: 'org.elasticsearch.plugin', name: 'transport-netty3-client', version: '5.3.0' testCompile group: 'org.elasticsearch.client', name: 'transport', version: '5.3.0' + // Unfortunately this will bump the transport version up to 5.5.0. + testCompile group: 'org.springframework.data', name: 'spring-data-elasticsearch', version: '3.0.0.RELEASE' + latestDepTestCompile group: 'org.elasticsearch.plugin', name: 'transport-netty3-client', version: '5.+' latestDepTestCompile group: 'org.elasticsearch.client', name: 'transport', version: '5.+' + latestDepTestCompile group: 'org.springframework.data', name: 'spring-data-elasticsearch', version: '3.+' } diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/Elasticsearch53NodeClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/Elasticsearch53NodeClientTest.groovy index e1d209ecea..2fe151b662 100644 --- a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/Elasticsearch53NodeClientTest.groovy +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/Elasticsearch53NodeClientTest.groovy @@ -134,4 +134,171 @@ class Elasticsearch53NodeClientTest extends AgentTestRunner { indexType = "test-type" id = "1" } + + def "test elasticsearch get"() { + setup: + assert TEST_WRITER == [] + def indexResult = client.admin().indices().prepareCreate(indexName).get() + TEST_WRITER.waitForTraces(1) + + expect: + indexResult.acknowledged + TEST_WRITER.size() == 1 + + when: + client.admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + def emptyResult = client.prepareGet(indexName, indexType, id).get() + + then: + !emptyResult.isExists() + emptyResult.id == id + emptyResult.type == indexType + emptyResult.index == indexName + + when: + def createResult = client.prepareIndex(indexName, indexType, id).setSource([:]).get() + + then: + createResult.id == id + createResult.type == indexType + createResult.index == indexName + createResult.status().status == 201 + + when: + def result = client.prepareGet(indexName, indexType, id).get() + + then: + result.isExists() + result.id == id + result.type == indexType + result.index == indexName + + and: + // IndexAction and PutMappingAction run in separate threads and order in which + // these spans are closed is not defined. So we force the order if it is wrong. + if (TEST_WRITER[3][0].resourceName == "IndexAction") { + def tmp = TEST_WRITER[3] + TEST_WRITER[3] = TEST_WRITER[4] + TEST_WRITER[4] = tmp + } + assertTraces(TEST_WRITER, 6) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "CreateIndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "CreateIndexAction" + "elasticsearch.request" "CreateIndexRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version"(-1) + defaultTags() + } + } + } + trace(3, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "PutMappingAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "PutMappingAction" + "elasticsearch.request" "PutMappingRequest" + defaultTags() + } + } + } + trace(4, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" indexType + "elasticsearch.request.write.version"(-3) + "elasticsearch.response.status" 201 + "elasticsearch.shard.replication.total" 2 + "elasticsearch.shard.replication.successful" 1 + "elasticsearch.shard.replication.failed" 0 + defaultTags() + } + } + } + trace(5, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version" 1 + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + indexType = "test-type" + id = "1" + } } diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Config.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Config.groovy new file mode 100644 index 0000000000..2e027fb075 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Config.groovy @@ -0,0 +1,53 @@ +package springdata + +import org.elasticsearch.common.io.FileSystemUtils +import org.elasticsearch.common.settings.Settings +import org.elasticsearch.env.Environment +import org.elasticsearch.node.InternalSettingsPreparer +import org.elasticsearch.node.Node +import org.elasticsearch.transport.Netty3Plugin +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.data.elasticsearch.core.ElasticsearchOperations +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate +import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories + +@Configuration +@EnableElasticsearchRepositories(basePackages = "springdata") +@ComponentScan(basePackages = "springdata") +class Config { + + @Bean + NodeBuilder nodeBuilder() { + return new NodeBuilder() + } + + @Bean + ElasticsearchOperations elasticsearchTemplate() { + def tmpDir = File.createTempFile("test-es-working-dir-", "") + tmpDir.delete() + tmpDir.mkdir() + tmpDir.deleteOnExit() + + System.addShutdownHook { + if (tmpDir != null) { + FileSystemUtils.deleteSubDirectories(tmpDir.toPath()) + tmpDir.delete() + } + } + + def settings = Settings.builder() + .put("http.enabled", "false") + .put("path.data", tmpDir.toString()) + .put("path.home", tmpDir.toString()) + .put("thread_pool.listener.size", 1) + .put("transport.type", "netty3") + .put("http.type", "netty3") + .build() + + println "ES work dir: $tmpDir" + + return new ElasticsearchTemplate(new Node(new Environment(InternalSettingsPreparer.prepareSettings(settings)), [Netty3Plugin]).start().client()) + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Doc.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Doc.groovy new file mode 100644 index 0000000000..b00adf02c8 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Doc.groovy @@ -0,0 +1,29 @@ +package springdata + +import groovy.transform.EqualsAndHashCode +import org.springframework.data.annotation.Id +import org.springframework.data.elasticsearch.annotations.Document + +@Document(indexName = "test-index") +@EqualsAndHashCode +class Doc { + @Id + private String id = "1" + private String data = "some data" + + String getId() { + return id + } + + void setId(String id) { + this.id = id + } + + String getData() { + return data + } + + void setData(String data) { + this.data = data + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/DocRepository.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/DocRepository.groovy new file mode 100644 index 0000000000..ab41d9513b --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/DocRepository.groovy @@ -0,0 +1,5 @@ +package springdata + +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository + +interface DocRepository extends ElasticsearchRepository {} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy new file mode 100644 index 0000000000..cb28979f38 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy @@ -0,0 +1,291 @@ +package springdata + +import com.anotherchrisberry.spock.extensions.retry.RetryOnFailure +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.DDTags +import io.opentracing.tag.Tags +import org.springframework.context.ApplicationContext +import org.springframework.context.annotation.AnnotationConfigApplicationContext +import spock.lang.Shared + +import static datadog.trace.agent.test.TestUtils.runUnderTrace +import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces + +@RetryOnFailure +class Elasticsearch53SpringRepositoryTest extends AgentTestRunner { + @Shared + ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config) + + @Shared + DocRepository repo = applicationContext.getBean(DocRepository) + + def setup() { + TEST_WRITER.clear() + runUnderTrace("delete") { + repo.deleteAll() + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + } + + def "test empty repo"() { + when: + def result = repo.findAll() + + then: + !result.iterator().hasNext() + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + errored false + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.search.types" "doc" + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + } + + def "test CRUD"() { + when: + def doc = new Doc() + + then: + repo.index(doc) == doc + + and: + assertTraces(TEST_WRITER, 2) { + trace(0, 1) { + span(0) { + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" "doc" + "elasticsearch.request.write.version"(-3) + "elasticsearch.response.status" 201 + "elasticsearch.shard.replication.failed" 0 + "elasticsearch.shard.replication.successful" 1 + "elasticsearch.shard.replication.total" 2 + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.shard.broadcast.failed" 0 + "elasticsearch.shard.broadcast.successful" 5 + "elasticsearch.shard.broadcast.total" 10 + defaultTags() + } + } + } + } + TEST_WRITER.clear() + + and: + repo.findById("1").get() == doc + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" "doc" + "elasticsearch.id" "1" + "elasticsearch.version" 3 + defaultTags() + } + } + } + } + TEST_WRITER.clear() + + when: + doc.data = "other data" + + then: + repo.index(doc) == doc + repo.findById("1").get() == doc + + and: + assertTraces(TEST_WRITER, 3) { + trace(0, 1) { + span(0) { + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" "doc" + "elasticsearch.request.write.version"(-3) + "elasticsearch.response.status" 200 + "elasticsearch.shard.replication.failed" 0 + "elasticsearch.shard.replication.successful" 1 + "elasticsearch.shard.replication.total" 2 + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.shard.broadcast.failed" 0 + "elasticsearch.shard.broadcast.successful" 5 + "elasticsearch.shard.broadcast.total" 10 + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" "doc" + "elasticsearch.id" "1" + "elasticsearch.version" 4 + defaultTags() + } + } + } + } + TEST_WRITER.clear() + + when: + repo.deleteById("1") + + then: + !repo.findAll().iterator().hasNext() + + and: + assertTraces(TEST_WRITER, 3) { + trace(0, 1) { + span(0) { + resourceName "DeleteAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "DeleteAction" + "elasticsearch.request" "DeleteRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" "doc" + "elasticsearch.request.write.version"(-3) + "elasticsearch.shard.replication.failed" 0 + "elasticsearch.shard.replication.successful" 1 + "elasticsearch.shard.replication.total" 2 + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.shard.broadcast.failed" 0 + "elasticsearch.shard.broadcast.successful" 5 + "elasticsearch.shard.broadcast.total" 10 + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.search.types" "doc" + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Elasticsearch53SpringTemplateTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Elasticsearch53SpringTemplateTest.groovy new file mode 100644 index 0000000000..6cc174118e --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5.3/src/test/groovy/springdata/Elasticsearch53SpringTemplateTest.groovy @@ -0,0 +1,366 @@ +package springdata + +import com.anotherchrisberry.spock.extensions.retry.RetryOnFailure +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.TestUtils +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.DDTags +import io.opentracing.tag.Tags +import org.elasticsearch.action.search.SearchResponse +import org.elasticsearch.common.io.FileSystemUtils +import org.elasticsearch.common.settings.Settings +import org.elasticsearch.env.Environment +import org.elasticsearch.index.IndexNotFoundException +import org.elasticsearch.node.InternalSettingsPreparer +import org.elasticsearch.node.Node +import org.elasticsearch.search.aggregations.bucket.nested.InternalNested +import org.elasticsearch.search.aggregations.bucket.terms.Terms +import org.elasticsearch.transport.Netty3Plugin +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate +import org.springframework.data.elasticsearch.core.ResultsExtractor +import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder +import spock.lang.Shared + +import java.util.concurrent.atomic.AtomicLong + +import static datadog.trace.agent.test.TestUtils.runUnderTrace +import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces +import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING + +class Elasticsearch53SpringTemplateTest extends AgentTestRunner { + public static final long TIMEOUT = 10000; // 10 seconds + + @Shared + int httpPort + @Shared + int tcpPort + @Shared + Node testNode + @Shared + File esWorkingDir + + @Shared + ElasticsearchTemplate template + + def setupSpec() { + httpPort = TestUtils.randomOpenPort() + tcpPort = TestUtils.randomOpenPort() + + esWorkingDir = File.createTempDir("test-es-working-dir-", "") + esWorkingDir.deleteOnExit() + println "ES work dir: $esWorkingDir" + + def settings = Settings.builder() + .put("path.home", esWorkingDir.path) + // Since we use listeners to close spans this should make our span closing deterministic which is good for tests + .put("thread_pool.listener.size", 1) + .put("http.port", httpPort) + .put("transport.tcp.port", tcpPort) + .put("transport.type", "netty3") + .put("http.type", "netty3") + .put(CLUSTER_NAME_SETTING.getKey(), "test-cluster") + .build() + testNode = new Node(new Environment(InternalSettingsPreparer.prepareSettings(settings)), [Netty3Plugin]) + testNode.start() + runUnderTrace("setup") { + // this may potentially create multiple requests and therefore multiple spans, so we wrap this call + // into a top level trace to get exactly one trace in the result. + testNode.client().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + } + TEST_WRITER.waitForTraces(1) + + template = new ElasticsearchTemplate(testNode.client()) + } + + def cleanupSpec() { + testNode?.close() + if (esWorkingDir != null) { + FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath()) + esWorkingDir.delete() + } + } + + @RetryOnFailure + def "test elasticsearch error"() { + when: + template.refresh(indexName) + + then: + thrown IndexNotFoundException + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + errored true + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + errorTags IndexNotFoundException, "no such index" + defaultTags() + } + } + } + } + + where: + indexName = "invalid-index" + } + + def "test elasticsearch get"() { + expect: + template.createIndex(indexName) + template.getClient().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + + when: + NativeSearchQuery query = new NativeSearchQueryBuilder() + .withIndices(indexName) + .withTypes(indexType) + .withIds([id]) + .build() + + then: + template.queryForIds(query) == [] + + when: + def result = template.index(IndexQueryBuilder.newInstance() + .withObject(new Doc()) + .withIndexName(indexName) + .withType(indexType) + .withId(id) + .build()) + template.refresh(Doc) + + then: + result == id + template.queryForList(query, Doc) == [new Doc()] + + and: + // IndexAction and PutMappingAction run in separate threads and order in which + // these spans are closed is not defined. So we force the order if it is wrong. + if (TEST_WRITER[3][0].resourceName == "IndexAction") { + def tmp = TEST_WRITER[3] + TEST_WRITER[3] = TEST_WRITER[4] + TEST_WRITER[4] = tmp + } + assertTraces(TEST_WRITER, 7) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "CreateIndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "CreateIndexAction" + "elasticsearch.request" "CreateIndexRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.search.types" indexType + defaultTags() + } + } + } + trace(3, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "PutMappingAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "PutMappingAction" + "elasticsearch.request" "PutMappingRequest" + defaultTags() + } + } + } + trace(4, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" indexType + "elasticsearch.request.write.version"(-3) + "elasticsearch.response.status" 201 + "elasticsearch.shard.replication.failed" 0 + "elasticsearch.shard.replication.successful" 1 + "elasticsearch.shard.replication.total" 2 + defaultTags() + } + } + } + trace(5, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "RefreshAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "RefreshAction" + "elasticsearch.request" "RefreshRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.shard.broadcast.failed" 0 + "elasticsearch.shard.broadcast.successful" 5 + "elasticsearch.shard.broadcast.total" 10 + defaultTags() + } + } + } + trace(6, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.search.types" indexType + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + indexType = "test-type" + id = "1" + } + + def "test results extractor"() { + setup: + template.createIndex(indexName) + testNode.client().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + template.index(IndexQueryBuilder.newInstance() + .withObject(new Doc(id: 1, data: "doc a")) + .withIndexName(indexName) + .withId("a") + .build()) + template.index(IndexQueryBuilder.newInstance() + .withObject(new Doc(id: 2, data: "doc b")) + .withIndexName(indexName) + .withId("b") + .build()) + template.refresh(indexName) + TEST_WRITER.waitForTraces(6) + TEST_WRITER.clear() + + and: + def query = new NativeSearchQueryBuilder().withIndices(indexName).build() + def hits = new AtomicLong() + List> results = [] + def bucketTags = [:] + + when: + template.query(query, new ResultsExtractor() { + + @Override + Doc extract(SearchResponse response) { + hits.addAndGet(response.getHits().totalHits()) + results.addAll(response.hits.collect { it.source }) + if (response.getAggregations() != null) { + InternalNested internalNested = response.getAggregations().get("tag") + if (internalNested != null) { + Terms terms = internalNested.getAggregations().get("count_agg") + Collection buckets = terms.getBuckets() + for (Terms.Bucket bucket : buckets) { + bucketTags.put(Integer.valueOf(bucket.getKeyAsString()), bucket.getDocCount()) + } + } + } + return null + } + }) + + then: + hits.get() == 2 + results[0] == [id: "2", data: "doc b"] + results[1] == [id: "1", data: "doc a"] + bucketTags == [:] + + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "SearchAction" + operationName "elasticsearch.query" + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "SearchAction" + "elasticsearch.request" "SearchRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + } + + where: + indexName = "test-index-extract" + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5/src/latestDepTest/groovy/Elasticsearch5NodeClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5/src/latestDepTest/groovy/Elasticsearch5NodeClientTest.groovy new file mode 100644 index 0000000000..33fc461aa1 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5/src/latestDepTest/groovy/Elasticsearch5NodeClientTest.groovy @@ -0,0 +1,303 @@ +import com.anotherchrisberry.spock.extensions.retry.RetryOnFailure +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.TestUtils +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.DDTags +import io.opentracing.tag.Tags +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest +import org.elasticsearch.common.io.FileSystemUtils +import org.elasticsearch.common.settings.Settings +import org.elasticsearch.env.Environment +import org.elasticsearch.index.IndexNotFoundException +import org.elasticsearch.node.Node +import org.elasticsearch.node.internal.InternalSettingsPreparer +import org.elasticsearch.transport.Netty3Plugin +import spock.lang.Shared + +import static datadog.trace.agent.test.TestUtils.runUnderTrace +import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces +import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING + +class Elasticsearch5NodeClientTest extends AgentTestRunner { + public static final long TIMEOUT = 10000; // 10 seconds + + @Shared + int httpPort + @Shared + int tcpPort + @Shared + Node testNode + @Shared + File esWorkingDir + + def client = testNode.client() + + def setupSpec() { + httpPort = TestUtils.randomOpenPort() + tcpPort = TestUtils.randomOpenPort() + + esWorkingDir = File.createTempDir("test-es-working-dir-", "") + esWorkingDir.deleteOnExit() + println "ES work dir: $esWorkingDir" + + def settings = Settings.builder() + .put("path.home", esWorkingDir.path) + // Since we use listeners to close spans this should make our span closing deterministic which is good for tests + .put("thread_pool.listener.size", 1) + .put("http.port", httpPort) + .put("transport.tcp.port", tcpPort) + .put("transport.type", "netty3") + .put("http.type", "netty3") + .put(CLUSTER_NAME_SETTING.getKey(), "test-cluster") + .build() + testNode = new Node(new Environment(InternalSettingsPreparer.prepareSettings(settings)), [Netty3Plugin]) + testNode.start() + runUnderTrace("setup") { + // this may potentially create multiple requests and therefore multiple spans, so we wrap this call + // into a top level trace to get exactly one trace in the result. + testNode.client().admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + } + TEST_WRITER.waitForTraces(1) + } + + def cleanupSpec() { + testNode?.close() + if (esWorkingDir != null) { + FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath()) + esWorkingDir.delete() + } + } + + @RetryOnFailure + def "test elasticsearch status"() { + setup: + def result = client.admin().cluster().health(new ClusterHealthRequest()) + + def status = result.get().status + + expect: + status.name() == "GREEN" + + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + } + } + + @RetryOnFailure + def "test elasticsearch error"() { + when: + client.prepareGet(indexName, indexType, id).get() + + then: + thrown IndexNotFoundException + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + errored true + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + errorTags IndexNotFoundException, "no such index" + defaultTags() + } + } + } + } + + where: + indexName = "invalid-index" + indexType = "test-type" + id = "1" + } + + def "test elasticsearch get"() { + setup: + assert TEST_WRITER == [] + def indexResult = client.admin().indices().prepareCreate(indexName).get() + TEST_WRITER.waitForTraces(1) + + expect: + indexResult.acknowledged + TEST_WRITER.size() == 1 + + when: + client.admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + def emptyResult = client.prepareGet(indexName, indexType, id).get() + + then: + !emptyResult.isExists() + emptyResult.id == id + emptyResult.type == indexType + emptyResult.index == indexName + + when: + def createResult = client.prepareIndex(indexName, indexType, id).setSource([:]).get() + + then: + createResult.id == id + createResult.type == indexType + createResult.index == indexName + createResult.status().status == 201 + + when: + def result = client.prepareGet(indexName, indexType, id).get() + + then: + result.isExists() + result.id == id + result.type == indexType + result.index == indexName + + and: + // IndexAction and PutMappingAction run in separate threads and order in which + // these spans are closed is not defined. So we force the order if it is wrong. + if (TEST_WRITER[3][0].resourceName == "IndexAction") { + def tmp = TEST_WRITER[3] + TEST_WRITER[3] = TEST_WRITER[4] + TEST_WRITER[4] = tmp + } + assertTraces(TEST_WRITER, 6) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "CreateIndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "CreateIndexAction" + "elasticsearch.request" "CreateIndexRequest" + "elasticsearch.request.indices" indexName + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version"(-1) + defaultTags() + } + } + } + trace(3, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "PutMappingAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "PutMappingAction" + "elasticsearch.request" "PutMappingRequest" + defaultTags() + } + } + } + trace(4, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" indexType + "elasticsearch.response.status" 201 + "elasticsearch.shard.replication.total" 2 + "elasticsearch.shard.replication.successful" 1 + "elasticsearch.shard.replication.failed" 0 + defaultTags() + } + } + } + trace(5, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version" 1 + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + indexType = "test-type" + id = "1" + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5/src/latestDepTest/groovy/Elasticsearch5TransportClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5/src/latestDepTest/groovy/Elasticsearch5TransportClientTest.groovy new file mode 100644 index 0000000000..aa129f2754 --- /dev/null +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5/src/latestDepTest/groovy/Elasticsearch5TransportClientTest.groovy @@ -0,0 +1,313 @@ +import com.anotherchrisberry.spock.extensions.retry.RetryOnFailure +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.TestUtils +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.DDTags +import io.opentracing.tag.Tags +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest +import org.elasticsearch.client.transport.TransportClient +import org.elasticsearch.common.io.FileSystemUtils +import org.elasticsearch.common.settings.Settings +import org.elasticsearch.common.transport.InetSocketTransportAddress +import org.elasticsearch.env.Environment +import org.elasticsearch.index.IndexNotFoundException +import org.elasticsearch.node.Node +import org.elasticsearch.node.internal.InternalSettingsPreparer +import org.elasticsearch.transport.Netty3Plugin +import org.elasticsearch.transport.RemoteTransportException +import org.elasticsearch.transport.client.PreBuiltTransportClient +import spock.lang.Shared + +import static datadog.trace.agent.test.TestUtils.runUnderTrace +import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces +import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING + +class Elasticsearch5TransportClientTest extends AgentTestRunner { + public static final long TIMEOUT = 10000; // 10 seconds + + @Shared + int httpPort + @Shared + int tcpPort + @Shared + Node testNode + @Shared + File esWorkingDir + + @Shared + TransportClient client + + def setupSpec() { + httpPort = TestUtils.randomOpenPort() + tcpPort = TestUtils.randomOpenPort() + + esWorkingDir = File.createTempDir("test-es-working-dir-", "") + esWorkingDir.deleteOnExit() + println "ES work dir: $esWorkingDir" + + def settings = Settings.builder() + .put("path.home", esWorkingDir.path) + .put("http.port", httpPort) + .put("transport.tcp.port", tcpPort) + .put("transport.type", "netty3") + .put("http.type", "netty3") + .put(CLUSTER_NAME_SETTING.getKey(), "test-cluster") + .build() + testNode = new Node(new Environment(InternalSettingsPreparer.prepareSettings(settings)), [Netty3Plugin]) + testNode.start() + + client = new PreBuiltTransportClient( + Settings.builder() + // Since we use listeners to close spans this should make our span closing deterministic which is good for tests + .put("thread_pool.listener.size", 1) + .put(CLUSTER_NAME_SETTING.getKey(), "test-cluster") + .build() + ) + client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), tcpPort)) + runUnderTrace("setup") { + // this may potentially create multiple requests and therefore multiple spans, so we wrap this call + // into a top level trace to get exactly one trace in the result. + client.admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet(TIMEOUT) + } + TEST_WRITER.waitForTraces(1) + } + + def cleanupSpec() { + testNode?.close() + if (esWorkingDir != null) { + FileSystemUtils.deleteSubDirectories(esWorkingDir.toPath()) + esWorkingDir.delete() + } + } + + @RetryOnFailure + def "test elasticsearch status"() { + setup: + def result = client.admin().cluster().health(new ClusterHealthRequest()) + + def status = result.get().status + + expect: + status.name() == "GREEN" + + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "ClusterHealthAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + "elasticsearch.action" "ClusterHealthAction" + "elasticsearch.request" "ClusterHealthRequest" + defaultTags() + } + } + } + } + } + + @RetryOnFailure + def "test elasticsearch error"() { + when: + client.prepareGet(indexName, indexType, id).get() + + then: + thrown IndexNotFoundException + + and: + assertTraces(TEST_WRITER, 1) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + errored true + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + errorTags RemoteTransportException, String + defaultTags() + } + } + } + } + + where: + indexName = "invalid-index" + indexType = "test-type" + id = "1" + } + + def "test elasticsearch get"() { + setup: + assert TEST_WRITER == [] + def indexResult = client.admin().indices().prepareCreate(indexName).get() + TEST_WRITER.waitForTraces(1) + + expect: + indexResult.acknowledged + TEST_WRITER.size() == 1 + + when: + def emptyResult = client.prepareGet(indexName, indexType, id).get() + + then: + !emptyResult.isExists() + emptyResult.id == id + emptyResult.type == indexType + emptyResult.index == indexName + + when: + def createResult = client.prepareIndex(indexName, indexType, id).setSource([:]).get() + + then: + createResult.id == id + createResult.type == indexType + createResult.index == indexName + createResult.status().status == 201 + + when: + def result = client.prepareGet(indexName, indexType, id).get() + + then: + result.isExists() + result.id == id + result.type == indexType + result.index == indexName + + and: + // IndexAction and PutMappingAction run in separate threads and order in which + // these spans are closed is not defined. So we force the order if it is wrong. + if (TEST_WRITER[2][0].resourceName == "IndexAction") { + def tmp = TEST_WRITER[2] + TEST_WRITER[2] = TEST_WRITER[3] + TEST_WRITER[3] = tmp + } + assertTraces(TEST_WRITER, 5) { + trace(0, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "CreateIndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "CreateIndexAction" + "elasticsearch.request" "CreateIndexRequest" + "elasticsearch.request.indices" indexName + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + defaultTags() + } + } + } + trace(1, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version"(-1) + defaultTags() + } + } + } + trace(2, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "PutMappingAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "elasticsearch.action" "PutMappingAction" + "elasticsearch.request" "PutMappingRequest" + defaultTags() + } + } + } + trace(3, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "IndexAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + "elasticsearch.action" "IndexAction" + "elasticsearch.request" "IndexRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.request.write.type" indexType + "elasticsearch.response.status" 201 + "elasticsearch.shard.replication.total" 2 + "elasticsearch.shard.replication.successful" 1 + "elasticsearch.shard.replication.failed" 0 + defaultTags() + } + } + } + trace(4, 1) { + span(0) { + serviceName "elasticsearch" + resourceName "GetAction" + operationName "elasticsearch.query" + spanType DDSpanTypes.ELASTICSEARCH + tags { + "$Tags.COMPONENT.key" "elasticsearch-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" tcpPort + "elasticsearch.action" "GetAction" + "elasticsearch.request" "GetRequest" + "elasticsearch.request.indices" indexName + "elasticsearch.type" indexType + "elasticsearch.id" "1" + "elasticsearch.version" 1 + defaultTags() + } + } + } + } + + where: + indexName = "test-index" + indexType = "test-type" + id = "1" + } +} diff --git a/dd-java-agent/instrumentation/elasticsearch-transport-5/src/test/groovy/Elasticsearch5TransportClientTest.groovy b/dd-java-agent/instrumentation/elasticsearch-transport-5/src/test/groovy/Elasticsearch5TransportClientTest.groovy index 7ae37c541c..a47f7c1ac7 100644 --- a/dd-java-agent/instrumentation/elasticsearch-transport-5/src/test/groovy/Elasticsearch5TransportClientTest.groovy +++ b/dd-java-agent/instrumentation/elasticsearch-transport-5/src/test/groovy/Elasticsearch5TransportClientTest.groovy @@ -101,7 +101,7 @@ class Elasticsearch5TransportClientTest extends AgentTestRunner { "$Tags.COMPONENT.key" "elasticsearch-java" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH - "$Tags.PEER_HOSTNAME.key" "127.0.0.1" + "$Tags.PEER_HOSTNAME.key" String "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" "$Tags.PEER_PORT.key" tcpPort "elasticsearch.action" "ClusterHealthAction" @@ -209,7 +209,7 @@ class Elasticsearch5TransportClientTest extends AgentTestRunner { "elasticsearch.action" "CreateIndexAction" "elasticsearch.request" "CreateIndexRequest" "elasticsearch.request.indices" indexName - "$Tags.PEER_HOSTNAME.key" "127.0.0.1" + "$Tags.PEER_HOSTNAME.key" String "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" "$Tags.PEER_PORT.key" tcpPort defaultTags() @@ -226,7 +226,7 @@ class Elasticsearch5TransportClientTest extends AgentTestRunner { "$Tags.COMPONENT.key" "elasticsearch-java" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH - "$Tags.PEER_HOSTNAME.key" "127.0.0.1" + "$Tags.PEER_HOSTNAME.key" String "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" "$Tags.PEER_PORT.key" tcpPort "elasticsearch.action" "GetAction" @@ -265,7 +265,7 @@ class Elasticsearch5TransportClientTest extends AgentTestRunner { "$Tags.COMPONENT.key" "elasticsearch-java" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH - "$Tags.PEER_HOSTNAME.key" "127.0.0.1" + "$Tags.PEER_HOSTNAME.key" String "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" "$Tags.PEER_PORT.key" tcpPort "elasticsearch.action" "IndexAction" @@ -290,7 +290,7 @@ class Elasticsearch5TransportClientTest extends AgentTestRunner { "$Tags.COMPONENT.key" "elasticsearch-java" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$DDTags.SPAN_TYPE" DDSpanTypes.ELASTICSEARCH - "$Tags.PEER_HOSTNAME.key" "127.0.0.1" + "$Tags.PEER_HOSTNAME.key" String "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" "$Tags.PEER_PORT.key" tcpPort "elasticsearch.action" "GetAction"