Skip to content

Instantly share code, notes, and snippets.

@rbellamy
Last active November 4, 2016 18:19
Show Gist options
  • Save rbellamy/63233cf847c177d63edd45f8b10c9210 to your computer and use it in GitHub Desktop.
Save rbellamy/63233cf847c177d63edd45f8b10c9210 to your computer and use it in GitHub Desktop.
Docker Helper Plugin
import sbt.{Process, _}
import Keys._
import com.typesafe.sbt.packager.docker.{DockerAlias, DockerPlugin}
import com.typesafe.sbt.packager.docker.DockerPlugin.autoImport._
import com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport.stage
/*
* This helper plugin manipulates the behavior of the [[com.typesafe.sbt.packager.DockerPlugin]] in the following ways
* 1. Modifies the [[dockerAlias]] to NOT include the [[dockerRepository]] by default. This allows us to publish local
* docker images in the form of <package name>:<package version> - this makes them more easily launched from the local
* docker daemon.
* 2. This then means that the [[publish]] task must first tag the image to include the [[dockerRepository]] before doing
* the docker push.
* 3. Adds a [[docker:clean]] task that will delete all images associated with this project.
*/
object DockerHelperPlugin extends AutoPlugin {
override def requires = DockerPlugin
override lazy val projectSettings = inConfig(Docker)(dockerSettings)
private def sudoCommand(useSudo: Boolean) =
if (useSudo)
Seq("sudo")
else
Seq()
def dockerSettings = Seq(
dockerUseSudo := false,
clean := cleanDockerTask.value,
publishLocal := publishLocalDocker.value,
tagDockerRepository := tagDocker.value,
publish <<= publish.dependsOn(tagDockerRepository)
)
lazy val dockerUseSudo = settingKey[Boolean]("Use sudo for docker commands") in Docker
lazy val clean = taskKey[Unit]("Clean docker images from local Docker daemon store.") in Docker
lazy val tagDockerRepository = taskKey[Unit]("Tag the current image with the repository.") in Docker
lazy val cleanDockerTask = Def.task {
val log: Logger = streams.value.log
log.info("Cleaning Docker images")
cleanDocker(dockerUseSudo.value, name.value, version.value, log)
}
/*
Alters the default behavior of the the DockerPlugin so that the publishLocal task leaves off the dockerRepository
value.
*/
lazy val publishLocalDocker = Def.task {
val alias: DockerAlias = DockerAlias(None, None, name.value, Some(version.value))
val buildOptions: Seq[String] = Seq("--force-rm") ++ Seq("-t", alias.versioned) ++ (
if (dockerUpdateLatest.value)
Seq("-t", dockerAlias.value.latest)
else
Seq()
)
val buildCommands: Seq[String] = sudoCommand(dockerUseSudo.value) ++ Seq("docker", "build") ++ buildOptions ++ Seq(".")
DockerPlugin.publishLocalDocker(stage.value, buildCommands, streams.value.log)
}
/*
Alters the default behavior of the DockerPlugin so that the publish task first tags the local image with the
dockerRepository. Required since we altered the publishLocal task.
*/
lazy val tagDocker = Def.task {
publishLocal.value
tagRepositoryDocker(dockerUseSudo.value, name.value, dockerRepository.value, (version in Docker).value, streams.value.log)
}
def cleanDocker(useSudo: Boolean, imageName: String, imageVersion: String, log: Logger): Unit = {
val listCommand = sudoCommand(useSudo) ++ Seq("docker", "images")
log.debug(s"Executing ${listCommand.mkString(" ")}")
val imageIds = (for {
i <- Process(listCommand).!!.split("\n").
map(_.split("\\s{2,}")) if i(0).indexOf(imageName) > -1 && i(1) == imageVersion
} yield i(2)).distinct
log.debug(s"imageId: ${imageIds.mkString(" ")}")
if (!imageIds.isEmpty) {
val cleanCommand = sudoCommand(useSudo) ++ Seq("docker", "rmi", "-f") ++ imageIds
log.debug(s"Executing ${cleanCommand.mkString(" ")}")
val ret = Process(cleanCommand) ! logger(log)
if (ret != 0)
throw new RuntimeException("Nonzero exit value: " + ret)
}
}
def tagRepositoryDocker(useSudo: Boolean, name: String, repository: Option[String], version: String, log: Logger): Unit = {
for (repo <- repository) {
val tagCommand = sudoCommand(useSudo) ++ Seq("docker", "tag", s"$name:$version", s"$repo/$name:$version")
log.debug(s"Executing ${tagCommand.mkString(" ")}")
val ret = Process(tagCommand) ! logger(log)
if (ret != 0)
throw new RuntimeException("Nonzero exit value: " + ret)
}
}
def logger(log: Logger) = {
new ProcessLogger {
def error(err: => String) = {
err match {
case s if !s.trim.isEmpty => log.error(s)
case s =>
}
}
def info(inf: => String) = inf match {
case s if !s.trim.isEmpty => log.info(s)
case s =>
}
def buffer[T](f: => T) = f
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment