Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sysvinit script for debian package #85

Merged
merged 4 commits into from
Nov 25, 2013
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.12.4
sbt.version=0.13.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't actually upgrade yet, as we still support 0.12 for at least another 6 months.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#! /bin/sh

### BEGIN INIT INFO
# Provides: ${{app_name}}
# Required-Start: $syslog
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: ${{descr}}
### END INIT INFO

PIDFILE=/var/run/${{app_name}}.pid
DAEMON_USER=${{daemon_user}}

. /lib/init/vars.sh
. /lib/lsb/init-functions

get_java_cmd() {
if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
echo "$JAVA_HOME/bin/java"
else
echo "java"
fi
}

JAVA_CMD=$(get_java_cmd)

RUN_CMD=$JAVA_CMD -cp ${{app_classpath}} ${{app_main_class}}

case "$1" in

start) log_daemon_msg "Starting ${{app_name}}"

start-stop-daemon --background --start --chuid $DAEMON_USER --make-pidfile --pidfile $PIDFILE --exec $RUN_CMD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I'm glad to see this stuff baked in now.


;;
stop) log_daemon_msg "Stopping ${{app_name}}"

start-stop-daemon --stop --pidfile $PIDFILE --chuid $DAEMON_USER

RETVAL=$?
[ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE
exit 2
;;
*) log_daemon_msg "Usage: /etc/init.d/${{app_name}} {start|stop}"
exit 2
;;
esac
exit 0
2 changes: 2 additions & 0 deletions src/main/scala/com/typesafe/sbt/PackagerPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ object SbtNativePackager extends Plugin
genericMappingSettings ++ archetypes.JavaAppPackaging.settings
def java_server: Seq[Setting[_]] =
genericMappingSettings ++ archetypes.JavaServerAppPackaging.settings
def java_server_sysvinit: Seq[Setting[_]] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm.... I'm wondering if we should instead do this via a flag setting.....

That may complicate things, but the combinatorial explosion of "sysvinit", "upstart" and whatever shows up for RedHat/Windows/MacOSX may be a bit large....

genericMappingSettings ++ archetypes.JavaServerAppSysVinitPackaging.settings
}

