diff --git a/debian/changelog b/debian/changelog index e36e151..d6dc615 100644 --- a/debian/changelog +++ b/debian/changelog @@ -20,6 +20,8 @@ propellor (1.0.0) UNRELEASED; urgency=medium Docker.docked. (API change) * Added support for using debootstrap from propellor. * Propellor can now be used to provision chroots. + * systemd-nspawn containers can now be managed by propellor, very similar + to its handling of docker containers. -- Joey Hess Mon, 10 Nov 2014 11:15:27 -0400 diff --git a/propellor.cabal b/propellor.cabal index 23708fd..f45900c 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -95,6 +95,7 @@ Library Propellor.Property.Service Propellor.Property.Ssh Propellor.Property.Sudo + Propellor.Property.Systemd Propellor.Property.Tor Propellor.Property.User Propellor.Property.HostingProvider.CloudAtCost diff --git a/src/Propellor/Property.hs b/src/Propellor/Property.hs index 1d750a7..6ace5e4 100644 --- a/src/Propellor/Property.hs +++ b/src/Propellor/Property.hs @@ -131,10 +131,6 @@ boolProperty desc a = property desc $ ifM (liftIO a) revert :: RevertableProperty -> RevertableProperty revert (RevertableProperty p1 p2) = RevertableProperty p2 p1 --- | Turns a revertable property into a regular property. -unrevertable :: RevertableProperty -> Property -unrevertable (RevertableProperty p1 _p2) = p1 - -- Changes the action that is performed to satisfy a property. adjustProperty :: Property -> (Propellor Result -> Propellor Result) -> Property adjustProperty p f = p { propertySatisfy = f (propertySatisfy p) } diff --git a/src/Propellor/Property/Chroot.hs b/src/Propellor/Property/Chroot.hs index ba7bf96..798330b 100644 --- a/src/Propellor/Property/Chroot.hs +++ b/src/Propellor/Property/Chroot.hs @@ -1,5 +1,5 @@ module Propellor.Property.Chroot ( - Chroot, + Chroot(..), chroot, provisioned, chain, @@ -24,7 +24,7 @@ instance Hostlike Chroot where -- | Defines a Chroot at the given location, containing the specified -- System. Properties can be added to configure the Chroot. -- --- > chroot "/srv/chroot/ghc-dev" (System (Debian Unstable) "amd64" +-- > chroot "/srv/chroot/ghc-dev" (System (Debian Unstable) "amd64") -- > & Apt.installed ["build-essential", "ghc", "haskell-platform"] -- > & ... chroot :: FilePath -> System -> Chroot @@ -48,7 +48,7 @@ provisioned c@(Chroot loc system _) = RevertableProperty (System (Debian _) _) -> debootstrap (System (Ubuntu _) _) -> debootstrap - debootstrap = unrevertable (Debootstrap.built loc system []) + debootstrap = toProp (Debootstrap.built loc system []) teardown = undefined diff --git a/src/Propellor/Property/Debootstrap.hs b/src/Propellor/Property/Debootstrap.hs index 4e7bc74..5f521c3 100644 --- a/src/Propellor/Property/Debootstrap.hs +++ b/src/Propellor/Property/Debootstrap.hs @@ -33,7 +33,7 @@ built target system@(System _ arch) extraparams = RevertableProperty setup teardown where setup = check (unpopulated target <||> ispartial) setupprop - `requires` unrevertable installed + `requires` toProp installed teardown = check (not <$> unpopulated target) teardownprop diff --git a/src/Propellor/Property/Systemd.hs b/src/Propellor/Property/Systemd.hs new file mode 100644 index 0000000..be08a84 --- /dev/null +++ b/src/Propellor/Property/Systemd.hs @@ -0,0 +1,103 @@ +module Propellor.Property.Systemd ( + installed, + persistentJournal, + container, + nspawned, +) where + +import Propellor +import qualified Propellor.Property.Chroot as Chroot +import qualified Propellor.Property.Apt as Apt +import Utility.SafeCommand + +import Data.List.Utils + +type MachineName = String + +type NspawnParam = CommandParam + +data Container = Container MachineName System [CommandParam] Host + +instance Hostlike Container where + (Container n s ps h) & p = Container n s ps (h & p) + (Container n s ps h) &^ p = Container n s ps (h &^ p) + getHost (Container _ _ _ h) = h + +-- dbus is only a Recommends of systemd, but is needed for communication +-- from the systemd inside a container to the one outside, so make sure it +-- gets installed. +installed :: Property +installed = Apt.installed ["systemd", "dbus"] + +-- | Sets up persistent storage of the journal. +persistentJournal :: Property +persistentJournal = check (not <$> doesDirectoryExist dir) $ + combineProperties "persistent systetemd journal" + [ cmdProperty "install" ["-d", "-g", "systemd-journal", dir] + , cmdProperty "setfacl" ["-R", "-nm", "g:adm:rx,d:g:adm:rx", dir] + ] + `requires` Apt.installed ["acl"] + where + dir = "/var/log/journal" + +-- | Defines a container with a given machine name, containing the specified +-- System. Properties can be added to configure the Container. +-- +-- > container "webserver" (System (Debian Unstable) "amd64") [] +container :: MachineName -> System -> [NspawnParam] -> Container +container name system ps = Container name system ps (Host name [] mempty) + +-- | Runs a container using systemd-nspawn. +-- +-- A systemd unit is set up for the container, so it will automatically +-- be started on boot. +-- +-- Systemd is automatically installed inside the container, and will +-- communicate with the host's systemd. This allows systemctl to be used to +-- examine the status of services running inside the container. +-- +-- When the host system has persistentJournal enabled, journactl can be +-- used to examine logs forwarded from the container. +-- +-- Reverting this property stops the container, removes the systemd unit, +-- and deletes the chroot and all its contents. +nspawned :: Container -> RevertableProperty +nspawned c@(Container name system _ h) = RevertableProperty setup teardown + where + -- TODO after container is running, use nsenter to enter it + -- and run propellor to finish provisioning. + setup = toProp (nspawnService c) + `requires` toProp chrootprovisioned + + teardown = toProp (revert (chrootprovisioned)) + `requires` toProp (revert (nspawnService c)) + + -- When provisioning the chroot, pass a version of the Host + -- that only has the Property of systemd being installed. + -- This is to avoid starting any daemons in the chroot, + -- which would not run in the container's namespace. + chrootprovisioned = Chroot.provisioned $ + Chroot.Chroot (containerDir name) system $ + h { hostProperties = [installed] } + +nspawnService :: Container -> RevertableProperty +nspawnService (Container name _ ps _) = RevertableProperty setup teardown + where + service = nspawnServiceName name + servicefile = "/etc/systemd/system/multi-user.target.wants" service + + setup = check (not <$> doesFileExist servicefile) $ + combineProperties ("container running " ++ service) + [ cmdProperty "systemctl" ["enable", service] + , cmdProperty "systemctl" ["start", service] + ] + + -- TODO adjust execStart line to reflect ps + + teardown = undefined + +nspawnServiceName :: MachineName -> String +nspawnServiceName name = "systemd-nspawn@" ++ name ++ ".service" + +containerDir :: MachineName -> FilePath +containerDir name = "/var/lib/container" ++ replace "/" "_" name