summaryrefslogtreecommitdiff
path: root/src/Propellor/Property/SiteSpecific
diff options
context:
space:
mode:
authorJoey Hess <joeyh@debian.org>2014-07-09 22:11:31 -0400
committerJoey Hess <joeyh@debian.org>2014-07-09 22:11:31 -0400
commit82da31b3e0e9acdfbca4c48eb12ab1f28515ba10 (patch)
tree0a3e0c6e134680e35665364b2cd6895863bcc990 /src/Propellor/Property/SiteSpecific
propellor (0.8.1) unstable; urgency=medium
* Run apt-get update in initial bootstrap. * --list-fields now includes a table of fields that are not currently set, but would be used if they got set. * Remove .gitignore from cabal file list, to avoid build failure on Debian. Closes: #754334 # imported from the archive
Diffstat (limited to 'src/Propellor/Property/SiteSpecific')
-rw-r--r--src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs178
-rw-r--r--src/Propellor/Property/SiteSpecific/GitHome.hs34
-rw-r--r--src/Propellor/Property/SiteSpecific/JoeySites.hs362
3 files changed, 574 insertions, 0 deletions
diff --git a/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs b/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs
new file mode 100644
index 00000000..4cb26a50
--- /dev/null
+++ b/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs
@@ -0,0 +1,178 @@
+module Propellor.Property.SiteSpecific.GitAnnexBuilder where
+
+import Propellor
+import qualified Propellor.Property.Apt as Apt
+import qualified Propellor.Property.User as User
+import qualified Propellor.Property.Cron as Cron
+import qualified Propellor.Property.Ssh as Ssh
+import qualified Propellor.Property.File as File
+import qualified Propellor.Property.Docker as Docker
+import Propellor.Property.Cron (CronTimes)
+
+builduser :: UserName
+builduser = "builder"
+
+homedir :: FilePath
+homedir = "/home/builder"
+
+gitbuilderdir :: FilePath
+gitbuilderdir = homedir </> "gitbuilder"
+
+builddir :: FilePath
+builddir = gitbuilderdir </> "build"
+
+type TimeOut = String -- eg, 5h
+
+autobuilder :: Architecture -> CronTimes -> TimeOut -> Property
+autobuilder arch crontimes timeout = combineProperties "gitannexbuilder"
+ [ Apt.serviceInstalledRunning "cron"
+ , Cron.niceJob "gitannexbuilder" crontimes builduser gitbuilderdir $
+ "git pull ; timeout " ++ timeout ++ " ./autobuild"
+ -- The builduser account does not have a password set,
+ -- instead use the password privdata to hold the rsync server
+ -- password used to upload the built image.
+ , withPrivData (Password builduser) context $ \getpw ->
+ property "rsync password" $ getpw $ \pw -> do
+ oldpw <- liftIO $ catchDefaultIO "" $
+ readFileStrict pwfile
+ if pw /= oldpw
+ then makeChange $ writeFile pwfile pw
+ else noChange
+ ]
+ where
+ context = Context ("gitannexbuilder " ++ arch)
+ pwfile = homedir </> "rsyncpassword"
+
+tree :: Architecture -> Property
+tree buildarch = combineProperties "gitannexbuilder tree"
+ [ Apt.installed ["git"]
+ -- gitbuilderdir directory already exists when docker volume is used,
+ -- but with wrong owner.
+ , File.dirExists gitbuilderdir
+ , File.ownerGroup gitbuilderdir builduser builduser
+ , check (not <$> (doesDirectoryExist (gitbuilderdir </> ".git"))) $
+ userScriptProperty builduser
+ [ "git clone git://git.kitenet.net/gitannexbuilder " ++ gitbuilderdir
+ , "cd " ++ gitbuilderdir
+ , "git checkout " ++ buildarch
+ ]
+ `describe` "gitbuilder setup"
+ , check (not <$> doesDirectoryExist builddir) $ userScriptProperty builduser
+ [ "git clone git://git-annex.branchable.com/ " ++ builddir
+ ]
+ ]
+
+buildDepsApt :: Property
+buildDepsApt = combineProperties "gitannexbuilder build deps"
+ [ Apt.buildDep ["git-annex"]
+ , buildDepsNoHaskellLibs
+ , "git-annex source build deps installed" ==> Apt.buildDepIn builddir
+ ]
+
+buildDepsNoHaskellLibs :: Property
+buildDepsNoHaskellLibs = Apt.installed
+ ["git", "rsync", "moreutils", "ca-certificates",
+ "debhelper", "ghc", "curl", "openssh-client", "git-remote-gcrypt",
+ "liblockfile-simple-perl", "cabal-install", "vim", "less",
+ -- needed by haskell libs
+ "libxml2-dev", "libidn11-dev", "libgsasl7-dev", "libgnutls-dev",
+ "alex", "happy", "c2hs"
+ ]
+
+-- Installs current versions of git-annex's deps from cabal, but only
+-- does so once.
+cabalDeps :: Property
+cabalDeps = flagFile go cabalupdated
+ where
+ go = userScriptProperty builduser ["cabal update && cabal install git-annex --only-dependencies || true"]
+ cabalupdated = homedir </> ".cabal" </> "packages" </> "hackage.haskell.org" </> "00-index.cache"
+
+standardAutoBuilderContainer :: (System -> Docker.Image) -> Architecture -> Int -> TimeOut -> Host
+standardAutoBuilderContainer dockerImage arch buildminute timeout = Docker.container (arch ++ "-git-annex-builder")
+ (dockerImage $ System (Debian Testing) arch)
+ & os (System (Debian Testing) arch)
+ & Apt.stdSourcesList
+ & Apt.installed ["systemd"]
+ & Apt.unattendedUpgrades
+ & User.accountFor builduser
+ & tree arch
+ & buildDepsApt
+ & autobuilder arch (show buildminute ++ " * * * *") timeout
+
+androidAutoBuilderContainer :: (System -> Docker.Image) -> Cron.CronTimes -> TimeOut -> Host
+androidAutoBuilderContainer dockerImage crontimes timeout =
+ androidContainer dockerImage "android-git-annex-builder" (tree "android") builddir
+ & Apt.unattendedUpgrades
+ & autobuilder "android" crontimes timeout
+
+-- Android is cross-built in a Debian i386 container, using the Android NDK.
+androidContainer :: (System -> Docker.Image) -> Docker.ContainerName -> Property -> FilePath -> Host
+androidContainer dockerImage name setupgitannexdir gitannexdir = Docker.container name
+ (dockerImage $ System (Debian Stable) "i386")
+ & os (System (Debian Stable) "i386")
+ & Apt.stdSourcesList
+ & Apt.installed ["systemd"]
+ & User.accountFor builduser
+ & File.dirExists gitbuilderdir
+ & File.ownerGroup homedir builduser builduser
+ & buildDepsNoHaskellLibs
+ & flagFile chrootsetup ("/chrootsetup")
+ `requires` setupgitannexdir
+ -- TODO: automate installing haskell libs
+ -- (Currently have to run
+ -- git-annex/standalone/android/install-haskell-packages
+ -- which is not fully automated.)
+ where
+ -- Use git-annex's android chroot setup script, which will install
+ -- ghc-android and the NDK, all build deps, etc, in the home
+ -- directory of the builder user.
+ chrootsetup = scriptProperty
+ [ "cd " ++ gitannexdir ++ " && ./standalone/android/buildchroot-inchroot"
+ ]
+
+-- armel builder has a companion container using amd64 that
+-- runs the build first to get TH splices. They need
+-- to have the same versions of all haskell libraries installed.
+armelCompanionContainer :: (System -> Docker.Image) -> Host
+armelCompanionContainer dockerImage = Docker.container "armel-git-annex-builder-companion"
+ (dockerImage $ System (Debian Unstable) "amd64")
+ & os (System (Debian Testing) "amd64")
+ & Apt.stdSourcesList
+ & Apt.installed ["systemd"]
+ & Apt.unattendedUpgrades
+ -- This volume is shared with the armel builder.
+ & Docker.volume gitbuilderdir
+ & User.accountFor builduser
+ -- Install current versions of build deps from cabal.
+ & tree "armel"
+ & buildDepsNoHaskellLibs
+ & cabalDeps
+ -- The armel builder can ssh to this companion.
+ & Docker.expose "22"
+ & Apt.serviceInstalledRunning "ssh"
+ & Ssh.authorizedKeys builduser (Context "armel-git-annex-builder")
+
+armelAutoBuilderContainer :: (System -> Docker.Image) -> Cron.CronTimes -> TimeOut -> Host
+armelAutoBuilderContainer dockerImage crontimes timeout = Docker.container "armel-git-annex-builder"
+ (dockerImage $ System (Debian Unstable) "armel")
+ & os (System (Debian Testing) "armel")
+ & Apt.stdSourcesList
+ & Apt.unattendedUpgrades
+ & Apt.installed ["systemd"]
+ & Apt.installed ["openssh-client"]
+ & Docker.link "armel-git-annex-builder-companion" "companion"
+ & Docker.volumes_from "armel-git-annex-builder-companion"
+ & User.accountFor builduser
+ -- TODO: automate installing haskell libs
+ -- (Currently have to run
+ -- git-annex/standalone/linux/install-haskell-packages
+ -- which is not fully automated.)
+ & buildDepsNoHaskellLibs
+ & autobuilder "armel" crontimes timeout
+ `requires` tree "armel"
+ & Ssh.keyImported SshRsa builduser (Context "armel-git-annex-builder")
+ & trivial writecompanionaddress
+ where
+ writecompanionaddress = scriptProperty
+ [ "echo \"$COMPANION_PORT_22_TCP_ADDR\" > " ++ homedir </> "companion_address"
+ ] `describe` "companion_address file"
diff --git a/src/Propellor/Property/SiteSpecific/GitHome.hs b/src/Propellor/Property/SiteSpecific/GitHome.hs
new file mode 100644
index 00000000..6ed02146
--- /dev/null
+++ b/src/Propellor/Property/SiteSpecific/GitHome.hs
@@ -0,0 +1,34 @@
+module Propellor.Property.SiteSpecific.GitHome where
+
+import Propellor
+import qualified Propellor.Property.Apt as Apt
+import Propellor.Property.User
+import Utility.SafeCommand
+
+-- | Clones Joey Hess's git home directory, and runs its fixups script.
+installedFor :: UserName -> Property
+installedFor user = check (not <$> hasGitDir user) $
+ property ("githome " ++ user) (go =<< liftIO (homedir user))
+ `requires` Apt.installed ["git"]
+ where
+ go home = do
+ let tmpdir = home </> "githome"
+ ensureProperty $ combineProperties "githome setup"
+ [ userScriptProperty user ["git clone " ++ url ++ " " ++ tmpdir]
+ , property "moveout" $ makeChange $ void $
+ moveout tmpdir home
+ , property "rmdir" $ makeChange $ void $
+ catchMaybeIO $ removeDirectory tmpdir
+ , userScriptProperty user ["rm -rf .aptitude/ .bashrc .profile; bin/mr checkout; bin/fixups"]
+ ]
+ moveout tmpdir home = do
+ fs <- dirContents tmpdir
+ forM fs $ \f -> boolSystem "mv" [File f, File home]
+
+url :: String
+url = "git://git.kitenet.net/joey/home"
+
+hasGitDir :: UserName -> IO Bool
+hasGitDir user = go =<< homedir user
+ where
+ go home = doesDirectoryExist (home </> ".git")
diff --git a/src/Propellor/Property/SiteSpecific/JoeySites.hs b/src/Propellor/Property/SiteSpecific/JoeySites.hs
new file mode 100644
index 00000000..c770907b
--- /dev/null
+++ b/src/Propellor/Property/SiteSpecific/JoeySites.hs
@@ -0,0 +1,362 @@
+-- | Specific configuation for Joey Hess's sites. Probably not useful to
+-- others except as an example.
+
+module Propellor.Property.SiteSpecific.JoeySites where
+
+import Propellor
+import qualified Propellor.Property.Apt as Apt
+import qualified Propellor.Property.File as File
+import qualified Propellor.Property.Gpg as Gpg
+import qualified Propellor.Property.Ssh as Ssh
+import qualified Propellor.Property.Git as Git
+import qualified Propellor.Property.Cron as Cron
+import qualified Propellor.Property.Service as Service
+import qualified Propellor.Property.User as User
+import qualified Propellor.Property.Obnam as Obnam
+import qualified Propellor.Property.Apache as Apache
+import Utility.SafeCommand
+import Utility.FileMode
+import Utility.Path
+
+import Data.List
+import System.Posix.Files
+
+oldUseNetServer :: [Host] -> Property
+oldUseNetServer hosts = propertyList ("olduse.net server")
+ [ oldUseNetInstalled "oldusenet-server"
+ , Obnam.latestVersion
+ , Obnam.backup datadir "33 4 * * *"
+ [ "--repository=sftp://2318@usw-s002.rsync.net/~/olduse.net"
+ , "--client-name=spool"
+ ] Obnam.OnlyClient
+ `requires` Ssh.keyImported SshRsa "root" (Context "olduse.net")
+ `requires` Ssh.knownHost hosts "usw-s002.rsync.net" "root"
+ , check (not . isSymbolicLink <$> getSymbolicLinkStatus newsspool) $
+ property "olduse.net spool in place" $ makeChange $ do
+ removeDirectoryRecursive newsspool
+ createSymbolicLink (datadir </> "news") newsspool
+ , Apt.installed ["leafnode"]
+ , "/etc/news/leafnode/config" `File.hasContent`
+ [ "# olduse.net configuration (deployed by propellor)"
+ , "expire = 1000000" -- no expiry via texpire
+ , "server = " -- no upstream server
+ , "debugmode = 1"
+ , "allowSTRANGERS = 42" -- lets anyone connect
+ , "nopost = 1" -- no new posting (just gather them)
+ ]
+ , "/etc/hosts.deny" `File.lacksLine` "leafnode: ALL"
+ , Apt.serviceInstalledRunning "openbsd-inetd"
+ , File.notPresent "/etc/cron.daily/leafnode"
+ , File.notPresent "/etc/cron.d/leafnode"
+ , Cron.niceJob "oldusenet-expire" "11 1 * * *" "news" newsspool $ intercalate ";"
+ [ "find \\( -path ./out.going -or -path ./interesting.groups -or -path './*/.overview' \\) -prune -or -type f -ctime +60 -print | xargs --no-run-if-empty rm"
+ , "find -type d -empty | xargs --no-run-if-empty rmdir"
+ ]
+ , Cron.niceJob "oldusenet-uucp" "*/5 * * * *" "news" "/" $
+ "/usr/bin/uucp " ++ datadir
+ , toProp $ Apache.siteEnabled "nntp.olduse.net" $ apachecfg "nntp.olduse.net" False
+ [ " DocumentRoot " ++ datadir ++ "/"
+ , " <Directory " ++ datadir ++ "/>"
+ , " Options Indexes FollowSymlinks"
+ , " AllowOverride None"
+ -- I had this in the file before.
+ -- This may be needed by a newer version of apache?
+ --, " Require all granted"
+ , " </Directory>"
+ ]
+ ]
+ where
+ newsspool = "/var/spool/news"
+ datadir = "/var/spool/oldusenet"
+
+oldUseNetShellBox :: Property
+oldUseNetShellBox = oldUseNetInstalled "oldusenet"
+
+oldUseNetInstalled :: Apt.Package -> Property
+oldUseNetInstalled pkg = check (not <$> Apt.isInstalled pkg) $
+ propertyList ("olduse.net " ++ pkg)
+ [ Apt.installed (words "build-essential devscripts debhelper git libncursesw5-dev libpcre3-dev pkg-config bison libicu-dev libidn11-dev libcanlock2-dev libuu-dev ghc libghc-strptime-dev libghc-hamlet-dev libghc-ifelse-dev libghc-hxt-dev libghc-utf8-string-dev libghc-missingh-dev libghc-sha-dev")
+ `describe` "olduse.net build deps"
+ , scriptProperty
+ [ "rm -rf /root/tmp/oldusenet" -- idenpotency
+ , "git clone git://olduse.net/ /root/tmp/oldusenet/source"
+ , "cd /root/tmp/oldusenet/source/"
+ , "dpkg-buildpackage -us -uc"
+ , "dpkg -i ../" ++ pkg ++ "_*.deb || true"
+ , "apt-get -fy install" -- dependencies
+ , "rm -rf /root/tmp/oldusenet"
+ -- screen fails unless the directory has this mode.
+ -- not sure what's going on.
+ , "chmod 777 /var/run/screen"
+ ] `describe` "olduse.net built"
+ ]
+
+
+kgbServer :: Property
+kgbServer = propertyList desc
+ [ withOS desc $ \o -> case o of
+ (Just (System (Debian Unstable) _)) ->
+ ensureProperty $ propertyList desc
+ [ Apt.serviceInstalledRunning "kgb-bot"
+ , "/etc/default/kgb-bot" `File.containsLine` "BOT_ENABLED=1"
+ `describe` "kgb bot enabled"
+ `onChange` Service.running "kgb-bot"
+ ]
+ _ -> error "kgb server needs Debian unstable (for kgb-bot 1.31+)"
+ , File.hasPrivContent "/etc/kgb-bot/kgb.conf" anyContext
+ `onChange` Service.restarted "kgb-bot"
+ ]
+ where
+ desc = "kgb.kitenet.net setup"
+
+mumbleServer :: [Host] -> Property
+mumbleServer hosts = combineProperties hn
+ [ Apt.serviceInstalledRunning "mumble-server"
+ , Obnam.latestVersion
+ , Obnam.backup "/var/lib/mumble-server" "55 5 * * *"
+ [ "--repository=sftp://joey@turtle.kitenet.net/~/lib/backup/" ++ hn ++ ".obnam"
+ , "--client-name=mumble"
+ ] Obnam.OnlyClient
+ `requires` Ssh.keyImported SshRsa "root" (Context hn)
+ `requires` Ssh.knownHost hosts "turtle.kitenet.net" "root"
+ , trivial $ cmdProperty "chown" ["-R", "mumble-server:mumble-server", "/var/lib/mumble-server"]
+ ]
+ where
+ hn = "mumble.debian.net"
+
+obnamLowMem :: Property
+obnamLowMem = combineProperties "obnam tuned for low memory use"
+ [ Obnam.latestVersion
+ , "/etc/obnam.conf" `File.containsLines`
+ [ "[config]"
+ , "# Suggested by liw to keep Obnam memory consumption down (at some speed cost)."
+ , "upload-queue-size = 128"
+ , "lru-size = 128"
+ ]
+ ]
+
+-- git.kitenet.net and git.joeyh.name
+gitServer :: [Host] -> Property
+gitServer hosts = propertyList "git.kitenet.net setup"
+ [ Obnam.latestVersion
+ , Obnam.backup "/srv/git" "33 3 * * *"
+ [ "--repository=sftp://2318@usw-s002.rsync.net/~/git.kitenet.net"
+ , "--encrypt-with=1B169BE1"
+ , "--client-name=wren"
+ ] Obnam.OnlyClient
+ `requires` Gpg.keyImported "1B169BE1" "root"
+ `requires` Ssh.keyImported SshRsa "root" (Context "git.kitenet.net")
+ `requires` Ssh.knownHost hosts "usw-s002.rsync.net" "root"
+ `requires` Ssh.authorizedKeys "family" (Context "git.kitenet.net")
+ `requires` User.accountFor "family"
+ , Apt.installed ["git", "rsync", "gitweb"]
+ -- backport avoids channel flooding on branch merge
+ , Apt.installedBackport ["kgb-client"]
+ -- backport supports ssh event notification
+ , Apt.installedBackport ["git-annex"]
+ , File.hasPrivContentExposed "/etc/kgb-bot/kgb-client.conf" anyContext
+ , toProp $ Git.daemonRunning "/srv/git"
+ , "/etc/gitweb.conf" `File.containsLines`
+ [ "$projectroot = '/srv/git';"
+ , "@git_base_url_list = ('git://git.kitenet.net', 'http://git.kitenet.net/git', 'https://git.kitenet.net/git', 'ssh://git.kitenet.net/srv/git');"
+ , "# disable snapshot download; overloads server"
+ , "$feature{'snapshot'}{'default'} = [];"
+ ]
+ `describe` "gitweb configured"
+ -- Repos push on to github.
+ , Ssh.knownHost hosts "github.com" "joey"
+ -- I keep the website used for gitweb checked into git..
+ , Git.cloned "root" "/srv/git/joey/git.kitenet.net.git" "/srv/web/git.kitenet.net" Nothing
+ , website "git.kitenet.net"
+ , website "git.joeyh.name"
+ , toProp $ Apache.modEnabled "cgi"
+ ]
+ where
+ website hn = toProp $ Apache.siteEnabled hn $ apachecfg hn True
+ [ " DocumentRoot /srv/web/git.kitenet.net/"
+ , " <Directory /srv/web/git.kitenet.net/>"
+ , " Options Indexes ExecCGI FollowSymlinks"
+ , " AllowOverride None"
+ , " AddHandler cgi-script .cgi"
+ , " DirectoryIndex index.cgi"
+ , " </Directory>"
+ , ""
+ , " ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/"
+ , " <Directory /usr/lib/cgi-bin>"
+ , " SetHandler cgi-script"
+ , " Options ExecCGI"
+ , " </Directory>"
+ ]
+
+type AnnexUUID = String
+
+-- | A website, with files coming from a git-annex repository.
+annexWebSite :: [Host] -> Git.RepoUrl -> HostName -> AnnexUUID -> [(String, Git.RepoUrl)] -> Property
+annexWebSite hosts origin hn uuid remotes = propertyList (hn ++" website using git-annex")
+ [ Git.cloned "joey" origin dir Nothing
+ `onChange` setup
+ , postupdatehook `File.hasContent`
+ [ "#!/bin/sh"
+ , "exec git update-server-info"
+ ] `onChange`
+ (postupdatehook `File.mode` (combineModes (ownerWriteMode:readModes ++ executeModes)))
+ , setupapache
+ ]
+ where
+ dir = "/srv/web/" ++ hn
+ postupdatehook = dir </> ".git/hooks/post-update"
+ setup = userScriptProperty "joey" setupscript
+ `requires` Ssh.keyImported SshRsa "joey" (Context hn)
+ `requires` Ssh.knownHost hosts "turtle.kitenet.net" "joey"
+ setupscript =
+ [ "cd " ++ shellEscape dir
+ , "git config annex.uuid " ++ shellEscape uuid
+ ] ++ map addremote remotes ++
+ [ "git annex get"
+ ]
+ addremote (name, url) = "git remote add " ++ shellEscape name ++ " " ++ shellEscape url
+ setupapache = toProp $ Apache.siteEnabled hn $ apachecfg hn True $
+ [ " ServerAlias www."++hn
+ , ""
+ , " DocumentRoot /srv/web/"++hn
+ , " <Directory /srv/web/"++hn++">"
+ , " Options FollowSymLinks"
+ , " AllowOverride None"
+ , " </Directory>"
+ , " <Directory /srv/web/"++hn++">"
+ , " Options Indexes FollowSymLinks ExecCGI"
+ , " AllowOverride None"
+ , " AddHandler cgi-script .cgi"
+ , " DirectoryIndex index.html index.cgi"
+ , " Order allow,deny"
+ , " allow from all"
+ , " </Directory>"
+ ]
+
+apachecfg :: HostName -> Bool -> Apache.ConfigFile -> Apache.ConfigFile
+apachecfg hn withssl middle
+ | withssl = vhost False ++ vhost True
+ | otherwise = vhost False
+ where
+ vhost ssl =
+ [ "<VirtualHost *:"++show port++">"
+ , " ServerAdmin grue@joeyh.name"
+ , " ServerName "++hn++":"++show port
+ ]
+ ++ mainhttpscert ssl
+ ++ middle ++
+ [ ""
+ , " ErrorLog /var/log/apache2/error.log"
+ , " LogLevel warn"
+ , " CustomLog /var/log/apache2/access.log combined"
+ , " ServerSignature On"
+ , " "
+ , " <Directory \"/usr/share/apache2/icons\">"
+ , " Options Indexes MultiViews"
+ , " AllowOverride None"
+ , " Order allow,deny"
+ , " Allow from all"
+ , " </Directory>"
+ , "</VirtualHost>"
+ ]
+ where
+ port = if ssl then 443 else 80 :: Int
+
+mainhttpscert :: Bool -> Apache.ConfigFile
+mainhttpscert False = []
+mainhttpscert True =
+ [ " SSLEngine on"
+ , " SSLCertificateFile /etc/ssl/certs/web.pem"
+ , " SSLCertificateKeyFile /etc/ssl/private/web.pem"
+ , " SSLCertificateChainFile /etc/ssl/certs/startssl.pem"
+ ]
+
+gitAnnexDistributor :: Property
+gitAnnexDistributor = combineProperties "git-annex distributor, including rsync server and signer"
+ [ Apt.installed ["rsync"]
+ , File.hasPrivContent "/etc/rsyncd.conf" (Context "git-annex distributor")
+ `onChange` Service.restarted "rsync"
+ , File.hasPrivContent "/etc/rsyncd.secrets" (Context "git-annex distributor")
+ `onChange` Service.restarted "rsync"
+ , "/etc/default/rsync" `File.containsLine` "RSYNC_ENABLE=true"
+ `onChange` Service.running "rsync"
+ , endpoint "/srv/web/downloads.kitenet.net/git-annex/autobuild"
+ , endpoint "/srv/web/downloads.kitenet.net/git-annex/autobuild/x86_64-apple-mavericks"
+ -- git-annex distribution signing key
+ , Gpg.keyImported "89C809CB" "joey"
+ ]
+ where
+ endpoint d = combineProperties ("endpoint " ++ d)
+ [ File.dirExists d
+ , File.ownerGroup d "joey" "joey"
+ ]
+
+-- Twitter, you kill us.
+twitRss :: Property
+twitRss = combineProperties "twitter rss"
+ [ Git.cloned "joey" "git://git.kitenet.net/twitrss.git" dir Nothing
+ , check (not <$> doesFileExist (dir </> "twitRss")) $
+ userScriptProperty "joey"
+ [ "cd " ++ dir
+ , "ghc --make twitRss"
+ ]
+ `requires` Apt.installed
+ [ "libghc-xml-dev"
+ , "libghc-feed-dev"
+ , "libghc-tagsoup-dev"
+ ]
+ , feed "http://twitter.com/search/realtime?q=git-annex" "git-annex-twitter"
+ , feed "http://twitter.com/search/realtime?q=olduse+OR+git-annex+OR+debhelper+OR+etckeeper+OR+ikiwiki+-ashley_ikiwiki" "twittergrep"
+ ]
+ where
+ dir = "/srv/web/tmp.kitenet.net/twitrss"
+ crontime = "15 * * * *"
+ feed url desc = Cron.job desc crontime "joey" dir $
+ "./twitRss " ++ shellEscape url ++ " > " ++ shellEscape ("../" ++ desc ++ ".rss")
+
+ircBouncer :: Property
+ircBouncer = propertyList "IRC bouncer"
+ [ Apt.installed ["znc"]
+ , User.accountFor "znc"
+ , File.dirExists (parentDir conf)
+ , File.hasPrivContent conf anyContext
+ , File.ownerGroup conf "znc" "znc"
+ , Cron.job "znconboot" "@reboot" "znc" "~" "znc"
+ -- ensure running if it was not already
+ , trivial $ userScriptProperty "znc" ["znc || true"]
+ `describe` "znc running"
+ ]
+ where
+ conf = "/home/znc/.znc/configs/znc.conf"
+
+kiteShellBox :: Property
+kiteShellBox = propertyList "kitenet.net shellinabox"
+ [ Apt.installed ["shellinabox"]
+ , File.hasContent "/etc/default/shellinabox"
+ [ "# Deployed by propellor"
+ , "SHELLINABOX_DAEMON_START=1"
+ , "SHELLINABOX_PORT=443"
+ , "SHELLINABOX_ARGS=\"--no-beep --service=/:SSH:kitenet.net\""
+ ]
+ `onChange` Service.restarted "shellinabox"
+ , Service.running "shellinabox"
+ ]
+
+githubBackup :: Property
+githubBackup = propertyList "github-backup box"
+ [ Apt.installed ["github-backup", "moreutils"]
+ , let f = "/home/joey/.github-keys"
+ in File.hasPrivContent f anyContext
+ `onChange` File.ownerGroup f "joey" "joey"
+ ]
+
+obnamRepos :: [String] -> Property
+obnamRepos rs = propertyList ("obnam repos for " ++ unwords rs)
+ (mkbase : map mkrepo rs)
+ where
+ mkbase = mkdir "/home/joey/lib/backup"
+ `requires` mkdir "/home/joey/lib"
+ mkrepo r = mkdir ("/home/joey/lib/backup/" ++ r ++ ".obnam")
+ mkdir d = File.dirExists d
+ `before` File.ownerGroup d "joey" "joey"
+