diff --git a/dockerHashing/src/main/scala/cromwell/docker/local/DockerCliClient.scala b/dockerHashing/src/main/scala/cromwell/docker/local/DockerCliClient.scala index 4f2e2b7e891..60f03f861ec 100644 --- a/dockerHashing/src/main/scala/cromwell/docker/local/DockerCliClient.scala +++ b/dockerHashing/src/main/scala/cromwell/docker/local/DockerCliClient.scala @@ -2,7 +2,8 @@ package cromwell.docker.local import scala.Function.const import scala.util.Try - +import sys.process._ +import io.circe.parser._ /** * Wrapper around the docker cli. * https://docs.docker.com/engine/reference/commandline/docker/ @@ -28,6 +29,39 @@ trait DockerCliClient { _.flatMap(parseHashLine).find(_.key == dockerCliKey).map(_.digest) } + /** + * Looks up a docker hash from manifest + * + * @param dockerCliKey The docker hash to lookup. + * @return The hash if found, None if not found, and Failure if an error occurs. + */ + def lookupHashDirect(dockerCliKey: DockerCliKey): Try[Option[String]] = { + Try { + val cmd = Seq("docker", "manifest", "inspect", "-v", dockerCliKey.fullName) + val output = new StringBuilder + val logger = ProcessLogger( + (o: String) => output.append(o + "\n"), + (e: String) => System.err.println(e) + ) + + val exitValue = cmd.!(logger) + + if (exitValue == 0) { + val outputJson: String = output.toString() + parse(outputJson) match { + case Left(failure) => throw new RuntimeException("Failed to parse JSON: " + failure.message) + case Right(json) => + json.hcursor.downField("Descriptor").downField("digest").as[String] match { + case Left(failure) => throw new RuntimeException("Unexpected JSON structure: " + failure.message) + case Right(digest) => Some(digest) + } + } + } else { + throw new RuntimeException(s"Process ran with error. Exit code: $exitValue") + } + } + } + /** * Pulls a docker image. * @param dockerCliKey The docker hash to lookup. diff --git a/dockerHashing/src/main/scala/cromwell/docker/local/DockerCliFlow.scala b/dockerHashing/src/main/scala/cromwell/docker/local/DockerCliFlow.scala index 513d27bc001..99ec0e0de68 100644 --- a/dockerHashing/src/main/scala/cromwell/docker/local/DockerCliFlow.scala +++ b/dockerHashing/src/main/scala/cromwell/docker/local/DockerCliFlow.scala @@ -29,7 +29,7 @@ class DockerCliFlow(implicit ec: ExecutionContext) extends DockerRegistry { implicit val timer = IO.timer(ec) DockerCliFlow - .lookupHashOrTimeout(firstLookupTimeout)(dockerInfoContext) + .lookupHashOrTimeout(firstLookupTimeout, true)(dockerInfoContext) .flatMap { // If the image isn't there, pull it and try again case (_: DockerInfoNotFound, _) => @@ -50,10 +50,11 @@ object DockerCliFlow { * @param context The image to lookup. * @return The docker hash response plus the context of our flow. */ - private def lookupHash(context: DockerInfoContext): (DockerInfoResponse, DockerInfoContext) = { + private def lookupHash(context: DockerInfoContext, useDirect: Boolean): (DockerInfoResponse, DockerInfoContext) = { val dockerCliKey = cliKeyFromImageId(context) DockerInfoActor.logger.debug("Looking up hash of {}", dockerCliKey.fullName) - val result = DockerCliClient.lookupHash(dockerCliKey) match { + val lookupFunction = if (useDirect) DockerCliClient.lookupHashDirect(_) else DockerCliClient.lookupHash(_) + val result = lookupFunction(dockerCliKey) match { case Success(None) => DockerInfoNotFound(context.request) case Success(Some(hash)) => DockerHashResult.fromString(hash) match { @@ -75,10 +76,10 @@ object DockerCliFlow { * @param context The image to lookup. * @return The docker hash response plus the context of our flow. */ - private def lookupHashOrTimeout(timeout: FiniteDuration)( + private def lookupHashOrTimeout(timeout: FiniteDuration, useDirect: Boolean = false)( context: DockerInfoContext - )(implicit cs: ContextShift[IO], timer: Timer[IO]): IO[(DockerInfoResponse, DockerInfoContext)] = - IO(lookupHash(context)) + )(implicit cs: ContextShift[IO], timer: Timer[IO]): IO[(DockerInfoResponse, DockerInfoContext)] = + IO(lookupHash(context, useDirect)) .timeout(timeout) .handleErrorWith { case _: TimeoutException => diff --git a/supportedBackends/google/batch/src/main/scala/cromwell/backend/google/batch/runnable/RunnableBuilder.scala b/supportedBackends/google/batch/src/main/scala/cromwell/backend/google/batch/runnable/RunnableBuilder.scala index d69502295cd..a753ab64274 100644 --- a/supportedBackends/google/batch/src/main/scala/cromwell/backend/google/batch/runnable/RunnableBuilder.scala +++ b/supportedBackends/google/batch/src/main/scala/cromwell/backend/google/batch/runnable/RunnableBuilder.scala @@ -147,12 +147,14 @@ object RunnableBuilder { scriptContainerPath: String, jobShell: String, volumes: List[Volume], - dockerhubCredentials: (String, String) + dockerhubCredentials: (String, String), + fuseEnabled: Boolean ): Runnable.Builder = { val container = (dockerhubCredentials._1, dockerhubCredentials._2) match { case (username, password) if username.nonEmpty && password.nonEmpty => Container.newBuilder + .setOptions(if(fuseEnabled) "--privileged" else "") .setImageUri(docker) .setEntrypoint(jobShell) .addCommands(scriptContainerPath) @@ -160,6 +162,7 @@ object RunnableBuilder { .setPassword(password) case _ => Container.newBuilder + .setOptions(if(fuseEnabled) "--privileged" else "") .setImageUri(docker) .setEntrypoint(jobShell) .addCommands(scriptContainerPath) diff --git a/supportedBackends/google/batch/src/main/scala/cromwell/backend/google/batch/runnable/UserRunnable.scala b/supportedBackends/google/batch/src/main/scala/cromwell/backend/google/batch/runnable/UserRunnable.scala index cba665dbf9e..3b696deff09 100644 --- a/supportedBackends/google/batch/src/main/scala/cromwell/backend/google/batch/runnable/UserRunnable.scala +++ b/supportedBackends/google/batch/src/main/scala/cromwell/backend/google/batch/runnable/UserRunnable.scala @@ -12,7 +12,8 @@ trait UserRunnable { scriptContainerPath = createParameters.commandScriptContainerPath.pathAsString, jobShell = "/bin/bash", volumes = volumes, - dockerhubCredentials = createParameters.dockerhubCredentials + dockerhubCredentials = createParameters.dockerhubCredentials, + fuseEnabled = createParameters.fuseEnabled ) val describeRunnable = RunnableBuilder.describeDocker("user runnable", userRunnable)