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 243eccc24..bbb85f07a 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala @@ -13,6 +13,9 @@ import com.typesafe.sbt.SbtNativePackager.Universal import com.typesafe.sbt.packager.Compat._ import com.typesafe.sbt.packager.{MappingsHelper, Stager} +import scala.sys.process.Process +import scala.util.Try + /** * == Docker Plugin == * @@ -80,6 +83,9 @@ object DockerPlugin extends AutoPlugin { dockerEntrypoint := Seq("bin/%s" format executableScriptName.value), dockerCmd := Seq(), dockerExecCommand := Seq("docker"), + dockerVersion := Try(Process(dockerExecCommand.value ++ Seq("version --format '{{.Server.Version}}'")).!!) + .toOption.map(_.trim) + .flatMap(DockerVersion.parse), dockerBuildOptions := Seq("--force-rm") ++ Seq("-t", dockerAlias.value.versioned) ++ ( if (dockerUpdateLatest.value) Seq("-t", dockerAlias.value.latest) @@ -96,7 +102,7 @@ object DockerPlugin extends AutoPlugin { val generalCommands = makeFrom(dockerBaseImage.value) +: makeMaintainer((maintainer in Docker).value).toSeq generalCommands ++ - Seq(makeWorkdir(dockerBaseDirectory), makeAdd(dockerBaseDirectory), makeChown(user, group, "." :: Nil)) ++ + Seq(makeWorkdir(dockerBaseDirectory)) ++ makeAdd(dockerVersion.value, dockerBaseDirectory, user, group) ++ dockerLabels.value.map(makeLabel) ++ makeExposePorts(dockerExposedPorts.value, dockerExposedUdpPorts.value) ++ makeVolumes(dockerExposedVolumes.value, user, group) ++ @@ -179,10 +185,14 @@ object DockerPlugin extends AutoPlugin { Cmd("WORKDIR", dockerBaseDirectory) /** - * @param dockerBaseDirectory, the installation directory + * @param dockerVersion + * @param dockerBaseDirectory the installation directory + * @param daemonUser + * @param daemonGroup * @return ADD command adding all files inside the installation directory */ - private final def makeAdd(dockerBaseDirectory: String): CmdLike = { + private final def makeAdd(dockerVersion: Option[DockerVersion], dockerBaseDirectory: String, + daemonUser: String, daemonGroup: String): Seq[CmdLike] = { /** * This is the file path of the file in the Docker image, and does not depend on the OS where the image @@ -190,7 +200,17 @@ object DockerPlugin extends AutoPlugin { * on e.g. Windows systems. */ val files = dockerBaseDirectory.split(UnixSeparatorChar)(1) - Cmd("ADD", s"$files /$files") + + if (dockerVersion.exists(DockerSupport.chownFlag)) { + Seq( + Cmd("ADD", s"--chown=$daemonUser:$daemonGroup $files /$files") + ) + } else { + Seq( + Cmd("ADD", s"$files /$files"), + makeChown(daemonUser, daemonGroup, "." :: Nil) + ) + } } /** @@ -278,7 +298,7 @@ object DockerPlugin extends AutoPlugin { } /** - * uses the `mappings in Unversial` to generate the + * uses the `mappings in Universal` to generate the * `mappings in Docker`. */ def mapGenericFilesToDocker: Seq[Setting[_]] = { 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 fb0b08b28..819a96455 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/DockerSpotifyClientPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerSpotifyClientPlugin.scala @@ -54,7 +54,10 @@ object DockerSpotifyClientPlugin extends AutoPlugin { override lazy val projectSettings: Seq[Setting[_]] = inConfig(Docker)(clientSettings) - def clientSettings = Seq(publishLocal := publishLocalDocker.value) + def clientSettings = Seq( + publishLocal := publishLocalDocker.value, + dockerVersion := dockerServerVersion.value + ) def publishLocalDocker: Def.Initialize[Task[Unit]] = Def.task { val context = stage.value @@ -81,4 +84,9 @@ object DockerSpotifyClientPlugin extends AutoPlugin { } } + def dockerServerVersion: Def.Initialize[Option[DockerVersion]] = Def.setting { + val docker: DockerClient = DefaultDockerClient.fromEnv().build() + DockerVersion.parse(docker.version().version()) + } + } diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/DockerSupport.scala b/src/main/scala/com/typesafe/sbt/packager/docker/DockerSupport.scala new file mode 100644 index 000000000..23e73e8d9 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerSupport.scala @@ -0,0 +1,9 @@ +package com.typesafe.sbt.packager.docker + +object DockerSupport { + + def chownFlag(version: DockerVersion): Boolean = { + (version.major == 17 && version.minor >= 9) || version.major > 17 + } + +} diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/DockerVersion.scala b/src/main/scala/com/typesafe/sbt/packager/docker/DockerVersion.scala new file mode 100644 index 000000000..0ab2218f1 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerVersion.scala @@ -0,0 +1,17 @@ +package com.typesafe.sbt.packager.docker + +import scala.util.matching.Regex + + +case class DockerVersion(major: Int, minor: Int, patch: Int, release: Option[String]) + +object DockerVersion { + private val DockerVersionPattern: Regex = "^'?([0-9]+).([0-9]+).([0-9]+)-?([-a-z]+)?'?$".r + + def parse(version: String): Option[DockerVersion] = { + Option(version).collect { + case DockerVersionPattern(major, minor, patch, release) => + new DockerVersion(major.toInt, minor.toInt, patch.toInt, Option(release)) + } + } +} 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 6c7fcb758..5b2e47d15 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala @@ -29,6 +29,7 @@ trait DockerKeys { "Docker CMD. Used together with dockerEntrypoint. Arguments passed in exec form" ) val dockerExecCommand = SettingKey[Seq[String]]("dockerExecCommand", "The shell command used to exec Docker") + val dockerVersion = SettingKey[Option[DockerVersion]]("dockerVersion", "The docker server version") 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 dockerLabels = SettingKey[Map[String, String]]("dockerLabels", "Labels applied to the Docker image") diff --git a/src/sphinx/formats/docker.rst b/src/sphinx/formats/docker.rst index 6a03e3f64..8ce5a8ae0 100644 --- a/src/sphinx/formats/docker.rst +++ b/src/sphinx/formats/docker.rst @@ -114,6 +114,9 @@ Environment Settings Overrides the default entrypoint for docker-specific service discovery tasks before running the application. Defaults to the bash executable script, available at ``bin/