Skip to content

Commit f98773a

Browse files
brkyvzpwendell
authored andcommitted
[SPARK-7205] Support .ivy2/local and .m2/repositories/ in --packages
In addition, I made a small change that will allow users to import 2 different artifacts with the same name. That change is made in `[organization]_[artifact]-[revision].[ext]`. This used to be only `[artifact].[ext]` which might have caused collisions between artifacts with the same artifactId, but different groupId's. cc pwendell Author: Burak Yavuz <[email protected]> Closes apache#5755 from brkyvz/local-caches and squashes the following commits: c47c9c5 [Burak Yavuz] Small fixes to --packages
1 parent 271c4c6 commit f98773a

File tree

2 files changed

+40
-21
lines changed

2 files changed

+40
-21
lines changed

core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -734,13 +734,31 @@ private[deploy] object SparkSubmitUtils {
734734
/**
735735
* Extracts maven coordinates from a comma-delimited string
736736
* @param remoteRepos Comma-delimited string of remote repositories
737+
* @param ivySettings The Ivy settings for this session
737738
* @return A ChainResolver used by Ivy to search for and resolve dependencies.
738739
*/
739-
def createRepoResolvers(remoteRepos: Option[String]): ChainResolver = {
740+
def createRepoResolvers(remoteRepos: Option[String], ivySettings: IvySettings): ChainResolver = {
740741
// We need a chain resolver if we want to check multiple repositories
741742
val cr = new ChainResolver
742743
cr.setName("list")
743744

745+
val localM2 = new IBiblioResolver
746+
localM2.setM2compatible(true)
747+
val m2Path = ".m2" + File.separator + "repository" + File.separator
748+
localM2.setRoot(new File(System.getProperty("user.home"), m2Path).toURI.toString)
749+
localM2.setUsepoms(true)
750+
localM2.setName("local-m2-cache")
751+
cr.add(localM2)
752+
753+
val localIvy = new IBiblioResolver
754+
localIvy.setRoot(new File(ivySettings.getDefaultIvyUserDir,
755+
"local" + File.separator).toURI.toString)
756+
val ivyPattern = Seq("[organisation]", "[module]", "[revision]", "[type]s",
757+
"[artifact](-[classifier]).[ext]").mkString(File.separator)
758+
localIvy.setPattern(ivyPattern)
759+
localIvy.setName("local-ivy-cache")
760+
cr.add(localIvy)
761+
744762
// the biblio resolver resolves POM declared dependencies
745763
val br: IBiblioResolver = new IBiblioResolver
746764
br.setM2compatible(true)
@@ -773,8 +791,7 @@ private[deploy] object SparkSubmitUtils {
773791

774792
/**
775793
* Output a comma-delimited list of paths for the downloaded jars to be added to the classpath
776-
* (will append to jars in SparkSubmit). The name of the jar is given
777-
* after a '!' by Ivy. It also sometimes contains '(bundle)' after '.jar'. Remove that as well.
794+
* (will append to jars in SparkSubmit).
778795
* @param artifacts Sequence of dependencies that were resolved and retrieved
779796
* @param cacheDirectory directory where jars are cached
780797
* @return a comma-delimited list of paths for the dependencies
@@ -783,10 +800,9 @@ private[deploy] object SparkSubmitUtils {
783800
artifacts: Array[AnyRef],
784801
cacheDirectory: File): String = {
785802
artifacts.map { artifactInfo =>
786-
val artifactString = artifactInfo.toString
787-
val jarName = artifactString.drop(artifactString.lastIndexOf("!") + 1)
803+
val artifact = artifactInfo.asInstanceOf[Artifact].getModuleRevisionId
788804
cacheDirectory.getAbsolutePath + File.separator +
789-
jarName.substring(0, jarName.lastIndexOf(".jar") + 4)
805+
s"${artifact.getOrganisation}_${artifact.getName}-${artifact.getRevision}.jar"
790806
}.mkString(",")
791807
}
792808

@@ -868,6 +884,7 @@ private[deploy] object SparkSubmitUtils {
868884
if (alternateIvyCache.trim.isEmpty) {
869885
new File(ivySettings.getDefaultIvyUserDir, "jars")
870886
} else {
887+
ivySettings.setDefaultIvyUserDir(new File(alternateIvyCache))
871888
ivySettings.setDefaultCache(new File(alternateIvyCache, "cache"))
872889
new File(alternateIvyCache, "jars")
873890
}
@@ -877,7 +894,7 @@ private[deploy] object SparkSubmitUtils {
877894
// create a pattern matcher
878895
ivySettings.addMatcher(new GlobPatternMatcher)
879896
// create the dependency resolvers
880-
val repoResolver = createRepoResolvers(remoteRepos)
897+
val repoResolver = createRepoResolvers(remoteRepos, ivySettings)
881898
ivySettings.addResolver(repoResolver)
882899
ivySettings.setDefaultResolver(repoResolver.getName)
883900

@@ -911,7 +928,8 @@ private[deploy] object SparkSubmitUtils {
911928
}
912929
// retrieve all resolved dependencies
913930
ivy.retrieve(rr.getModuleDescriptor.getModuleRevisionId,
914-
packagesDirectory.getAbsolutePath + File.separator + "[artifact](-[classifier]).[ext]",
931+
packagesDirectory.getAbsolutePath + File.separator +
932+
"[organization]_[artifact]-[revision].[ext]",
915933
retrieveOptions.setConfs(Array(ivyConfName)))
916934
System.setOut(sysOut)
917935
resolveDependencyPaths(rr.getArtifacts.toArray, packagesDirectory)

core/src/test/scala/org/apache/spark/deploy/SparkSubmitUtilsSuite.scala

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package org.apache.spark.deploy
1919

2020
import java.io.{PrintStream, OutputStream, File}
2121

22+
import org.apache.ivy.core.settings.IvySettings
23+
2224
import scala.collection.mutable.ArrayBuffer
2325

2426
import org.scalatest.{BeforeAndAfterAll, FunSuite}
@@ -56,24 +58,23 @@ class SparkSubmitUtilsSuite extends FunSuite with BeforeAndAfterAll {
5658
}
5759

5860
test("create repo resolvers") {
59-
val resolver1 = SparkSubmitUtils.createRepoResolvers(None)
61+
val settings = new IvySettings
62+
val res1 = SparkSubmitUtils.createRepoResolvers(None, settings)
6063
// should have central and spark-packages by default
61-
assert(resolver1.getResolvers.size() === 2)
62-
assert(resolver1.getResolvers.get(0).asInstanceOf[IBiblioResolver].getName === "central")
63-
assert(resolver1.getResolvers.get(1).asInstanceOf[IBiblioResolver].getName === "spark-packages")
64+
assert(res1.getResolvers.size() === 4)
65+
assert(res1.getResolvers.get(0).asInstanceOf[IBiblioResolver].getName === "local-m2-cache")
66+
assert(res1.getResolvers.get(1).asInstanceOf[IBiblioResolver].getName === "local-ivy-cache")
67+
assert(res1.getResolvers.get(2).asInstanceOf[IBiblioResolver].getName === "central")
68+
assert(res1.getResolvers.get(3).asInstanceOf[IBiblioResolver].getName === "spark-packages")
6469

6570
val repos = "a/1,b/2,c/3"
66-
val resolver2 = SparkSubmitUtils.createRepoResolvers(Option(repos))
67-
assert(resolver2.getResolvers.size() === 5)
71+
val resolver2 = SparkSubmitUtils.createRepoResolvers(Option(repos), settings)
72+
assert(resolver2.getResolvers.size() === 7)
6873
val expected = repos.split(",").map(r => s"$r/")
6974
resolver2.getResolvers.toArray.zipWithIndex.foreach { case (resolver: IBiblioResolver, i) =>
70-
if (i == 0) {
71-
assert(resolver.getName === "central")
72-
} else if (i == 1) {
73-
assert(resolver.getName === "spark-packages")
74-
} else {
75-
assert(resolver.getName === s"repo-${i - 1}")
76-
assert(resolver.getRoot === expected(i - 2))
75+
if (i > 3) {
76+
assert(resolver.getName === s"repo-${i - 3}")
77+
assert(resolver.getRoot === expected(i - 4))
7778
}
7879
}
7980
}

0 commit comments

Comments
 (0)