Skip to content

Commit

Permalink
Initial refactoring on #453
Browse files Browse the repository at this point in the history
  • Loading branch information
muuki88 committed Feb 10, 2015
1 parent bc565ad commit ac82b8d
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 54 deletions.
176 changes: 122 additions & 54 deletions src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,28 @@ object DockerPlugin extends AutoPlugin {
dockerExposedVolumes := Seq(),
dockerRepository := None,
dockerUpdateLatest := false,
dockerEntrypoint := Seq("bin/%s" format executableScriptName.value)
dockerEntrypoint := Seq("bin/%s" format executableScriptName.value),
dockerCmd := Seq(),
dockerCommands := {
val dockerBaseDirectory = (defaultLinuxInstallLocation in Docker).value
val user = (daemonUser in Docker).value
val group = (daemonGroup in Docker).value

val generalCommands = makeFrom(dockerBaseImage.value) +: makeMaintainer((maintainer in Docker).value).toSeq

generalCommands ++
Seq(
makeAdd(dockerBaseDirectory),
makeWorkdir(dockerBaseDirectory),
makeChown(user, group, "."),
makeUser(user),
makeEntrypoint(dockerEntrypoint.value),
makeCmd(dockerCmd.value)
) ++
makeExposePorts(dockerExposedPorts.value) ++
makeVolumes(dockerExposedVolumes.value, user, group)

}

) ++ mapGenericFilesToDocker ++ inConfig(Docker)(Seq(
executableScriptName := executableScriptName.value,
Expand All @@ -91,79 +112,126 @@ object DockerPlugin extends AutoPlugin {
target := target.value / "docker",

daemonUser := "daemon",
daemonGroup := daemonUser.value,
defaultLinuxInstallLocation := "/opt/docker",

dockerPackageMappings <<= sourceDirectory map { dir =>
MappingsHelper contentOf dir
},
dockerGenerateConfig <<= (dockerBaseImage, defaultLinuxInstallLocation,
maintainer, daemonUser, executableScriptName,
dockerExposedPorts, dockerExposedVolumes, target, dockerEntrypoint) map generateDockerConfig,
dockerGenerateConfig <<= (dockerCommands, target) map generateDockerConfig,
dockerTarget <<= (dockerRepository, packageName, version) map {
(repo, name, version) =>
repo.map(_ + "/").getOrElse("") + name + ":" + version
(repo, name, version) => repo.map(_ + "/").getOrElse("") + name + ":" + version
}
))

private[this] final def makeDockerContent(dockerBaseImage: String, dockerBaseDirectory: String, maintainer: String, daemonUser: String, execScript: String, exposedPorts: Seq[Int], exposedVolumes: Seq[String], entrypoint: Seq[String]) = {
val fromCommand = Cmd("FROM", dockerBaseImage)

val maintainerCommand: Option[Cmd] = {
if (maintainer.isEmpty)
None
else
Some(Cmd("MAINTAINER", maintainer))
}

/**
* @param maintainer (optional)
* @return MAINTAINER if defined
*/
private final def makeMaintainer(maintainer: String): Option[CmdLike] =
if (maintainer == null || maintainer.isEmpty) None else Some(Cmd("MAINTAINER", maintainer))

/**
* @param dockerBaseImage
* @return FROM command
*/
private final def makeFrom(dockerBaseImage: String): CmdLike = Cmd("FROM", dockerBaseImage)

/**
* @param dockerBaseDirectory, the installation directory
* @param WORKDIR command, setting dockerBaseDirectory as cwd
*/
private final def makeWorkdir(dockerBaseDirectory: String): CmdLike = Cmd("WORKDIR", dockerBaseDirectory)

/**
* @param dockerBaseDirectory, the installation directory
* @return ADD command adding all files inside the installation directory
*/
private final def makeAdd(dockerBaseDirectory: String): CmdLike = {
val files = dockerBaseDirectory.split(java.io.File.separator)(1)
Cmd("ADD", s"$files /$files")
}

val dockerCommands = Seq(
Cmd("ADD", s"$files /$files"),
Cmd("WORKDIR", "%s" format dockerBaseDirectory),
ExecCmd("RUN", "chown", "-R", daemonUser, "."),
Cmd("USER", daemonUser),
ExecCmd("ENTRYPOINT", entrypoint: _*),
ExecCmd("CMD")
)

val exposeCommand: Option[CmdLike] = {
if (exposedPorts.isEmpty)
None
else
Some(Cmd("EXPOSE", exposedPorts.mkString(" ")))
}

// If the exposed volume does not exist, the volume is made available
// with root ownership. This may be too strict for some directories,
// and we lose the feature that all directories below the install path
// can be written to by the binary. Therefore the directories are
// created before the ownership is changed.
val volumeCommands: Seq[CmdLike] = {
if (exposedVolumes.isEmpty)
Seq()
else
Seq(
ExecCmd("RUN", Seq("mkdir", "-p") ++ exposedVolumes: _*),
ExecCmd("VOLUME", exposedVolumes: _*)
)
}

val commands =
Seq(fromCommand) ++ maintainerCommand ++ volumeCommands ++ exposeCommand ++ dockerCommands
/**
* @param daemonUser
* @param daemonGroup
* @param directory to chown recursively
* @return chown command, owning the installation directory with the daemonuser
*/
private final def makeChown(daemonUser: String, daemonGroup: String, directory: String): CmdLike =
ExecCmd("RUN", "chown", "-R", s"$daemonUser:$daemonGroup", directory)

/**
* @param daemonUser
* @return USER docker command
*/
private final def makeUser(daemonUser: String): CmdLike = Cmd("USER", daemonUser)

/**
* @param entrypoint
* @return ENTRYPOINT command
*/
private final def makeEntrypoint(entrypoint: Seq[String]): CmdLike = ExecCmd("ENTRYPOINT", entrypoint: _*)

/**
* Default CMD implementation as default parameters to ENTRYPOINT.
* @param args
* @return CMD with args in exec form
*/
private final def makeCmd(args: Seq[String]): CmdLike = ExecCmd("CMD", args: _*)

/**
* @param exposedPorts
* @return if ports are exposed the EXPOSE command
*/
private final def makeExposePorts(exposedPorts: Seq[Int]): Option[CmdLike] = {
if (exposedPorts.isEmpty) None else Some(Cmd("EXPOSE", exposedPorts mkString " "))
}

Dockerfile(commands: _*).makeContent
/**
* If the exposed volume does not exist, the volume is made available
* with root ownership. This may be too strict for some directories,
* and we lose the feature that all directories below the install path
* can be written to by the binary. Therefore the directories are
* created before the ownership is changed.
*
* All directories created afterwards are chowned.
*
* @param exposedVolumes
* @return commands to create, chown and declare volumes
*/
private final def makeVolumes(exposedVolumes: Seq[String], daemonUser: String, daemonGroup: String): Seq[CmdLike] = {
if (exposedVolumes.isEmpty) Seq()
else Seq(
ExecCmd("RUN", Seq("mkdir", "-p") ++ exposedVolumes: _*),
makeChown(daemonUser, daemonGroup, exposedVolumes mkString " "),
ExecCmd("VOLUME", exposedVolumes: _*)
)
}

private[this] final def generateDockerConfig(
dockerBaseImage: String, dockerBaseDirectory: String, maintainer: String, daemonUser: String, execScript: String, exposedPorts: Seq[Int], exposedVolumes: Seq[String], target: File, entrypoint: Seq[String]
) = {
val dockerContent = makeDockerContent(dockerBaseImage, dockerBaseDirectory, maintainer, daemonUser, execScript, exposedPorts, exposedVolumes, entrypoint)
/**
* @param commands representing the Dockerfile
* @return String representation of the Dockerfile described by commands
*/
private final def makeDockerContent(commands: Seq[CmdLike]): String = Dockerfile(commands: _*).makeContent

/**
* @param commands, docker content
* @param target directory for Dockerfile
* @return Dockerfile
*/
private[this] final def generateDockerConfig(commands: Seq[CmdLike], target: File): File = {
val dockerContent = makeDockerContent(commands)

val f = target / "Dockerfile"
IO.write(f, dockerContent)
f
}

/**
* uses the `mappings in Unversial` to generate the
* `mappings in Docker`.
*/
def mapGenericFilesToDocker: Seq[Setting[_]] = {
def renameDests(from: Seq[(File, String)], dest: String) = {
for {
Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/com/typesafe/sbt/packager/docker/Keys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ trait DockerKeys {
val dockerRepository = SettingKey[Option[String]]("dockerRepository", "Repository for published Docker 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 dockerCommands = TaskKey[Seq[CmdLike]]("dockerCommands", "List of docker commands that form the Dockerfile")
}

0 comments on commit ac82b8d

Please sign in to comment.