Remove old muzzle check plugin (#3741)
* Remove old muzzle check plugin * Polish
This commit is contained in:
parent
7634394664
commit
6e706778fd
|
@ -29,7 +29,8 @@ dependencies {
|
|||
implementation(gradleApi())
|
||||
implementation(localGroovy())
|
||||
|
||||
implementation("io.opentelemetry.instrumentation.muzzle-generation:io.opentelemetry.instrumentation.muzzle-generation.gradle.plugin:0.1.0-SNAPSHOT")
|
||||
implementation("io.opentelemetry.instrumentation.muzzle-generation:io.opentelemetry.instrumentation.muzzle-generation.gradle.plugin:0.2.0-SNAPSHOT")
|
||||
implementation("io.opentelemetry.instrumentation.muzzle-check:io.opentelemetry.instrumentation.muzzle-check.gradle.plugin:0.2.0-SNAPSHOT")
|
||||
|
||||
implementation("org.eclipse.aether:aether-connector-basic:1.1.0")
|
||||
implementation("org.eclipse.aether:aether-transport-http:1.1.0")
|
||||
|
|
|
@ -1,359 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.gradle.muzzle.AcceptableVersions
|
||||
import io.opentelemetry.instrumentation.gradle.muzzle.BogusClassLoader
|
||||
import io.opentelemetry.instrumentation.gradle.muzzle.MuzzleDirective
|
||||
import io.opentelemetry.instrumentation.gradle.muzzle.MuzzleExtension
|
||||
import org.apache.maven.repository.internal.MavenRepositorySystemUtils
|
||||
import org.eclipse.aether.RepositorySystem
|
||||
import org.eclipse.aether.RepositorySystemSession
|
||||
import org.eclipse.aether.artifact.Artifact
|
||||
import org.eclipse.aether.artifact.DefaultArtifact
|
||||
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory
|
||||
import org.eclipse.aether.repository.LocalRepository
|
||||
import org.eclipse.aether.repository.RemoteRepository
|
||||
import org.eclipse.aether.resolution.VersionRangeRequest
|
||||
import org.eclipse.aether.resolution.VersionRangeResult
|
||||
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory
|
||||
import org.eclipse.aether.spi.connector.transport.TransporterFactory
|
||||
import org.eclipse.aether.transport.http.HttpTransporterFactory
|
||||
import org.eclipse.aether.version.Version
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.util.stream.StreamSupport
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
// Select a random set of versions to test
|
||||
val RANGE_COUNT_LIMIT = 10
|
||||
|
||||
val muzzleConfig = extensions.create<MuzzleExtension>("muzzle")
|
||||
|
||||
val muzzleTooling by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
val muzzleBootstrap by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
val compileMuzzle by tasks.registering {
|
||||
dependsOn(muzzleBootstrap)
|
||||
dependsOn(muzzleTooling)
|
||||
dependsOn(tasks.named("classes"))
|
||||
}
|
||||
|
||||
val muzzle by tasks.registering {
|
||||
group = "Muzzle"
|
||||
description = "Run instrumentation muzzle on compile time dependencies"
|
||||
dependsOn(compileMuzzle)
|
||||
}
|
||||
|
||||
tasks.register("printMuzzleReferences") {
|
||||
group = "Muzzle"
|
||||
description = "Print references created by instrumentation muzzle"
|
||||
dependsOn(compileMuzzle)
|
||||
doLast {
|
||||
val instrumentationCL = createInstrumentationClassloader()
|
||||
val assertionMethod = instrumentationCL
|
||||
.loadClass(
|
||||
"io.opentelemetry.javaagent.tooling.muzzle.matcher.MuzzleGradlePluginUtil")
|
||||
.getMethod("printMuzzleReferences", ClassLoader::class.java)
|
||||
assertionMethod.invoke(null, instrumentationCL)
|
||||
}
|
||||
}
|
||||
|
||||
val projectRepositories = mutableListOf<RemoteRepository>().apply {
|
||||
// Manually add mavenCentral until https://github.com/gradle/gradle/issues/17295
|
||||
// Adding mavenLocal is much more complicated but hopefully isn't required for normal usage of
|
||||
// Muzzle.
|
||||
add(
|
||||
RemoteRepository.Builder(
|
||||
"MavenCentral", "default", "https://repo.maven.apache.org/maven2/")
|
||||
.build())
|
||||
for (repository in repositories) {
|
||||
if (repository is MavenArtifactRepository) {
|
||||
add(
|
||||
RemoteRepository.Builder(
|
||||
repository.getName(),
|
||||
"default",
|
||||
repository.url.toString())
|
||||
.build())
|
||||
}
|
||||
}
|
||||
}.toList()
|
||||
|
||||
val hasRelevantTask = gradle.startParameter.taskNames.any {
|
||||
// removing leading ':' if present
|
||||
val taskName = it.removePrefix(":")
|
||||
val projectPath = project.path.substring(1)
|
||||
// Either the specific muzzle task in this project or the top level, full-project
|
||||
// muzzle task.
|
||||
// Either the specific muzzle task in this project or the top level, full-project
|
||||
// muzzle task.
|
||||
taskName == "${projectPath}:muzzle" || taskName == "muzzle"
|
||||
}
|
||||
|
||||
if (hasRelevantTask) {
|
||||
val system = newRepositorySystem()
|
||||
val session = newRepositorySystemSession(system)
|
||||
|
||||
afterEvaluate {
|
||||
var runAfter = muzzle
|
||||
|
||||
for (muzzleDirective in muzzleConfig.directives.get()) {
|
||||
logger.info("configured ${muzzleDirective}")
|
||||
|
||||
if (muzzleDirective.coreJdk.get()) {
|
||||
runAfter = addMuzzleTask(muzzleDirective, null, runAfter)
|
||||
} else {
|
||||
for (singleVersion in muzzleDirectiveToArtifacts(muzzleDirective, system, session)) {
|
||||
runAfter = addMuzzleTask(muzzleDirective, singleVersion, runAfter)
|
||||
}
|
||||
if (muzzleDirective.assertInverse.get()) {
|
||||
for (inverseDirective in inverseOf(muzzleDirective, system, session)) {
|
||||
for (singleVersion in muzzleDirectiveToArtifacts(inverseDirective, system, session)) {
|
||||
runAfter = addMuzzleTask(inverseDirective, singleVersion, runAfter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createInstrumentationClassloader(): ClassLoader {
|
||||
logger.info("Creating instrumentation classpath for: ${name}")
|
||||
val runtimeClasspath = sourceSets.main.get().runtimeClasspath
|
||||
return classpathLoader(runtimeClasspath, createMuzzleCheckLoader())
|
||||
}
|
||||
|
||||
fun classpathLoader(classpath: FileCollection, parent: ClassLoader): ClassLoader {
|
||||
val urls: Array<URL> = StreamSupport.stream(classpath.spliterator(), false)
|
||||
.map {
|
||||
logger.info("--${it}")
|
||||
it.toURI().toURL()
|
||||
}
|
||||
.toArray(::arrayOfNulls)
|
||||
return URLClassLoader(urls, parent)
|
||||
}
|
||||
|
||||
fun createMuzzleCheckLoader(): ClassLoader {
|
||||
logger.info("creating classpath for auto-tooling")
|
||||
return classpathLoader(muzzleTooling, ClassLoader.getPlatformClassLoader())
|
||||
}
|
||||
|
||||
fun newRepositorySystem(): RepositorySystem {
|
||||
return MavenRepositorySystemUtils.newServiceLocator().apply {
|
||||
addService(RepositoryConnectorFactory::class.java, BasicRepositoryConnectorFactory::class.java)
|
||||
addService(TransporterFactory::class.java, HttpTransporterFactory::class.java)
|
||||
}.run {
|
||||
getService(RepositorySystem::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun newRepositorySystemSession(system: RepositorySystem): RepositorySystemSession {
|
||||
val muzzleRepo = file("${buildDir}/muzzleRepo")
|
||||
val localRepo = LocalRepository(muzzleRepo)
|
||||
return MavenRepositorySystemUtils.newSession().apply {
|
||||
setLocalRepositoryManager(system.newLocalRepositoryManager(this, localRepo))
|
||||
}
|
||||
}
|
||||
|
||||
fun addMuzzleTask(muzzleDirective: MuzzleDirective, versionArtifact: Artifact?, runAfter: TaskProvider<Task>)
|
||||
: TaskProvider<Task> {
|
||||
val taskName = if (versionArtifact == null) {
|
||||
"muzzle-Assert${muzzleDirective}"
|
||||
} else {
|
||||
StringBuilder("muzzle-Assert").apply {
|
||||
if (muzzleDirective.assertPass.get()) {
|
||||
append("Pass")
|
||||
} else {
|
||||
append("Fail")
|
||||
}
|
||||
append('-')
|
||||
.append(versionArtifact.groupId)
|
||||
.append('-')
|
||||
.append(versionArtifact.artifactId)
|
||||
.append('-')
|
||||
.append(versionArtifact.version)
|
||||
if (!muzzleDirective.name.get().isEmpty()) {
|
||||
append(muzzleDirective.nameSlug)
|
||||
}
|
||||
}.run { toString() }
|
||||
}
|
||||
val config = configurations.create(taskName)
|
||||
if (versionArtifact != null) {
|
||||
val dep = (dependencies.create(versionArtifact.run { "${groupId}:${artifactId}:${version}" }) as ModuleDependency).apply {
|
||||
isTransitive = true
|
||||
exclude("com.sun.jdmk", "jmxtools")
|
||||
exclude("com.sun.jmx", "jmxri")
|
||||
for (excluded in muzzleDirective.excludedDependencies.get()) {
|
||||
val (group, module) = excluded.split(':')
|
||||
exclude(group, module)
|
||||
}
|
||||
}
|
||||
config.dependencies.add(dep)
|
||||
|
||||
for (additionalDependency in muzzleDirective.additionalDependencies.get()) {
|
||||
val additional = if (additionalDependency.count { it == ':' } < 2) {
|
||||
// Dependency definition without version, use the artifact's version.
|
||||
"${additionalDependency}:${versionArtifact.version}"
|
||||
} else {
|
||||
additionalDependency
|
||||
}
|
||||
val additionalDep = (dependencies.create(additional) as ModuleDependency).apply {
|
||||
isTransitive = true
|
||||
}
|
||||
config.dependencies.add(additionalDep)
|
||||
}
|
||||
}
|
||||
|
||||
val muzzleTask = tasks.register(taskName) {
|
||||
dependsOn(configurations.named("runtimeClasspath"))
|
||||
doLast {
|
||||
val instrumentationCL = createInstrumentationClassloader()
|
||||
val ccl = Thread.currentThread().contextClassLoader
|
||||
val bogusLoader = BogusClassLoader()
|
||||
val userCL = createClassLoaderForTask(config)
|
||||
Thread.currentThread().contextClassLoader = bogusLoader
|
||||
try {
|
||||
// find all instrumenters, get muzzle, and assert
|
||||
val assertionMethod = instrumentationCL
|
||||
.loadClass("io.opentelemetry.javaagent.tooling.muzzle.matcher.MuzzleGradlePluginUtil")
|
||||
.getMethod(
|
||||
"assertInstrumentationMuzzled",
|
||||
ClassLoader::class.java,
|
||||
ClassLoader::class.java,
|
||||
Boolean::class.javaPrimitiveType)
|
||||
assertionMethod.invoke(
|
||||
null,
|
||||
instrumentationCL,
|
||||
userCL,
|
||||
muzzleDirective.assertPass.get())
|
||||
} finally {
|
||||
Thread.currentThread().contextClassLoader = ccl
|
||||
}
|
||||
|
||||
for (thread in Thread.getAllStackTraces().keys) {
|
||||
if (thread.contextClassLoader === bogusLoader
|
||||
|| thread.contextClassLoader === instrumentationCL
|
||||
|| thread.contextClassLoader === userCL) {
|
||||
throw GradleException(
|
||||
"Task ${taskName} has spawned a thread: ${thread} with classloader ${thread.contextClassLoader}. " +
|
||||
"This will prevent GC of dynamic muzzle classes. Aborting muzzle run.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runAfter.configure { finalizedBy(muzzleTask) }
|
||||
return muzzleTask
|
||||
}
|
||||
|
||||
fun createClassLoaderForTask(muzzleTaskConfiguration: Configuration): ClassLoader {
|
||||
val userUrls = objects.fileCollection()
|
||||
logger.info("Creating task classpath")
|
||||
userUrls.from(muzzleTaskConfiguration.resolvedConfiguration.files)
|
||||
return classpathLoader(userUrls.plus(muzzleBootstrap), ClassLoader.getPlatformClassLoader())
|
||||
}
|
||||
|
||||
fun inverseOf(muzzleDirective: MuzzleDirective, system: RepositorySystem, session: RepositorySystemSession): Set<MuzzleDirective> {
|
||||
val inverseDirectives = mutableSetOf<MuzzleDirective>()
|
||||
|
||||
val allVersionsArtifact = DefaultArtifact(
|
||||
muzzleDirective.group.get(),
|
||||
muzzleDirective.module.get(),
|
||||
muzzleDirective.classifier.get(),
|
||||
"jar",
|
||||
"[,)")
|
||||
val directiveArtifact = DefaultArtifact(
|
||||
muzzleDirective.group.get(),
|
||||
muzzleDirective.module.get(),
|
||||
muzzleDirective.classifier.get(),
|
||||
"jar",
|
||||
muzzleDirective.versions.get())
|
||||
|
||||
val repos = projectRepositories
|
||||
val allRangeRequest = VersionRangeRequest().apply {
|
||||
repositories = repos
|
||||
artifact = allVersionsArtifact
|
||||
}
|
||||
val allRangeResult = system.resolveVersionRange(session, allRangeRequest)
|
||||
|
||||
val rangeRequest = VersionRangeRequest().apply {
|
||||
repositories = repos
|
||||
artifact = directiveArtifact
|
||||
}
|
||||
val rangeResult = system.resolveVersionRange(session, rangeRequest)
|
||||
|
||||
allRangeResult.versions.removeAll(rangeResult.versions)
|
||||
|
||||
for (version in filterVersions(allRangeResult, muzzleDirective.normalizedSkipVersions)) {
|
||||
val inverseDirective = objects.newInstance(MuzzleDirective::class).apply {
|
||||
group.set(muzzleDirective.group)
|
||||
module.set(muzzleDirective.module)
|
||||
classifier.set(muzzleDirective.classifier)
|
||||
versions.set(version)
|
||||
assertPass.set(!muzzleDirective.assertPass.get())
|
||||
excludedDependencies.set(muzzleDirective.excludedDependencies)
|
||||
}
|
||||
inverseDirectives.add(inverseDirective)
|
||||
}
|
||||
|
||||
return inverseDirectives
|
||||
}
|
||||
|
||||
fun filterVersions(range: VersionRangeResult, skipVersions: Set<String>) = sequence {
|
||||
val predicate = AcceptableVersions(skipVersions)
|
||||
if (predicate.test(range.lowestVersion)) {
|
||||
yield(range.lowestVersion.toString())
|
||||
}
|
||||
if (predicate.test(range.highestVersion)) {
|
||||
yield(range.highestVersion.toString())
|
||||
}
|
||||
|
||||
val copy: List<Version> = range.versions.shuffled()
|
||||
for (version in copy) {
|
||||
if (predicate.test(version)) {
|
||||
yield(version.toString())
|
||||
}
|
||||
}
|
||||
}.distinct().take(RANGE_COUNT_LIMIT)
|
||||
|
||||
fun muzzleDirectiveToArtifacts(muzzleDirective: MuzzleDirective, system: RepositorySystem, session: RepositorySystemSession) = sequence<Artifact> {
|
||||
val directiveArtifact: Artifact = DefaultArtifact(
|
||||
muzzleDirective.group.get(),
|
||||
muzzleDirective.module.get(),
|
||||
muzzleDirective.classifier.get(),
|
||||
"jar",
|
||||
muzzleDirective.versions.get())
|
||||
|
||||
val rangeRequest = VersionRangeRequest().apply {
|
||||
repositories = projectRepositories
|
||||
artifact = directiveArtifact
|
||||
}
|
||||
val rangeResult = system.resolveVersionRange(session, rangeRequest)
|
||||
|
||||
val allVersionArtifacts = filterVersions(rangeResult, muzzleDirective.normalizedSkipVersions)
|
||||
.map {
|
||||
DefaultArtifact(
|
||||
muzzleDirective.group.get(),
|
||||
muzzleDirective.module.get(),
|
||||
muzzleDirective.classifier.get(),
|
||||
"jar",
|
||||
it)
|
||||
}
|
||||
|
||||
allVersionArtifacts.ifEmpty {
|
||||
throw GradleException("No muzzle artifacts found for $muzzleDirective")
|
||||
}
|
||||
|
||||
yieldAll(allVersionArtifacts)
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.gradle.muzzle
|
||||
|
||||
import org.eclipse.aether.version.Version
|
||||
import java.util.Locale
|
||||
import java.util.function.Predicate
|
||||
|
||||
internal class AcceptableVersions(private val skipVersions: Collection<String>) :
|
||||
Predicate<Version?> {
|
||||
|
||||
override fun test(version: Version?): Boolean {
|
||||
if (version == null) {
|
||||
return false
|
||||
}
|
||||
val versionString = version.toString().toLowerCase(Locale.ROOT)
|
||||
if (skipVersions.contains(versionString)) {
|
||||
return false
|
||||
}
|
||||
val draftVersion = versionString.contains("rc")
|
||||
|| versionString.contains(".cr")
|
||||
|| versionString.contains("alpha")
|
||||
|| versionString.contains("beta")
|
||||
|| versionString.contains("-b")
|
||||
|| versionString.contains(".m")
|
||||
|| versionString.contains("-m")
|
||||
|| versionString.contains("-dev")
|
||||
|| versionString.contains("-ea")
|
||||
|| versionString.contains("-atlassian-")
|
||||
|| versionString.contains("public_draft")
|
||||
|| versionString.contains("snapshot")
|
||||
|| GIT_SHA_PATTERN.matches(versionString)
|
||||
return !draftVersion
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val GIT_SHA_PATTERN = Regex("^.*-[0-9a-f]{7,}$")
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.gradle.muzzle
|
||||
|
||||
import java.security.SecureClassLoader
|
||||
|
||||
internal class BogusClassLoader : SecureClassLoader() {
|
||||
override fun toString(): String {
|
||||
return "bogus"
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.gradle.muzzle
|
||||
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.SetProperty
|
||||
import java.util.stream.Collectors
|
||||
|
||||
@Deprecated("Should be removed when we start using external muzzle check plugin")
|
||||
abstract class MuzzleDirective {
|
||||
|
||||
abstract val name: Property<String>
|
||||
abstract val group: Property<String>
|
||||
abstract val module: Property<String>
|
||||
abstract val classifier: Property<String>
|
||||
abstract val versions: Property<String>
|
||||
abstract val skipVersions: SetProperty<String>
|
||||
abstract val additionalDependencies: ListProperty<String>
|
||||
abstract val excludedDependencies: ListProperty<String>
|
||||
abstract val assertPass: Property<Boolean>
|
||||
abstract val assertInverse: Property<Boolean>
|
||||
internal abstract val coreJdk: Property<Boolean> // use coreJdk() function below to enable
|
||||
|
||||
init {
|
||||
name.convention("")
|
||||
classifier.convention("")
|
||||
skipVersions.convention(emptySet())
|
||||
additionalDependencies.convention(listOf())
|
||||
excludedDependencies.convention(listOf())
|
||||
assertPass.convention(false)
|
||||
assertInverse.convention(false)
|
||||
coreJdk.convention(false)
|
||||
}
|
||||
|
||||
fun coreJdk() {
|
||||
coreJdk.set(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds extra dependencies to the current muzzle test.
|
||||
*
|
||||
* @param compileString An extra dependency in the gradle canonical form:
|
||||
* '<group_id>:<artifact_id>:<version_id>'.
|
||||
*/
|
||||
fun extraDependency(compileString: String?) {
|
||||
additionalDependencies.add(compileString!!)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds transitive dependencies to exclude from the current muzzle test.
|
||||
*
|
||||
* @param excludeString A dependency in the gradle canonical form: '<group_id>:<artifact_id>'
|
||||
*/
|
||||
fun excludeDependency(excludeString: String?) {
|
||||
excludedDependencies.add(excludeString!!)
|
||||
}
|
||||
|
||||
fun skip(vararg version: String?) {
|
||||
skipVersions.addAll(*version)
|
||||
}
|
||||
|
||||
internal val nameSlug: String
|
||||
get() = NORMALIZE_NAME_SLUG.replace(name.get().trim(), "-")
|
||||
|
||||
internal val normalizedSkipVersions: Set<String>
|
||||
get() = skipVersions.getOrElse(setOf()).stream()
|
||||
.map(String::toLowerCase)
|
||||
.collect(Collectors.toSet())
|
||||
|
||||
override fun toString(): String {
|
||||
val sb = StringBuilder()
|
||||
if (coreJdk.getOrElse(false)) {
|
||||
if (assertPass.getOrElse(false)) {
|
||||
sb.append("Pass")
|
||||
} else {
|
||||
sb.append("Fail")
|
||||
}
|
||||
sb.append("-core-jdk")
|
||||
} else {
|
||||
if (assertPass.getOrElse(false)) {
|
||||
sb.append("pass")
|
||||
} else {
|
||||
sb.append("fail")
|
||||
}
|
||||
sb.append(group.get())
|
||||
.append(':')
|
||||
.append(module.get())
|
||||
.append(':')
|
||||
.append(versions.get())
|
||||
if (classifier.isPresent) {
|
||||
sb.append(':').append(classifier.get())
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val NORMALIZE_NAME_SLUG = Regex("[^a-zA-Z0-9]+")
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.gradle.muzzle
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import javax.inject.Inject
|
||||
|
||||
@Deprecated("Should be removed when we start using external muzzle check plugin")
|
||||
abstract class MuzzleExtension @Inject constructor(private val objectFactory: ObjectFactory) {
|
||||
|
||||
internal abstract val directives: ListProperty<MuzzleDirective>
|
||||
|
||||
fun pass(action: Action<MuzzleDirective>) {
|
||||
val pass = objectFactory.newInstance(MuzzleDirective::class.java)
|
||||
action.execute(pass)
|
||||
pass.assertPass.set(true)
|
||||
directives.add(pass)
|
||||
}
|
||||
|
||||
fun fail(action: Action<MuzzleDirective>) {
|
||||
val fail = objectFactory.newInstance(MuzzleDirective::class.java)
|
||||
action.execute(fail)
|
||||
fail.assertPass.set(false)
|
||||
directives.add(fail)
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.gradle.muzzle;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
import org.eclipse.aether.version.Version;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class MuzzlePluginTest {
|
||||
|
||||
@Test
|
||||
void rangeRequest() {
|
||||
AcceptableVersions predicate = new AcceptableVersions(Collections.emptyList());
|
||||
|
||||
assertThat(predicate.test(new TestVersion("10.1.0-rc2+19-8e20bb26"))).isFalse();
|
||||
assertThat(predicate.test(new TestVersion("2.4.5.BUILD-SNAPSHOT"))).isFalse();
|
||||
}
|
||||
|
||||
static class TestVersion implements Version {
|
||||
|
||||
private final String version;
|
||||
|
||||
TestVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Version o) {
|
||||
return toString().compareTo(o.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.tooling.muzzle.matcher;
|
||||
|
||||
import static java.lang.System.lineSeparator;
|
||||
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.extension.muzzle.ClassRef;
|
||||
import io.opentelemetry.javaagent.extension.muzzle.FieldRef;
|
||||
import io.opentelemetry.javaagent.extension.muzzle.MethodRef;
|
||||
import io.opentelemetry.javaagent.extension.muzzle.Source;
|
||||
import io.opentelemetry.javaagent.tooling.muzzle.ClassLoaderMatcher;
|
||||
import io.opentelemetry.javaagent.tooling.muzzle.Mismatch;
|
||||
import io.opentelemetry.javaagent.tooling.muzzle.ReferenceMatcher;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/** Entry point for the muzzle gradle plugin. */
|
||||
// Runs in special classloader so tedious to provide access to the Gradle logger.
|
||||
@SuppressWarnings("SystemOut")
|
||||
@Deprecated
|
||||
public final class MuzzleGradlePluginUtil {
|
||||
private static final String INDENT = " ";
|
||||
|
||||
/**
|
||||
* Verifies that all instrumentations present in the {@code agentClassLoader} can be safely
|
||||
* applied to the passed {@code userClassLoader}.
|
||||
*
|
||||
* <p>This method throws whenever one of the following step fails (and {@code assertPass} is
|
||||
* true):
|
||||
*
|
||||
* <ol>
|
||||
* <li>{@code userClassLoader} is not matched by the {@link
|
||||
* InstrumentationModule#classLoaderMatcher()} method
|
||||
* <li>{@link ReferenceMatcher} of any instrumentation module finds any mismatch
|
||||
* <li>any helper class defined in {@link InstrumentationModule#getMuzzleHelperClassNames()}
|
||||
* fails to be injected into {@code userClassLoader}
|
||||
* </ol>
|
||||
*
|
||||
* <p>When {@code assertPass = false} this method behaves in an opposite way: failure in any of
|
||||
* the first two steps is expected (helper classes are not injected at all).
|
||||
*
|
||||
* <p>This method is repeatedly called by the {@code :muzzle} gradle task - each tested dependency
|
||||
* version passes different {@code userClassLoader}.
|
||||
*/
|
||||
public static void assertInstrumentationMuzzled(
|
||||
ClassLoader agentClassLoader, ClassLoader userClassLoader, boolean assertPass) {
|
||||
|
||||
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(agentClassLoader);
|
||||
|
||||
Map<String, List<Mismatch>> allMismatches;
|
||||
try {
|
||||
allMismatches = ClassLoaderMatcher.matchesAll(userClassLoader, assertPass);
|
||||
|
||||
allMismatches.forEach(
|
||||
(moduleName, mismatches) -> {
|
||||
boolean passed = mismatches.isEmpty();
|
||||
|
||||
if (passed && !assertPass) {
|
||||
System.err.println("MUZZLE PASSED " + moduleName + " BUT FAILURE WAS EXPECTED");
|
||||
throw new IllegalStateException(
|
||||
"Instrumentation unexpectedly passed Muzzle validation");
|
||||
} else if (!passed && assertPass) {
|
||||
System.err.println("FAILED MUZZLE VALIDATION: " + moduleName + " mismatches:");
|
||||
|
||||
for (Mismatch mismatch : mismatches) {
|
||||
System.err.println("-- " + mismatch);
|
||||
}
|
||||
throw new IllegalStateException("Instrumentation failed Muzzle validation");
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(contextClassLoader);
|
||||
}
|
||||
|
||||
int validatedModulesCount = allMismatches.size();
|
||||
if (validatedModulesCount == 0) {
|
||||
String errorMessage = "Did not found any InstrumentationModule to validate!";
|
||||
System.err.println(errorMessage);
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints all references from all instrumentation modules present in the passed {@code
|
||||
* instrumentationClassLoader}.
|
||||
*
|
||||
* <p>Called by the {@code printMuzzleReferences} gradle task.
|
||||
*/
|
||||
public static void printMuzzleReferences(ClassLoader instrumentationClassLoader) {
|
||||
for (InstrumentationModule instrumentationModule :
|
||||
ServiceLoader.load(InstrumentationModule.class, instrumentationClassLoader)) {
|
||||
try {
|
||||
System.out.println(instrumentationModule.getClass().getName());
|
||||
for (ClassRef ref : instrumentationModule.getMuzzleReferences().values()) {
|
||||
System.out.print(prettyPrint(ref));
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
String message =
|
||||
"Unexpected exception printing references for "
|
||||
+ instrumentationModule.getClass().getName();
|
||||
System.out.println(message);
|
||||
throw new IllegalStateException(message, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String prettyPrint(ClassRef ref) {
|
||||
StringBuilder builder = new StringBuilder(INDENT).append(ref).append(lineSeparator());
|
||||
if (!ref.getSources().isEmpty()) {
|
||||
builder.append(INDENT).append(INDENT).append("Sources:").append(lineSeparator());
|
||||
for (Source source : ref.getSources()) {
|
||||
builder
|
||||
.append(INDENT)
|
||||
.append(INDENT)
|
||||
.append(INDENT)
|
||||
.append("at: ")
|
||||
.append(source)
|
||||
.append(lineSeparator());
|
||||
}
|
||||
}
|
||||
for (FieldRef field : ref.getFields()) {
|
||||
builder.append(INDENT).append(INDENT).append(field).append(lineSeparator());
|
||||
}
|
||||
for (MethodRef method : ref.getMethods()) {
|
||||
builder.append(INDENT).append(INDENT).append(method).append(lineSeparator());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private MuzzleGradlePluginUtil() {}
|
||||
}
|
Loading…
Reference in New Issue