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

SystemD services now source /etc/default/{{app_name}} (resolves #737) #745

Merged
merged 1 commit into from
Feb 23, 2016
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ project/project
log/
target/
.cache
.ensime*
Copy link
Contributor

Choose a reason for hiding this comment

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

pure personal interest: what editor do you use with ensime?

Copy link
Member

Choose a reason for hiding this comment

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

I'd like to recommend emacs + evil (:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's it! Emacs and evil :)

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# #####################################
# ##### Environment Configuration #####
# #####################################

# This file is parsed by systemd. You can modify it to specify environment
# variables for your application.
#
# For a description of the format, see: `man systemd.exec`, section
# `EnvironmentFile`.

# Available replacements
# ------------------------------------------------
# ${{author}} debian author
# ${{descr}} debian package description
# ${{exec}} startup script name
# ${{chdir}} app directory
# ${{retries}} retries for startup
# ${{retryTimeout}} retry timeout
# ${{app_name}} normalized app name
# ${{daemon_user}} daemon user
# -------------------------------------------------

# Setting JAVA_OPTS
# -----------------
# JAVA_OPTS="-Dpidfile.path=/var/run/${{app_name}}/play.pid"

# Setting PIDFILE
# ---------------
# PIDFILE="/var/run/${{app_name}}/play.pid"
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Requires=${{start_facilities}}
[Service]
Type=simple
WorkingDirectory=${{chdir}}
EnvironmentFile=${{env_config}}
ExecStart=${{chdir}}/bin/${{exec}}
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ trait JavaAppKeys {
val bashScriptDefines = TaskKey[Seq[String]]("bashScriptDefines", "A list of definitions that should be written to the bash file template.")
val bashScriptExtraDefines = TaskKey[Seq[String]]("bashScriptExtraDefines", "A list of extra definitions that should be written to the bash file template.")
val bashScriptConfigLocation = TaskKey[Option[String]]("bashScriptConfigLocation", "The location where the bash script will load default argument configuration from.")
// TODO - we should change this key name in future versions; it also specified
// the location of the systemd EnvironmentFile
val bashScriptEnvConfigLocation = SettingKey[Option[String]]("bashScriptEnvConfigLocation", "The location of a bash script that will be sourced before running the app.")
val batScriptExtraDefines = TaskKey[Seq[String]]("batScriptExtraDefines", "A list of extra definitions that should be written to the bat file template.")
val scriptClasspathOrdering = TaskKey[Seq[(File, String)]]("scriptClasspathOrdering", "The order of the classpath used at runtime for the bat/bash scripts.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ object JavaServerAppPackaging extends AutoPlugin {
/** These settings will be provided by this archetype*/
def javaServerSettings: Seq[Setting[_]] = linuxSettings ++ debianSettings ++ rpmSettings

protected def etcDefaultTemplateSource: java.net.URL = getClass.getResource(ETC_DEFAULT + "-template")

/**
* general settings which apply to all linux server archetypes
*
Expand All @@ -59,27 +57,30 @@ object JavaServerAppPackaging extends AutoPlugin {
},
// === etc config mapping ===
bashScriptEnvConfigLocation := Some("/etc/default/" + (packageName in Linux).value),
linuxEtcDefaultTemplate <<= sourceDirectory map { dir =>
val overrideScript = dir / "templates" / ETC_DEFAULT
if (overrideScript.exists) overrideScript.toURI.toURL
else etcDefaultTemplateSource
},
linuxStartScriptName := None,
makeEtcDefault <<= (packageName in Linux, target in Universal, linuxEtcDefaultTemplate, linuxScriptReplacements)
map makeEtcDefaultScript,
linuxPackageMappings <++= (makeEtcDefault, bashScriptEnvConfigLocation) map { (conf, envLocation) =>
val mapping = for (
path <- envLocation;
c <- conf
) yield LinuxPackageMapping(Seq(c -> path), LinuxFileMetaData(Users.Root, Users.Root, "644")).withConfig()

mapping.toSeq
}

linuxStartScriptName := None
)
Copy link
Contributor

Choose a reason for hiding this comment

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

These were redundant, right? As we only use debian:packageBin or rpm:packageBin

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, they didn't work anymore in the global config scope since I added the dependency for serverLoading, and that config key is not defined globally.


/* etcDefaultConfig is dependent on serverLoading (systemd, systemv, etc.),
* and is therefore distro specific. As such, these settings cannot be defined
* in the global config scope. */
private[this] val etcDefaultConfig: Seq[Setting[_]] = Seq(
linuxEtcDefaultTemplate := getEtcTemplateSource(
sourceDirectory.value,
serverLoading.value),
makeEtcDefault := makeEtcDefaultScript(
(packageName in Linux).value,
(target in Universal).value,
linuxEtcDefaultTemplate.value,
linuxScriptReplacements.value),
linuxPackageMappings ++= etcDefaultMapping(
makeEtcDefault.value,
bashScriptEnvConfigLocation.value)
)

def debianSettings: Seq[Setting[_]] = {
import DebianPlugin.Names.{ Preinst, Postinst, Prerm, Postrm }
inConfig(Debian)(etcDefaultConfig) ++
Copy link
Contributor

Choose a reason for hiding this comment

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

+1

inConfig(Debian)(Seq(
serverLoading := Upstart,
startRunlevels <<= (serverLoading) apply defaultStartRunlevels,
Expand Down Expand Up @@ -131,6 +132,7 @@ object JavaServerAppPackaging extends AutoPlugin {

def rpmSettings: Seq[Setting[_]] = {
import RpmPlugin.Names.{ Pre, Post, Preun, Postun }
inConfig(Rpm)(etcDefaultConfig) ++
inConfig(Rpm)(Seq(
serverLoading := SystemV,
startRunlevels <<= (serverLoading) apply defaultStartRunlevels,
Expand Down Expand Up @@ -242,6 +244,45 @@ object JavaServerAppPackaging extends AutoPlugin {
}
}

/* Find the template source for the given Server loading scheme, with cascading fallback
* If the serverLoader scheme is SystemD, then searches for files in this order:
*
* (assuming sourceDirectory is `src`)
*
* - src/templates/etc-default-systemd
* - src/templates/etc-default
* - Provided template
*/

private[this] def getEtcTemplateSource(sourceDirectory: File, loader: ServerLoader): java.net.URL = {
val (suffix, default) = loader match {
case Upstart =>
("-upstart", getClass.getResource(ETC_DEFAULT + "-template"))
case SystemV =>
("-systemv", getClass.getResource(ETC_DEFAULT + "-template"))
case Systemd =>
("-systemd", getClass.getResource(ETC_DEFAULT + "-systemd-template"))
}

val overrides = List[File](
sourceDirectory / "templates" / (ETC_DEFAULT + suffix),
sourceDirectory / "templates" / ETC_DEFAULT)
overrides.
find(_.exists).
map(_.toURI.toURL).
getOrElse(default)
}

// Used to tell our packager to install our /etc/default/{{appName}} config file.
protected def etcDefaultMapping(conf: Option[File], envLocation: Option[String]): Seq[LinuxPackageMapping] = {
val mapping = for (
path <- envLocation;
c <- conf
) yield LinuxPackageMapping(Seq(c -> path), LinuxFileMetaData(Users.Root, Users.Root, "644")).withConfig()

mapping.toSeq
}

protected def startScriptMapping(name: String, script: Option[File], loader: ServerLoader, scriptDir: String, scriptName: Option[String]): Seq[LinuxPackageMapping] = {
val (path, permissions, isConf) = loader match {
case Upstart => ("/etc/init/" + scriptName.getOrElse(name + ".conf"), "0644", "true")
Expand Down
35 changes: 35 additions & 0 deletions src/sbt-test/debian/override-etc-default/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import com.typesafe.sbt.packager.archetypes.ServerLoader

enablePlugins(JavaServerAppPackaging, JDebPackaging)

serverLoading in Debian := ServerLoader.Upstart

// TODO change this after #437 is fixed
daemonUser in Linux := "root"

daemonGroup in Linux := "app-group"

mainClass in Compile := Some("empty")

name := "debian-test"

name in Debian := "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."""

TaskKey[Unit]("check-etc-default") <<= (target, streams) map { (target, out) =>
val extracted = target / "tmp" / "extracted-package"
extracted.mkdirs()
Seq("dpkg-deb", "-R", (target / "debian-test_0.1.0_all.deb").absolutePath, extracted.absolutePath).!

val script = IO.read(extracted / "etc" / "default" / "debian-test")
assert(script.startsWith("# right etc-default template"), s"etc-default script wasn't picked, contents instead are:\n$script")
()
}
3 changes: 3 additions & 0 deletions src/sbt-test/debian/override-etc-default/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version"))

libraryDependencies += "org.vafer" % "jdeb" % "1.3" artifacts (Artifact("jdeb", "jar", "jar"))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# right etc-default template
6 changes: 6 additions & 0 deletions src/sbt-test/debian/override-etc-default/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Run the debian packaging.
> debian:packageBin
$ exists target/debian-test_0.1.0_all.deb

# Check files for defaults
> check-etc-default
7 changes: 7 additions & 0 deletions src/sbt-test/debian/systemd-deb/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ TaskKey[Unit]("check-startup-script") <<= (target, streams) map { (target, out)
val script = IO.read(target / "debian-test-0.1.0" / "usr" / "lib" / "systemd" / "system" / "debian-test.service")
assert(script.contains("Requires=network.target"), "script doesn't contain Default-Start header\n" + script)
assert(script.contains("User=testuser"), "script doesn't contain `User` header\n" + script)
assert(script.contains("EnvironmentFile=/etc/default/debian-test"), "script doesn't contain EnvironmentFile header\n" + script)
out.log.success("Successfully tested systemd start up script")
()
}

TaskKey[Unit]("check-etc-default") <<= (target, streams) map { (target, out) =>
val script = IO.read(target / "debian-test-0.1.0" / "etc" / "default" / "debian-test")
assert(script.contains("systemd"), s"systemd etc-default template wasn't selected; contents are:\n" + script)
()
}
3 changes: 2 additions & 1 deletion src/sbt-test/debian/systemd-deb/test
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ $ exists target/debian-test_0.1.0_all.deb

$ exists target/debian-test-0.1.0/usr/lib/systemd/system/debian-test.service

> check-startup-script
> check-startup-script
> check-etc-default
16 changes: 14 additions & 2 deletions src/sphinx/archetypes/cheatsheet.rst
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,21 @@ You can use ``${{variable_name}}`` to reference variables when writing your scri

.. _server-app-config:

Server App Config - ``src/templates/etc-default``
Server App Config - ``src/templates/etc-default-{systemv,systemd}``
-------------------------------------------------

Creating a file here will override the ``/etc/default/<application>`` template
used when SystemV is the server loader.
for the corresponding loader.

The file `/etc/default/<application>` is used as follows given the loader:

- `systemv`: sourced as a bourne script.
- `systemd`: used as an EnvironmentFile directive parameter (see `man
systemd.exec`, section `EnvironmentFile` for a description of the expected file
format).
- `upstart`: presently ignored.

If you're only overriding `JAVA_OPTS`, your environment file could be compatible
with both systemv and systemd loaders; if such is the case, you can specify a
single file at `src/templates/etc-default` which will serve as an override for
all loaders.
8 changes: 5 additions & 3 deletions src/sphinx/archetypes/java_server/customize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ Linux Configuration
There are different ways described in :doc:`Customizing the Application </archetypes/java_app/customize>`
and can be used the same way.


The server archetype adds an additional way with an ``etc-default`` file placed in ``src/templates``, which currently
only works for **SystemV**. The file gets sourced before the actual startscript is executed.
The server archetype adds an additional way with an ``etc-default`` file placed
in ``src/templates``, which currently only works for **SystemV** and
**systemd**. The file gets sourced before the actual startscript is executed.
The file will be installed to ``/etc/default/<normalizedName>``

Example `/etc/default/<normalizedName>` for SystemV:

.. code-block :: bash
# Available replacements
Expand Down
2 changes: 1 addition & 1 deletion src/sphinx/archetypes/java_server/my-first-project.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ rights. **<package>** is a placeholder for your actual application name. By defa
Folder User Permissions Purpose
=============================== ====== =========== =======
/usr/share/**<package>** root 755 / (655) static, non-changeable files
/etc/default/**<package>**.conf root 644 default config file
/etc/default/**<package>** root 644 default config file
/etc/**<package>** root 644 config folder -> link to /usr/share/**<package-name>**/conf
/var/run/**<package>** daemon 644 if the application generates a pid on its own
/var/log/**<package>** daemon 644 log folder -> symlinked from /usr/share/**<package>**/log
Expand Down