Isvara
Isvara

Reputation: 3463

Why is sbt-native-packager staging bin/start as a directory instead of a script?

I'm using sbt-native-packager and sbt-docker to generate Docker images as described in this tutorial. The Docker image won't run (permission denied), and on inspection it turns out that bin/start is being created as an empty directory.

dan@cobalt:~/projects/confabulous/deva$ ls -l target/universal/stage/bin/
total 24
-rwxrw-r-- 1 dan dan 11591 Aug  5 20:44 deva
-rw-rw-r-- 1 dan dan  6211 Aug  5 20:44 deva.bat
drwxrwxr-x 2 dan dan  4096 Dec 31  1969 start

It also has a null timestamp for some reason. Why is it being created as a directory and not a shell script?

Here's my plugins.sbt:

addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.4")

addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.7.4")

addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.1")

addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "0.5.0")

resolvers += Classpaths.sbtPluginReleases

And here's my build.sbt:

import DockerKeys._
import sbtdocker.{Dockerfile, ImageName}
import com.typesafe.sbt.packager.Keys._

organization  := "com.confabulous"

name          := "deva"

version       := "0.0.1"

scalaVersion  := "2.10.3"

scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8", "-language:postfixOps")

resolvers ++= Seq(
  "sonatype releases"  at "https://oss.sonatype.org/content/repositories/releases/",
  "sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/",
  "typesafe repo"      at "http://repo.typesafe.com/typesafe/releases/"
)

libraryDependencies ++= Seq(
  "ch.qos.logback"      %   "logback-classic" % "1.0.9",
  "com.typesafe.akka"   %   "akka-slf4j_2.10" % "2.3.3",
  "com.typesafe.akka"   %%  "akka-actor"      % "2.3.3",
  "com.typesafe.akka"   %%  "akka-remote"     % "2.3.3",
  "com.typesafe.akka"   %%  "akka-agent"      % "2.3.3",
  "com.typesafe.slick"  %%  "slick"           % "2.0.1-RC1",
  "org.mozilla"         %   "rhino"           % "1.7R4",
  "org.postgresql"      %   "postgresql"      % "9.3-1101-jdbc3",
  "org.msgpack"         %%  "msgpack-scala"   % "0.6.8",
  "com.livestream"      %%  "scredis"         % "1.1.2",
  "com.confabulous"     %%  "messages"        % "0.0.1-SNAPSHOT",
  "com.confabulous"     %%  "db"              % "0.0.1-SNAPSHOT"
)

packageArchetype.java_server

sbtdocker.Plugin.dockerSettings

mappings in Universal += baseDirectory.value / "docker" / "start" -> "bin/start"

docker <<= docker.dependsOn(com.typesafe.sbt.packager.universal.Keys.stage.in(Compile))

// Define a Dockerfile
dockerfile in docker <<= (name, stagingDirectory in Universal) map {
    case (appName, stageDir) =>
        val workingDir = s"/opt/${appName}"
        new Dockerfile {
            // Use a base image that contain Java
            from("relateiq/oracle-java7")
            maintainer("Dan Ellis <[email protected]>")
            expose(1600)
            add(stageDir, workingDir)
            run("chmod",  "+x",  s"/opt/${appName}/bin/${appName}")
            run("chmod",  "+x",  s"/opt/${appName}/bin/start")
            workDir(workingDir)
            entryPointShell(s"bin/start", appName, "$@")
        }
}

imageName in docker := {
    ImageName(
        namespace = Some("confabulous.com"),
        repository = name.value
        //,tag = Some("v" + version.value))
    )
}

Upvotes: 1

Views: 1695

Answers (2)

Gary Coady
Gary Coady

Reputation: 1196

Another option is to make use of the experimental support for Docker in sbt-native-packager.

If you remove the Docker-related lines from build.sbt, and add a maintainer setting, you can:

sbt docker:stage generate a Dockerfile and context in target/docker/stage

sbt docker:publishLocal generate a local image

sbt docker:publish generate an image and push it remotely

If you want to modify the commands passed to the start script, you can make modifications like this:

bashScriptExtraDefines := Seq(
  "CLUSTER_IP=$(/sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}')",
  "addResidual ${CLUSTER_IP}"
)

addResidual is a function in the start script generated by sbt-native-packager, that adds an extra parameter to the command line arguments for the program.

addJava is a function that adds an extra parameter to the Java binary that runs your program.


Your build.sbt might look like this:

import com.typesafe.sbt.packager.Keys._

organization  := "com.confabulous"

name          := "deva"

version       := "0.0.1"

scalaVersion  := "2.10.3"

scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8", "-language:postfixOps")

resolvers ++= Seq(
  "sonatype releases"  at "https://oss.sonatype.org/content/repositories/releases/",
  "sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/",
  "typesafe repo"      at "http://repo.typesafe.com/typesafe/releases/"
)

libraryDependencies ++= Seq(
  "ch.qos.logback"      %   "logback-classic" % "1.0.9",
  "com.typesafe.akka"   %   "akka-slf4j_2.10" % "2.3.3",
  "com.typesafe.akka"   %%  "akka-actor"      % "2.3.3",
  "com.typesafe.akka"   %%  "akka-remote"     % "2.3.3",
  "com.typesafe.akka"   %%  "akka-agent"      % "2.3.3",
  "com.typesafe.slick"  %%  "slick"           % "2.0.1-RC1",
  "org.mozilla"         %   "rhino"           % "1.7R4",
  "org.postgresql"      %   "postgresql"      % "9.3-1101-jdbc3",
  "org.msgpack"         %%  "msgpack-scala"   % "0.6.8",
  "com.livestream"      %%  "scredis"         % "1.1.2",
  "com.confabulous"     %%  "messages"        % "0.0.1-SNAPSHOT",
  "com.confabulous"     %%  "db"              % "0.0.1-SNAPSHOT"
)

packageArchetype.java_server

maintainer := "Dan Ellis <[email protected]>"

More information should be available from the sbt-native-packager documentation. Comments are also welcome.

Upvotes: 3

Gary Coady
Gary Coady

Reputation: 1196

The referenced article is part 2 of a series, where the address of the container is passed into the Java program by a script. The script itself is referenced in part 1.

mappings in Universal takes a sequence of (File, String) tuples. The File is copied to the path specified by the String in the resulting image.

In this case, if there is no file present at baseDirectory.value / "docker" / "start", then nothing is available to copy, and the resulting is the behavior you describe.

You should create an appropriate start script, as discussed in part 1.

Upvotes: 3

Related Questions