-
Notifications
You must be signed in to change notification settings - Fork 335
Expand file tree
/
Copy pathMuzzleMavenRepoUtils.kt
More file actions
233 lines (217 loc) · 8.72 KB
/
MuzzleMavenRepoUtils.kt
File metadata and controls
233 lines (217 loc) · 8.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package datadog.gradle.plugin.muzzle
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.file.FileTransporterFactory
import org.eclipse.aether.transport.http.HttpTransporterFactory
import org.eclipse.aether.version.Version
import org.gradle.api.GradleException
import java.nio.file.Files
internal object MuzzleMavenRepoUtils {
/**
* Remote repositories used to query version ranges and fetch dependencies
*/
@JvmStatic
val MUZZLE_REPOS: List<RemoteRepository> by lazy {
val central = RemoteRepository.Builder("central", "default", "https://repo1.maven.org/maven2/").build()
val mavenProxyUrl = System.getenv("MAVEN_REPOSITORY_PROXY")
if (mavenProxyUrl == null) {
listOf(central)
} else {
val proxy = RemoteRepository.Builder("central-proxy", "default", mavenProxyUrl).build()
listOf(proxy, central)
}
}
/**
* Create new RepositorySystem for muzzle's Maven/Aether resolutions.
* Supports both HTTP/HTTPS and file:// repositories.
*/
@JvmStatic
fun newRepositorySystem(): RepositorySystem {
val locator = MavenRepositorySystemUtils.newServiceLocator().apply {
addService(RepositoryConnectorFactory::class.java, BasicRepositoryConnectorFactory::class.java)
addService(TransporterFactory::class.java, HttpTransporterFactory::class.java)
addService(TransporterFactory::class.java, FileTransporterFactory::class.java)
}
return locator.getService(RepositorySystem::class.java)
}
/**
* Returns a new RepositorySystemSession for Muzzle's repo session.
*/
@JvmStatic
fun newRepositorySystemSession(system: RepositorySystem): RepositorySystemSession {
val session = MavenRepositorySystemUtils.newSession().apply {
val tmpDir = Files.createTempDirectory("muzzle-generated-tmpdir-").toFile().apply {
deleteOnExit()
}
val localRepo = LocalRepository(tmpDir)
localRepositoryManager = system.newLocalRepositoryManager(this, localRepo)
}
return session
}
/**
* Create a list of muzzle directives which assert the opposite of the given MuzzleDirective.
*/
fun inverseOf(
muzzleDirective: MuzzleDirective,
system: RepositorySystem,
session: RepositorySystemSession,
defaultRepos: List<RemoteRepository> = MUZZLE_REPOS
): Set<MuzzleDirective> {
val allVersionsArtifact = DefaultArtifact(
muzzleDirective.group,
muzzleDirective.module,
"jar",
"[,)"
)
val repos = muzzleDirective.getRepositories(defaultRepos)
val allRangeRequest = VersionRangeRequest().apply {
repositories = repos
artifact = allVersionsArtifact
}
val allRangeResult = system.resolveVersionRange(session, allRangeRequest)
val directiveArtifact = DefaultArtifact(
muzzleDirective.group,
muzzleDirective.module,
"jar",
muzzleDirective.versions
)
val rangeRequest = VersionRangeRequest().apply {
repositories = repos
artifact = directiveArtifact
}
val rangeResult = system.resolveVersionRange(session, rangeRequest)
val rangeResultVersions = rangeResult.versions.toSet()
allRangeResult.versions.removeAll(rangeResultVersions)
return MuzzleVersionUtils.filterAndLimitVersions(
allRangeResult,
muzzleDirective.skipVersions,
muzzleDirective.includeSnapshots
).map { version ->
MuzzleDirective().apply {
name = muzzleDirective.name
group = muzzleDirective.group
module = muzzleDirective.module
versions = version.toString()
assertPass = !muzzleDirective.assertPass
additionalRepositories = muzzleDirective.additionalRepositories
excludedDependencies = muzzleDirective.excludedDependencies
includeSnapshots = muzzleDirective.includeSnapshots
}
}.toSet()
}
/**
* Resolves the version range for a given MuzzleDirective using the provided RepositorySystem and RepositorySystemSession.
* Equivalent to the Groovy implementation in MuzzlePlugin.
*/
fun resolveVersionRange(
muzzleDirective: MuzzleDirective,
system: RepositorySystem,
session: RepositorySystemSession,
defaultRepos: List<RemoteRepository> = MUZZLE_REPOS
): VersionRangeResult {
val directiveArtifact: Artifact = DefaultArtifact(
muzzleDirective.group,
muzzleDirective.module,
muzzleDirective.classifier ?: "",
"jar",
muzzleDirective.versions
)
val rangeRequest = VersionRangeRequest().apply {
repositories = muzzleDirective.getRepositories(defaultRepos)
artifact = directiveArtifact
}
// In rare cases, the version resolution range silently failed with the maven proxy,
// retries 3 times at most then suggest to restart the job later.
var range = system.resolveVersionRange(session, rangeRequest)
for (i in 0..3) {
if (range.lowestVersion != null && range.highestVersion != null) {
return range
}
range = system.resolveVersionRange(session, rangeRequest)
}
throw IllegalStateException("The version range resolution failed during report, this is not expected. Advised course of action: Restart the job later.")
}
/**
* Resolves instrumentation names and their corresponding artifact versions for a given directive.
*
* Loads the `MuzzleVersionScanPlugin` class using the provided `ClassLoader`, invokes its
* `listInstrumentationNames` method to get all instrumentation names for the directive, and
* constructs a map of `TestedArtifact` objects keyed by their unique identifier. For each
* instrumentation name, the lowest and highest versions are determined and stored.
*
* @param directive the `MuzzleDirective` containing group, module, and name information
* @param cl the `ClassLoader` used to load the scan plugin class
* @param lowVersion the lowest version to consider
* @param highVersion the highest version to consider
* @return a map of instrumentation name keys to their corresponding `TestedArtifact` objects
*/
fun resolveInstrumentationAndJarVersions(
directive: MuzzleDirective,
cl: ClassLoader,
lowVersion: Version,
highVersion: Version
): Map<String, TestedArtifact> {
val scanPluginClass = cl.loadClass("datadog.trace.agent.tooling.muzzle.MuzzleVersionScanPlugin")
val listMethod = scanPluginClass.getMethod("listInstrumentationNames", ClassLoader::class.java, String::class.java)
@Suppress("UNCHECKED_CAST")
val names = listMethod.invoke(null, cl, directive.name) as Set<String>
val ret = mutableMapOf<String, TestedArtifact>()
for (n in names) {
val testedArtifact = TestedArtifact(n, directive.group ?: "", directive.module ?: "", lowVersion, highVersion)
val value = ret[testedArtifact.key()] ?: testedArtifact
ret[testedArtifact.key()] = TestedArtifact(
value.instrumentation,
value.group,
value.module,
lowest(lowVersion, value.lowVersion),
highest(highVersion, value.highVersion)
)
}
return ret
}
/**
* Returns the highest of two Version objects.
*/
fun highest(a: Version, b: Version): Version = if (a > b) a else b
/**
* Returns the lowest of two Version objects.
*/
fun lowest(a: Version, b: Version): Version = if (a < b) a else b
/**
* Convert a muzzle directive to a set of artifacts for all filtered versions.
* Throws GradleException if no artifacts are found.
*/
fun muzzleDirectiveToArtifacts(
muzzleDirective: MuzzleDirective,
rangeResult: VersionRangeResult
): Set<Artifact> {
val versions = MuzzleVersionUtils.filterAndLimitVersions(
rangeResult,
muzzleDirective.skipVersions,
muzzleDirective.includeSnapshots
)
val allVersionArtifacts = versions.map { version ->
DefaultArtifact(
muzzleDirective.group,
muzzleDirective.module,
muzzleDirective.classifier ?: "",
"jar",
version.toString()
)
}.toSet()
if (allVersionArtifacts.isEmpty()) {
throw GradleException("No muzzle artifacts found for ${muzzleDirective.group}:${muzzleDirective.module} ${muzzleDirective.versions} ${muzzleDirective.classifier}")
}
return allVersionArtifacts
}
}