// TODO - Add a few targets that detect the current OS and build a package for that OS.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ package com.typesafe.sbt.packager.archetypes
* Makes use of the associated upstart-template, with a few hooks
*
*/
object JavaAppUpstartScript {
object JavaAppUpstartScript extends JavaAppScript {

private[this] def upstartTemplateSource: java.net.URL = getClass.getResource("upstart-template")
protected def templateSource: java.net.URL = getClass.getResource("upstart-template")

private[this] def postinstTemplateSource: java.net.URL = getClass.getResource("postinst-template")
private[this] def preremTemplateSource: java.net.URL = getClass.getResource("prerem-template")
/**
*
* @param author -
* @param description - short description
* @param descr - short description
* @param execScript - name of the script in /usr/bin
* @param chdir - execution path of the script
* @param retries - on fail, how often should a restart be tried
Expand All @@ -36,11 +34,53 @@ object JavaAppUpstartScript {
"retries" -> retries.toString,
"retryTimeout" -> retryTimeout.toString)

}

object JavaAppSysVinitScript extends JavaAppScript {
protected def templateSource: java.net.URL = getClass.getResource("sysvinit-template")


/**
*
* @param author -
* @param description - short description
* @return Seq of key,replacement pairs
*/
def makeReplacements(
author: String,
description: String,
appDir: String,
appName: String,
appMainClass: String,
appClasspath: String,
daemonUser: String

): Seq[(String, String)] =
Seq(
"author" -> author,
"descr" -> description,
"app_name" -> appName,
"app_dir" -> appDir,
"app_main_class" -> appMainClass,
"app_classpath" -> appClasspath,
"daemon_user" -> daemonUser
)
}


trait JavaAppScript {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice re-use.


protected def templateSource: java.net.URL

protected def postinstTemplateSource: java.net.URL = getClass.getResource("postinst-template")
protected def preremTemplateSource: java.net.URL = getClass.getResource("prerem-template")


def generateScript(replacements: Seq[(String, String)]): String =
TemplateWriter.generateScript(upstartTemplateSource, replacements)
TemplateWriter.generateScript(templateSource, replacements)

def generatePrerm(appName: String): String =
TemplateWriter.generateScript(preremTemplateSource, Seq("app_name" -> appName))
def generatePostinst(appName: String): String =
TemplateWriter.generateScript(postinstTemplateSource, Seq("app_name" -> appName))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import com.typesafe.sbt.packager.linux.LinuxPackageMapping
*
* **NOTE: EXPERIMENTAL** This currently only supports debian upstart scripts.
*/
object JavaServerAppPackaging {
object JavaServerAppPackaging extends JavaServerAppPackaging {

def settings: Seq[Setting[_]] =
JavaAppPackaging.settings ++
Expand All @@ -42,26 +42,79 @@ object JavaServerAppPackaging {
debianMakePrermScript <<= (normalizedName, target in Universal) map makeDebianPrermScript,
debianMakePostinstScript <<= (normalizedName, target in Universal) map makeDebianPostinstScript)

private[this] final def makeDebianPrermScript(name: String, tmpDir: File): Option[File] = {

private def makeDebianUpstartScript(replacements: Seq[(String, String)], name: String, tmpDir: File): Option[File] =
if (replacements.isEmpty) None
else {
val scriptBits = JavaAppUpstartScript.generateScript(replacements)
val script = tmpDir / "tmp" / "bin" / (name + ".conf")
IO.write(script, scriptBits)
Some(script)
}
}


object JavaServerAppSysVinitPackaging extends JavaServerAppPackaging {

def settings: Seq[Setting[_]] =
JavaAppPackaging.settings ++ debianSysVinitSettings

def debianSysVinitSettings: Seq[Setting[_]] = {
Seq(
debianSysVinitScriptReplacements <<= (maintainer in Debian, packageSummary in Debian, daemonUser in Debian,
normalizedName, name, sbt.Keys.version, defaultLinuxInstallLocation, sbt.Keys.mainClass in Compile, scriptClasspath)
map { (author, descr, daemonUser, normalizedName, name, version, installLocation, mainClass, cp) =>
// TODO name-version is copied from UniversalPlugin. This should be consolidated into a setting (install location...)
val appDir = installLocation + "/" + normalizedName
val appClasspath = cp.map(appDir + "/lib/" + _).mkString(":")

JavaAppSysVinitScript.makeReplacements(
author = author, description = descr,
appDir = appDir,
appName = name,
appClasspath = appClasspath,
appMainClass = mainClass.get,
daemonUser = daemonUser
)
},
debianMakeSysVinitScript <<= (debianSysVinitScriptReplacements, normalizedName, target in Universal) map makeDebianSysVinitScript,
linuxPackageMappings in Debian <++= (debianMakeSysVinitScript, normalizedName) map { (script, name) =>
for {
s <- script.toSeq
} yield LinuxPackageMapping(Seq(s -> ("/etc/init.d/" + name))).withPerms("0755")
},
// TODO - only make these if the upstart config exists...
debianMakePrermScript <<= (normalizedName, target in Universal) map makeDebianPrermScript,
debianMakePostinstScript <<= (normalizedName, target in Universal) map makeDebianPostinstScript)
}


private def makeDebianSysVinitScript(replacements: Seq[(String, String)], name: String, tmpDir: File): Option[File] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are their differences between System V init scripts in rpm vs. debian that would not let us re-use this mechanism?

if (replacements.isEmpty) None
else {
val scriptBits = JavaAppSysVinitScript.generateScript(replacements)
val script = tmpDir / "tmp" / "bin" / (name + ".conf")
IO.write(script, scriptBits)
Some(script)
}
}


trait JavaServerAppPackaging {

def settings: Seq[Setting[_]]

protected def makeDebianPrermScript(name: String, tmpDir: File): Option[File] = {
val scriptBits = JavaAppUpstartScript.generatePrerm(name)
val script = tmpDir / "tmp" / "bin" / "debian-prerm"
IO.write(script, scriptBits)
Some(script)
}

private[this] final def makeDebianPostinstScript(name: String, tmpDir: File): Option[File] = {
protected def makeDebianPostinstScript(name: String, tmpDir: File): Option[File] = {
val scriptBits = JavaAppUpstartScript.generatePostinst(name)
val script = tmpDir / "tmp" / "bin" / "debian-postinst"
IO.write(script, scriptBits)
Some(script)
}

private[this] final def makeDebianUpstartScript(replacements: Seq[(String, String)], name: String, tmpDir: File): Option[File] =
if (replacements.isEmpty) None
else {
val scriptBits = JavaAppUpstartScript.generateScript(replacements)
val script = tmpDir / "tmp" / "bin" / (name + ".conf")
IO.write(script, scriptBits)
Some(script)
}
}
}
17 changes: 17 additions & 0 deletions src/main/scala/com/typesafe/sbt/packager/debian/Keys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ trait DebianKeys {
| retries - on fail, how often should a restart be tried
| retryTimeout - pause between retries
""".stripMargin)

// Debian sysVinit scripts
val debianMakeSysVinitScript = TaskKey[Option[File]]("makeSysVinitScript", "Creates or discovers the sysVinit script used by this project")
val debianSysVinitScriptReplacements = TaskKey[Seq[(String, String)]]("sysVinitScriptReplacements",
"""|Replacements of template parameters used in the sysVinit script.
| Default supported templates:
| execScript - name of the script in /usr/bin
| daemonUser - daemon user
| author - author of this project
| descr - short description
| retries - on fail, how often should a restart be tried
| retryTimeout - pause between retries
""".stripMargin)

}

/** Keys used for Debian specific settings. */
Expand All @@ -77,5 +91,8 @@ object Keys extends DebianKeys {
def target = sbt.Keys.target
def streams = sbt.Keys.streams

//init script parameters
def daemonUser = linux.Keys.daemonUser
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. I think we can re-use this for the RPM.


val debianPackageInstallSize = TaskKey[Long]("debian-installed-size")
}
1 change: 1 addition & 0 deletions src/main/scala/com/typesafe/sbt/packager/linux/Keys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ trait Keys {
val packageSummary = SettingKey[String]("package-summary", "Summary of the contents of a linux package.")
val packageDescription = SettingKey[String]("package-description", "The description of the package. Used when searching.")
val maintainer = SettingKey[String]("maintainer", "The name/email address of a maintainer for the native package.")
val daemonUser = SettingKey[String]("daemon-user", "User to start application daemon")
val linuxPackageMappings = TaskKey[Seq[LinuxPackageMapping]]("linux-package-mappings", "File to install location mappings including owner and privileges.")
val linuxPackageSymlinks = TaskKey[Seq[LinuxSymlink]]("linux-package-symlinks", "Symlinks we should produce in the underlying package.")
val generateManPages = TaskKey[Unit]("generate-man-pages", "Shows all the man files in the current project")
Expand Down
15 changes: 15 additions & 0 deletions src/sbt-test/debian/sysvinit-deb/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import NativePackagerKeys._

packageArchetype.java_server_sysvinit

name := "debian-test"

version := "0.1.0"

maintainer := "Josh Suereth <[email protected]>"

packageSummary := "Test debian package"

packageDescription := """A fun package description of our software,
with multiple lines."""

1 change: 1 addition & 0 deletions src/sbt-test/debian/sysvinit-deb/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version"))
6 changes: 6 additions & 0 deletions src/sbt-test/debian/sysvinit-deb/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Run the debian packaging.
> debian:package-bin
$ exists target/debian-test-0.1.0.deb

$ exists target/debian-test-0.1.0/etc
$ exists target/debian-test-0.1.0/etc/init.d/debian-test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Love that you added tests :)