
com.codacy.plugins.runners.BinaryDockerRunner.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of codacy-plugins-runner-binary_2.12 Show documentation
Show all versions of codacy-plugins-runner-binary_2.12 Show documentation
A library for managing and running Codacy tools
The newest version!
package com.codacy.plugins.runners
import com.codacy.plugins.utils.{CommandRunner, Log}
import play.api.libs.json.Reads
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future, TimeoutException}
import scala.util.{Failure, Properties, Success, Try}
import com.codacy.plugins.parser.ToolOutputParser
object BinaryDockerRunner {
case class Config(containerMemoryLimit: Option[String] = Some("3000000000"))
}
class BinaryDockerRunner[T](docker: IDocker, config: BinaryDockerRunner.Config = BinaryDockerRunner.Config())
extends DockerRunner[T] {
def imageExistsLocally(): Boolean = {
Log.time(s"Verifying if docker image ${docker.dockerImage} exists locally.") {
val verificationCmd =
List(DockerConstants.dockerBin, "inspect", "--format='{{.Config.Image}}'", "--type=image", docker.dockerImage)
val res = CommandRunner.exec(verificationCmd) match {
case Success(res) => res.nonEmpty
case Failure(_) => false
}
if (!res) {
Log.debug(s"${docker.dockerImage} doesn't exist locally.")
}
res
}
}
def pull(timeout: Duration = DockerRunner.defaultPullTimeout): Unit = {
val cmd = List(DockerConstants.dockerBin, "pull", docker.dockerImage)
Log.time(s"Pulling docker image ${docker.dockerImage}.") {
val result = CommandRunner.execAsync(cmd)
Try(Await.result(result.exitCode, timeout)).failed.foreach { _ =>
Log.info(s"Docker ${docker.dockerImage} timed out after ${timeout.toSeconds} seconds. Destroying process.")
result.destroy()
Log.info(s"Finished destroying process of docker ${docker.dockerImage}")
()
}
}
}
def run(dockerConfig: DockerConfig)(implicit resultRds: Reads[T]): Try[List[T]] = {
if (!imageExistsLocally()) pull()
val dockerCmd = getDockerCmd(dockerConfig)
val commandResult = Log.time(s"Running docker image ${docker.dockerImage}.") {
val result = CommandRunner.execAsync(dockerCmd)
Try(Await.result(result.exitCode, dockerConfig.awaitResponseTimeout)) match {
case Success(code) =>
Success((code, result.stdout, result.stderr))
case Failure(e) =>
Future {
Log.info(
s"Docker ${docker.dockerImage} timed out after ${dockerConfig.awaitResponseTimeout.toSeconds} seconds. Destroying process.")
result.destroy()
Log.info(s"Finished destroying process of docker ${docker.dockerImage}")
}(ExecutionContext.global)
Failure(e)
}
}
commandResult.flatMap {
case (0, result, _) =>
Success(ToolOutputParser.parseDockerOutputLines[T](result))
case (exitCode, stdout, stderr) =>
val output =
s"""
|Docker exited with code $exitCode
|stdout: ${stdout.mkString(Properties.lineSeparator)}
|stderr: ${stderr.mkString(Properties.lineSeparator)}
""".stripMargin
if (exitCode == 2) {
Failure(new TimeoutException(output))
} else {
Failure(new Throwable(output))
}
}
}
private def asArgsLists(dockerConfig: DockerConfig,
config: BinaryDockerRunner.Config): (List[String], List[String]) = {
val vols = dockerConfig.volumes.flatMap(volume => List("-v", s"${volume.hostPath}:${volume.dockerPath}"))
val entrypoint = dockerConfig.entry.fold(List.empty[String])(entrypoint => List(s"--entrypoint=$entrypoint"))
val autoRemoveCmd = if (dockerConfig.autoRemove) List("--rm=true") else List.empty[String]
val memory = config.containerMemoryLimit.toList.flatMap(memory => List("-m", memory))
val envVars = dockerConfig.envVars.flatMap(v => List("-e", v.toString))
val args = vols ++ entrypoint ++ memory ++ envVars
val postArgs = autoRemoveCmd ++ dockerConfig.commands
(args, postArgs)
}
private def getDockerCmd(dockerConfig: DockerConfig): List[String] = {
val (args, postArgs) = asArgsLists(dockerConfig, config)
val cmd = docker.baseCmd.getOrElse {
if (docker.needsCompilation || dockerConfig.canUseInternet) {
DockerConstants.dockerRunNonStrictCmd
} else {
DockerConstants.dockerRunCmd
}
}
(cmd.split(" ") ++ args ++ List(docker.dockerImage) ++ postArgs).toList
}
}
object DockerConstants {
private val rmOpts = sys.props.get("codacy.tests.noremove").map(_ => List.empty).getOrElse(List("--rm=true"))
val dockerBin: String = sys.env.getOrElse("CODACY_DOCKER_BIN", "docker")
val dockerStrictFlags: String = Seq("--net=none", "--cap-drop=ALL").mkString(" ")
val dockerRunNonDaemonCmd: String =
Seq(dockerBin, "run", dockerStrictFlags, "--privileged=false", "--user=docker").mkString(" ")
val dockerRunCmd: String = (dockerRunNonDaemonCmd +: rmOpts).mkString(" ")
val dockerRunNonStrictCmd: String =
(Seq(dockerBin, "run", s"--user=${DockerRunner.DockerUsername}") ++ rmOpts).mkString(" ")
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy