From a125a60081f286b9b486dd91092c3d149409eece Mon Sep 17 00:00:00 2001 From: Mathias Kub Date: Thu, 4 Aug 2016 16:31:44 +0200 Subject: [PATCH] Adding 'dockerBuild{Command, Options}' and renaming 'dockerTag' (#854) * dockerBuildCommand The 'dockerBuildCommand' let users customize the command for building Docker images. The current default is 'docker build [dockerBuildOptions] .'. * dockerBuildOptions Furthermore 'dockerBuildOptions' defines which build options are used. The current default is '--force-rm -t [dockerAlias]' (which is expanded if 'dockerUpdateLatest' is set to 'true'. This results in an execution of 'docker build --force-rm -t [dockerAlias] .' (if 'dockerUpdateLatest' is 'false'). * dockerTag being renamed to dockerAlias The 'dockerTag' setting is renamed to 'dockerAlias' as this better reflects the setting. Docker aliases consist of a registry path, username, name and tag ([REGISTRY_HOST/][USERNAME/]NAME[:TAG]). To not confuse the Docker image tag with this setting (as it takes the full alias name), the setting is renamed. --- .../sbt/packager/docker/DockerAlias.scala | 19 ++++++++ .../sbt/packager/docker/DockerPlugin.scala | 46 +++++++++---------- .../docker/DockerSpotifyClientPlugin.scala | 2 +- .../typesafe/sbt/packager/docker/Keys.scala | 4 +- src/sbt-test/docker/alias/build.sbt | 7 +++ .../docker/{tag => alias}/project/plugins.sbt | 0 .../{tag => alias}/src/main/scala/Main.scala | 0 src/sbt-test/docker/alias/test | 3 ++ src/sbt-test/docker/build-command/build.sbt | 9 ++++ .../docker/build-command/project/plugins.sbt | 1 + .../src/main/resources/docker-test/Dockerfile | 3 ++ src/sbt-test/docker/build-command/test | 3 ++ src/sbt-test/docker/build-options/build.sbt | 7 +++ .../docker/build-options/project/plugins.sbt | 1 + .../build-options/src/main/scala/Main.scala | 3 ++ src/sbt-test/docker/build-options/test | 4 ++ src/sbt-test/docker/tag/build.sbt | 7 --- src/sbt-test/docker/tag/test | 3 -- src/sphinx/formats/docker.rst | 15 ++++-- 19 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 src/main/scala/com/typesafe/sbt/packager/docker/DockerAlias.scala create mode 100644 src/sbt-test/docker/alias/build.sbt rename src/sbt-test/docker/{tag => alias}/project/plugins.sbt (100%) rename src/sbt-test/docker/{tag => alias}/src/main/scala/Main.scala (100%) create mode 100644 src/sbt-test/docker/alias/test create mode 100644 src/sbt-test/docker/build-command/build.sbt create mode 100644 src/sbt-test/docker/build-command/project/plugins.sbt create mode 100644 src/sbt-test/docker/build-command/src/main/resources/docker-test/Dockerfile create mode 100644 src/sbt-test/docker/build-command/test create mode 100644 src/sbt-test/docker/build-options/build.sbt create mode 100644 src/sbt-test/docker/build-options/project/plugins.sbt create mode 100644 src/sbt-test/docker/build-options/src/main/scala/Main.scala create mode 100644 src/sbt-test/docker/build-options/test delete mode 100644 src/sbt-test/docker/tag/build.sbt delete mode 100644 src/sbt-test/docker/tag/test diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/DockerAlias.scala b/src/main/scala/com/typesafe/sbt/packager/docker/DockerAlias.scala new file mode 100644 index 000000000..9fb84ecc7 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerAlias.scala @@ -0,0 +1,19 @@ +package com.typesafe.sbt.packager.docker + +/** + * This class represents a Docker alias. + * It generates a string in the form of {{{[REGISTRY_HOST/][USERNAME/]NAME[:TAG]}}}, + * e.g. ''my-registry.com:1234/my-user/my-service:1.0.0'' or just ''my-service:1.0.0''. + * @param registryHost Optional hostname of the registry (including port if applicable) + * @param username Optional username or other qualifier + * @param name Name of the image, e.g. the artifact name + * @param tag Optional tag for the image, e.g. the version + */ +case class DockerAlias(registryHost: Option[String], username: Option[String], name: String, tag: Option[String]) { + protected val untagged = registryHost.map(_ + "/").getOrElse("") + username.map(_ + "/").getOrElse("") + name + + /** Tag with (optional) given version */ + val versioned = untagged + tag.map(":" + _).getOrElse("") + /** Tag with version 'latest' */ + val latest = s"$untagged:latest" +} diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala index a2449fedb..0e1f3ae78 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala @@ -55,6 +55,8 @@ object DockerPlugin extends AutoPlugin { object autoImport extends DockerKeys { val Docker = config("docker") + + val DockerAlias = com.typesafe.sbt.packager.docker.DockerAlias } import autoImport._ @@ -74,10 +76,17 @@ object DockerPlugin extends AutoPlugin { dockerExposedPorts := Seq(), dockerExposedVolumes := Seq(), dockerRepository := None, - dockerTag := dockerRepository.value.map(_ + "/").getOrElse("") + packageName.value + ":" + version.value, + dockerAlias := DockerAlias(dockerRepository.value, None, packageName.value, Some(version.value)), dockerUpdateLatest := false, dockerEntrypoint := Seq("bin/%s" format executableScriptName.value), dockerCmd := Seq(), + dockerBuildOptions := Seq("--force-rm") ++ Seq("-t", dockerAlias.value.versioned) ++ ( + if (dockerUpdateLatest.value) + Seq("-t", dockerAlias.value.latest) + else + Seq() + ), + dockerBuildCommand := Seq("docker", "build") ++ dockerBuildOptions.value ++ Seq("."), dockerCommands := { val dockerBaseDirectory = (defaultLinuxInstallLocation in Docker).value val user = (daemonUser in Docker).value @@ -106,16 +115,16 @@ object DockerPlugin extends AutoPlugin { mappings ++= Seq(dockerGenerateConfig.value) pair relativeTo(target.value), name := name.value, packageName := packageName.value, - publishLocal <<= (stage, dockerTag, dockerUpdateLatest, streams) map { - (context, target, updateLatest, s) => - publishLocalDocker(context, target, updateLatest, s.log) + publishLocal <<= (stage, dockerAlias, dockerBuildCommand, streams) map { + (context, alias, buildCommand, s) => + publishLocalDocker(context, buildCommand, s.log) + s.log.info(s"Built image $alias") }, - publish <<= (publishLocal, dockerTag, dockerUpdateLatest, streams) map { - (_, target, updateLatest, s) => - publishDocker(target, s.log) + publish <<= (publishLocal, dockerAlias, dockerUpdateLatest, streams) map { + (_, alias, updateLatest, s) => + publishDocker(alias.versioned, s.log) if (updateLatest) { - val name = target.substring(0, target.lastIndexOf(":")) + ":latest" - publishDocker(name, s.log) + publishDocker(alias.latest, s.log) } }, sourceDirectory := sourceDirectory.value / "docker", @@ -283,27 +292,14 @@ object DockerPlugin extends AutoPlugin { } } - def publishLocalDocker(context: File, tag: String, latest: Boolean, log: Logger): Unit = { - val cmd = Seq("docker", "build", "--force-rm", "-t", tag, ".") - - log.debug("Executing Native " + cmd.mkString(" ")) + def publishLocalDocker(context: File, buildCommand: Seq[String], log: Logger): Unit = { + log.debug("Executing Native " + buildCommand.mkString(" ")) log.debug("Working directory " + context.toString) - val ret = Process(cmd, context) ! publishLocalLogger(log) + val ret = Process(buildCommand, context) ! publishLocalLogger(log) if (ret != 0) throw new RuntimeException("Nonzero exit value: " + ret) - else - log.info("Built image " + tag) - - if (latest) { - val name = tag.substring(0, tag.lastIndexOf(":")) + ":latest" - val latestCmd = Seq("docker", "tag", tag, name) - Process(latestCmd).! match { - case 0 => log.info("Update Latest from image " + tag) - case n => sys.error("Failed to run docker tag") - } - } } def publishDocker(tag: String, log: Logger): Unit = { diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/DockerSpotifyClientPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/docker/DockerSpotifyClientPlugin.scala index 4e2436fbe..5044c2488 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/DockerSpotifyClientPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerSpotifyClientPlugin.scala @@ -64,7 +64,7 @@ object DockerSpotifyClientPlugin extends AutoPlugin { def publishLocalDocker = Def.task { val context = stage.value - val tag = dockerTag.value + val tag = dockerAlias.value.versioned val latest = dockerUpdateLatest.value val log = streams.value.log diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala b/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala index 3f5f325e8..25cf1e4e5 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala @@ -15,10 +15,12 @@ trait DockerKeys { val dockerExposedPorts = SettingKey[Seq[Int]]("dockerExposedPorts", "Ports exposed by Docker image") val dockerExposedVolumes = SettingKey[Seq[String]]("dockerExposedVolumes", "Volumes exposed by Docker image") val dockerRepository = SettingKey[Option[String]]("dockerRepository", "Repository for published Docker image") - val dockerTag = SettingKey[String]("dockerTag", "Docker tag for the built image") + val dockerAlias = SettingKey[DockerAlias]("dockerAlias", "Docker alias for the built image") val dockerUpdateLatest = SettingKey[Boolean]("dockerUpdateLatest", "Set to update latest tag") val dockerEntrypoint = SettingKey[Seq[String]]("dockerEntrypoint", "Entrypoint arguments passed in exec form") val dockerCmd = SettingKey[Seq[String]]("dockerCmd", "Docker CMD. Used together with dockerEntrypoint. Arguments passed in exec form") + val dockerBuildOptions = SettingKey[Seq[String]]("dockerBuildOptions", "Options used for the Docker build") + val dockerBuildCommand = SettingKey[Seq[String]]("dockerBuildCommand", "Command for building the Docker image") val dockerCommands = TaskKey[Seq[CmdLike]]("dockerCommands", "List of docker commands that form the Dockerfile") } diff --git a/src/sbt-test/docker/alias/build.sbt b/src/sbt-test/docker/alias/build.sbt new file mode 100644 index 000000000..6db90ca2a --- /dev/null +++ b/src/sbt-test/docker/alias/build.sbt @@ -0,0 +1,7 @@ +enablePlugins(JavaAppPackaging) + +name := "docker-alias-test" + +version := "0.1.0" + +dockerAlias := DockerAlias(None, None, "docker-alias-test", Some("0.1.0")) diff --git a/src/sbt-test/docker/tag/project/plugins.sbt b/src/sbt-test/docker/alias/project/plugins.sbt similarity index 100% rename from src/sbt-test/docker/tag/project/plugins.sbt rename to src/sbt-test/docker/alias/project/plugins.sbt diff --git a/src/sbt-test/docker/tag/src/main/scala/Main.scala b/src/sbt-test/docker/alias/src/main/scala/Main.scala similarity index 100% rename from src/sbt-test/docker/tag/src/main/scala/Main.scala rename to src/sbt-test/docker/alias/src/main/scala/Main.scala diff --git a/src/sbt-test/docker/alias/test b/src/sbt-test/docker/alias/test new file mode 100644 index 000000000..d0f0dfc54 --- /dev/null +++ b/src/sbt-test/docker/alias/test @@ -0,0 +1,3 @@ +# Stage the distribution and ensure files show up. +> docker:publishLocal +$ exec bash -c 'docker run docker-alias-test:0.1.0 | grep -q "Hello world"' diff --git a/src/sbt-test/docker/build-command/build.sbt b/src/sbt-test/docker/build-command/build.sbt new file mode 100644 index 000000000..d40cfc166 --- /dev/null +++ b/src/sbt-test/docker/build-command/build.sbt @@ -0,0 +1,9 @@ +enablePlugins(JavaAppPackaging) + +name := "docker-build-command-test" + +version := "0.1.0" + +import NativePackagerHelper._ +mappings in Docker ++= directory("src/main/resources/docker-test") +dockerBuildCommand := Seq("docker", "build", "-t", "docker-build-command-test:0.1.0", "docker-test/") diff --git a/src/sbt-test/docker/build-command/project/plugins.sbt b/src/sbt-test/docker/build-command/project/plugins.sbt new file mode 100644 index 000000000..b53de154c --- /dev/null +++ b/src/sbt-test/docker/build-command/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version")) diff --git a/src/sbt-test/docker/build-command/src/main/resources/docker-test/Dockerfile b/src/sbt-test/docker/build-command/src/main/resources/docker-test/Dockerfile new file mode 100644 index 000000000..4d8863f2d --- /dev/null +++ b/src/sbt-test/docker/build-command/src/main/resources/docker-test/Dockerfile @@ -0,0 +1,3 @@ +FROM busybox + +CMD ["echo", "docker build command override test"] \ No newline at end of file diff --git a/src/sbt-test/docker/build-command/test b/src/sbt-test/docker/build-command/test new file mode 100644 index 000000000..d71cae692 --- /dev/null +++ b/src/sbt-test/docker/build-command/test @@ -0,0 +1,3 @@ +# Stage the distribution and ensure files show up. +> docker:publishLocal +$ exec bash -c 'docker run docker-build-command-test:0.1.0 | grep -q "docker build command override test"' diff --git a/src/sbt-test/docker/build-options/build.sbt b/src/sbt-test/docker/build-options/build.sbt new file mode 100644 index 000000000..83afa6191 --- /dev/null +++ b/src/sbt-test/docker/build-options/build.sbt @@ -0,0 +1,7 @@ +enablePlugins(JavaAppPackaging) + +name := "docker-build-options-test" + +version := "0.1.0" + +dockerBuildOptions := dockerBuildOptions.value ++ Seq("-t", "docker-build-options-test:0.1.0-random-tag") diff --git a/src/sbt-test/docker/build-options/project/plugins.sbt b/src/sbt-test/docker/build-options/project/plugins.sbt new file mode 100644 index 000000000..b53de154c --- /dev/null +++ b/src/sbt-test/docker/build-options/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version")) diff --git a/src/sbt-test/docker/build-options/src/main/scala/Main.scala b/src/sbt-test/docker/build-options/src/main/scala/Main.scala new file mode 100644 index 000000000..61471c658 --- /dev/null +++ b/src/sbt-test/docker/build-options/src/main/scala/Main.scala @@ -0,0 +1,3 @@ +object Main extends App { + println("Hello world") +} diff --git a/src/sbt-test/docker/build-options/test b/src/sbt-test/docker/build-options/test new file mode 100644 index 000000000..2159252ce --- /dev/null +++ b/src/sbt-test/docker/build-options/test @@ -0,0 +1,4 @@ +# Stage the distribution and ensure files show up. +> docker:publishLocal +$ exec bash -c 'docker run docker-build-options-test:0.1.0 | grep -q "Hello world"' +$ exec bash -c 'docker run docker-build-options-test:0.1.0-random-tag | grep -q "Hello world"' diff --git a/src/sbt-test/docker/tag/build.sbt b/src/sbt-test/docker/tag/build.sbt deleted file mode 100644 index ac356f427..000000000 --- a/src/sbt-test/docker/tag/build.sbt +++ /dev/null @@ -1,7 +0,0 @@ -enablePlugins(JavaAppPackaging) - -name := "docker-tag-test" - -version := "0.1.0" - -dockerTag := "docker-tag-test:0.1.0" diff --git a/src/sbt-test/docker/tag/test b/src/sbt-test/docker/tag/test deleted file mode 100644 index 494dc4226..000000000 --- a/src/sbt-test/docker/tag/test +++ /dev/null @@ -1,3 +0,0 @@ -# Stage the distribution and ensure files show up. -> docker:publishLocal -$ exec bash -c 'docker run docker-tag-test:0.1.0 | grep -q "Hello world"' diff --git a/src/sphinx/formats/docker.rst b/src/sphinx/formats/docker.rst index a7a02498e..6aab79381 100644 --- a/src/sphinx/formats/docker.rst +++ b/src/sphinx/formats/docker.rst @@ -115,9 +115,18 @@ Publishing Settings ``dockerUpdateLatest`` The flag to automatic update the latest tag when the ``docker:publish`` task is run. Default value is ``FALSE``. - ``dockerTag`` - The tag to be used during build for the resulting image. - Defaults to ``[dockerRepository]/[name]:[version]``. + ``dockerAlias`` + The alias to be used for tagging the resulting image of the Docker build. + The type of the setting key is ``DockerAlias`. + Defaults to ``[dockerRepository/][name]:[version]``. + + ``dockerBuildOptions`` + Overrides the default Docker build options. + Defaults to ``Seq("--force-rm", "-t", "[dockerAlias]")``. This default is expanded if ``dockerUpdateLatest`` is set to true. + + ``dockerBuildCommand`` + Overrides the default Docker build command. + Defaults to ``Seq("docker", "build", "[dockerBuildOptions]", ".")``. Tasks -----