From 9a9da92b590c247fd8a1804227af891e01cf1de3 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Wed, 23 Aug 2017 17:03:27 -0700 Subject: [PATCH] Add version checking via class/method for jetty --- .../src/main/groovy/VersionScanPlugin.groovy | 6 ++ dd-java-agent/integrations/jetty/jetty.gradle | 13 ++++ .../trace/agent/InstrumentationChecker.java | 44 +++++++++--- .../dd-trace-supported-framework.yaml | 71 ++++++++++--------- .../agent/InstrumentationCheckerTest.groovy | 4 +- .../resources/supported-version-test.yaml | 20 +++--- settings.gradle | 1 + 7 files changed, 103 insertions(+), 56 deletions(-) create mode 100644 dd-java-agent/integrations/jetty/jetty.gradle diff --git a/buildSrc/src/main/groovy/VersionScanPlugin.groovy b/buildSrc/src/main/groovy/VersionScanPlugin.groovy index 13174602ec..dea9e3073c 100644 --- a/buildSrc/src/main/groovy/VersionScanPlugin.groovy +++ b/buildSrc/src/main/groovy/VersionScanPlugin.groovy @@ -139,6 +139,12 @@ class VersionScanPlugin implements Plugin { def errors = [] for (String className : verifyPresent.keySet()) { + if (project.versionScan.scanMethods && verifyPresent.get(className) == null) { + throw new AssertionError("When 'scanMethods' is enabled, a method must be configured for '$className'") + } else if (!project.versionScan.scanMethods && verifyPresent.get(className) != null) { + throw new AssertionError("When 'scanMethods' is not enabled, configured method must be null for '$className'") + } + String identifier = project.versionScan.scanMethods ? "$className|${verifyPresent.get(className)}" : className if (!keyPresent.get().contains(identifier)) { errors << "not a 'keyPresent' identifier: $identifier" diff --git a/dd-java-agent/integrations/jetty/jetty.gradle b/dd-java-agent/integrations/jetty/jetty.gradle new file mode 100644 index 0000000000..cf84c809fd --- /dev/null +++ b/dd-java-agent/integrations/jetty/jetty.gradle @@ -0,0 +1,13 @@ +apply plugin: 'version-scan' + +versionScan { + group = "org.eclipse.jetty" + module = "jetty-server" + versions = "[8.0,)" + legacyGroup = "org.mortbay.jetty" + legacyModule = "jetty" + scanMethods = true + verifyPresent = [ + "org.eclipse.jetty.server.ServletRequestHttpWrapper": "getPart", + ] +} diff --git a/dd-java-agent/src/main/java/com/datadoghq/trace/agent/InstrumentationChecker.java b/dd-java-agent/src/main/java/com/datadoghq/trace/agent/InstrumentationChecker.java index 603b63bdec..a236f064d4 100644 --- a/dd-java-agent/src/main/java/com/datadoghq/trace/agent/InstrumentationChecker.java +++ b/dd-java-agent/src/main/java/com/datadoghq/trace/agent/InstrumentationChecker.java @@ -4,6 +4,7 @@ import com.datadoghq.trace.resolver.FactoryUtils; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; import java.io.File; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -131,11 +132,33 @@ public class InstrumentationChecker { log.debug("Checking rule {}", check); boolean matched = true; - for (final String identifyingClass : check.identifyingPresentClasses) { - final boolean classPresent = isClassPresent(identifyingClass); + for (final Map.Entry identifier : + check.identifyingPresentClasses.entrySet()) { + final boolean classPresent = isClassPresent(identifier.getKey()); if (!classPresent) { - log.debug( - "Instrumentation {} not applied due to missing class {}.", rule, identifyingClass); + log.debug("Instrumentation {} not applied due to missing class {}.", rule, identifier); + } else { + String identifyingMethod = identifier.getValue(); + if (identifyingMethod != null && !identifyingMethod.isEmpty()) { + Class clazz = getClassIfPresent(identifier.getKey(), classLoader); + // already confirmed above the class is there. + Method[] declaredMethods = clazz.getDeclaredMethods(); + boolean methodFound = false; + for (Method m : declaredMethods) { + if (m.getName().equals(identifyingMethod)) { + methodFound = true; + break; + } + } + if (!methodFound) { + log.debug( + "Instrumentation {} not applied due to missing method {}.{}", + rule, + identifier.getKey(), + identifyingMethod); + matched = false; + } + } } matched &= classPresent; } @@ -183,11 +206,14 @@ public class InstrumentationChecker { } static boolean isClassPresent(final String identifyingPresentClass, ClassLoader classLoader) { + return getClassIfPresent(identifyingPresentClass, classLoader) != null; + } + + static Class getClassIfPresent(final String identifyingPresentClass, ClassLoader classLoader) { try { - return identifyingPresentClass != null - && Class.forName(identifyingPresentClass, false, classLoader) != null; - } catch (final ClassNotFoundException e) { - return false; + return Class.forName(identifyingPresentClass, false, classLoader); + } catch (final Exception e) { + return null; } } @@ -199,7 +225,7 @@ public class InstrumentationChecker { private String supportedVersion; @JsonProperty("identifying_present_classes") - private List identifyingPresentClasses = Collections.emptyList(); + private Map identifyingPresentClasses = Collections.emptyMap(); @JsonProperty("identifying_missing_classes") private List identifyingMissingClasses = Collections.emptyList(); diff --git a/dd-java-agent/src/main/resources/dd-trace-supported-framework.yaml b/dd-java-agent/src/main/resources/dd-trace-supported-framework.yaml index 732c90e485..951c324a0b 100644 --- a/dd-java-agent/src/main/resources/dd-trace-supported-framework.yaml +++ b/dd-java-agent/src/main/resources/dd-trace-supported-framework.yaml @@ -1,18 +1,18 @@ ### This file define all supported libraries ### Syntax: -### : # the rule name defined in the otarules.btm -### - artifact: # the artifact name to be tested -### supported_version: # a regex expression to express the version required by the rule -### identifying_present_classes: # a list of classes that distinctly identify the range of supported libraries -### - some.key.FrameworkClass # only if missing, supported_version regex is used. +### : # the rule name defined in the otarules.btm +### - artifact: # the artifact name to be tested +### supported_version: # a regex expression to express the version required by the rule +### identifying_present_classes: # a list of classes that distinctly identify the range of supported libraries +### some.key.FrameworkClass: orMethod # only if missing, supported_version regex is used. ### - ... opentracing-apache-httpclient: - artifact: httpclient supported_version: 4\.[3|4|5]\..* identifying_present_classes: - - org.apache.http.conn.SchemePortResolver - - org.apache.http.conn.ssl.SSLContexts + org.apache.http.conn.SchemePortResolver: + org.apache.http.conn.ssl.SSLContexts: - artifact: commons-httpclient supported_version: none @@ -21,82 +21,83 @@ opentracing-aws-sdk: - artifact: aws-java-sdk supported_version: 1\.11\..* identifying_present_classes: - - com.amazonaws.http.client.HttpClientFactory - - com.amazonaws.http.apache.utils.ApacheUtils - - com.amazonaws.http.request.HttpRequestFactory + com.amazonaws.http.client.HttpClientFactory: + com.amazonaws.http.apache.utils.ApacheUtils: + com.amazonaws.http.request.HttpRequestFactory: opentracing-cassandra-driver: - artifact: cassandra-driver-core supported_version: 3\.2.* identifying_present_classes: - - com.datastax.driver.core.utils.MoreObjects - - com.datastax.driver.core.RemoteEndpointAwareNettySSLOptions - - com.datastax.driver.core.GuavaCompatibility + com.datastax.driver.core.utils.MoreObjects: + com.datastax.driver.core.RemoteEndpointAwareNettySSLOptions: + com.datastax.driver.core.GuavaCompatibility: opentracing-jms-2_producer: - artifact: javax.jms-api supported_version: 2\..* identifying_present_classes: - - javax.jms.JMSContext - - javax.jms.CompletionListener + javax.jms.JMSContext: + javax.jms.CompletionListener: opentracing-jms-2_consumer: - artifact: javax.jms-api supported_version: 2\..* identifying_present_classes: - - javax.jms.JMSContext - - javax.jms.CompletionListener + javax.jms.JMSContext: + javax.jms.CompletionListener: opentracing-mongo-driver: - artifact: mongo-java-driver supported_version: 3\..* identifying_present_classes: - - com.mongodb.operation.AsyncReadOperation - - com.mongodb.client.model.MapReduceAction + com.mongodb.operation.AsyncReadOperation: + com.mongodb.client.model.MapReduceAction: - artifact: mongodb-driver-async supported_version: 3\..* identifying_present_classes: - - com.mongodb.operation.AsyncReadOperation - - com.mongodb.client.model.MapReduceAction + com.mongodb.operation.AsyncReadOperation: + com.mongodb.client.model.MapReduceAction: opentracing-mongo-driver-helper: - artifact: mongo-java-driver supported_version: 3\..* identifying_present_classes: - - com.mongodb.operation.AsyncReadOperation - - com.mongodb.client.model.MapReduceAction + com.mongodb.operation.AsyncReadOperation: + com.mongodb.client.model.MapReduceAction: - artifact: mongodb-driver-async supported_version: 3\..* identifying_present_classes: - - com.mongodb.operation.AsyncReadOperation - - com.mongodb.client.model.MapReduceAction + com.mongodb.operation.AsyncReadOperation: + com.mongodb.client.model.MapReduceAction: opentracing-okhttp3: - artifact: okhttp supported_version: 3\..* identifying_present_classes: - - okhttp3.Cookie - - okhttp3.ConnectionPool - - okhttp3.Headers + okhttp3.Cookie: + okhttp3.ConnectionPool: + okhttp3.Headers: opentracing-web-servlet-filter_jetty: - artifact: jetty-server supported_version: (8\.|9\.).* -# identifying_present_classes: + identifying_present_classes: + org.eclipse.jetty.server.ServletRequestHttpWrapper: getPart opentracing-web-servlet-filter_tomcat: - artifact: opentracing-web-servlet-filter_tomcat supported_version: (8\.|9\.).* identifying_present_classes: - - org.apache.catalina.WebResource - - org.apache.catalina.webresources.TrackedInputStream - - org.apache.catalina.webresources.AbstractArchiveResource + org.apache.catalina.WebResource: + org.apache.catalina.webresources.TrackedInputStream: + org.apache.catalina.webresources.AbstractArchiveResource: - artifact: opentracing-web-servlet-filter_tomcat supported_version: (8\.|9\.).* identifying_present_classes: - - org.apache.catalina.WebResource - - org.apache.catalina.webresources.TrackedInputStream - - org.apache.catalina.webresources.AbstractArchiveResource + org.apache.catalina.WebResource: + org.apache.catalina.webresources.TrackedInputStream: + org.apache.catalina.webresources.AbstractArchiveResource: diff --git a/dd-java-agent/src/test/groovy/com/datadoghq/trace/agent/InstrumentationCheckerTest.groovy b/dd-java-agent/src/test/groovy/com/datadoghq/trace/agent/InstrumentationCheckerTest.groovy index c33b08bf23..7d72db0446 100644 --- a/dd-java-agent/src/test/groovy/com/datadoghq/trace/agent/InstrumentationCheckerTest.groovy +++ b/dd-java-agent/src/test/groovy/com/datadoghq/trace/agent/InstrumentationCheckerTest.groovy @@ -24,7 +24,9 @@ class InstrumentationCheckerTest extends Specification { rules.sort() == ["unsupportedRuleOne", "unsupportedRuleThree", "unsupportedRuleTwo"] } - static class DemoClass1 {} + static class DemoClass1 { + void testMethod(String arg) {} + } static class DemoClass2 {} diff --git a/dd-java-agent/src/test/resources/supported-version-test.yaml b/dd-java-agent/src/test/resources/supported-version-test.yaml index 4807555539..e73eaf9a92 100644 --- a/dd-java-agent/src/test/resources/supported-version-test.yaml +++ b/dd-java-agent/src/test/resources/supported-version-test.yaml @@ -4,38 +4,36 @@ unsupportedRuleOne: - artifact: artifact-2 supported_version: none - identifying_present_classes: - - unsupportedRuleTwo: - artifact: artifact-1 supported_version: 2\.3\. identifying_present_classes: - - com.datadoghq.trace.agent.InstrumentationCheckerTest$MissingClass + com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1: missingMethod supportedRuleOne: - artifact: artifact-3 supported_version: 5\..* identifying_present_classes: - - com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1 - - com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass2 - - com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass3 + com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1: + com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass2: + com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass3: supportedRuleTwo: - artifact: artifact-1 supported_version: 1\.2\.3\..* identifying_present_classes: - - com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1 - - com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass2 + com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1: testMethod + com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass2: - artifact: artifact-2 supported_version: 4\..* identifying_present_classes: - - com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass3 + com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass3: missingMethod unsupportedRuleThree: - artifact: foo supported_version: 1 identifying_present_classes: - - com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1 - - com.datadoghq.trace.agent.InstrumentationCheckerTest$MissingClass + com.datadoghq.trace.agent.InstrumentationCheckerTest$DemoClass1: + com.datadoghq.trace.agent.InstrumentationCheckerTest$MissingClass: diff --git a/settings.gradle b/settings.gradle index 29ff4626e0..62f6b36990 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,6 +14,7 @@ include ':dd-trace-annotations' include ':dd-java-agent:integrations:apache-httpclient' include ':dd-java-agent:integrations:aws-sdk' include ':dd-java-agent:integrations:cassandra' +include ':dd-java-agent:integrations:jetty' include ':dd-java-agent:integrations:jms' include ':dd-java-agent:integrations:mongo' include ':dd-java-agent:integrations:mongo-async'