From 0460a04474d2ea4f439708bb9f8ded24fba329ac Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 16:58:11 -0400 Subject: [PATCH 01/88] propellor spin --- Propellor/Property/File.hs | 7 +++++++ Propellor/Property/OpenId.hs | 15 +++++++++++++++ Propellor/Types.hs | 1 + config-joey.hs | 14 +++++++++++++- 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Propellor/Property/OpenId.hs diff --git a/Propellor/Property/File.hs b/Propellor/Property/File.hs index 80c69d9..0c1155f 100644 --- a/Propellor/Property/File.hs +++ b/Propellor/Property/File.hs @@ -11,6 +11,13 @@ hasContent :: FilePath -> [Line] -> Property f `hasContent` newcontent = fileProperty ("replace " ++ f) (\_oldcontent -> newcontent) f +-- | Ensures a file has contents that comes from PrivData. +-- Note: Does not do anything with the permissions of the file to prevent +-- it from being seen. +hasPrivContent :: FilePath -> Property +hasPrivContent f = Property ("privcontent " ++ f) $ + withPrivData (PrivFile f) (\v -> ensureProperty $ f `hasContent` lines v) + -- | Ensures that a line is present in a file, adding it to the end if not. containsLine :: FilePath -> Line -> Property f `containsLine` l = fileProperty (f ++ " contains:" ++ l) go f diff --git a/Propellor/Property/OpenId.hs b/Propellor/Property/OpenId.hs new file mode 100644 index 0000000..4ebf18f --- /dev/null +++ b/Propellor/Property/OpenId.hs @@ -0,0 +1,15 @@ +module Propellor.Property.OpenId where + +import Propellor +import qualified Propellor.Property.File as File +import qualified Propellor.Property.Apt as Apt + +providerFor :: [UserName] -> Property +providerFor users = propertyList ("openid provider") $ + [ serviceRunning "apache2" + `requires` Apt.installed ["apache2"] + , Apt.installed ["simpleid"] + ] ++ map identfile users + where + identfile u = File.hasPrivContent $ concat + [ "/var/lib/simpleid/identities/", u, ".identity" ] diff --git a/Propellor/Types.hs b/Propellor/Types.hs index 52c0c99..856e0ea 100644 --- a/Propellor/Types.hs +++ b/Propellor/Types.hs @@ -100,6 +100,7 @@ data PrivDataField = DockerAuthentication | SshPrivKey UserName | Password UserName + | PrivFile FilePath deriving (Read, Show, Ord, Eq) diff --git a/config-joey.hs b/config-joey.hs index f2cc5e7..f38fc83 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -12,6 +12,7 @@ import qualified Propellor.Property.User as User import qualified Propellor.Property.Hostname as Hostname --import qualified Propellor.Property.Reboot as Reboot import qualified Propellor.Property.Tor as Tor +import qualified Propellor.Property.OpenId as OpenId import qualified Propellor.Property.Docker as Docker import qualified Propellor.Property.SiteSpecific.GitHome as GitHome import qualified Propellor.Property.SiteSpecific.GitAnnexBuilder as GitAnnexBuilder @@ -35,6 +36,7 @@ host hostname@"clam.kitenet.net" = standardSystem Unstable $ props & Apt.installed ["git-annex", "mtr"] & Tor.isBridge & JoeySites.oldUseNetshellBox + & Docker.docked container hostname "openid-provider" & Docker.configured & Docker.garbageCollected -- Orca is the main git-annex build box. @@ -59,7 +61,8 @@ host _ = Nothing -- | This is where Docker containers are set up. A container -- can vary by hostname where it's used, or be the same everywhere. container :: HostName -> Docker.ContainerName -> Maybe (Docker.Container) -container _host name +container _parenthost name + -- Simple web server, publishing the outside host's /var/www | name == "webserver" = Just $ Docker.containerFrom (image $ System (Debian Unstable) "amd64") [ Docker.publish "8080:80" @@ -68,6 +71,14 @@ container _host name & serviceRunning "apache2" `requires` Apt.installed ["apache2"] ] + -- My own openid provider. Uses php, so containerized for security + -- and administrative sanity. + | name == "openid-provider" = Just $ Docker.containerFrom + (image $ System (Debian Stable) "amd64") + [ Docker.publish "8081:80" + , Docker.inside $ props + & OpenId.providerFor ["joey", "liw"] + ] -- armel builder has a companion container that run amd64 and -- runs the build first to get TH splices. They share a home @@ -96,6 +107,7 @@ container _host name -- | Docker images I prefer to use. image :: System -> Docker.Image image (System (Debian Unstable) arch) = "joeyh/debian-unstable-" ++ arch +image (System (Debian Stable) arch) = "joeyh/debian-stable-" ++ arch image _ = "debian-stable-official" -- does not currently exist! -- This is my standard system setup From c40fb997ed3026526c7fa14ce58322a84375ed8c Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 17:04:12 -0400 Subject: [PATCH 02/88] propellor spin --- config-joey.hs | 1 + privdata/clam.kitenet.net.gpg | 39 +++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index f38fc83..4d4b8f1 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -77,6 +77,7 @@ container _parenthost name (image $ System (Debian Stable) "amd64") [ Docker.publish "8081:80" , Docker.inside $ props + & Apt.stdSourcesList Stable `onChange` Apt.upgrade & OpenId.providerFor ["joey", "liw"] ] diff --git a/privdata/clam.kitenet.net.gpg b/privdata/clam.kitenet.net.gpg index 72f72a8..69d8f12 100644 --- a/privdata/clam.kitenet.net.gpg +++ b/privdata/clam.kitenet.net.gpg @@ -1,22 +1,25 @@ -----BEGIN PGP MESSAGE----- Version: GnuPG v1 -hQIMA7ODiaEXBlRZAQ//fmOcGRNxe/ooyFebOl54oFJtUvmWclBN8ycWb+1FEiED -4293/YYL13OXStSDCMc1o0Rq6SxRpkD/xavcc2wqBa4rTEvOzU/YdhXRLOCr2QwQ -Mhn4vtLmQqaQwYz5tzPkfRwtB/Wx/R4dJBfNF5vp+nl788fF+cdgLLSihY+TEPSk -+Wo2PZ0jNvCSpVR99Rh3o3ut57shsVGGa4Z4uaXfLVOu118Z00iyKZ9pHFa7gLH4 -nU1Y8N8JPg0Z+zJvTbJGU66k5LMZx9a/cu/+dwk2KPm3uldld4dwFk9zkmnzsIzS -UhWWsuea4OGanjDsPZzECkLY/AOWxRL7+4qC6c9vsFagktJezRNqNImeSkYi9fR5 -xw4VnhL5JwC2RF3gMC8XHYSx5C1ByGIq0gaklJjdPRn3Kj7/zSOefgNZC/O+wSfG -V5W7kW7x6vvMv9og3k4BBpD4p2s94O8xtztLE+wOXxJclFen37FNhwuJyp7PiBN6 -T4PgekpqPfX9Xp4M1tgyUVV9m8Jeof0TtS/YsKeYqaGk1ZKPOJvqXnZTL5LOkaqE -KTWYnWdBROwNXhsaIUnu8YHqf2mRA5VlCl1Uspd3SIyU1Xh0LL9stPnxdyJGghrG -RTmTJsEkzPAxnjSop72sEkKjqwkHxNbEkXg690QEPon+m/FAg083yTtKH/whbQ7S -wFIBtEWDmBQyFmc1fvi1IouM9fUij6AwtJx2JrWE2d68BqE1moFGGiRSnf7itNc0 -YFashaGMSRZAzlx6quMJtg3sE/Xw4zra1b8SkvmH6FoQnQ2rXriG5U4Hc6bW0jIX -48O96/NbIwabZiwC5BKGmSPpQBDnyzruWR/Qsnw6uar5/ZKsIOvPhICCvChO03So -6C6WLHFb9trLqpB+r8BOMjUG/FPqZ4lRanQ3Xn///lLD2uuhH27Pmt/XDpwRJgsz -V+uM6TVQMBe5XyE3LOk7Yn0oosohYF0LFFzQH0mO5cykx+Ctjt1muxKoUmcN99ms -j99fwMhrk1qlzlu2Yoe5caph4M44TXbQRGhPX7jXDJzYbRdS -=GYf9 +hQIMA7ODiaEXBlRZARAAuRttWmrr3tFgQnbnaQpWxiAQToL94e0SctFiYqiEGRNa +D63/ZaBhBkvKSx57+SyOloqfBaeWM63vd4Yacocypl2zOjC4aEN7/MKyQRl+xhmk +EwQ4kFfJ3dmYrgXt7NAdIarjHsK5/Bv7PGVIrcwD3zqV+FUyuxt2L2ETG61kYo+m +xNWl1NCvHDZ1QOfvw4ldBo7+LO2odzoZAxBF0ZgQFqo/r/6RZaqFNJRLdVTLERTq +E4igjtgfq6blrpyeupKpFu6oy8/7WeBXthnyoduftk+aBTkXWzb+i30zIzNNsc4+ +GE68a5tM0XE8nGwKp4yz0AZHhEYzv+BZXI7HQMAZ+m0srVn637SDHeAgOBU8NjrA +SbZt0ubQ28Qaux7C7awLJ5SjvlQyLT61jLaN6SMcpeLmgkjRVN+eiVOE/qmXzhHv +AobUwJgBOktiN6+WtRcxq7WduNf6Jtxw8UB5gVWiEeg6o+29ZBfIKVMT/Jly4rTO +M13HbmSVzwdGcUL1D7Gf3oY2R7eS4VR8ShCQmF8aB8TXdsw4mo71HnUa7u5N4hCP +jLtJG24+f39TWWRjMQjtFXi5hkep4OG5CBViWdCWOjlfn4Kmr5zCXaunkO9cgDAd +s8UZdmALu2MPoVdcVm+KLq2JQi1jBWEqRu5krx/nSi+eRRX2/y95CKPEPqZoU+rS +wM0BzlW+pEDc7aFlcYCrWTiwO0BWT2iBmbse9/r2NyJPpuFf7GOMI2v65jXQ+avy +1r69zPdAXNgJ19Gid/q1CXCYnYLLVHqigd8XNs12ANaVvkOnBi3gAf309SIPJtCa +uFVBxNasLTMQ3Ta7v7TLa0PopdBuFqfcy9d3BBiOKqokvhWFJobaG/WhF85ercRJ +F8lse9fgo5xfrDoCFk7u9rzhHl8xKLl24thKFTDzwm+yuzXOoLq8+Km/xYuzQXZK +JCjPvIUDaCCc1E/Yeoc3RafAiOuNwnjHW15TRdlohmgXzYlTCYF491WVKQfpL2Sd +VO8Uar094M1d52Rv8/1HCTBKJ0hnK259l4dguzw4sl2BcrFPBz9SJ0f6V/eAHE0h +la5QtLdwDDRI2giMXKfmzRiRA/5kBW01YaK7tt0om6L7Ri4Rs3JAhVgjcWDtH6fI +w807PpsIHaK8r3yDJoeqUnDYOsImuNgdctQkeroPsFYmV3fu5Hb5tYDkKzm5lE0z +C6mz09PD0M5hsnqmZXaw +=UFa1 -----END PGP MESSAGE----- From 4ea2771b69617bc537687f9b2fec4b0a32880de5 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 17:10:52 -0400 Subject: [PATCH 03/88] propellor spin --- Propellor/Property/Cmd.hs | 11 ++++++++++- Propellor/Property/OpenId.hs | 2 ++ config-joey.hs | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Propellor/Property/Cmd.hs b/Propellor/Property/Cmd.hs index dc5073d..f661cf8 100644 --- a/Propellor/Property/Cmd.hs +++ b/Propellor/Property/Cmd.hs @@ -4,6 +4,7 @@ module Propellor.Property.Cmd ( scriptProperty, userScriptProperty, serviceRunning, + serviceRestarted, ) where import Control.Monad @@ -47,13 +48,21 @@ userScriptProperty user script = cmdProperty "su" ["-c", shellcmd, user] where shellcmd = intercalate " ; " ("set -e" : "cd" : script) +type ServiceName = String + -- | Ensures that a service is running. -- -- Note that due to the general poor state of init scripts, the best -- we can do is try to start the service, and if it fails, assume -- this means it's already running. -serviceRunning :: String -> Property +serviceRunning :: ServiceName -> Property serviceRunning svc = Property ("running " ++ svc) $ do void $ ensureProperty $ scriptProperty ["service " ++ shellEscape svc ++ " start >/dev/null 2>&1 || true"] return NoChange + +serviceRestarted :: ServiceName -> Property +serviceRestarted svc = Property ("restarted " ++ svc) $ do + void $ ensureProperty $ + scriptProperty ["service " ++ shellEscape svc ++ " restart >/dev/null 2>&1 || true"] + return NoChange diff --git a/Propellor/Property/OpenId.hs b/Propellor/Property/OpenId.hs index 4ebf18f..0f207a5 100644 --- a/Propellor/Property/OpenId.hs +++ b/Propellor/Property/OpenId.hs @@ -9,6 +9,8 @@ providerFor users = propertyList ("openid provider") $ [ serviceRunning "apache2" `requires` Apt.installed ["apache2"] , Apt.installed ["simpleid"] + `onChange` serviceRestarted "apache2" + , serviceRestarted "apache2" ] ++ map identfile users where identfile u = File.hasPrivContent $ concat diff --git a/config-joey.hs b/config-joey.hs index 4d4b8f1..c57dd5a 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -71,6 +71,7 @@ container _parenthost name & serviceRunning "apache2" `requires` Apt.installed ["apache2"] ] + -- My own openid provider. Uses php, so containerized for security -- and administrative sanity. | name == "openid-provider" = Just $ Docker.containerFrom From e435d97927d37fbea032b83e791dd77dcbd47307 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 17:29:56 -0400 Subject: [PATCH 04/88] propellor spin --- Propellor/Property/OpenId.hs | 13 ++++++++++--- config-joey.hs | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Propellor/Property/OpenId.hs b/Propellor/Property/OpenId.hs index 0f207a5..f744037 100644 --- a/Propellor/Property/OpenId.hs +++ b/Propellor/Property/OpenId.hs @@ -4,14 +4,21 @@ import Propellor import qualified Propellor.Property.File as File import qualified Propellor.Property.Apt as Apt -providerFor :: [UserName] -> Property -providerFor users = propertyList ("openid provider") $ +import Data.List + +providerFor :: [UserName] -> HostName -> Property +providerFor users hostname = propertyList ("openid provider") $ [ serviceRunning "apache2" `requires` Apt.installed ["apache2"] , Apt.installed ["simpleid"] `onChange` serviceRestarted "apache2" - , serviceRestarted "apache2" + , File.fileProperty ("simpleid host " ++ hostname) + (map setbaseurl) "/etc/simpleid/config.inc" ] ++ map identfile users where identfile u = File.hasPrivContent $ concat [ "/var/lib/simpleid/identities/", u, ".identity" ] + setbaseurl l + | "SIMPLEID_BASE_URL" `isInfixOf` l = + "define('SIMPLEID_BASE_URL', 'http://"++hostname++"/simpleid');" + | otherwise = l diff --git a/config-joey.hs b/config-joey.hs index c57dd5a..8445f7c 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -61,7 +61,7 @@ host _ = Nothing -- | This is where Docker containers are set up. A container -- can vary by hostname where it's used, or be the same everywhere. container :: HostName -> Docker.ContainerName -> Maybe (Docker.Container) -container _parenthost name +container parenthost name -- Simple web server, publishing the outside host's /var/www | name == "webserver" = Just $ Docker.containerFrom (image $ System (Debian Unstable) "amd64") @@ -70,6 +70,7 @@ container _parenthost name , Docker.inside $ props & serviceRunning "apache2" `requires` Apt.installed ["apache2"] + & Apt.unattendedUpgrades ] -- My own openid provider. Uses php, so containerized for security @@ -78,8 +79,9 @@ container _parenthost name (image $ System (Debian Stable) "amd64") [ Docker.publish "8081:80" , Docker.inside $ props - & Apt.stdSourcesList Stable `onChange` Apt.upgrade - & OpenId.providerFor ["joey", "liw"] + & Apt.stdSourcesList Stable + & Apt.unattendedUpgrades + & OpenId.providerFor ["joey", "liw"] parenthost ] -- armel builder has a companion container that run amd64 and @@ -89,6 +91,8 @@ container _parenthost name | name == "armel-git-annex-builder-companion" = Just $ Docker.containerFrom (image $ System (Debian Unstable) "amd64") [ Docker.volume GitAnnexBuilder.homedir + , Docker.inside $ props + & Apt.unattendedUpgrades ] | name == "armel-git-annex-builder" = Just $ Docker.containerFrom (image $ System (Debian Unstable) "armel") @@ -96,13 +100,17 @@ container _parenthost name , Docker.volumes_from (name ++ "-companion") , Docker.inside $ props -- & GitAnnexBuilder.builder "armel" "15 * * * *" True + & Apt.unattendedUpgrades ] | "-git-annex-builder" `isSuffixOf` name = let arch = takeWhile (/= '-') name in Just $ Docker.containerFrom (image $ System (Debian Unstable) arch) - [ Docker.inside $ props & GitAnnexBuilder.builder arch "15 * * * *" True ] + [ Docker.inside $ props + & GitAnnexBuilder.builder arch "15 * * * *" True + & Apt.unattendedUpgrades + ] | otherwise = Nothing From a04539a2ee46a3c040bf7a44a6954fa51a035315 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 17:51:10 -0400 Subject: [PATCH 05/88] propellor spin --- Propellor/Property/OpenId.hs | 8 ++++---- config-joey.hs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Propellor/Property/OpenId.hs b/Propellor/Property/OpenId.hs index f744037..2a55ff3 100644 --- a/Propellor/Property/OpenId.hs +++ b/Propellor/Property/OpenId.hs @@ -6,13 +6,13 @@ import qualified Propellor.Property.Apt as Apt import Data.List -providerFor :: [UserName] -> HostName -> Property -providerFor users hostname = propertyList ("openid provider") $ +providerFor :: [UserName] -> String -> Property +providerFor users baseurl = propertyList ("openid provider") $ [ serviceRunning "apache2" `requires` Apt.installed ["apache2"] , Apt.installed ["simpleid"] `onChange` serviceRestarted "apache2" - , File.fileProperty ("simpleid host " ++ hostname) + , File.fileProperty ("simpleid host " ++ baseurl) (map setbaseurl) "/etc/simpleid/config.inc" ] ++ map identfile users where @@ -20,5 +20,5 @@ providerFor users hostname = propertyList ("openid provider") $ [ "/var/lib/simpleid/identities/", u, ".identity" ] setbaseurl l | "SIMPLEID_BASE_URL" `isInfixOf` l = - "define('SIMPLEID_BASE_URL', 'http://"++hostname++"/simpleid');" + "define('SIMPLEID_BASE_URL', 'http://"++baseurl++"/simpleid');" | otherwise = l diff --git a/config-joey.hs b/config-joey.hs index 8445f7c..879e8ba 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -81,7 +81,8 @@ container parenthost name , Docker.inside $ props & Apt.stdSourcesList Stable & Apt.unattendedUpgrades - & OpenId.providerFor ["joey", "liw"] parenthost + & OpenId.providerFor ["joey", "liw"] + (parenthost++":8081") ] -- armel builder has a companion container that run amd64 and From e2d1cf70eb9bfc613d0723c0cc6e0fd2ca6e656c Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 17:52:14 -0400 Subject: [PATCH 06/88] propellor spin --- Propellor/Property/OpenId.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Propellor/Property/OpenId.hs b/Propellor/Property/OpenId.hs index 2a55ff3..4f22bdb 100644 --- a/Propellor/Property/OpenId.hs +++ b/Propellor/Property/OpenId.hs @@ -7,18 +7,20 @@ import qualified Propellor.Property.Apt as Apt import Data.List providerFor :: [UserName] -> String -> Property -providerFor users baseurl = propertyList ("openid provider") $ +providerFor users baseurl = propertyList desc $ [ serviceRunning "apache2" `requires` Apt.installed ["apache2"] , Apt.installed ["simpleid"] `onChange` serviceRestarted "apache2" - , File.fileProperty ("simpleid host " ++ baseurl) + , File.fileProperty desc (map setbaseurl) "/etc/simpleid/config.inc" ] ++ map identfile users where identfile u = File.hasPrivContent $ concat [ "/var/lib/simpleid/identities/", u, ".identity" ] + url = "http://"++baseurl++"/simpleid" + desc = "openid provider " ++ url setbaseurl l | "SIMPLEID_BASE_URL" `isInfixOf` l = - "define('SIMPLEID_BASE_URL', 'http://"++baseurl++"/simpleid');" + "define('SIMPLEID_BASE_URL', '"++url++"');" | otherwise = l From 53eb3b9b1fd4df59e2b49866cbce616e43ba6ddf Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:08:16 -0400 Subject: [PATCH 07/88] ipv6to4: Ensure interface is brought up automatically on boot. --- Propellor/Property/Network.hs | 1 + debian/changelog | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/Propellor/Property/Network.hs b/Propellor/Property/Network.hs index eae5828..6009778 100644 --- a/Propellor/Property/Network.hs +++ b/Propellor/Property/Network.hs @@ -20,6 +20,7 @@ ipv6to4 = fileProperty "ipv6to4" go interfaces , "\taddress 2002:5044:5531::1" , "\tnetmask 64" , "\tgateway ::192.88.99.1" + , "auto sit0" , "# End automatically added by propeller" ] diff --git a/debian/changelog b/debian/changelog index 4455768..365485a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +propellor (0.2.4) UNRELEASED; urgency=medium + + * ipv6to4: Ensure interface is brought up automatically on boot. + + -- Joey Hess Tue, 08 Apr 2014 18:07:12 -0400 + propellor (0.2.3) unstable; urgency=medium * docker: Fix laziness bug that caused running containers to be From 8f69e9d5d870626e08231125fee48ec1b66ea3a2 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:09:56 -0400 Subject: [PATCH 08/88] propellor spin From 27a00f86826f7f76afc05d02c8990ac38c0802fd Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:15:29 -0400 Subject: [PATCH 09/88] propellor spin --- config-joey.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index 879e8ba..b431a4b 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -81,8 +81,7 @@ container parenthost name , Docker.inside $ props & Apt.stdSourcesList Stable & Apt.unattendedUpgrades - & OpenId.providerFor ["joey", "liw"] - (parenthost++":8081") + & OpenId.providerFor ["joey", "liw"] "openid.kitenet.net:8081" ] -- armel builder has a companion container that run amd64 and From 39f3acd6e473ee25e6c37fd5c8a5b4237d34127c Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:41:30 -0400 Subject: [PATCH 10/88] propellor spin --- Propellor/Property.hs | 10 ++++++++++ Propellor/Property/Apt.hs | 7 +++++-- config-joey.hs | 2 +- debian/changelog | 2 ++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Propellor/Property.hs b/Propellor/Property.hs index e7ec704..c2a8972 100644 --- a/Propellor/Property.hs +++ b/Propellor/Property.hs @@ -33,6 +33,16 @@ combineProperties desc ps = Property desc $ go ps NoChange FailedChange -> return FailedChange _ -> go ls (r <> rs) +-- | Combines together two properties, resulting in one property +-- that ensures the first, and if the first succeeds, ensures the second. +-- The property uses the description of the first property. +before :: Property -> Property -> Property +p1 `before` p2 = Property (propertyDesc p1) $ do + r <- ensureProperty p1 + case r of + FailedChange -> return FailedChange + _ -> ensureProperty p2 + -- | Makes a perhaps non-idempotent Property be idempotent by using a flag -- file to indicate whether it has run before. -- Use with caution. diff --git a/Propellor/Property/Apt.hs b/Propellor/Property/Apt.hs index 8bbb1b1..87c69da 100644 --- a/Propellor/Property/Apt.hs +++ b/Propellor/Property/Apt.hs @@ -147,9 +147,12 @@ autoRemove = runApt ["-y", "autoremove"] -- | Enables unattended upgrades. Revert to disable. unattendedUpgrades :: RevertableProperty -unattendedUpgrades = RevertableProperty (go True) (go False) +unattendedUpgrades = RevertableProperty enable disable where - go enabled = (if enabled then installed else removed) ["unattended-upgrades"] + enable = setup True `before` installed ["cron"] + disable = setup False + + setup enabled = (if enabled then installed else removed) ["unattended-upgrades"] `onChange` reConfigure "unattended-upgrades" [("unattended-upgrades/enable_auto_updates" , "boolean", v)] `describe` ("unattended upgrades " ++ v) diff --git a/config-joey.hs b/config-joey.hs index b431a4b..b7d9cf2 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -61,7 +61,7 @@ host _ = Nothing -- | This is where Docker containers are set up. A container -- can vary by hostname where it's used, or be the same everywhere. container :: HostName -> Docker.ContainerName -> Maybe (Docker.Container) -container parenthost name +container _parenthost name -- Simple web server, publishing the outside host's /var/www | name == "webserver" = Just $ Docker.containerFrom (image $ System (Debian Unstable) "amd64") diff --git a/debian/changelog b/debian/changelog index 365485a..4b04fb3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ propellor (0.2.4) UNRELEASED; urgency=medium * ipv6to4: Ensure interface is brought up automatically on boot. + * Enabling unattended upgrades now ensures that cron is installed and + running to perform them. -- Joey Hess Tue, 08 Apr 2014 18:07:12 -0400 From 306aa46f09c12ce326f03253213e9b96c483422b Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:42:18 -0400 Subject: [PATCH 11/88] propellor spin From 0e47d3ed2ec899c1c3765030dff2c7120fc8c58f Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:42:54 -0400 Subject: [PATCH 12/88] propellor spin --- Propellor/CmdLine.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index 5ea982c..a7cacdc 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -95,7 +95,7 @@ onlyProcess a = bracket lock unlock (const a) unknownhost :: HostName -> IO a unknownhost h = errorMessage $ unlines - [ "Unknown host: " ++ h + [ "Propellor does not know about host: " ++ h , "(Perhaps you should specify the real hostname on the command line?)" , "(Or, edit propellor's config.hs to configure this host)" ] From b722083211d3b77911afb2a776115a7af4375265 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:46:23 -0400 Subject: [PATCH 13/88] propellor spin --- Propellor/Property/Apt.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Propellor/Property/Apt.hs b/Propellor/Property/Apt.hs index 87c69da..ac1d9a1 100644 --- a/Propellor/Property/Apt.hs +++ b/Propellor/Property/Apt.hs @@ -149,7 +149,9 @@ autoRemove = runApt ["-y", "autoremove"] unattendedUpgrades :: RevertableProperty unattendedUpgrades = RevertableProperty enable disable where - enable = setup True `before` installed ["cron"] + enable = setup True + `before` installed ["cron"] + `before` serviceRunning "cron" disable = setup False setup enabled = (if enabled then installed else removed) ["unattended-upgrades"] From 00c88875a590007135833308330a754d367c840a Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:48:13 -0400 Subject: [PATCH 14/88] propellor spin From 7dd75e068020e54cbb2db616ed12afe2b28f8700 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:54:39 -0400 Subject: [PATCH 15/88] propellor spin From 7ccc78a2c77c1d687ff5b02f9b74e173473bce64 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:56:40 -0400 Subject: [PATCH 16/88] propellor spin --- config-joey.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index b7d9cf2..6e58d5d 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -47,8 +47,8 @@ host hostname@"orca.kitenet.net" = standardSystem Unstable $ props & Apt.buildDep ["git-annex"] & Docker.docked container hostname "amd64-git-annex-builder" & Docker.docked container hostname "i386-git-annex-builder" - & Docker.docked container hostname "armel-git-annex-builder-companion" - & Docker.docked container hostname "armel-git-annex-builder" + ! Docker.docked container hostname "armel-git-annex-builder-companion" + ! Docker.docked container hostname "armel-git-annex-builder" & Docker.garbageCollected -- My laptop host _hostname@"darkstar.kitenet.net" = Just $ props From 7561ee0443a33ffc0574dc6b606c9128da3fba4f Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 18:57:16 -0400 Subject: [PATCH 17/88] propellor spin From a52a2a89dfe92d7bed4a6446101657a288fd3bae Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 19:31:03 -0400 Subject: [PATCH 18/88] serviceInstalledRunning --- Propellor/Property/Apt.hs | 12 ++++++--- Propellor/Property/Cmd.hs | 23 ----------------- Propellor/Property/Cron.hs | 3 +-- Propellor/Property/OpenId.hs | 6 ++--- Propellor/Property/Service.hs | 25 +++++++++++++++++++ .../Property/SiteSpecific/GitAnnexBuilder.hs | 2 +- config-joey.hs | 3 +-- config-simple.hs | 3 +-- 8 files changed, 41 insertions(+), 36 deletions(-) create mode 100644 Propellor/Property/Service.hs diff --git a/Propellor/Property/Apt.hs b/Propellor/Property/Apt.hs index ac1d9a1..ff9b3de 100644 --- a/Propellor/Property/Apt.hs +++ b/Propellor/Property/Apt.hs @@ -8,6 +8,7 @@ import Control.Monad import Propellor import qualified Propellor.Property.File as File +import qualified Propellor.Property.Service as Service import Propellor.Property.File (Line) sourcesList :: FilePath @@ -149,9 +150,7 @@ autoRemove = runApt ["-y", "autoremove"] unattendedUpgrades :: RevertableProperty unattendedUpgrades = RevertableProperty enable disable where - enable = setup True - `before` installed ["cron"] - `before` serviceRunning "cron" + enable = setup True `before` Service.running "cron" disable = setup False setup enabled = (if enabled then installed else removed) ["unattended-upgrades"] @@ -176,3 +175,10 @@ reConfigure package vals = reconfigure `requires` setselections hPutStrLn h $ unwords [package, template, tmpltype, value] hClose h reconfigure = cmdProperty "dpkg-reconfigure" ["-fnone", package] + +-- | Ensures that a service is installed and running. +-- +-- Assumes that there is a 1:1 mapping between service names and apt +-- package names. +serviceInstalledRunning :: Package -> Property +serviceInstalledRunning svc = Service.running svc `requires` installed [svc] diff --git a/Propellor/Property/Cmd.hs b/Propellor/Property/Cmd.hs index f661cf8..c715fd2 100644 --- a/Propellor/Property/Cmd.hs +++ b/Propellor/Property/Cmd.hs @@ -3,16 +3,12 @@ module Propellor.Property.Cmd ( cmdProperty', scriptProperty, userScriptProperty, - serviceRunning, - serviceRestarted, ) where -import Control.Monad import Control.Applicative import Data.List import Propellor.Types -import Propellor.Engine import Utility.Monad import Utility.SafeCommand import Utility.Env @@ -47,22 +43,3 @@ userScriptProperty :: UserName -> [String] -> Property userScriptProperty user script = cmdProperty "su" ["-c", shellcmd, user] where shellcmd = intercalate " ; " ("set -e" : "cd" : script) - -type ServiceName = String - --- | Ensures that a service is running. --- --- Note that due to the general poor state of init scripts, the best --- we can do is try to start the service, and if it fails, assume --- this means it's already running. -serviceRunning :: ServiceName -> Property -serviceRunning svc = Property ("running " ++ svc) $ do - void $ ensureProperty $ - scriptProperty ["service " ++ shellEscape svc ++ " start >/dev/null 2>&1 || true"] - return NoChange - -serviceRestarted :: ServiceName -> Property -serviceRestarted svc = Property ("restarted " ++ svc) $ do - void $ ensureProperty $ - scriptProperty ["service " ++ shellEscape svc ++ " restart >/dev/null 2>&1 || true"] - return NoChange diff --git a/Propellor/Property/Cron.hs b/Propellor/Property/Cron.hs index 30bdb51..fa6019e 100644 --- a/Propellor/Property/Cron.hs +++ b/Propellor/Property/Cron.hs @@ -18,8 +18,7 @@ job desc times user cddir command = ("/etc/cron.d/" ++ desc) `File.hasContent` , "" , times ++ "\t" ++ user ++ "\t" ++ "cd " ++ cddir ++ " && " ++ command ] - `requires` Apt.installed ["cron"] - `requires` serviceRunning "cron" + `requires` Apt.serviceInstalledRunning "cron" `describe` ("cronned " ++ desc) -- | Installs a cron job, and runs it niced and ioniced. diff --git a/Propellor/Property/OpenId.hs b/Propellor/Property/OpenId.hs index 4f22bdb..c397bdb 100644 --- a/Propellor/Property/OpenId.hs +++ b/Propellor/Property/OpenId.hs @@ -3,15 +3,15 @@ module Propellor.Property.OpenId where import Propellor import qualified Propellor.Property.File as File import qualified Propellor.Property.Apt as Apt +import qualified Propellor.Property.Service as Service import Data.List providerFor :: [UserName] -> String -> Property providerFor users baseurl = propertyList desc $ - [ serviceRunning "apache2" - `requires` Apt.installed ["apache2"] + [ Apt.serviceInstalledRunning "apache2" , Apt.installed ["simpleid"] - `onChange` serviceRestarted "apache2" + `onChange` Service.restarted "apache2" , File.fileProperty desc (map setbaseurl) "/etc/simpleid/config.inc" ] ++ map identfile users diff --git a/Propellor/Property/Service.hs b/Propellor/Property/Service.hs new file mode 100644 index 0000000..2fb3e0c --- /dev/null +++ b/Propellor/Property/Service.hs @@ -0,0 +1,25 @@ +module Propellor.Property.Service where + +import Propellor +import Utility.SafeCommand + +type ServiceName = String + +-- | Ensures that a service is running. Does not ensure that +-- any package providing that service is installed. See +-- Apt.serviceInstalledRunning +-- +-- Note that due to the general poor state of init scripts, the best +-- we can do is try to start the service, and if it fails, assume +-- this means it's already running. +running :: ServiceName -> Property +running svc = Property ("running " ++ svc) $ do + void $ ensureProperty $ + scriptProperty ["service " ++ shellEscape svc ++ " start >/dev/null 2>&1 || true"] + return NoChange + +restarted :: ServiceName -> Property +restarted svc = Property ("restarted " ++ svc) $ do + void $ ensureProperty $ + scriptProperty ["service " ++ shellEscape svc ++ " restart >/dev/null 2>&1 || true"] + return NoChange diff --git a/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs b/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs index 149c8e6..580a52d 100644 --- a/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs +++ b/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs @@ -24,7 +24,7 @@ builder arch crontimes rsyncupload = combineProperties "gitannexbuilder" , Apt.buildDep ["git-annex"] , Apt.installed ["git", "rsync", "moreutils", "ca-certificates", "liblockfile-simple-perl", "cabal-install", "vim", "less"] - , serviceRunning "cron" `requires` Apt.installed ["cron"] + , Apt.serviceInstalledRunning "cron" , User.accountFor builduser , check (not <$> doesDirectoryExist gitbuilderdir) $ userScriptProperty builduser [ "git clone git://git.kitenet.net/gitannexbuilder " ++ gitbuilderdir diff --git a/config-joey.hs b/config-joey.hs index 6e58d5d..baabd8c 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -68,8 +68,7 @@ container _parenthost name [ Docker.publish "8080:80" , Docker.volume "/var/www:/var/www" , Docker.inside $ props - & serviceRunning "apache2" - `requires` Apt.installed ["apache2"] + & Apt.serviceInstalledRunning "apache2" & Apt.unattendedUpgrades ] diff --git a/config-simple.hs b/config-simple.hs index d5015ef..5e43b46 100644 --- a/config-simple.hs +++ b/config-simple.hs @@ -47,7 +47,6 @@ container _ "webserver" = Just $ Docker.containerFrom "joeyh/debian-unstable" [ Docker.publish "80:80" , Docker.volume "/var/www:/var/www" , Docker.inside $ props - & serviceRunning "apache2" - `requires` Apt.installed ["apache2"] + & Apt.serviceInstalledRunning "apache2" ] container _ _ = Nothing From c98ff4b715a050cf9dd0458f1a8ba7394cdfbd81 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 19:42:54 -0400 Subject: [PATCH 19/88] refactor --- config-joey.hs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index baabd8c..525f9d7 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -63,24 +63,20 @@ host _ = Nothing container :: HostName -> Docker.ContainerName -> Maybe (Docker.Container) container _parenthost name -- Simple web server, publishing the outside host's /var/www - | name == "webserver" = Just $ Docker.containerFrom - (image $ System (Debian Unstable) "amd64") + | name == "webserver" = Just $ standardContainer Stable "amd64" [ Docker.publish "8080:80" , Docker.volume "/var/www:/var/www" , Docker.inside $ props & Apt.serviceInstalledRunning "apache2" - & Apt.unattendedUpgrades ] -- My own openid provider. Uses php, so containerized for security -- and administrative sanity. - | name == "openid-provider" = Just $ Docker.containerFrom - (image $ System (Debian Stable) "amd64") + | name == "openid-provider" = Just $ standardContainer Stable "amd64" [ Docker.publish "8081:80" , Docker.inside $ props - & Apt.stdSourcesList Stable - & Apt.unattendedUpgrades - & OpenId.providerFor ["joey", "liw"] "openid.kitenet.net:8081" + & OpenId.providerFor ["joey", "liw"] + "openid.kitenet.net:8081" ] -- armel builder has a companion container that run amd64 and @@ -147,6 +143,15 @@ standardSystem suite customprops = Just $ -- Currently not enable due to #726375 endprops = [] -- [Apt.installed ["systemd-sysv"] `onChange` Reboot.now] +-- This is my standard container setup. +standardContainer :: DebianSuite -> Architecture -> [Docker.Containerized Property] -> Docker.Container +standardContainer suite arch ps = Docker.containerFrom + (image $ System (Debian suite) arch) $ + [ Docker.inside $ props + & Apt.stdSourcesList suite + & Apt.unattendedUpgrades + ] ++ ps + -- Clean up a system as installed by cloudatcost.com cleanCloudAtCost :: HostName -> Property cleanCloudAtCost hostname = propertyList "cloudatcost cleanup" From 969f01eb73cee1e49faf0195de5c784182349261 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 8 Apr 2014 21:28:15 -0400 Subject: [PATCH 20/88] todo --- TODO | 6 ++++++ config-joey.hs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 3b816ad..40bbd01 100644 --- a/TODO +++ b/TODO @@ -14,3 +14,9 @@ * Should properties be a tree rather than a list? * Only make docker garbage collection run once a day or something to avoid GC after a temp fail. +* Need a way for a dns server host to look at the properties of + the other hosts and generate a zone file. For example, mapping + openid.kitenet.net to a CNAME to clam.kitenet.net, which is where + the docker container for that service is located. Moving containers + to a different host, or duplicating a container on multiple hosts + would then update DNS too diff --git a/config-joey.hs b/config-joey.hs index 525f9d7..3b796ce 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -143,7 +143,7 @@ standardSystem suite customprops = Just $ -- Currently not enable due to #726375 endprops = [] -- [Apt.installed ["systemd-sysv"] `onChange` Reboot.now] --- This is my standard container setup. +-- This is my standard container setup, featuring automatic upgrades. standardContainer :: DebianSuite -> Architecture -> [Docker.Containerized Property] -> Docker.Container standardContainer suite arch ps = Docker.containerFrom (image $ System (Debian suite) arch) $ From 064cdd8fc575e5a16fa20bf382387560e9e4c580 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 9 Apr 2014 00:54:27 -0400 Subject: [PATCH 21/88] propellor spin --- Propellor/PrivData.hs | 1 + Propellor/Property/Scheduled.hs | 58 ++++++ TODO | 2 - Utility/QuickCheck.hs | 52 +++++ Utility/Scheduled.hs | 358 ++++++++++++++++++++++++++++++++ config-joey.hs | 8 +- config-simple.hs | 3 +- debian/changelog | 1 + propellor.cabal | 10 +- 9 files changed, 484 insertions(+), 9 deletions(-) create mode 100644 Propellor/Property/Scheduled.hs create mode 100644 Utility/QuickCheck.hs create mode 100644 Utility/Scheduled.hs diff --git a/Propellor/PrivData.hs b/Propellor/PrivData.hs index e768ae9..2897d42 100644 --- a/Propellor/PrivData.hs +++ b/Propellor/PrivData.hs @@ -23,6 +23,7 @@ withPrivData field a = maybe missing a =<< getPrivData field where missing = do warningMessage $ "Missing privdata " ++ show field + putStrLn $ "Fix this by running: propellor --set $hostname '" ++ show field ++ "'" return FailedChange getPrivData :: PrivDataField -> IO (Maybe String) diff --git a/Propellor/Property/Scheduled.hs b/Propellor/Property/Scheduled.hs new file mode 100644 index 0000000..42ff006 --- /dev/null +++ b/Propellor/Property/Scheduled.hs @@ -0,0 +1,58 @@ +module Propellor.Property.Scheduled + ( period + , Recurrance(..) + , WeekDay + , MonthDay + , YearDay + ) where + +import Propellor +import Utility.Scheduled + +import Data.Time.Clock +import Data.Time.LocalTime +import qualified Data.Map as M + +-- | Makes a Property only be checked every so often. +-- +-- This uses the description of the Property to keep track of when it was +-- last run. +period :: Property -> Recurrance -> Property +period prop recurrance = Property desc $ do + lasttime <- getLastChecked (propertyDesc prop) + nexttime <- fmap startTime <$> nextTime schedule lasttime + t <- localNow + if Just t >= nexttime + then do + r <- ensureProperty prop + setLastChecked t (propertyDesc prop) + return r + else noChange + where + schedule = Schedule recurrance AnyTime + desc = propertyDesc prop ++ " (period " ++ show recurrance ++ ")" + +lastCheckedFile :: FilePath +lastCheckedFile = localdir ".lastchecked" + +getLastChecked :: Desc -> IO (Maybe LocalTime) +getLastChecked desc = M.lookup desc <$> readLastChecked + +localNow :: IO LocalTime +localNow = do + now <- getCurrentTime + tz <- getTimeZone now + return $ utcToLocalTime tz now + +setLastChecked :: LocalTime -> Desc -> IO () +setLastChecked time desc = do + m <- readLastChecked + writeLastChecked (M.insert desc time m) + +readLastChecked :: IO (M.Map Desc LocalTime) +readLastChecked = fromMaybe M.empty <$> catchDefaultIO Nothing go + where + go = readish <$> readFile lastCheckedFile + +writeLastChecked :: M.Map Desc LocalTime -> IO () +writeLastChecked = writeFile lastCheckedFile . show diff --git a/TODO b/TODO index 40bbd01..a1f1c68 100644 --- a/TODO +++ b/TODO @@ -12,8 +12,6 @@ says they are unchanged even when they changed and triggered a reprovision. * Should properties be a tree rather than a list? -* Only make docker garbage collection run once a day or something - to avoid GC after a temp fail. * Need a way for a dns server host to look at the properties of the other hosts and generate a zone file. For example, mapping openid.kitenet.net to a CNAME to clam.kitenet.net, which is where diff --git a/Utility/QuickCheck.hs b/Utility/QuickCheck.hs new file mode 100644 index 0000000..7f7234c --- /dev/null +++ b/Utility/QuickCheck.hs @@ -0,0 +1,52 @@ +{- QuickCheck with additional instances + - + - Copyright 2012-2014 Joey Hess + - + - Licensed under the GNU GPL version 3 or higher. + -} + +{-# OPTIONS_GHC -fno-warn-orphans #-} +{-# LANGUAGE TypeSynonymInstances #-} + +module Utility.QuickCheck + ( module X + , module Utility.QuickCheck + ) where + +import Test.QuickCheck as X +import Data.Time.Clock.POSIX +import System.Posix.Types +import qualified Data.Map as M +import qualified Data.Set as S +import Control.Applicative + +instance (Arbitrary k, Arbitrary v, Eq k, Ord k) => Arbitrary (M.Map k v) where + arbitrary = M.fromList <$> arbitrary + +instance (Arbitrary v, Eq v, Ord v) => Arbitrary (S.Set v) where + arbitrary = S.fromList <$> arbitrary + +{- Times before the epoch are excluded. -} +instance Arbitrary POSIXTime where + arbitrary = fromInteger <$> nonNegative arbitrarySizedIntegral + +instance Arbitrary EpochTime where + arbitrary = fromInteger <$> nonNegative arbitrarySizedIntegral + +{- Pids are never negative, or 0. -} +instance Arbitrary ProcessID where + arbitrary = arbitrarySizedBoundedIntegral `suchThat` (> 0) + +{- Inodes are never negative. -} +instance Arbitrary FileID where + arbitrary = nonNegative arbitrarySizedIntegral + +{- File sizes are never negative. -} +instance Arbitrary FileOffset where + arbitrary = nonNegative arbitrarySizedIntegral + +nonNegative :: (Num a, Ord a) => Gen a -> Gen a +nonNegative g = g `suchThat` (>= 0) + +positive :: (Num a, Ord a) => Gen a -> Gen a +positive g = g `suchThat` (> 0) diff --git a/Utility/Scheduled.hs b/Utility/Scheduled.hs new file mode 100644 index 0000000..6b0609d --- /dev/null +++ b/Utility/Scheduled.hs @@ -0,0 +1,358 @@ +{- scheduled activities + - + - Copyright 2013 Joey Hess + - + - Licensed under the GNU GPL version 3 or higher. + -} + +module Utility.Scheduled ( + Schedule(..), + Recurrance(..), + ScheduledTime(..), + NextTime(..), + WeekDay, + MonthDay, + YearDay, + nextTime, + startTime, + fromSchedule, + fromScheduledTime, + toScheduledTime, + fromRecurrance, + toRecurrance, + toSchedule, + parseSchedule, + prop_schedule_roundtrips +) where + +import Utility.Data +import Utility.QuickCheck +import Utility.PartialPrelude +import Utility.Misc + +import Control.Applicative +import Data.List +import Data.Time.Clock +import Data.Time.LocalTime +import Data.Time.Calendar +import Data.Time.Calendar.WeekDate +import Data.Time.Calendar.OrdinalDate +import Data.Tuple.Utils +import Data.Char + +{- Some sort of scheduled event. -} +data Schedule = Schedule Recurrance ScheduledTime + deriving (Eq, Read, Show, Ord) + +data Recurrance + = Daily + | Weekly (Maybe WeekDay) + | Monthly (Maybe MonthDay) + | Yearly (Maybe YearDay) + -- ^ Days, Weeks, or Months of the year evenly divisible by a number. + -- (Divisible Year is years evenly divisible by a number.) + | Divisible Int Recurrance + deriving (Eq, Read, Show, Ord) + +type WeekDay = Int +type MonthDay = Int +type YearDay = Int + +data ScheduledTime + = AnyTime + | SpecificTime Hour Minute + deriving (Eq, Read, Show, Ord) + +type Hour = Int +type Minute = Int + +{- Next time a Schedule should take effect. The NextTimeWindow is used + - when a Schedule is allowed to start at some point within the window. -} +data NextTime + = NextTimeExactly LocalTime + | NextTimeWindow LocalTime LocalTime + deriving (Eq, Read, Show) + +startTime :: NextTime -> LocalTime +startTime (NextTimeExactly t) = t +startTime (NextTimeWindow t _) = t + +nextTime :: Schedule -> Maybe LocalTime -> IO (Maybe NextTime) +nextTime schedule lasttime = do + now <- getCurrentTime + tz <- getTimeZone now + return $ calcNextTime schedule lasttime $ utcToLocalTime tz now + +{- Calculate the next time that fits a Schedule, based on the + - last time it occurred, and the current time. -} +calcNextTime :: Schedule -> Maybe LocalTime -> LocalTime -> Maybe NextTime +calcNextTime (Schedule recurrance scheduledtime) lasttime currenttime + | scheduledtime == AnyTime = do + next <- findfromtoday True + return $ case next of + NextTimeWindow _ _ -> next + NextTimeExactly t -> window (localDay t) (localDay t) + | otherwise = NextTimeExactly . startTime <$> findfromtoday False + where + findfromtoday anytime = findfrom recurrance afterday today + where + today = localDay currenttime + afterday = sameaslastday || toolatetoday + toolatetoday = not anytime && localTimeOfDay currenttime >= nexttime + sameaslastday = lastday == Just today + lastday = localDay <$> lasttime + nexttime = case scheduledtime of + AnyTime -> TimeOfDay 0 0 0 + SpecificTime h m -> TimeOfDay h m 0 + exactly d = NextTimeExactly $ LocalTime d nexttime + window startd endd = NextTimeWindow + (LocalTime startd nexttime) + (LocalTime endd (TimeOfDay 23 59 0)) + findfrom r afterday day = case r of + Daily + | afterday -> Just $ exactly $ addDays 1 day + | otherwise -> Just $ exactly day + Weekly Nothing + | afterday -> skip 1 + | otherwise -> case (wday <$> lastday, wday day) of + (Nothing, _) -> Just $ window day (addDays 6 day) + (Just old, curr) + | old == curr -> Just $ window day (addDays 6 day) + | otherwise -> skip 1 + Monthly Nothing + | afterday -> skip 1 + | maybe True (\old -> mnum day > mday old && mday day >= (mday old `mod` minmday)) lastday -> + -- Window only covers current month, + -- in case there is a Divisible requirement. + Just $ window day (endOfMonth day) + | otherwise -> skip 1 + Yearly Nothing + | afterday -> skip 1 + | maybe True (\old -> ynum day > ynum old && yday day >= (yday old `mod` minyday)) lastday -> + Just $ window day (endOfYear day) + | otherwise -> skip 1 + Weekly (Just w) + | w < 0 || w > maxwday -> Nothing + | w == wday day -> if afterday + then Just $ exactly $ addDays 7 day + else Just $ exactly day + | otherwise -> Just $ exactly $ + addDays (fromIntegral $ (w - wday day) `mod` 7) day + Monthly (Just m) + | m < 0 || m > maxmday -> Nothing + -- TODO can be done more efficiently than recursing + | m == mday day -> if afterday + then skip 1 + else Just $ exactly day + | otherwise -> skip 1 + Yearly (Just y) + | y < 0 || y > maxyday -> Nothing + | y == yday day -> if afterday + then skip 365 + else Just $ exactly day + | otherwise -> skip 1 + Divisible n r'@Daily -> handlediv n r' yday (Just maxyday) + Divisible n r'@(Weekly _) -> handlediv n r' wnum (Just maxwnum) + Divisible n r'@(Monthly _) -> handlediv n r' mnum (Just maxmnum) + Divisible n r'@(Yearly _) -> handlediv n r' ynum Nothing + Divisible _ r'@(Divisible _ _) -> findfrom r' afterday day + where + skip n = findfrom r False (addDays n day) + handlediv n r' getval mmax + | n > 0 && maybe True (n <=) mmax = + findfromwhere r' (divisible n . getval) afterday day + | otherwise = Nothing + findfromwhere r p afterday day + | maybe True (p . getday) next = next + | otherwise = maybe Nothing (findfromwhere r p True . getday) next + where + next = findfrom r afterday day + getday = localDay . startTime + divisible n v = v `rem` n == 0 + +endOfMonth :: Day -> Day +endOfMonth day = + let (y,m,_d) = toGregorian day + in fromGregorian y m (gregorianMonthLength y m) + +endOfYear :: Day -> Day +endOfYear day = + let (y,_m,_d) = toGregorian day + in endOfMonth (fromGregorian y maxmnum 1) + +-- extracting various quantities from a Day +wday :: Day -> Int +wday = thd3 . toWeekDate +wnum :: Day -> Int +wnum = snd3 . toWeekDate +mday :: Day -> Int +mday = thd3 . toGregorian +mnum :: Day -> Int +mnum = snd3 . toGregorian +yday :: Day -> Int +yday = snd . toOrdinalDate +ynum :: Day -> Int +ynum = fromIntegral . fst . toOrdinalDate + +{- Calendar max and mins. -} +maxyday :: Int +maxyday = 366 -- with leap days +minyday :: Int +minyday = 365 +maxwnum :: Int +maxwnum = 53 -- some years have more than 52 +maxmday :: Int +maxmday = 31 +minmday :: Int +minmday = 28 +maxmnum :: Int +maxmnum = 12 +maxwday :: Int +maxwday = 7 + +fromRecurrance :: Recurrance -> String +fromRecurrance (Divisible n r) = + fromRecurrance' (++ "s divisible by " ++ show n) r +fromRecurrance r = fromRecurrance' ("every " ++) r + +fromRecurrance' :: (String -> String) -> Recurrance -> String +fromRecurrance' a Daily = a "day" +fromRecurrance' a (Weekly n) = onday n (a "week") +fromRecurrance' a (Monthly n) = onday n (a "month") +fromRecurrance' a (Yearly n) = onday n (a "year") +fromRecurrance' a (Divisible _n r) = fromRecurrance' a r -- not used + +onday :: Maybe Int -> String -> String +onday (Just n) s = "on day " ++ show n ++ " of " ++ s +onday Nothing s = s + +toRecurrance :: String -> Maybe Recurrance +toRecurrance s = case words s of + ("every":"day":[]) -> Just Daily + ("on":"day":sd:"of":"every":something:[]) -> withday sd something + ("every":something:[]) -> noday something + ("days":"divisible":"by":sn:[]) -> + Divisible <$> getdivisor sn <*> pure Daily + ("on":"day":sd:"of":something:"divisible":"by":sn:[]) -> + Divisible + <$> getdivisor sn + <*> withday sd something + ("every":something:"divisible":"by":sn:[]) -> + Divisible + <$> getdivisor sn + <*> noday something + (something:"divisible":"by":sn:[]) -> + Divisible + <$> getdivisor sn + <*> noday something + _ -> Nothing + where + constructor "week" = Just Weekly + constructor "month" = Just Monthly + constructor "year" = Just Yearly + constructor u + | "s" `isSuffixOf` u = constructor $ reverse $ drop 1 $ reverse u + | otherwise = Nothing + withday sd u = do + c <- constructor u + d <- readish sd + Just $ c (Just d) + noday u = do + c <- constructor u + Just $ c Nothing + getdivisor sn = do + n <- readish sn + if n > 0 + then Just n + else Nothing + +fromScheduledTime :: ScheduledTime -> String +fromScheduledTime AnyTime = "any time" +fromScheduledTime (SpecificTime h m) = + show h' ++ (if m > 0 then ":" ++ pad 2 (show m) else "") ++ " " ++ ampm + where + pad n s = take (n - length s) (repeat '0') ++ s + (h', ampm) + | h == 0 = (12, "AM") + | h < 12 = (h, "AM") + | h == 12 = (h, "PM") + | otherwise = (h - 12, "PM") + +toScheduledTime :: String -> Maybe ScheduledTime +toScheduledTime "any time" = Just AnyTime +toScheduledTime v = case words v of + (s:ampm:[]) + | map toUpper ampm == "AM" -> + go s h0 + | map toUpper ampm == "PM" -> + go s (\h -> (h0 h) + 12) + | otherwise -> Nothing + (s:[]) -> go s id + _ -> Nothing + where + h0 h + | h == 12 = 0 + | otherwise = h + go :: String -> (Int -> Int) -> Maybe ScheduledTime + go s adjust = + let (h, m) = separate (== ':') s + in SpecificTime + <$> (adjust <$> readish h) + <*> if null m then Just 0 else readish m + +fromSchedule :: Schedule -> String +fromSchedule (Schedule recurrance scheduledtime) = unwords + [ fromRecurrance recurrance + , "at" + , fromScheduledTime scheduledtime + ] + +toSchedule :: String -> Maybe Schedule +toSchedule = eitherToMaybe . parseSchedule + +parseSchedule :: String -> Either String Schedule +parseSchedule s = do + r <- maybe (Left $ "bad recurrance: " ++ recurrance) Right + (toRecurrance recurrance) + t <- maybe (Left $ "bad time of day: " ++ scheduledtime) Right + (toScheduledTime scheduledtime) + Right $ Schedule r t + where + (rws, tws) = separate (== "at") (words s) + recurrance = unwords rws + scheduledtime = unwords tws + +instance Arbitrary Schedule where + arbitrary = Schedule <$> arbitrary <*> arbitrary + +instance Arbitrary ScheduledTime where + arbitrary = oneof + [ pure AnyTime + , SpecificTime + <$> choose (0, 23) + <*> choose (1, 59) + ] + +instance Arbitrary Recurrance where + arbitrary = oneof + [ pure Daily + , Weekly <$> arbday + , Monthly <$> arbday + , Yearly <$> arbday + , Divisible + <$> positive arbitrary + <*> oneof -- no nested Divisibles + [ pure Daily + , Weekly <$> arbday + , Monthly <$> arbday + , Yearly <$> arbday + ] + ] + where + arbday = oneof + [ Just <$> nonNegative arbitrary + , pure Nothing + ] + +prop_schedule_roundtrips :: Schedule -> Bool +prop_schedule_roundtrips s = toSchedule (fromSchedule s) == Just s diff --git a/config-joey.hs b/config-joey.hs index 3b796ce..6c4507d 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -2,6 +2,7 @@ import Propellor import Propellor.CmdLine +import Propellor.Property.Scheduled import qualified Propellor.Property.File as File import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.Network as Network @@ -38,21 +39,22 @@ host hostname@"clam.kitenet.net" = standardSystem Unstable $ props & JoeySites.oldUseNetshellBox & Docker.docked container hostname "openid-provider" & Docker.configured - & Docker.garbageCollected + & Docker.garbageCollected `period` Daily -- Orca is the main git-annex build box. host hostname@"orca.kitenet.net" = standardSystem Unstable $ props & Hostname.set hostname & Apt.unattendedUpgrades & Docker.configured - & Apt.buildDep ["git-annex"] + & Apt.buildDep ["git-annex"] `period` Daily & Docker.docked container hostname "amd64-git-annex-builder" & Docker.docked container hostname "i386-git-annex-builder" ! Docker.docked container hostname "armel-git-annex-builder-companion" ! Docker.docked container hostname "armel-git-annex-builder" - & Docker.garbageCollected + & Docker.garbageCollected `period` Daily -- My laptop host _hostname@"darkstar.kitenet.net" = Just $ props & Docker.configured + & Apt.buildDep ["git-annex"] `period` Daily -- add more hosts here... --host "foo.example.com" = diff --git a/config-simple.hs b/config-simple.hs index 5e43b46..6784f76 100644 --- a/config-simple.hs +++ b/config-simple.hs @@ -3,6 +3,7 @@ import Propellor import Propellor.CmdLine +import Propellor.Property.Scheduled import qualified Propellor.Property.File as File import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.Network as Network @@ -34,7 +35,7 @@ host hostname@"mybox.example.com" = Just $ props & Network.ipv6to4 & File.dirExists "/var/www" & Docker.docked container hostname "webserver" - & Docker.garbageCollected + & Docker.garbageCollected `period` Daily & Cron.runPropellor "30 * * * *" -- add more hosts here... --host "foo.example.com" = diff --git a/debian/changelog b/debian/changelog index 4b04fb3..d83b6ad 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,7 @@ propellor (0.2.4) UNRELEASED; urgency=medium * ipv6to4: Ensure interface is brought up automatically on boot. * Enabling unattended upgrades now ensures that cron is installed and running to perform them. + * Properties can be scheduled to only be checked after a given time period. -- Joey Hess Tue, 08 Apr 2014 18:07:12 -0400 diff --git a/propellor.cabal b/propellor.cabal index 0869ef5..c3f4f4c 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -38,7 +38,7 @@ Executable propellor GHC-Options: -Wall Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5, IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal, - containers, network, async + containers, network, async, time, QuickCheck if (! os(windows)) Build-Depends: unix @@ -48,7 +48,7 @@ Executable config GHC-Options: -Wall -threaded Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5, IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal, - containers, network, async + containers, network, async, time, QuickCheck if (! os(windows)) Build-Depends: unix @@ -57,7 +57,7 @@ Library GHC-Options: -Wall Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5, IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal, - containers, network, async + containers, network, async, time, QuickCheck if (! os(windows)) Build-Depends: unix @@ -73,6 +73,8 @@ Library Propellor.Property.File Propellor.Property.Network Propellor.Property.Reboot + Propellor.Property.Scheduled + Propellor.Property.Service Propellor.Property.Ssh Propellor.Property.Sudo Propellor.Property.Tor @@ -103,9 +105,11 @@ Library Utility.PosixFiles Utility.Process Utility.SafeCommand + Utility.Scheduled Utility.ThreadScheduler Utility.Tmp Utility.UserInfo + Utility.QuickCheck source-repository head type: git From 0205912d8a5905d96f5f10d3402a270dcb5475e4 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 9 Apr 2014 00:55:11 -0400 Subject: [PATCH 22/88] propellor spin From 99ab46498c55c447cad1dfbe668a4b50204edea3 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 9 Apr 2014 00:56:56 -0400 Subject: [PATCH 23/88] propellor spin From 335cce275afb78931416748c9e9acabb22c68f64 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 9 Apr 2014 00:57:57 -0400 Subject: [PATCH 24/88] propellor spin --- Propellor/Property/Scheduled.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/Propellor/Property/Scheduled.hs b/Propellor/Property/Scheduled.hs index 42ff006..2e7ae06 100644 --- a/Propellor/Property/Scheduled.hs +++ b/Propellor/Property/Scheduled.hs @@ -20,6 +20,7 @@ import qualified Data.Map as M period :: Property -> Recurrance -> Property period prop recurrance = Property desc $ do lasttime <- getLastChecked (propertyDesc prop) + print lasttime nexttime <- fmap startTime <$> nextTime schedule lasttime t <- localNow if Just t >= nexttime From ad02c89c6aa6902b9b16375f61df2139e47dfcb3 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 9 Apr 2014 01:15:11 -0400 Subject: [PATCH 25/88] update --- Propellor/Property/Scheduled.hs | 11 ++++++++++- Utility/Scheduled.hs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Propellor/Property/Scheduled.hs b/Propellor/Property/Scheduled.hs index 2e7ae06..bef2a29 100644 --- a/Propellor/Property/Scheduled.hs +++ b/Propellor/Property/Scheduled.hs @@ -1,5 +1,6 @@ module Propellor.Property.Scheduled ( period + , periodParse , Recurrance(..) , WeekDay , MonthDay @@ -31,7 +32,15 @@ period prop recurrance = Property desc $ do else noChange where schedule = Schedule recurrance AnyTime - desc = propertyDesc prop ++ " (period " ++ show recurrance ++ ")" + desc = propertyDesc prop ++ " (period " ++ fromRecurrance recurrance ++ ")" + +-- | Like period, but parse a human-friendly string. +periodParse :: Property -> String -> Property +periodParse prop s = case toRecurrance s of + Just recurrance -> period prop recurrance + Nothing -> Property "periodParse" $ do + warningMessage $ "failed periodParse: " ++ s + noChange lastCheckedFile :: FilePath lastCheckedFile = localdir ".lastchecked" diff --git a/Utility/Scheduled.hs b/Utility/Scheduled.hs index 6b0609d..11e3b56 100644 --- a/Utility/Scheduled.hs +++ b/Utility/Scheduled.hs @@ -49,9 +49,9 @@ data Recurrance | Weekly (Maybe WeekDay) | Monthly (Maybe MonthDay) | Yearly (Maybe YearDay) + | Divisible Int Recurrance -- ^ Days, Weeks, or Months of the year evenly divisible by a number. -- (Divisible Year is years evenly divisible by a number.) - | Divisible Int Recurrance deriving (Eq, Read, Show, Ord) type WeekDay = Int From 9961c4dd288a32ea19e95767742cf63836d9c32a Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 9 Apr 2014 02:16:37 -0400 Subject: [PATCH 26/88] forgot file --- propellor.cabal | 1 + 1 file changed, 1 insertion(+) diff --git a/propellor.cabal b/propellor.cabal index c3f4f4c..a6510df 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -72,6 +72,7 @@ Library Propellor.Property.Docker Propellor.Property.File Propellor.Property.Network + Propellor.Property.OpenId Propellor.Property.Reboot Propellor.Property.Scheduled Propellor.Property.Service From 942552158c2e56cadfa225f87f0aba8dc689a384 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 9 Apr 2014 12:00:23 -0400 Subject: [PATCH 27/88] openid needs a good clock --- config-joey.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/config-joey.hs b/config-joey.hs index 6c4507d..a9ca469 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -38,6 +38,7 @@ host hostname@"clam.kitenet.net" = standardSystem Unstable $ props & Tor.isBridge & JoeySites.oldUseNetshellBox & Docker.docked container hostname "openid-provider" + `requires` Apt.installed ["ntp"] & Docker.configured & Docker.garbageCollected `period` Daily -- Orca is the main git-annex build box. From cf73387300bd66ab05ffd2632aed7903e0ffbb96 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 9 Apr 2014 18:37:11 -0400 Subject: [PATCH 28/88] updat --- TODO | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO b/TODO index a1f1c68..6f0de94 100644 --- a/TODO +++ b/TODO @@ -18,3 +18,6 @@ the docker container for that service is located. Moving containers to a different host, or duplicating a container on multiple hosts would then update DNS too +* There is no way for a property of a docker container to require + some property be met outside the container. For example, some servers + need ntp installed for a good date source. From 627e92f9c93faa78cad2762d62d0c1f1b820cab0 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:10:08 -0400 Subject: [PATCH 29/88] propellor spin --- config-joey.hs | 68 +++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index a9ca469..d233192 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -11,7 +11,7 @@ import qualified Propellor.Property.Cron as Cron import qualified Propellor.Property.Sudo as Sudo import qualified Propellor.Property.User as User import qualified Propellor.Property.Hostname as Hostname ---import qualified Propellor.Property.Reboot as Reboot +import qualified Propellor.Property.Reboot as Reboot import qualified Propellor.Property.Tor as Tor import qualified Propellor.Property.OpenId as OpenId import qualified Propellor.Property.Docker as Docker @@ -30,8 +30,9 @@ main = defaultMain [host, Docker.containerProperties container] -- Edit this to configure propellor! host :: HostName -> Maybe [Property] -- Clam is a tor bridge, and an olduse.net shellbox and other fun stuff. -host hostname@"clam.kitenet.net" = standardSystem Unstable $ props +host hostname@"clam.kitenet.net" = Just $ withSystemd $ props & cleanCloudAtCost hostname + & standardSystem Unstable & Apt.unattendedUpgrades & Network.ipv6to4 & Apt.installed ["git-annex", "mtr"] @@ -42,7 +43,8 @@ host hostname@"clam.kitenet.net" = standardSystem Unstable $ props & Docker.configured & Docker.garbageCollected `period` Daily -- Orca is the main git-annex build box. -host hostname@"orca.kitenet.net" = standardSystem Unstable $ props +host hostname@"orca.kitenet.net" = Just $ props -- no systemd due to #726375 + & standardSystem Unstable & Hostname.set hostname & Apt.unattendedUpgrades & Docker.configured @@ -52,6 +54,16 @@ host hostname@"orca.kitenet.net" = standardSystem Unstable $ props ! Docker.docked container hostname "armel-git-annex-builder-companion" ! Docker.docked container hostname "armel-git-annex-builder" & Docker.garbageCollected `period` Daily +-- Diatom is my downloads and git repos server, and secondary dns server. +host hostname@"diatom.kitenet.net" = Just $ withSystemd $ props + & standardSystem Stable + & Hostname.set hostname + & Apt.unattendedUpgrades + & Apt.serviceInstalledRunning "apache2" + & Apt.serviceInstalledRunning "bind9" + & Apt.serviceInstalledRunning "ntp" + & Apt.installed ["git", "git-annex"] + & Apt.buildDep ["git-annex"] `period` Daily -- My laptop host _hostname@"darkstar.kitenet.net" = Just $ props & Docker.configured @@ -119,32 +131,30 @@ image (System (Debian Stable) arch) = "joeyh/debian-stable-" ++ arch image _ = "debian-stable-official" -- does not currently exist! -- This is my standard system setup -standardSystem :: DebianSuite -> [Property] -> Maybe [Property] -standardSystem suite customprops = Just $ - standardprops : customprops ++ endprops - where - standardprops = propertyList "standard system" $ props - & Apt.stdSourcesList suite `onChange` Apt.upgrade - & Apt.installed ["etckeeper"] - & Apt.installed ["ssh"] - & GitHome.installedFor "root" - & User.hasSomePassword "root" - -- Harden the system, but only once root's authorized_keys - -- is safely in place. - & check (Ssh.hasAuthorizedKeys "root") - (Ssh.passwordAuthentication False) - & User.accountFor "joey" - & User.hasSomePassword "joey" - & Sudo.enabledFor "joey" - & GitHome.installedFor "joey" - & Apt.installed ["vim", "screen", "less"] - & Cron.runPropellor "30 * * * *" - -- I use postfix, or no MTA. - & Apt.removed ["exim4", "exim4-daemon-light", "exim4-config", "exim4-base"] - `onChange` Apt.autoRemove - -- May reboot, so comes last - -- Currently not enable due to #726375 - endprops = [] -- [Apt.installed ["systemd-sysv"] `onChange` Reboot.now] +standardSystem :: DebianSuite -> Property +standardSystem suite = propertyList "standard system" $ props + & Apt.stdSourcesList suite `onChange` Apt.upgrade + & Apt.installed ["etckeeper"] + & Apt.installed ["ssh"] + & GitHome.installedFor "root" + & User.hasSomePassword "root" + -- Harden the system, but only once root's authorized_keys + -- is safely in place. + & check (Ssh.hasAuthorizedKeys "root") + (Ssh.passwordAuthentication False) + & User.accountFor "joey" + & User.hasSomePassword "joey" + & Sudo.enabledFor "joey" + & GitHome.installedFor "joey" + & Apt.installed ["vim", "screen", "less"] + & Cron.runPropellor "30 * * * *" + -- I use postfix, or no MTA. + & Apt.removed ["exim4", "exim4-daemon-light", "exim4-config", "exim4-base"] + `onChange` Apt.autoRemove + +withSystemd :: [Property] -> [Property] +withSystemd props = props ++ + [Apt.installed ["systemd-sysv"] `onChange` Reboot.now] -- This is my standard container setup, featuring automatic upgrades. standardContainer :: DebianSuite -> Architecture -> [Docker.Containerized Property] -> Docker.Container From 1687f4d771c9be7e1f11d34c0f1f274aef4a927c Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:17:39 -0400 Subject: [PATCH 30/88] propellor spin --- config-joey.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index d233192..a98ed72 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -153,8 +153,7 @@ standardSystem suite = propertyList "standard system" $ props `onChange` Apt.autoRemove withSystemd :: [Property] -> [Property] -withSystemd props = props ++ - [Apt.installed ["systemd-sysv"] `onChange` Reboot.now] +withSystemd ps = ps ++ [Apt.installed ["systemd-sysv"] `onChange` Reboot.now] -- This is my standard container setup, featuring automatic upgrades. standardContainer :: DebianSuite -> Architecture -> [Docker.Containerized Property] -> Docker.Container From 68028803bac71f226e03902cfdb033bb1fb2dcc9 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:29:47 -0400 Subject: [PATCH 31/88] propellor spin --- Propellor/Property/Apt.hs | 11 ++++++++++- Propellor/Types.hs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Propellor/Property/Apt.hs b/Propellor/Property/Apt.hs index ff9b3de..937d140 100644 --- a/Propellor/Property/Apt.hs +++ b/Propellor/Property/Apt.hs @@ -47,13 +47,22 @@ debCdn = binandsrc "http://cdn.debian.net/debian" kernelOrg :: DebianSuite -> [Line] kernelOrg = binandsrc "http://mirrors.kernel.org/debian" +-- | Only available for Stable and Testing +securityUpdates :: DebianSuite -> [Line] +securityUpdates suite + | suite == Stable || suite == Testing = + let l = "deb http://security.debian.org/ " ++ showSuite suite ++ "/updates " ++ unwords stdSections + in [l, srcLine l] + | otherwise = [] + -- | Makes sources.list have a standard content using the mirror CDN, -- with a particular DebianSuite. -- -- Since the CDN is sometimes unreliable, also adds backup lines using -- kernel.org. stdSourcesList :: DebianSuite -> Property -stdSourcesList suite = setSourcesList (debCdn suite ++ kernelOrg suite) +stdSourcesList suite = setSourcesList + (debCdn suite ++ kernelOrg suite ++ securityUpdates suite) `describe` ("standard sources.list for " ++ show suite) setSourcesList :: [Line] -> Property diff --git a/Propellor/Types.hs b/Propellor/Types.hs index 856e0ea..c6be30c 100644 --- a/Propellor/Types.hs +++ b/Propellor/Types.hs @@ -63,7 +63,7 @@ data Distribution deriving (Show) data DebianSuite = Experimental | Unstable | Testing | Stable | DebianRelease Release - deriving (Show) + deriving (Show, Eq) type Release = String From fbd1e5c109968cb127f4535031e4dfbe753a8996 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:30:57 -0400 Subject: [PATCH 32/88] propellor spin --- config-joey.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-joey.hs b/config-joey.hs index a98ed72..069ea39 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -55,7 +55,7 @@ host hostname@"orca.kitenet.net" = Just $ props -- no systemd due to #726375 ! Docker.docked container hostname "armel-git-annex-builder" & Docker.garbageCollected `period` Daily -- Diatom is my downloads and git repos server, and secondary dns server. -host hostname@"diatom.kitenet.net" = Just $ withSystemd $ props +host hostname@"diatom.kitenet.net" = Just $ props & standardSystem Stable & Hostname.set hostname & Apt.unattendedUpgrades From b13574372c70b0b83ca4d68699f2c3e2b46071d0 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:37:03 -0400 Subject: [PATCH 33/88] propellor spin --- Propellor/CmdLine.hs | 2 +- config-joey.hs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index a7cacdc..ca15556 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -192,7 +192,7 @@ spin host = do bootstrapcmd = shellWrap $ intercalate " ; " [ "if [ ! -d " ++ localdir ++ " ]" , "then " ++ intercalate " && " - [ "apt-get -y install git" + [ "apt-get -y install git make" , "echo " ++ toMarked statusMarker (show NeedGitClone) ] , "else " ++ intercalate " && " diff --git a/config-joey.hs b/config-joey.hs index 069ea39..dfa56e5 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -59,10 +59,11 @@ host hostname@"diatom.kitenet.net" = Just $ props & standardSystem Stable & Hostname.set hostname & Apt.unattendedUpgrades - & Apt.serviceInstalledRunning "apache2" - & Apt.serviceInstalledRunning "bind9" & Apt.serviceInstalledRunning "ntp" - & Apt.installed ["git", "git-annex"] + & Apt.serviceInstalledRunning "bind9" + & Apt.serviceInstalledRunning "apache2" + & Apt.serviceInstalledRunning "git-daemon-sysvinit" + & Apt.installed ["git", "git-annex", "rsync"] & Apt.buildDep ["git-annex"] `period` Daily -- My laptop host _hostname@"darkstar.kitenet.net" = Just $ props From 208c8181a976152e496b1434f86413828beee0c7 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:37:44 -0400 Subject: [PATCH 34/88] propellor spin From bb1bc1ddc6627288d05668795d16f4acef4076e3 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:40:38 -0400 Subject: [PATCH 35/88] propellor spin --- Propellor/CmdLine.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index ca15556..e4cab86 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -197,7 +197,7 @@ spin host = do ] , "else " ++ intercalate " && " [ "cd " ++ localdir - , "if ! test -x ./propellor; then make build; fi" + , "if ! test -x ./propellor; then make deps build; fi" , "./propellor --boot " ++ host ] , "fi" From 59b5965d3df4ff75349602ee0a7ff27738031cb2 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:42:49 -0400 Subject: [PATCH 36/88] propellor spin --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9b4a7d8..5bf9f40 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ build: dist/setup-config ln -sf dist/build/config/config propellor deps: - @if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-async-dev; fi || true + @if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-async-dev || cabal update; cabal install --only-dependencies; fi || true dist/setup-config: propellor.cabal if [ "$(CABAL)" = ./Setup ]; then ghc --make Setup; fi From 41252b8b3255fd8db1f71ed77039f9aa0072a7f3 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:43:53 -0400 Subject: [PATCH 37/88] propellor spin --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5bf9f40..cba466d 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,8 @@ build: dist/setup-config ln -sf dist/build/config/config propellor deps: - @if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-async-dev || cabal update; cabal install --only-dependencies; fi || true + @if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev; fi || true + @if [ $$(whoami) = root ]; then apt-get -u install libghc-async-dev || cabal update; cabal install async; fi || true dist/setup-config: propellor.cabal if [ "$(CABAL)" = ./Setup ]; then ghc --make Setup; fi From 98afa8d9e5d3e23b7498ad14cc89346e32527e35 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:47:13 -0400 Subject: [PATCH 38/88] propellor spin From a08ae04bcb922ed076a123ecade16d58e6f75982 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:48:24 -0400 Subject: [PATCH 39/88] propellor spin --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index cba466d..580e9a0 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ build: dist/setup-config ln -sf dist/build/config/config propellor deps: - @if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev; fi || true + @if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev; fi || true @if [ $$(whoami) = root ]; then apt-get -u install libghc-async-dev || cabal update; cabal install async; fi || true dist/setup-config: propellor.cabal From 894907a7964ea051cf3a062fc53031c152b203b9 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:51:12 -0400 Subject: [PATCH 40/88] propellor spin --- Propellor/SimpleSh.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/SimpleSh.hs b/Propellor/SimpleSh.hs index 99a6fc2..7e0f19f 100644 --- a/Propellor/SimpleSh.hs +++ b/Propellor/SimpleSh.hs @@ -27,7 +27,7 @@ simpleSh namedpipe = do createDirectoryIfMissing True dir modifyFileMode dir (removeModes otherGroupModes) s <- socket AF_UNIX Stream defaultProtocol - bind s (SockAddrUnix namedpipe) + bindSocket s (SockAddrUnix namedpipe) listen s 2 forever $ do (client, _addr) <- accept s From 1abb8c9cec45004e3e109dbe2b08d27491cfe3a6 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:52:08 -0400 Subject: [PATCH 41/88] propellor spin From 8852887907aeefd890720c91eb5fe5a4b6031067 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 00:53:12 -0400 Subject: [PATCH 42/88] propellor spin --- debian/changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/debian/changelog b/debian/changelog index d83b6ad..ed882cf 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,9 @@ propellor (0.2.4) UNRELEASED; urgency=medium * Enabling unattended upgrades now ensures that cron is installed and running to perform them. * Properties can be scheduled to only be checked after a given time period. + * Fix bootstrapping of dependencies. + * Fix compilation on Debian stable. + * Include security updates in sources.list for stable and testing. -- Joey Hess Tue, 08 Apr 2014 18:07:12 -0400 From a7547537efbf4d64d71bc6a17517505dca30b758 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 01:06:54 -0400 Subject: [PATCH 43/88] propellor spin --- Propellor/Property/SiteSpecific/GitHome.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Propellor/Property/SiteSpecific/GitHome.hs b/Propellor/Property/SiteSpecific/GitHome.hs index 38e0cb9..482100c 100644 --- a/Propellor/Property/SiteSpecific/GitHome.hs +++ b/Propellor/Property/SiteSpecific/GitHome.hs @@ -9,7 +9,7 @@ import Utility.SafeCommand installedFor :: UserName -> Property installedFor user = check (not <$> hasGitDir user) $ Property ("githome " ++ user) (go =<< homedir user) - `requires` Apt.installed ["git", "myrepos"] + `requires` Apt.installed ["git"] where go Nothing = noChange go (Just home) = do @@ -20,7 +20,7 @@ installedFor user = check (not <$> hasGitDir user) $ moveout tmpdir home , Property "rmdir" $ makeChange $ void $ catchMaybeIO $ removeDirectory tmpdir - , userScriptProperty user ["rm -rf .aptitude/ .bashrc .profile; mr checkout; bin/fixups"] + , userScriptProperty user ["rm -rf .aptitude/ .bashrc .profile; bin/mr checkout; bin/fixups"] ] moveout tmpdir home = do fs <- dirContents tmpdir From da1262217972ad852d6bfca6d461ee2c37151c4b Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 01:08:13 -0400 Subject: [PATCH 44/88] propellor spin From d5c3a2f1dfd3cb620e7f2c736965a264964dad7c Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 01:10:02 -0400 Subject: [PATCH 45/88] propellor spin --- privdata/diatom.kitenet.net.gpg | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 privdata/diatom.kitenet.net.gpg diff --git a/privdata/diatom.kitenet.net.gpg b/privdata/diatom.kitenet.net.gpg new file mode 100644 index 0000000..7c36ab2 --- /dev/null +++ b/privdata/diatom.kitenet.net.gpg @@ -0,0 +1,19 @@ +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1 + +hQIMA7ODiaEXBlRZAQ//Qsi46/S4X9qWNSCqFUuUOdoKnuOro0SIKfR19Z0SlseL +AH5cPWUX2eIFA3tzku5Psm8enxGc2jyMhfS5KQkVMLoV/SdgLTEfbsF2TkOGUIFf +AMEt+HOPercftwzU+KnwyNJ6kfCinlgmehLwAHLvD8HfzsL9lD59dJGkYQ61cDZ8 +NQSOJwbLVzlXGoMjUcQ6ihmg7gOEGptO7F+p4oamOYwpzibaFGX2BsczMRDcjlGY +B+ufxINqj2bV17lHchNs/Je8uF5Owe+5zoK2cf6TTCdtlIcWjuw6YIMUPWHhIx3C +DCrEFS/rOJCyY+M8CwIfqS0JTJVNIKJfhP8LbbaoyRyXB2XF2eLM1bQ25p//fpav ++MRQ/0SqnGXYV7ZQE/a+/dESi8/u2yua1m1DBwXzAp468pCTaZCm9gwV+D9Ggsbr +uCU5K/cTa7wPyzfYtki0jkM+R1uk1HqWuHHt0/CD1VnDM3Zrj2JVkoE+pR1LhiSH +qKj8/zF935QmGrCUUjo+1bBn20BDiiFPiiPo4KN3At2uK4qQo1F0c+JUQUHGKV9r +O/c4v0dhPj/Qq5kSp5higO8n2Afv68wAfCWBkBo6SpCS7nuR7xvLWD7pWBTS/0BG +BcL4recUTckQHPo+VUNMYlSNeUhnlv/2TK7/qsfPMYTi0Xu/Fr+bnKn3QOPbgITS +cgHrplzueGhsVhhy+Cpn31FptA7txwcAWuWcZmT7ych0APt/PdkZ1CdeQ3gQop0p +BXaUlY7N4PacFyrC8Jha4p8THbbmfg6zTwaPggH8HonOIL5iA2yZz78uvZwqUd5i +QD0LMQZ3ZgNiqlwLxA8e6heSNA== +=V6He +-----END PGP MESSAGE----- From d1fdace57e9d8c34dba5cc21726e2fa4c6a70492 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 01:46:33 -0400 Subject: [PATCH 46/88] propellor spin --- Propellor/Property/Dns.hs | 63 +++++++++++++++++++++++++++++++++++ Propellor/Property/Service.hs | 12 +++++-- config-joey.hs | 14 ++++++++ propellor.cabal | 1 + 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 Propellor/Property/Dns.hs diff --git a/Propellor/Property/Dns.hs b/Propellor/Property/Dns.hs new file mode 100644 index 0000000..34e790d --- /dev/null +++ b/Propellor/Property/Dns.hs @@ -0,0 +1,63 @@ +module Propellor.Property.Dns where + +import Propellor +import Propellor.Property.File +import qualified Propellor.Property.Apt as Apt +import qualified Propellor.Property.Service as Service + +namedconf :: FilePath +namedconf = "/etc/bind/named.conf.local" + +data Zone = Zone + { zdomain :: Domain + , ztype :: Type + , zfile :: FilePath + , zmasters :: [IPAddr] + , zconfiglines :: [String] + } + +zoneDesc :: Zone -> String +zoneDesc z = zdomain z ++ " (" ++ show (ztype z) ++ ")" + +type IPAddr = String + +type Domain = String + +data Type = Master | Secondary + deriving (Show, Eq) + +secondary :: Domain -> [IPAddr] -> Zone +secondary domain masters = Zone + { zdomain = domain + , ztype = Secondary + , zfile = "db." ++ domain + , zmasters = masters + , zconfiglines = ["allow-transfer { }"] + } + +zoneStanza :: Zone -> [Line] +zoneStanza z = + [ "// automatically generated by propellor" + , "zone \"" ++ zdomain z ++ "\" {" + , cfgline "type" (if ztype z == Master then "master" else "slave") + , cfgline "file" ("\"" ++ zfile z ++ "\"") + ] ++ + (if null (zmasters z) then [] else mastersblock) ++ + (map (\l -> "\t" ++ l ++ ";") (zconfiglines z)) ++ + [ "};" + , "" + ] + where + cfgline f v = "\t" ++ f ++ " " ++ v ++ ";" + mastersblock = + [ "\tmasters {" ] ++ + (map (\ip -> "\t\t" ++ ip ++ ";") (zmasters z)) ++ + [ "\t};" ] + +-- | Rewrites the whole named.conf.local file to serve the specificed +-- zones. +zones :: [Zone] -> Property +zones zs = hasContent namedconf (concatMap zoneStanza zs) + `describe` ("dns server for zones: " ++ unwords (map zoneDesc zs)) + `requires` Apt.serviceInstalledRunning "bind9" + `onChange` Service.reloaded "bind9" diff --git a/Propellor/Property/Service.hs b/Propellor/Property/Service.hs index 2fb3e0c..c6498e5 100644 --- a/Propellor/Property/Service.hs +++ b/Propellor/Property/Service.hs @@ -14,12 +14,18 @@ type ServiceName = String -- this means it's already running. running :: ServiceName -> Property running svc = Property ("running " ++ svc) $ do - void $ ensureProperty $ - scriptProperty ["service " ++ shellEscape svc ++ " start >/dev/null 2>&1 || true"] - return NoChange + void $ ensureProperty $ + scriptProperty ["service " ++ shellEscape svc ++ " start >/dev/null 2>&1 || true"] + return NoChange restarted :: ServiceName -> Property restarted svc = Property ("restarted " ++ svc) $ do void $ ensureProperty $ scriptProperty ["service " ++ shellEscape svc ++ " restart >/dev/null 2>&1 || true"] return NoChange + +reloaded :: ServiceName -> Property +reloaded svc = Property ("reloaded " ++ svc) $ do + void $ ensureProperty $ + scriptProperty ["service " ++ shellEscape svc ++ " reload >/dev/null 2>&1 || true"] + return NoChange diff --git a/config-joey.hs b/config-joey.hs index dfa56e5..9c64acb 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -13,6 +13,7 @@ import qualified Propellor.Property.User as User import qualified Propellor.Property.Hostname as Hostname import qualified Propellor.Property.Reboot as Reboot import qualified Propellor.Property.Tor as Tor +import qualified Propellor.Property.Dns as Dns import qualified Propellor.Property.OpenId as OpenId import qualified Propellor.Property.Docker as Docker import qualified Propellor.Property.SiteSpecific.GitHome as GitHome @@ -61,6 +62,7 @@ host hostname@"diatom.kitenet.net" = Just $ props & Apt.unattendedUpgrades & Apt.serviceInstalledRunning "ntp" & Apt.serviceInstalledRunning "bind9" + & Dns.zones myDnsSecondary & Apt.serviceInstalledRunning "apache2" & Apt.serviceInstalledRunning "git-daemon-sysvinit" & Apt.installed ["git", "git-annex", "rsync"] @@ -180,3 +182,15 @@ cleanCloudAtCost hostname = propertyList "cloudatcost cleanup" , User.nuked "user" User.YesReallyDeleteHome ] ] + +myDnsSecondary :: [Dns.Zone] +myDnsSecondary = + [ Dns.secondary "kitenet.net" master + , Dns.secondary "joeyh.name" master + , Dns.secondary "ikiwiki.info" master + , Dns.secondary "olduse.net" master + , Dns.secondary "branchable.com" branchablemaster + ] + where + master = ["80.68.85.49", "2001:41c8:125:49::10"] -- wren + branchablemaster = ["66.228.46.55", "2600:3c03::f03c:91ff:fedf:c0e5"] diff --git a/propellor.cabal b/propellor.cabal index a6510df..8f53d59 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -69,6 +69,7 @@ Library Propellor.Property.Cmd Propellor.Property.Hostname Propellor.Property.Cron + Propellor.Property.Dns Propellor.Property.Docker Propellor.Property.File Propellor.Property.Network From eb71c76711a3952a1851fea4f80bbf17a0fc61b4 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 02:51:25 -0400 Subject: [PATCH 47/88] propellor spin --- Propellor/Property/Git.hs | 47 +++++++++++++++++++++++++++++++++++++++ config-joey.hs | 9 ++++++-- propellor.cabal | 1 + 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 Propellor/Property/Git.hs diff --git a/Propellor/Property/Git.hs b/Propellor/Property/Git.hs new file mode 100644 index 0000000..356ff87 --- /dev/null +++ b/Propellor/Property/Git.hs @@ -0,0 +1,47 @@ +module Propellor.Property.Git where + +import Propellor +import Propellor.Property.File +import qualified Propellor.Property.Apt as Apt +import qualified Propellor.Property.Service as Service + +import Data.List + +-- | Exports all git repos in a directory (that user nobody can read) +-- using git-daemon, run from inetd. +-- +-- Note that reverting this property does not remove or stop inetd. +daemonRunning :: FilePath -> RevertableProperty +daemonRunning exportdir = RevertableProperty setup unsetup + where + setup = containsLine conf (mkl "tcp4") + `requires` + containsLine conf (mkl "tcp6") + `requires` + dirExists exportdir + `requires` + Apt.serviceInstalledRunning "openbsd-inetd" + `onChange` + Service.reloaded "openbsd-inetd" + unsetup = lacksLine conf (mkl "tcp4") + `requires` + lacksLine conf (mkl "tcp6") + `onChange` + Service.reloaded "openbsd-inetd" + + conf = "/etc/inetd.conf" + + mkl tcpv = intercalate "\t" + [ "git" + , "stream" + , tcpv + , "nowait" + , "nobody" + , "/usr/bin/git" + , "git" + , "daemon" + , "--inetd" + , "--export-all" + , "--base-path=" ++ exportdir + , exportdir + ] diff --git a/config-joey.hs b/config-joey.hs index 9c64acb..b4aeebe 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -16,6 +16,7 @@ import qualified Propellor.Property.Tor as Tor import qualified Propellor.Property.Dns as Dns import qualified Propellor.Property.OpenId as OpenId import qualified Propellor.Property.Docker as Docker +import qualified Propellor.Property.Git as Git import qualified Propellor.Property.SiteSpecific.GitHome as GitHome import qualified Propellor.Property.SiteSpecific.GitAnnexBuilder as GitAnnexBuilder import qualified Propellor.Property.SiteSpecific.JoeySites as JoeySites @@ -61,12 +62,16 @@ host hostname@"diatom.kitenet.net" = Just $ props & Hostname.set hostname & Apt.unattendedUpgrades & Apt.serviceInstalledRunning "ntp" - & Apt.serviceInstalledRunning "bind9" & Dns.zones myDnsSecondary & Apt.serviceInstalledRunning "apache2" - & Apt.serviceInstalledRunning "git-daemon-sysvinit" & Apt.installed ["git", "git-annex", "rsync"] & Apt.buildDep ["git-annex"] `period` Daily + & Git.daemonRunning "/srv/git" + -- git repos restore (how?) + -- kgb installation and setup + -- ssh keys for branchable and github repo hooks + -- gitweb + -- downloads.kitenet.net setup (including ssh key to turtle) -- My laptop host _hostname@"darkstar.kitenet.net" = Just $ props & Docker.configured diff --git a/propellor.cabal b/propellor.cabal index 8f53d59..03d1474 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -72,6 +72,7 @@ Library Propellor.Property.Dns Propellor.Property.Docker Propellor.Property.File + Propellor.Property.Git Propellor.Property.Network Propellor.Property.OpenId Propellor.Property.Reboot From 7a0074454bbae2506c102a57add9af17a32907cc Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 03:06:35 -0400 Subject: [PATCH 48/88] propellor spin --- Propellor/Property/File.hs | 10 ++++++++++ Propellor/Property/Git.hs | 3 ++- Propellor/Types.hs | 1 + config-joey.hs | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Propellor/Property/File.hs b/Propellor/Property/File.hs index 0c1155f..64dce66 100644 --- a/Propellor/Property/File.hs +++ b/Propellor/Property/File.hs @@ -58,3 +58,13 @@ fileProperty desc a f = Property desc $ go =<< doesFileExist f dirExists :: FilePath -> Property dirExists d = check (not <$> doesDirectoryExist d) $ Property (d ++ " exists") $ makeChange $ createDirectoryIfMissing True d + +-- | Ensures that a file/dir has the specified owner and group. +ownerGroup :: FilePath -> UserName -> GroupName -> Property +ownerGroup f owner group = Property (f ++ " owner " ++ og) $ do + r <- ensureProperty $ cmdProperty "chown" [og, f] + if r == FailedChange + then return r + else noChange + where + og = owner ++ ":" ++ group diff --git a/Propellor/Property/Git.hs b/Propellor/Property/Git.hs index 356ff87..c049416 100644 --- a/Propellor/Property/Git.hs +++ b/Propellor/Property/Git.hs @@ -22,7 +22,8 @@ daemonRunning exportdir = RevertableProperty setup unsetup `requires` Apt.serviceInstalledRunning "openbsd-inetd" `onChange` - Service.reloaded "openbsd-inetd" + Service.running "openbsd-inetd" + `describe` ("git-daemon exporting " ++ exportdir) unsetup = lacksLine conf (mkl "tcp4") `requires` lacksLine conf (mkl "tcp6") diff --git a/Propellor/Types.hs b/Propellor/Types.hs index c6be30c..3be10d3 100644 --- a/Propellor/Types.hs +++ b/Propellor/Types.hs @@ -4,6 +4,7 @@ import Data.Monoid import System.Console.ANSI type HostName = String +type GroupName = String type UserName = String data Property = Property diff --git a/config-joey.hs b/config-joey.hs index b4aeebe..d07e12b 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -67,6 +67,7 @@ host hostname@"diatom.kitenet.net" = Just $ props & Apt.installed ["git", "git-annex", "rsync"] & Apt.buildDep ["git-annex"] `period` Daily & Git.daemonRunning "/srv/git" + & File.ownerGroup "/srv/git" "joey" "joey" -- git repos restore (how?) -- kgb installation and setup -- ssh keys for branchable and github repo hooks From 222e2fe8a4cc555840ce7e2f5f9015a21f325d37 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 11:02:29 -0400 Subject: [PATCH 49/88] propellor spin --- Propellor/Property.hs | 4 +++- Propellor/Property/Scheduled.hs | 1 - config-joey.hs | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Propellor/Property.hs b/Propellor/Property.hs index c2a8972..ca492e3 100644 --- a/Propellor/Property.hs +++ b/Propellor/Property.hs @@ -3,6 +3,7 @@ module Propellor.Property where import System.Directory import Control.Monad import Data.Monoid +import Control.Monad.IfElse import Propellor.Types import Propellor.Engine @@ -54,7 +55,8 @@ flagFile property flagfile = Property (propertyDesc property) $ go False = do r <- ensureProperty property when (r == MadeChange) $ - writeFile flagfile "" + unlessM (doesFileExist flagfile) $ + writeFile flagfile "" return r --- | Whenever a change has to be made for a Property, causes a hook diff --git a/Propellor/Property/Scheduled.hs b/Propellor/Property/Scheduled.hs index bef2a29..827c648 100644 --- a/Propellor/Property/Scheduled.hs +++ b/Propellor/Property/Scheduled.hs @@ -21,7 +21,6 @@ import qualified Data.Map as M period :: Property -> Recurrance -> Property period prop recurrance = Property desc $ do lasttime <- getLastChecked (propertyDesc prop) - print lasttime nexttime <- fmap startTime <$> nextTime schedule lasttime t <- localNow if Just t >= nexttime diff --git a/config-joey.hs b/config-joey.hs index d07e12b..f8ac5e5 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -42,6 +42,7 @@ host hostname@"clam.kitenet.net" = Just $ withSystemd $ props & JoeySites.oldUseNetshellBox & Docker.docked container hostname "openid-provider" `requires` Apt.installed ["ntp"] + & Docker.docked container hostname "ancient.kitenet.net" & Docker.configured & Docker.garbageCollected `period` Daily -- Orca is the main git-annex build box. @@ -69,6 +70,9 @@ host hostname@"diatom.kitenet.net" = Just $ props & Git.daemonRunning "/srv/git" & File.ownerGroup "/srv/git" "joey" "joey" -- git repos restore (how?) + -- family annex needs family members to have accounts, + -- ssh host key etc.. finesse? + -- (also should upgrade git-annex-shell for it..) -- kgb installation and setup -- ssh keys for branchable and github repo hooks -- gitweb @@ -103,6 +107,19 @@ container _parenthost name "openid.kitenet.net:8081" ] + | name == "ancient.kitenet.net" = Just $ standardContainer Stable "amd64" + [ Docker.publish "1994:80" + , Docker.inside $ props + & Apt.serviceInstalledRunning "apache2" + & Apt.installed ["git"] + & scriptProperty + [ "cd /var/" + , "rm -rf www" + , "git clone git://git.kitenet.net/git/kitewiki www" + , "git checkout remotes/origin/old-kitenet.net" + ] `flagFile` "/var/www/blastfromthepast.html" + ] + -- armel builder has a companion container that run amd64 and -- runs the build first to get TH splices. They share a home -- directory, and need to have the same versions of all haskell From 6b0e6abef2541eafaf7f669ff86ba86fa0f546b2 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 11:03:47 -0400 Subject: [PATCH 50/88] propellor spin --- config-joey.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index f8ac5e5..9d9ad64 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -42,7 +42,7 @@ host hostname@"clam.kitenet.net" = Just $ withSystemd $ props & JoeySites.oldUseNetshellBox & Docker.docked container hostname "openid-provider" `requires` Apt.installed ["ntp"] - & Docker.docked container hostname "ancient.kitenet.net" + & Docker.docked container hostname "ancient-kitenet" & Docker.configured & Docker.garbageCollected `period` Daily -- Orca is the main git-annex build box. @@ -107,7 +107,7 @@ container _parenthost name "openid.kitenet.net:8081" ] - | name == "ancient.kitenet.net" = Just $ standardContainer Stable "amd64" + | name == "ancient-kitenet" = Just $ standardContainer Stable "amd64" [ Docker.publish "1994:80" , Docker.inside $ props & Apt.serviceInstalledRunning "apache2" From 01075e3632a0aff3a13cc0cabfe81f4e14195da1 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 11:04:43 -0400 Subject: [PATCH 51/88] propellor spin From 02b9934ee847e2918caf0832886c0bce4ae09006 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 11:09:16 -0400 Subject: [PATCH 52/88] propellor spin --- config-joey.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-joey.hs b/config-joey.hs index 9d9ad64..a890670 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -115,7 +115,7 @@ container _parenthost name & scriptProperty [ "cd /var/" , "rm -rf www" - , "git clone git://git.kitenet.net/git/kitewiki www" + , "git clone git://git.kitenet.net/kitewiki www" , "git checkout remotes/origin/old-kitenet.net" ] `flagFile` "/var/www/blastfromthepast.html" ] From 589e72eceb6b926a56d664072ae2b7a9b7987bbf Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 11:10:17 -0400 Subject: [PATCH 53/88] propellor spin --- config-joey.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/config-joey.hs b/config-joey.hs index a890670..2c6374c 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -116,6 +116,7 @@ container _parenthost name [ "cd /var/" , "rm -rf www" , "git clone git://git.kitenet.net/kitewiki www" + , "cd www" , "git checkout remotes/origin/old-kitenet.net" ] `flagFile` "/var/www/blastfromthepast.html" ] From 13a4d4889c48fc3ee44956440a87f4656da7fcc9 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:03:49 -0400 Subject: [PATCH 54/88] Use ssh connection caching, especially when bootstrapping. --- Propellor/CmdLine.hs | 27 ++++++++++++++++++++------- TODO | 3 --- debian/changelog | 1 + 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index e4cab86..e32ccdb 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -16,6 +16,7 @@ import qualified Propellor.Property.Docker as Docker import qualified Propellor.Property.Docker.Shim as DockerShim import Utility.FileMode import Utility.SafeCommand +import Utility.UserInfo usage :: IO a usage = do @@ -167,9 +168,10 @@ spin host = do url <- getUrl void $ gitCommit [Param "--allow-empty", Param "-a", Param "-m", Param "propellor spin"] void $ boolSystem "git" [Param "push"] - go url =<< gpgDecrypt (privDataFile host) + cacheparams <- toCommand <$> sshCachingParams host + go cacheparams url =<< gpgDecrypt (privDataFile host) where - go url privdata = withBothHandles createProcessSuccess (proc "ssh" [user, bootstrapcmd]) $ \(toh, fromh) -> do + go cacheparams url privdata = withBothHandles createProcessSuccess (proc "ssh" $ cacheparams ++ [user, bootstrapcmd]) $ \(toh, fromh) -> do let finish = do senddata toh (privDataFile host) privDataMarker privdata hClose toh @@ -185,7 +187,7 @@ spin host = do hClose toh hClose fromh sendGitClone host url - go url privdata + go cacheparams url privdata user = "root@"++host @@ -221,12 +223,11 @@ spin host = do sendGitClone :: HostName -> String -> IO () sendGitClone host url = void $ actionMessage ("Pushing git repository to " ++ host) $ do branch <- getCurrentBranch + cacheparams <- sshCachingParams host withTmpFile "propellor.git" $ \tmp _ -> allM id - -- TODO: ssh connection caching, or better push method - -- with less connections. [ boolSystem "git" [Param "bundle", Param "create", File tmp, Param "HEAD"] - , boolSystem "scp" [File tmp, Param ("root@"++host++":"++remotebundle)] - , boolSystem "ssh" [Param ("root@"++host), Param $ unpackcmd branch] + , boolSystem "scp" $ cacheparams ++ [File tmp, Param ("root@"++host++":"++remotebundle)] + , boolSystem "ssh" $ cacheparams ++ [Param ("root@"++host), Param $ unpackcmd branch] ] where remotebundle = "/usr/local/propellor.git" @@ -341,3 +342,15 @@ checkDebugMode = go =<< getEnv "PROPELLOR_DEBUG" updateGlobalLogger rootLoggerName $ setLevel DEBUG . setHandlers [f] go _ = noop + +-- Parameters can be passed to both ssh and scp. +sshCachingParams :: HostName -> IO [CommandParam] +sshCachingParams hostname = do + home <- myHomeDir + let cachedir = home ".ssh" "propellor" + createDirectoryIfMissing False cachedir + let socketfile = cachedir hostname ++ ".sock" + return + [ Param "-o", Param ("ControlPath=" ++ socketfile) + , Params "-o ControlMaster=auto -o ControlPersist=yes" + ] diff --git a/TODO b/TODO index 6f0de94..0cc8db1 100644 --- a/TODO +++ b/TODO @@ -2,9 +2,6 @@ run it once for the whole. For example, may want to restart apache, but only once despite many config changes being made to satisfy properties. onChange is a poor substitute. -* --spin needs 4 ssh connections when bootstrapping a new host - that does not have the git repo yet. Should be possible to get that - down to 1. * Currently only Debian and derivatives are supported by most Properties. One way to improve that would be to parameterize Properties with a Distribution witness. diff --git a/debian/changelog b/debian/changelog index ed882cf..55043d5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -7,6 +7,7 @@ propellor (0.2.4) UNRELEASED; urgency=medium * Fix bootstrapping of dependencies. * Fix compilation on Debian stable. * Include security updates in sources.list for stable and testing. + * Use ssh connection caching, especially when bootstrapping. -- Joey Hess Tue, 08 Apr 2014 18:07:12 -0400 From 67976820dadf6dd7f871c5f5da8d04d6ed068d7a Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:03:59 -0400 Subject: [PATCH 55/88] propellor spin From 670c102d80603a121599a10944549f34aa5f6b19 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:04:34 -0400 Subject: [PATCH 56/88] propellor spin From 1ce626e90eb09b6457ba8e6ad698b8787635ab15 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:05:45 -0400 Subject: [PATCH 57/88] propellor spin From bf50e7aa5dbb1556e214da082c31f045b0c489ce Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:06:36 -0400 Subject: [PATCH 58/88] propellor spin --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 580e9a0..16d97e8 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,8 @@ build: dist/setup-config ln -sf dist/build/config/config propellor deps: - @if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev; fi || true - @if [ $$(whoami) = root ]; then apt-get -u install libghc-async-dev || cabal update; cabal install async; fi || true + @if [ $$(whoami) = root ]; then apt-get --no-install-recommends -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev; fi || true + @if [ $$(whoami) = root ]; then apt-get --no-install-recommends -y install libghc-async-dev || cabal update; cabal install async; fi || true dist/setup-config: propellor.cabal if [ "$(CABAL)" = ./Setup ]; then ghc --make Setup; fi From fa56de42d2b4e2bc0dd029b7193bd47fae42bf36 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:07:13 -0400 Subject: [PATCH 59/88] propellor spin --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 16d97e8..f31e5fa 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,8 @@ build: dist/setup-config ln -sf dist/build/config/config propellor deps: - @if [ $$(whoami) = root ]; then apt-get --no-install-recommends -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev; fi || true - @if [ $$(whoami) = root ]; then apt-get --no-install-recommends -y install libghc-async-dev || cabal update; cabal install async; fi || true + @if [ $$(whoami) = root ]; then apt-get --no-upgrade --no-install-recommends -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev; fi || true + @if [ $$(whoami) = root ]; then apt-get --no-upgrade --no-install-recommends -y install libghc-async-dev || cabal update; cabal install async; fi || true dist/setup-config: propellor.cabal if [ "$(CABAL)" = ./Setup ]; then ghc --make Setup; fi From 02050fef42384d94414c053c240115be8f699fcd Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:07:29 -0400 Subject: [PATCH 60/88] propellor spin From e44e4217fdb327c2d941ac5359f30bbadf5c1adf Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:08:16 -0400 Subject: [PATCH 61/88] propellor spin --- Propellor/CmdLine.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index e32ccdb..1dbc40f 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -194,7 +194,7 @@ spin host = do bootstrapcmd = shellWrap $ intercalate " ; " [ "if [ ! -d " ++ localdir ++ " ]" , "then " ++ intercalate " && " - [ "apt-get -y install git make" + [ "apt-get --no-recommends --no-upgrade -y install git make" , "echo " ++ toMarked statusMarker (show NeedGitClone) ] , "else " ++ intercalate " && " From ff3f25fb4ea1e9aec49d821b9110b3147addbf1b Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:08:38 -0400 Subject: [PATCH 62/88] propellor spin --- Propellor/CmdLine.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index 1dbc40f..6ddf890 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -194,7 +194,7 @@ spin host = do bootstrapcmd = shellWrap $ intercalate " ; " [ "if [ ! -d " ++ localdir ++ " ]" , "then " ++ intercalate " && " - [ "apt-get --no-recommends --no-upgrade -y install git make" + [ "apt-get --no-install-recommends --no-upgrade -y install git make" , "echo " ++ toMarked statusMarker (show NeedGitClone) ] , "else " ++ intercalate " && " From f62f48de13d82adb749dae2f90bdf8b66b022606 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:11:21 -0400 Subject: [PATCH 63/88] propellor spin --- Propellor/CmdLine.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index 6ddf890..832b280 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -126,7 +126,7 @@ updateFirst cmdline next = do void $ actionMessage "Git fetch" $ boolSystem "git" [Param "fetch"] - whenM (doesFileExist keyring) $ do + whenM (doesFileExist keyring <&&> pure False) $ do {- To verify origin branch commit's signature, have to - convince gpg to use our keyring. While running git log. - Which has no way to pass options to gpg. From 5acaf8758f752574140dd79de7996d91a81d1cd4 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 16:12:06 -0400 Subject: [PATCH 64/88] revert --- Propellor/CmdLine.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index 832b280..6ddf890 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -126,7 +126,7 @@ updateFirst cmdline next = do void $ actionMessage "Git fetch" $ boolSystem "git" [Param "fetch"] - whenM (doesFileExist keyring <&&> pure False) $ do + whenM (doesFileExist keyring) $ do {- To verify origin branch commit's signature, have to - convince gpg to use our keyring. While running git log. - Which has no way to pass options to gpg. From 25942fb0cca0ca90933026bf959506e099ff95a4 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 17:22:32 -0400 Subject: [PATCH 65/88] Propellor monad is a Reader for HostAttr So far, the hostname is only used to improve a message in withPrivData, but I anticipate using HostAttr for a lot more. --- Propellor.hs | 5 +++ Propellor/CmdLine.hs | 18 +++++---- Propellor/Engine.hs | 23 ++++++++---- Propellor/Exception.hs | 16 ++++++++ Propellor/Message.hs | 25 ++++++++----- Propellor/PrivData.hs | 15 +++++--- Propellor/Property.hs | 19 ++++++---- Propellor/Property/Cmd.hs | 5 ++- Propellor/Property/Docker.hs | 37 ++++++++++--------- Propellor/Property/File.hs | 4 +- Propellor/Property/Scheduled.hs | 10 ++--- .../Property/SiteSpecific/GitAnnexBuilder.hs | 5 ++- Propellor/Property/SiteSpecific/GitHome.hs | 2 +- Propellor/Property/Ssh.hs | 2 +- Propellor/Property/Sudo.hs | 2 +- Propellor/Types.hs | 35 +++++++++++++++++- debian/changelog | 4 +- propellor.cabal | 12 ++++-- 18 files changed, 163 insertions(+), 76 deletions(-) create mode 100644 Propellor/Exception.hs diff --git a/Propellor.hs b/Propellor.hs index e39fc97..1f1d7ec 100644 --- a/Propellor.hs +++ b/Propellor.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE PackageImports #-} + -- | Pulls in lots of useful modules for building and using Properties. -- -- Propellor enures that the system it's run in satisfies a list of @@ -31,6 +33,7 @@ module Propellor ( , module Propellor.Property.Cmd , module Propellor.PrivData , module Propellor.Engine + , module Propellor.Exception , module Propellor.Message , localdir @@ -43,6 +46,7 @@ import Propellor.Engine import Propellor.Property.Cmd import Propellor.PrivData import Propellor.Message +import Propellor.Exception import Utility.PartialPrelude as X import Utility.Process as X @@ -62,6 +66,7 @@ import Control.Applicative as X import Control.Monad as X import Data.Monoid as X import Control.Monad.IfElse as X +import "mtl" Control.Monad.Reader as X -- | This is where propellor installs itself when deploying a host. localdir :: FilePath diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index 6ddf890..2026c47 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -66,21 +66,23 @@ defaultMain getprops = do go _ (Continue cmdline) = go False cmdline go _ (Set host field) = setPrivData host field go _ (AddKey keyid) = addKey keyid - go _ (Chain host) = withprops host $ \ps -> do - r <- ensureProperties' ps + go _ (Chain host) = withprops host $ \hostattr ps -> do + r <- runPropellor hostattr $ ensureProperties ps putStrLn $ "\n" ++ show r go _ (Docker host) = Docker.chain host go True cmdline@(Spin _) = buildFirst cmdline $ go False cmdline go True cmdline = updateFirst cmdline $ go False cmdline - go False (Spin host) = withprops host $ const $ spin host + go False (Spin host) = withprops host $ const . const $ spin host go False (Run host) = ifM ((==) 0 <$> getRealUserID) - ( onlyProcess $ withprops host ensureProperties + ( onlyProcess $ withprops host mainProperties , go True (Spin host) ) go False (Boot host) = onlyProcess $ withprops host $ boot - withprops host a = maybe (unknownhost host) a $ + withprops host a = maybe (unknownhost host) (a hostattr) $ headMaybe $ catMaybes $ map (\get -> get host) getprops + where + hostattr = mkHostAttr host onlyProcess :: IO a -> IO a onlyProcess a = bracket lock unlock (const a) @@ -275,15 +277,15 @@ fromMarked marker s len = length marker matches = filter (marker `isPrefixOf`) $ lines s -boot :: [Property] -> IO () -boot ps = do +boot :: HostAttr -> [Property] -> IO () +boot hostattr ps = do sendMarked stdout statusMarker $ show Ready reply <- hGetContentsStrict stdin makePrivDataDir maybe noop (writeFileProtected privDataLocal) $ fromMarked privDataMarker reply - ensureProperties ps + mainProperties hostattr ps addKey :: String -> IO () addKey keyid = exitBool =<< allM id [ gpg, gitadd, gitcommit ] diff --git a/Propellor/Engine.hs b/Propellor/Engine.hs index 1ae224c..c527dc3 100644 --- a/Propellor/Engine.hs +++ b/Propellor/Engine.hs @@ -1,30 +1,37 @@ +{-# LANGUAGE PackageImports #-} + module Propellor.Engine where import System.Exit import System.IO import Data.Monoid import System.Console.ANSI +import "mtl" Control.Monad.Reader import Propellor.Types import Propellor.Message -import Utility.Exception +import Propellor.Exception -ensureProperty :: Property -> IO Result -ensureProperty = catchDefaultIO FailedChange . propertySatisfy +runPropellor :: HostAttr -> Propellor a -> IO a +runPropellor hostattr a = runReaderT (runWithHostAttr a) hostattr -ensureProperties :: [Property] -> IO () -ensureProperties ps = do - r <- ensureProperties' [Property "overall" $ ensureProperties' ps] +mainProperties :: HostAttr -> [Property] -> IO () +mainProperties hostattr ps = do + r <- runPropellor hostattr $ + ensureProperties [Property "overall" $ ensureProperties ps] setTitle "propellor: done" hFlush stdout case r of FailedChange -> exitWith (ExitFailure 1) _ -> exitWith ExitSuccess -ensureProperties' :: [Property] -> IO Result -ensureProperties' ps = ensure ps NoChange +ensureProperties :: [Property] -> Propellor Result +ensureProperties ps = ensure ps NoChange where ensure [] rs = return rs ensure (l:ls) rs = do r <- actionMessage (propertyDesc l) (ensureProperty l) ensure ls (r <> rs) + +ensureProperty :: Property -> Propellor Result +ensureProperty = catchPropellor . propertySatisfy diff --git a/Propellor/Exception.hs b/Propellor/Exception.hs new file mode 100644 index 0000000..bd9212a --- /dev/null +++ b/Propellor/Exception.hs @@ -0,0 +1,16 @@ +{-# LANGUAGE PackageImports #-} + +module Propellor.Exception where + +import qualified "MonadCatchIO-transformers" Control.Monad.CatchIO as M +import Control.Exception +import Control.Applicative + +import Propellor.Types + +-- | Catches IO exceptions and returns FailedChange. +catchPropellor :: Propellor Result -> Propellor Result +catchPropellor a = either (\_ -> FailedChange) id <$> tryPropellor a + +tryPropellor :: Propellor a -> Propellor (Either IOException a) +tryPropellor = M.try diff --git a/Propellor/Message.hs b/Propellor/Message.hs index 5a7d8c4..2e63061 100644 --- a/Propellor/Message.hs +++ b/Propellor/Message.hs @@ -1,30 +1,35 @@ +{-# LANGUAGE PackageImports #-} + module Propellor.Message where import System.Console.ANSI import System.IO import System.Log.Logger +import "mtl" Control.Monad.Reader import Propellor.Types -- | Shows a message while performing an action, with a colored status -- display. -actionMessage :: ActionResult r => Desc -> IO r -> IO r +actionMessage :: (MonadIO m, ActionResult r) => Desc -> m r -> m r actionMessage desc a = do - setTitle $ "propellor: " ++ desc - hFlush stdout + liftIO $ do + setTitle $ "propellor: " ++ desc + hFlush stdout r <- a - setTitle "propellor: running" - let (msg, intensity, color) = getActionResult r - putStr $ desc ++ " ... " - colorLine intensity color msg - hFlush stdout + liftIO $ do + setTitle "propellor: running" + let (msg, intensity, color) = getActionResult r + putStr $ desc ++ " ... " + colorLine intensity color msg + hFlush stdout return r -warningMessage :: String -> IO () -warningMessage s = colorLine Vivid Red $ "** warning: " ++ s +warningMessage :: MonadIO m => String -> m () +warningMessage s = liftIO $ colorLine Vivid Red $ "** warning: " ++ s colorLine :: ColorIntensity -> Color -> String -> IO () colorLine intensity color msg = do diff --git a/Propellor/PrivData.hs b/Propellor/PrivData.hs index 2897d42..7f5a23d 100644 --- a/Propellor/PrivData.hs +++ b/Propellor/PrivData.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE PackageImports #-} + module Propellor.PrivData where import qualified Data.Map as M @@ -7,6 +9,7 @@ import System.IO import System.Directory import Data.Maybe import Control.Monad +import "mtl" Control.Monad.Reader import Propellor.Types import Propellor.Message @@ -18,13 +21,15 @@ import Utility.Tmp import Utility.SafeCommand import Utility.Misc -withPrivData :: PrivDataField -> (String -> IO Result) -> IO Result -withPrivData field a = maybe missing a =<< getPrivData field +withPrivData :: PrivDataField -> (String -> Propellor Result) -> Propellor Result +withPrivData field a = maybe missing a =<< liftIO (getPrivData field) where missing = do - warningMessage $ "Missing privdata " ++ show field - putStrLn $ "Fix this by running: propellor --set $hostname '" ++ show field ++ "'" - return FailedChange + host <- getHostName + liftIO $ do + warningMessage $ "Missing privdata " ++ show field + putStrLn $ "Fix this by running: propellor --set "++host++" '" ++ show field ++ "'" + return FailedChange getPrivData :: PrivDataField -> IO (Maybe String) getPrivData field = do diff --git a/Propellor/Property.hs b/Propellor/Property.hs index ca492e3..7af69ea 100644 --- a/Propellor/Property.hs +++ b/Propellor/Property.hs @@ -1,18 +1,21 @@ +{-# LANGUAGE PackageImports #-} + module Propellor.Property where import System.Directory import Control.Monad import Data.Monoid import Control.Monad.IfElse +import "mtl" Control.Monad.Reader import Propellor.Types import Propellor.Engine import Utility.Monad -makeChange :: IO () -> IO Result -makeChange a = a >> return MadeChange +makeChange :: IO () -> Propellor Result +makeChange a = liftIO a >> return MadeChange -noChange :: IO Result +noChange :: Propellor Result noChange = return NoChange -- | Combines a list of properties, resulting in a single property @@ -20,7 +23,7 @@ noChange = return NoChange -- and print out the description of each as it's run. Does not stop -- on failure; does propigate overall success/failure. propertyList :: Desc -> [Property] -> Property -propertyList desc ps = Property desc $ ensureProperties' ps +propertyList desc ps = Property desc $ ensureProperties ps -- | Combines a list of properties, resulting in one property that -- ensures each in turn, stopping on failure. @@ -49,12 +52,12 @@ p1 `before` p2 = Property (propertyDesc p1) $ do -- Use with caution. flagFile :: Property -> FilePath -> Property flagFile property flagfile = Property (propertyDesc property) $ - go =<< doesFileExist flagfile + go =<< liftIO (doesFileExist flagfile) where go True = return NoChange go False = do r <- ensureProperty property - when (r == MadeChange) $ + when (r == MadeChange) $ liftIO $ unlessM (doesFileExist flagfile) $ writeFile flagfile "" return r @@ -76,13 +79,13 @@ infixl 1 ==> -- | Makes a Property only be performed when a test succeeds. check :: IO Bool -> Property -> Property -check c property = Property (propertyDesc property) $ ifM c +check c property = Property (propertyDesc property) $ ifM (liftIO c) ( ensureProperty property , return NoChange ) boolProperty :: Desc -> IO Bool -> Property -boolProperty desc a = Property desc $ ifM a +boolProperty desc a = Property desc $ ifM (liftIO a) ( return MadeChange , return FailedChange ) diff --git a/Propellor/Property/Cmd.hs b/Propellor/Property/Cmd.hs index c715fd2..875c1f9 100644 --- a/Propellor/Property/Cmd.hs +++ b/Propellor/Property/Cmd.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE PackageImports #-} + module Propellor.Property.Cmd ( cmdProperty, cmdProperty', @@ -7,6 +9,7 @@ module Propellor.Property.Cmd ( import Control.Applicative import Data.List +import "mtl" Control.Monad.Reader import Propellor.Types import Utility.Monad @@ -22,7 +25,7 @@ cmdProperty cmd params = cmdProperty' cmd params [] -- | A property that can be satisfied by running a command, -- with added environment. cmdProperty' :: String -> [String] -> [(String, String)] -> Property -cmdProperty' cmd params env = Property desc $ do +cmdProperty' cmd params env = Property desc $ liftIO $ do env' <- addEntries env <$> getEnvironment ifM (boolSystemEnv cmd (map Param params) (Just env')) ( return MadeChange diff --git a/Propellor/Property/Docker.hs b/Propellor/Property/Docker.hs index b573e64..1df3425 100644 --- a/Propellor/Property/Docker.hs +++ b/Propellor/Property/Docker.hs @@ -53,7 +53,7 @@ docked findc hn cn = findContainer findc hn cn $ teardown = combineProperties ("undocked " ++ fromContainerId cid) [ stoppedContainer cid , Property ("cleaned up " ++ fromContainerId cid) $ - report <$> mapM id + liftIO $ report <$> mapM id [ removeContainer cid , removeImage image ] @@ -74,7 +74,7 @@ findContainer findc hn cn mk = case findc hn cn of where cid = ContainerId hn cn cantfind = containerDesc (ContainerId hn cn) $ Property "" $ do - warningMessage $ "missing definition for docker container \"" ++ fromContainerId cid + liftIO $ warningMessage $ "missing definition for docker container \"" ++ fromContainerId cid return FailedChange -- | Causes *any* docker images that are not in use by running containers to @@ -90,9 +90,9 @@ garbageCollected = propertyList "docker garbage collected" ] where gccontainers = Property "docker containers garbage collected" $ - report <$> (mapM removeContainer =<< listContainers AllContainers) + liftIO $ report <$> (mapM removeContainer =<< listContainers AllContainers) gcimages = Property "docker images garbage collected" $ do - report <$> (mapM removeImage =<< listImages) + liftIO $ report <$> (mapM removeImage =<< listImages) -- | Pass to defaultMain to add docker containers. -- You need to provide the function mapping from @@ -239,19 +239,19 @@ containerDesc cid p = p `describe` desc runningContainer :: ContainerId -> Image -> [Containerized Property] -> Property runningContainer cid@(ContainerId hn cn) image containerprops = containerDesc cid $ Property "running" $ do - l <- listContainers RunningContainers + l <- liftIO $ listContainers RunningContainers if cid `elem` l then do -- Check if the ident has changed; if so the -- parameters of the container differ and it must -- be restarted. - runningident <- getrunningident + runningident <- liftIO $ getrunningident if runningident == Just ident - then return NoChange + then noChange else do - void $ stopContainer cid + void $ liftIO $ stopContainer cid restartcontainer - else ifM (elem cid <$> listContainers AllContainers) + else ifM (liftIO $ elem cid <$> listContainers AllContainers) ( restartcontainer , go image ) @@ -259,8 +259,8 @@ runningContainer cid@(ContainerId hn cn) image containerprops = containerDesc ci ident = ContainerIdent image hn cn runps restartcontainer = do - oldimage <- fromMaybe image <$> commitContainer cid - void $ removeContainer cid + oldimage <- liftIO $ fromMaybe image <$> commitContainer cid + void $ liftIO $ removeContainer cid go oldimage getrunningident :: IO (Maybe ContainerIdent) @@ -280,10 +280,11 @@ runningContainer cid@(ContainerId hn cn) image containerprops = containerDesc ci ] go img = do - clearProvisionedFlag cid - createDirectoryIfMissing True (takeDirectory $ identFile cid) - shim <- Shim.setup (localdir "propellor") (localdir shimdir cid) - writeFile (identFile cid) (show ident) + liftIO $ do + clearProvisionedFlag cid + createDirectoryIfMissing True (takeDirectory $ identFile cid) + shim <- liftIO $ Shim.setup (localdir "propellor") (localdir shimdir cid) + liftIO $ writeFile (identFile cid) (show ident) ensureProperty $ boolProperty "run" $ runContainer img (runps ++ ["-i", "-d", "-t"]) [shim, "--docker", fromContainerId cid] @@ -339,7 +340,7 @@ chain s = case toContainerId s of -- being run. So, retry connections to the client for up to -- 1 minute. provisionContainer :: ContainerId -> Property -provisionContainer cid = containerDesc cid $ Property "provision" $ do +provisionContainer cid = containerDesc cid $ Property "provision" $ liftIO $ do let shim = Shim.file (localdir "propellor") (localdir shimdir cid) r <- simpleShClientRetry 60 (namedPipe cid) shim params (go Nothing) when (r /= FailedChange) $ @@ -372,8 +373,8 @@ stopContainer cid = boolSystem dockercmd [Param "stop", Param $ fromContainerId stoppedContainer :: ContainerId -> Property stoppedContainer cid = containerDesc cid $ Property desc $ - ifM (elem cid <$> listContainers RunningContainers) - ( cleanup `after` ensureProperty + ifM (liftIO $ elem cid <$> listContainers RunningContainers) + ( liftIO cleanup `after` ensureProperty (boolProperty desc $ stopContainer cid) , return NoChange ) diff --git a/Propellor/Property/File.hs b/Propellor/Property/File.hs index 64dce66..10dee75 100644 --- a/Propellor/Property/File.hs +++ b/Propellor/Property/File.hs @@ -38,10 +38,10 @@ notPresent f = check (doesFileExist f) $ Property (f ++ " not present") $ makeChange $ nukeFile f fileProperty :: Desc -> ([Line] -> [Line]) -> FilePath -> Property -fileProperty desc a f = Property desc $ go =<< doesFileExist f +fileProperty desc a f = Property desc $ go =<< liftIO (doesFileExist f) where go True = do - ls <- lines <$> readFile f + ls <- liftIO $ lines <$> readFile f let ls' = a ls if ls' == ls then noChange diff --git a/Propellor/Property/Scheduled.hs b/Propellor/Property/Scheduled.hs index 827c648..8341765 100644 --- a/Propellor/Property/Scheduled.hs +++ b/Propellor/Property/Scheduled.hs @@ -20,13 +20,13 @@ import qualified Data.Map as M -- last run. period :: Property -> Recurrance -> Property period prop recurrance = Property desc $ do - lasttime <- getLastChecked (propertyDesc prop) - nexttime <- fmap startTime <$> nextTime schedule lasttime - t <- localNow + lasttime <- liftIO $ getLastChecked (propertyDesc prop) + nexttime <- liftIO $ fmap startTime <$> nextTime schedule lasttime + t <- liftIO localNow if Just t >= nexttime then do r <- ensureProperty prop - setLastChecked t (propertyDesc prop) + liftIO $ setLastChecked t (propertyDesc prop) return r else noChange where @@ -38,7 +38,7 @@ periodParse :: Property -> String -> Property periodParse prop s = case toRecurrance s of Just recurrance -> period prop recurrance Nothing -> Property "periodParse" $ do - warningMessage $ "failed periodParse: " ++ s + liftIO $ warningMessage $ "failed periodParse: " ++ s noChange lastCheckedFile :: FilePath diff --git a/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs b/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs index 580a52d..204a9ca 100644 --- a/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs +++ b/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs @@ -44,12 +44,13 @@ builder arch crontimes rsyncupload = combineProperties "gitannexbuilder" let f = homedir "rsyncpassword" if rsyncupload then withPrivData (Password builduser) $ \p -> do - oldp <- catchDefaultIO "" $ readFileStrict f + oldp <- liftIO $ catchDefaultIO "" $ + readFileStrict f if p /= oldp then makeChange $ writeFile f p else noChange else do - ifM (doesFileExist f) + ifM (liftIO $ doesFileExist f) ( noChange , makeChange $ writeFile f "no password configured" ) diff --git a/Propellor/Property/SiteSpecific/GitHome.hs b/Propellor/Property/SiteSpecific/GitHome.hs index 482100c..1ba56b9 100644 --- a/Propellor/Property/SiteSpecific/GitHome.hs +++ b/Propellor/Property/SiteSpecific/GitHome.hs @@ -8,7 +8,7 @@ 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 =<< homedir user) + Property ("githome " ++ user) (go =<< liftIO (homedir user)) `requires` Apt.installed ["git"] where go Nothing = noChange diff --git a/Propellor/Property/Ssh.hs b/Propellor/Property/Ssh.hs index 36766f5..59845f8 100644 --- a/Propellor/Property/Ssh.hs +++ b/Propellor/Property/Ssh.hs @@ -53,7 +53,7 @@ uniqueHostKeys = flagFile prop "/etc/ssh/.unique_host_keys" `onChange` restartSshd where prop = Property "ssh unique host keys" $ do - void $ boolSystem "sh" + void $ liftIO $ boolSystem "sh" [ Param "-c" , Param "rm -f /etc/ssh/ssh_host_*" ] diff --git a/Propellor/Property/Sudo.hs b/Propellor/Property/Sudo.hs index 68b8d05..66ceb58 100644 --- a/Propellor/Property/Sudo.hs +++ b/Propellor/Property/Sudo.hs @@ -13,7 +13,7 @@ enabledFor :: UserName -> Property enabledFor user = Property desc go `requires` Apt.installed ["sudo"] where go = do - locked <- isLockedPassword user + locked <- liftIO $ isLockedPassword user ensureProperty $ fileProperty desc (modify locked . filter (wanted locked)) diff --git a/Propellor/Types.hs b/Propellor/Types.hs index 3be10d3..b163292 100644 --- a/Propellor/Types.hs +++ b/Propellor/Types.hs @@ -1,20 +1,53 @@ +{-# LANGUAGE PackageImports #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + module Propellor.Types where import Data.Monoid +import Control.Applicative import System.Console.ANSI +import "mtl" Control.Monad.Reader +import "MonadCatchIO-transformers" Control.Monad.CatchIO type HostName = String type GroupName = String type UserName = String +-- | The core data type of Propellor, this reprecents a property +-- that the system should have, and an action to ensure it has the +-- property. data Property = Property { propertyDesc :: Desc -- | must be idempotent; may run repeatedly - , propertySatisfy :: IO Result + , propertySatisfy :: Propellor Result } +-- | A property that can be reverted. data RevertableProperty = RevertableProperty Property Property +-- | Propellor's monad provides read-only access to attributes of the +-- system. +newtype Propellor a = Propellor { runWithHostAttr :: ReaderT HostAttr IO a } + deriving + ( Monad + , Functor + , Applicative + , MonadReader HostAttr + , MonadIO + , MonadCatchIO + ) + +-- | The attributes of a system. For example, its hostname. +newtype HostAttr = HostAttr + { _hostname :: HostName + } + +mkHostAttr :: HostName -> HostAttr +mkHostAttr = HostAttr + +getHostName :: Propellor HostName +getHostName = asks _hostname + class IsProp p where -- | Sets description. describe :: p -> Desc -> p diff --git a/debian/changelog b/debian/changelog index 55043d5..a9a142d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -propellor (0.2.4) UNRELEASED; urgency=medium +propellor (0.3.0) UNRELEASED; urgency=medium * ipv6to4: Ensure interface is brought up automatically on boot. * Enabling unattended upgrades now ensures that cron is installed and @@ -8,6 +8,8 @@ propellor (0.2.4) UNRELEASED; urgency=medium * Fix compilation on Debian stable. * Include security updates in sources.list for stable and testing. * Use ssh connection caching, especially when bootstrapping. + * Properties now run in a Propellor monad, which provides access to + attributes of the host. -- Joey Hess Tue, 08 Apr 2014 18:07:12 -0400 diff --git a/propellor.cabal b/propellor.cabal index 03d1474..0c7e349 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -1,5 +1,5 @@ Name: propellor -Version: 0.2.3 +Version: 0.3.0 Cabal-Version: >= 1.6 License: GPL Maintainer: Joey Hess @@ -38,7 +38,8 @@ Executable propellor GHC-Options: -Wall Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5, IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal, - containers, network, async, time, QuickCheck + containers, network, async, time, QuickCheck, mtl, + MonadCatchIO-transformers if (! os(windows)) Build-Depends: unix @@ -48,7 +49,8 @@ Executable config GHC-Options: -Wall -threaded Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5, IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal, - containers, network, async, time, QuickCheck + containers, network, async, time, QuickCheck, mtl, + MonadCatchIO-transformers if (! os(windows)) Build-Depends: unix @@ -57,7 +59,8 @@ Library GHC-Options: -Wall Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5, IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal, - containers, network, async, time, QuickCheck + containers, network, async, time, QuickCheck, mtl, + MonadCatchIO-transformers if (! os(windows)) Build-Depends: unix @@ -88,6 +91,7 @@ Library Propellor.Message Propellor.PrivData Propellor.Engine + Propellor.Exception Propellor.Types Other-Modules: Propellor.CmdLine From 2372d6a3f8193145662e393aa61b585d8bafd32d Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 17:46:03 -0400 Subject: [PATCH 66/88] use HostAttr to simplify config file --- Propellor/Property/Docker.hs | 36 +++++++++++++++++++--------------- Propellor/Property/Hostname.hs | 13 +++++++----- Propellor/Types.hs | 2 +- config-joey.hs | 32 +++++++++++++++--------------- config-simple.hs | 4 ++-- 5 files changed, 47 insertions(+), 40 deletions(-) diff --git a/Propellor/Property/Docker.hs b/Propellor/Property/Docker.hs index 1df3425..3828535 100644 --- a/Propellor/Property/Docker.hs +++ b/Propellor/Property/Docker.hs @@ -40,36 +40,40 @@ installed = Apt.installed ["docker.io"] -- removed. docked :: (HostName -> ContainerName -> Maybe (Container)) - -> HostName -> ContainerName -> RevertableProperty -docked findc hn cn = findContainer findc hn cn $ - \(Container image containerprops) -> - let setup = provisionContainer cid - `requires` - runningContainer cid image containerprops - `requires` - installed - teardown = combineProperties ("undocked " ++ fromContainerId cid) - [ stoppedContainer cid +docked findc cn = RevertableProperty (go "docked" setup) (go "undocked" teardown) + where + go desc a = Property (desc ++ " " ++ cn) $ do + hn <- getHostName + let cid = ContainerId hn cn + ensureProperties [findContainer findc hn cn $ a cid] + + setup cid (Container image containerprops) = + provisionContainer cid + `requires` + runningContainer cid image containerprops + `requires` + installed + + teardown cid (Container image _) = + combineProperties ("undocked " ++ fromContainerId cid) + [ stoppedContainer cid , Property ("cleaned up " ++ fromContainerId cid) $ liftIO $ report <$> mapM id [ removeContainer cid , removeImage image ] ] - in RevertableProperty setup teardown - where - cid = ContainerId hn cn findContainer :: (HostName -> ContainerName -> Maybe (Container)) -> HostName -> ContainerName - -> (Container -> RevertableProperty) - -> RevertableProperty + -> (Container -> Property) + -> Property findContainer findc hn cn mk = case findc hn cn of - Nothing -> RevertableProperty cantfind cantfind + Nothing -> cantfind Just container -> mk container where cid = ContainerId hn cn diff --git a/Propellor/Property/Hostname.hs b/Propellor/Property/Hostname.hs index 2663537..0708b3f 100644 --- a/Propellor/Property/Hostname.hs +++ b/Propellor/Property/Hostname.hs @@ -3,14 +3,17 @@ module Propellor.Property.Hostname where import Propellor import qualified Propellor.Property.File as File --- | Sets the hostname. Configures both /etc/hostname and the current --- hostname. +-- | Ensures that the hostname is set to the HostAttr value. +-- Configures both /etc/hostname and the current hostname. -- --- When provided with a FQDN, also configures /etc/hosts, +-- When the hostname is a FQDN, also configures /etc/hosts, -- with an entry for 127.0.1.1, which is standard at least on Debian -- to set the FDQN (127.0.0.1 is localhost). -set :: HostName -> Property -set hostname = combineProperties desc go +sane :: Property +sane = Property ("sane hostname") (ensureProperty . setTo =<< getHostName) + +setTo :: HostName -> Property +setTo hostname = combineProperties desc go `onChange` cmdProperty "hostname" [host] where desc = "hostname " ++ hostname diff --git a/Propellor/Types.hs b/Propellor/Types.hs index b163292..6a1c888 100644 --- a/Propellor/Types.hs +++ b/Propellor/Types.hs @@ -27,7 +27,7 @@ data RevertableProperty = RevertableProperty Property Property -- | Propellor's monad provides read-only access to attributes of the -- system. -newtype Propellor a = Propellor { runWithHostAttr :: ReaderT HostAttr IO a } +newtype Propellor p = Propellor { runWithHostAttr :: ReaderT HostAttr IO p } deriving ( Monad , Functor diff --git a/config-joey.hs b/config-joey.hs index 2c6374c..d1a3323 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -32,35 +32,35 @@ main = defaultMain [host, Docker.containerProperties container] -- Edit this to configure propellor! host :: HostName -> Maybe [Property] -- Clam is a tor bridge, and an olduse.net shellbox and other fun stuff. -host hostname@"clam.kitenet.net" = Just $ withSystemd $ props - & cleanCloudAtCost hostname +host "clam.kitenet.net" = Just $ withSystemd $ props + & cleanCloudAtCost & standardSystem Unstable & Apt.unattendedUpgrades & Network.ipv6to4 & Apt.installed ["git-annex", "mtr"] & Tor.isBridge & JoeySites.oldUseNetshellBox - & Docker.docked container hostname "openid-provider" + & Docker.docked container "openid-provider" `requires` Apt.installed ["ntp"] - & Docker.docked container hostname "ancient-kitenet" + & Docker.docked container "ancient-kitenet" & Docker.configured & Docker.garbageCollected `period` Daily -- Orca is the main git-annex build box. -host hostname@"orca.kitenet.net" = Just $ props -- no systemd due to #726375 +host "orca.kitenet.net" = Just $ props -- no systemd due to #726375 & standardSystem Unstable - & Hostname.set hostname + & Hostname.sane & Apt.unattendedUpgrades & Docker.configured & Apt.buildDep ["git-annex"] `period` Daily - & Docker.docked container hostname "amd64-git-annex-builder" - & Docker.docked container hostname "i386-git-annex-builder" - ! Docker.docked container hostname "armel-git-annex-builder-companion" - ! Docker.docked container hostname "armel-git-annex-builder" + & Docker.docked container "amd64-git-annex-builder" + & Docker.docked container "i386-git-annex-builder" + ! Docker.docked container "armel-git-annex-builder-companion" + ! Docker.docked container "armel-git-annex-builder" & Docker.garbageCollected `period` Daily -- Diatom is my downloads and git repos server, and secondary dns server. -host hostname@"diatom.kitenet.net" = Just $ props +host "diatom.kitenet.net" = Just $ props & standardSystem Stable - & Hostname.set hostname + & Hostname.sane & Apt.unattendedUpgrades & Apt.serviceInstalledRunning "ntp" & Dns.zones myDnsSecondary @@ -78,7 +78,7 @@ host hostname@"diatom.kitenet.net" = Just $ props -- gitweb -- downloads.kitenet.net setup (including ssh key to turtle) -- My laptop -host _hostname@"darkstar.kitenet.net" = Just $ props +host "darkstar.kitenet.net" = Just $ props & Docker.configured & Apt.buildDep ["git-annex"] `period` Daily @@ -192,9 +192,9 @@ standardContainer suite arch ps = Docker.containerFrom ] ++ ps -- Clean up a system as installed by cloudatcost.com -cleanCloudAtCost :: HostName -> Property -cleanCloudAtCost hostname = propertyList "cloudatcost cleanup" - [ Hostname.set hostname +cleanCloudAtCost :: Property +cleanCloudAtCost = propertyList "cloudatcost cleanup" + [ Hostname.sane , Ssh.uniqueHostKeys , "worked around grub/lvm boot bug #743126" ==> "/etc/default/grub" `File.containsLine` "GRUB_DISABLE_LINUX_UUID=true" diff --git a/config-simple.hs b/config-simple.hs index 6784f76..8011e97 100644 --- a/config-simple.hs +++ b/config-simple.hs @@ -25,7 +25,7 @@ main = defaultMain [host, Docker.containerProperties container] -- -- Edit this to configure propellor! host :: HostName -> Maybe [Property] -host hostname@"mybox.example.com" = Just $ props +host "mybox.example.com" = Just $ props & Apt.stdSourcesList Unstable `onChange` Apt.upgrade & Apt.unattendedUpgrades @@ -34,7 +34,7 @@ host hostname@"mybox.example.com" = Just $ props & User.hasSomePassword "root" & Network.ipv6to4 & File.dirExists "/var/www" - & Docker.docked container hostname "webserver" + & Docker.docked container "webserver" & Docker.garbageCollected `period` Daily & Cron.runPropellor "30 * * * *" -- add more hosts here... From 21b3ed28019665e971617faa4d6f80085430ef84 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 17:48:07 -0400 Subject: [PATCH 67/88] propellor spin From df9791ee2626865b29196c7ac1a5975939475a6c Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 17:50:16 -0400 Subject: [PATCH 68/88] deps --- Makefile | 2 +- debian/control | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f31e5fa..e53de8c 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ build: dist/setup-config ln -sf dist/build/config/config propellor deps: - @if [ $$(whoami) = root ]; then apt-get --no-upgrade --no-install-recommends -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev; fi || true + @if [ $$(whoami) = root ]; then apt-get --no-upgrade --no-install-recommends -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev libghc-mtl-dev libghc-monadcatchio-transformers-dev; fi || true @if [ $$(whoami) = root ]; then apt-get --no-upgrade --no-install-recommends -y install libghc-async-dev || cabal update; cabal install async; fi || true dist/setup-config: propellor.cabal diff --git a/debian/control b/debian/control index 3f5cb2d..bfdc588 100644 --- a/debian/control +++ b/debian/control @@ -11,6 +11,8 @@ Build-Depends: libghc-unix-compat-dev, libghc-ansi-terminal-dev, libghc-ifelse-dev, + libghc-mtl-dev, + libghc-monadcatchio-transformers-dev, Maintainer: Joey Hess Standards-Version: 3.9.5 Vcs-Git: git://git.kitenet.net/propellor @@ -28,6 +30,8 @@ Depends: ${misc:Depends}, ${shlibs:Depends}, libghc-unix-compat-dev, libghc-ansi-terminal-dev, libghc-ifelse-dev, + libghc-mtl-dev, + libghc-monadcatchio-transformers-dev, git, Description: property-based host configuration management in haskell Propellor enures that the system it's run in satisfies a list of From 46b555b30919f068d3da949264e797c484298fae Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 17:51:23 -0400 Subject: [PATCH 69/88] propellor spin From 09c50c5646b942c2d5661825863d1c809c5f6af4 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 17:51:41 -0400 Subject: [PATCH 70/88] propellor spin From 981085fe8148c23985e1735f0a0926d2efd62375 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 17:55:23 -0400 Subject: [PATCH 71/88] propellor spin From 50cd59cb3e6d20afe48a50fa9dc0c3a9cf9d9960 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 21:09:20 -0400 Subject: [PATCH 72/88] new more expressive config.hs WIP --- Propellor.hs | 24 ++-- Propellor/Attr.hs | 47 +++++++ Propellor/CmdLine.hs | 65 ++++----- Propellor/Engine.hs | 10 +- Propellor/PrivData.hs | 1 + Propellor/Property.hs | 51 +++++-- Propellor/Property/Apt.hs | 4 +- Propellor/Property/Hostname.hs | 12 +- Propellor/Property/SiteSpecific/JoeySites.hs | 4 +- Propellor/Types.hs | 84 ++++++++---- Propellor/Types/Attr.hs | 16 +++ TODO | 4 +- config-joey.hs | 132 +++++++++---------- propellor.cabal | 2 + 14 files changed, 291 insertions(+), 165 deletions(-) create mode 100644 Propellor/Attr.hs create mode 100644 Propellor/Types/Attr.hs diff --git a/Propellor.hs b/Propellor.hs index 1f1d7ec..e631224 100644 --- a/Propellor.hs +++ b/Propellor.hs @@ -2,8 +2,9 @@ -- | Pulls in lots of useful modules for building and using Properties. -- --- Propellor enures that the system it's run in satisfies a list of --- properties, taking action as necessary when a property is not yet met. +-- When propellor runs on a Host, it ensures that its list of Properties +-- is satisfied, taking action as necessary when a Property is not +-- currently satisfied. -- -- A simple propellor program example: -- @@ -13,15 +14,16 @@ -- > import qualified Propellor.Property.Apt as Apt -- > -- > main :: IO () --- > main = defaultMain getProperties +-- > main = defaultMain hosts -- > --- > getProperties :: HostName -> Maybe [Property] --- > getProperties "example.com" = Just --- > [ Apt.installed ["mydaemon"] --- > , "/etc/mydaemon.conf" `File.containsLine` "secure=1" --- > `onChange` cmdProperty "service" ["mydaemon", "restart"] --- > ] --- > getProperties _ = Nothing +-- > hosts :: [Host] +-- > hosts = +-- > [ host "example.com" +-- > & Apt.installed ["mydaemon"] +-- > & "/etc/mydaemon.conf" `File.containsLine` "secure=1" +-- > `onChange` cmdProperty "service" ["mydaemon", "restart"] +-- > ! Apt.installed ["unwantedpackage"] +-- > ] -- -- See config.hs for a more complete example, and clone Propellor's -- git repository for a deployable system using Propellor: @@ -31,6 +33,7 @@ module Propellor ( module Propellor.Types , module Propellor.Property , module Propellor.Property.Cmd + , module Propellor.Attr , module Propellor.PrivData , module Propellor.Engine , module Propellor.Exception @@ -47,6 +50,7 @@ import Propellor.Property.Cmd import Propellor.PrivData import Propellor.Message import Propellor.Exception +import Propellor.Attr import Utility.PartialPrelude as X import Utility.Process as X diff --git a/Propellor/Attr.hs b/Propellor/Attr.hs new file mode 100644 index 0000000..4bc1c2c --- /dev/null +++ b/Propellor/Attr.hs @@ -0,0 +1,47 @@ +{-# LANGUAGE PackageImports #-} + +module Propellor.Attr where + +import Propellor.Types +import Propellor.Types.Attr + +import "mtl" Control.Monad.Reader +import qualified Data.Set as S +import qualified Data.Map as M + +pureAttrProperty :: Desc -> (Attr -> Attr) -> AttrProperty +pureAttrProperty desc = AttrProperty $ Property ("has " ++ desc) + (return NoChange) + +hostname :: HostName -> AttrProperty +hostname name = pureAttrProperty ("hostname " ++ name) $ + \d -> d { _hostname = name } + +getHostName :: Propellor HostName +getHostName = asks _hostname + +cname :: Domain -> AttrProperty +cname domain = pureAttrProperty ("cname " ++ domain) (addCName domain) + +cnameFor :: IsProp p => Domain -> (Domain -> p) -> AttrProperty +cnameFor domain mkp = + let p = mkp domain + in AttrProperty p (addCName domain) + +addCName :: HostName -> Attr -> Attr +addCName domain d = d { _cnames = S.insert domain (_cnames d) } + +hostnameless :: Attr +hostnameless = newAttr (error "hostname Attr not specified") + +hostAttr :: Host -> Attr +hostAttr (Host _ mkattrs) = mkattrs hostnameless + +hostProperties :: Host -> [Property] +hostProperties (Host ps _) = ps + +hostMap :: [Host] -> M.Map HostName Host +hostMap l = M.fromList $ zip (map (_hostname . hostAttr) l) l + +findHost :: [Host] -> HostName -> Maybe Host +findHost l hn = M.lookup hn (hostMap l) diff --git a/Propellor/CmdLine.hs b/Propellor/CmdLine.hs index 2026c47..5be91c4 100644 --- a/Propellor/CmdLine.hs +++ b/Propellor/CmdLine.hs @@ -55,8 +55,8 @@ processCmdLine = go =<< getArgs else return $ Run s go _ = usage -defaultMain :: [HostName -> Maybe [Property]] -> IO () -defaultMain getprops = do +defaultMain :: [Host] -> IO () +defaultMain hostlist = do DockerShim.cleanEnv checkDebugMode cmdline <- processCmdLine @@ -64,25 +64,26 @@ defaultMain getprops = do go True cmdline where go _ (Continue cmdline) = go False cmdline - go _ (Set host field) = setPrivData host field + go _ (Set hn field) = setPrivData hn field go _ (AddKey keyid) = addKey keyid - go _ (Chain host) = withprops host $ \hostattr ps -> do - r <- runPropellor hostattr $ ensureProperties ps + go _ (Chain hn) = withprops hn $ \attr ps -> do + r <- runPropellor attr $ ensureProperties ps putStrLn $ "\n" ++ show r - go _ (Docker host) = Docker.chain host + go _ (Docker hn) = Docker.chain hn go True cmdline@(Spin _) = buildFirst cmdline $ go False cmdline go True cmdline = updateFirst cmdline $ go False cmdline - go False (Spin host) = withprops host $ const . const $ spin host - go False (Run host) = ifM ((==) 0 <$> getRealUserID) - ( onlyProcess $ withprops host mainProperties - , go True (Spin host) + go False (Spin hn) = withprops hn $ const . const $ spin hn + go False (Run hn) = ifM ((==) 0 <$> getRealUserID) + ( onlyProcess $ withprops hn mainProperties + , go True (Spin hn) ) - go False (Boot host) = onlyProcess $ withprops host $ boot + go False (Boot hn) = onlyProcess $ withprops hn boot - withprops host a = maybe (unknownhost host) (a hostattr) $ - headMaybe $ catMaybes $ map (\get -> get host) getprops - where - hostattr = mkHostAttr host + withprops :: HostName -> (Attr -> [Property] -> IO ()) -> IO () + withprops hn a = maybe + (unknownhost hn) + (\h -> a (hostAttr h) (hostProperties h)) + (findHost hostlist hn) onlyProcess :: IO a -> IO a onlyProcess a = bracket lock unlock (const a) @@ -166,16 +167,16 @@ getCurrentGitSha1 :: String -> IO String getCurrentGitSha1 branchref = readProcess "git" ["show-ref", "--hash", branchref] spin :: HostName -> IO () -spin host = do +spin hn = do url <- getUrl void $ gitCommit [Param "--allow-empty", Param "-a", Param "-m", Param "propellor spin"] void $ boolSystem "git" [Param "push"] - cacheparams <- toCommand <$> sshCachingParams host - go cacheparams url =<< gpgDecrypt (privDataFile host) + cacheparams <- toCommand <$> sshCachingParams hn + go cacheparams url =<< gpgDecrypt (privDataFile hn) where go cacheparams url privdata = withBothHandles createProcessSuccess (proc "ssh" $ cacheparams ++ [user, bootstrapcmd]) $ \(toh, fromh) -> do let finish = do - senddata toh (privDataFile host) privDataMarker privdata + senddata toh (privDataFile hn) privDataMarker privdata hClose toh -- Display remaining output. @@ -188,10 +189,10 @@ spin host = do NeedGitClone -> do hClose toh hClose fromh - sendGitClone host url + sendGitClone hn url go cacheparams url privdata - user = "root@"++host + user = "root@"++hn bootstrapcmd = shellWrap $ intercalate " ; " [ "if [ ! -d " ++ localdir ++ " ]" @@ -202,7 +203,7 @@ spin host = do , "else " ++ intercalate " && " [ "cd " ++ localdir , "if ! test -x ./propellor; then make deps build; fi" - , "./propellor --boot " ++ host + , "./propellor --boot " ++ hn ] , "fi" ] @@ -218,18 +219,18 @@ spin host = do showremote s = putStrLn s senddata toh f marker s = void $ - actionMessage ("Sending " ++ f ++ " (" ++ show (length s) ++ " bytes) to " ++ host) $ do + actionMessage ("Sending " ++ f ++ " (" ++ show (length s) ++ " bytes) to " ++ hn) $ do sendMarked toh marker s return True sendGitClone :: HostName -> String -> IO () -sendGitClone host url = void $ actionMessage ("Pushing git repository to " ++ host) $ do +sendGitClone hn url = void $ actionMessage ("Pushing git repository to " ++ hn) $ do branch <- getCurrentBranch - cacheparams <- sshCachingParams host + cacheparams <- sshCachingParams hn withTmpFile "propellor.git" $ \tmp _ -> allM id [ boolSystem "git" [Param "bundle", Param "create", File tmp, Param "HEAD"] - , boolSystem "scp" $ cacheparams ++ [File tmp, Param ("root@"++host++":"++remotebundle)] - , boolSystem "ssh" $ cacheparams ++ [Param ("root@"++host), Param $ unpackcmd branch] + , boolSystem "scp" $ cacheparams ++ [File tmp, Param ("root@"++hn++":"++remotebundle)] + , boolSystem "ssh" $ cacheparams ++ [Param ("root@"++hn), Param $ unpackcmd branch] ] where remotebundle = "/usr/local/propellor.git" @@ -277,15 +278,15 @@ fromMarked marker s len = length marker matches = filter (marker `isPrefixOf`) $ lines s -boot :: HostAttr -> [Property] -> IO () -boot hostattr ps = do +boot :: Attr -> [Property] -> IO () +boot attr ps = do sendMarked stdout statusMarker $ show Ready reply <- hGetContentsStrict stdin makePrivDataDir maybe noop (writeFileProtected privDataLocal) $ fromMarked privDataMarker reply - mainProperties hostattr ps + mainProperties attr ps addKey :: String -> IO () addKey keyid = exitBool =<< allM id [ gpg, gitadd, gitcommit ] @@ -347,11 +348,11 @@ checkDebugMode = go =<< getEnv "PROPELLOR_DEBUG" -- Parameters can be passed to both ssh and scp. sshCachingParams :: HostName -> IO [CommandParam] -sshCachingParams hostname = do +sshCachingParams hn = do home <- myHomeDir let cachedir = home ".ssh" "propellor" createDirectoryIfMissing False cachedir - let socketfile = cachedir hostname ++ ".sock" + let socketfile = cachedir hn ++ ".sock" return [ Param "-o", Param ("ControlPath=" ++ socketfile) , Params "-o ControlMaster=auto -o ControlPersist=yes" diff --git a/Propellor/Engine.hs b/Propellor/Engine.hs index c527dc3..81d979a 100644 --- a/Propellor/Engine.hs +++ b/Propellor/Engine.hs @@ -12,12 +12,12 @@ import Propellor.Types import Propellor.Message import Propellor.Exception -runPropellor :: HostAttr -> Propellor a -> IO a -runPropellor hostattr a = runReaderT (runWithHostAttr a) hostattr +runPropellor :: Attr -> Propellor a -> IO a +runPropellor attr a = runReaderT (runWithAttr a) attr -mainProperties :: HostAttr -> [Property] -> IO () -mainProperties hostattr ps = do - r <- runPropellor hostattr $ +mainProperties :: Attr -> [Property] -> IO () +mainProperties attr ps = do + r <- runPropellor attr $ ensureProperties [Property "overall" $ ensureProperties ps] setTitle "propellor: done" hFlush stdout diff --git a/Propellor/PrivData.hs b/Propellor/PrivData.hs index 7f5a23d..5adc9e9 100644 --- a/Propellor/PrivData.hs +++ b/Propellor/PrivData.hs @@ -12,6 +12,7 @@ import Control.Monad import "mtl" Control.Monad.Reader import Propellor.Types +import Propellor.Attr import Propellor.Message import Utility.Monad import Utility.PartialPrelude diff --git a/Propellor/Property.hs b/Propellor/Property.hs index 7af69ea..ccc060f 100644 --- a/Propellor/Property.hs +++ b/Propellor/Property.hs @@ -9,6 +9,8 @@ import Control.Monad.IfElse import "mtl" Control.Monad.Reader import Propellor.Types +import Propellor.Types.Attr +import Propellor.Attr import Propellor.Engine import Utility.Monad @@ -94,17 +96,46 @@ boolProperty desc a = Property desc $ ifM (liftIO a) revert :: RevertableProperty -> RevertableProperty revert (RevertableProperty p1 p2) = RevertableProperty p2 p1 --- | Starts a list of Properties -props :: [Property] -props = [] +-- | Starts accumulating the properties of a Host. +-- +-- > host "example.com" +-- > & someproperty +-- > ! oldproperty +-- > & otherproperty +host :: HostName -> Host +host hn = Host [] (\_ -> newAttr hn) + +-- | Adds a property to a Host +-- Can add Properties, RevertableProperties, and AttrProperties +(&) :: IsProp p => Host -> p -> Host +(Host ps as) & p = Host (ps ++ [toProp p]) (as . getAttr p) --- | Adds a property to the list. --- Can add both Properties and RevertableProperties. -(&) :: IsProp p => [Property] -> p -> [Property] -ps & p = ps ++ [toProp p] infixl 1 & --- | Adds a property to the list in reverted form. -(!) :: [Property] -> RevertableProperty -> [Property] -ps ! p = ps ++ [toProp $ revert p] +-- | Adds a property to the Host in reverted form. +(!) :: Host -> RevertableProperty -> Host +(Host ps as) ! p = Host (ps ++ [toProp q]) (as . getAttr q) + where + q = revert p + infixl 1 ! + +-- | Makes a propertyList of a set of properties, using the same syntax +-- used by `host`. +-- +-- > template "my template" $ props +-- & someproperty +-- ! oldproperty +-- +-- Note that none of the properties can define Attrs, because +-- they will not propigate out to the host that this is added to. +-- +-- Unfortunately, this is not currently enforced at the type level, so +-- attempting to set an Attr in here will be run time error. +template :: Desc -> Host -> Property +template desc h@(Host ps _) + | hostAttr h == hostAttr props = propertyList desc ps + | otherwise = error $ desc ++ ": template contains Attr" + +props :: Host +props = Host [] (\_ -> hostnameless) diff --git a/Propellor/Property/Apt.hs b/Propellor/Property/Apt.hs index 937d140..4da13a2 100644 --- a/Propellor/Property/Apt.hs +++ b/Propellor/Property/Apt.hs @@ -180,8 +180,8 @@ reConfigure package vals = reconfigure `requires` setselections setselections = Property "preseed" $ makeChange $ withHandle StdinHandle createProcessSuccess (proc "debconf-set-selections" []) $ \h -> do - forM_ vals $ \(template, tmpltype, value) -> - hPutStrLn h $ unwords [package, template, tmpltype, value] + forM_ vals $ \(tmpl, tmpltype, value) -> + hPutStrLn h $ unwords [package, tmpl, tmpltype, value] hClose h reconfigure = cmdProperty "dpkg-reconfigure" ["-fnone", package] diff --git a/Propellor/Property/Hostname.hs b/Propellor/Property/Hostname.hs index 0708b3f..03613ac 100644 --- a/Propellor/Property/Hostname.hs +++ b/Propellor/Property/Hostname.hs @@ -13,14 +13,14 @@ sane :: Property sane = Property ("sane hostname") (ensureProperty . setTo =<< getHostName) setTo :: HostName -> Property -setTo hostname = combineProperties desc go - `onChange` cmdProperty "hostname" [host] +setTo hn = combineProperties desc go + `onChange` cmdProperty "hostname" [basehost] where - desc = "hostname " ++ hostname - (host, domain) = separate (== '.') hostname + desc = "hostname " ++ hn + (basehost, domain) = separate (== '.') hn go = catMaybes - [ Just $ "/etc/hostname" `File.hasContent` [host] + [ Just $ "/etc/hostname" `File.hasContent` [basehost] , if null domain then Nothing else Just $ File.fileProperty desc @@ -28,7 +28,7 @@ setTo hostname = combineProperties desc go ] hostip = "127.0.1.1" - hostline = hostip ++ "\t" ++ hostname ++ " " ++ host + hostline = hostip ++ "\t" ++ hn ++ " " ++ basehost addhostline ls = hostline : filter (not . hashostip) ls hashostip l = headMaybe (words l) == Just hostip diff --git a/Propellor/Property/SiteSpecific/JoeySites.hs b/Propellor/Property/SiteSpecific/JoeySites.hs index 029064d..4637317 100644 --- a/Propellor/Property/SiteSpecific/JoeySites.hs +++ b/Propellor/Property/SiteSpecific/JoeySites.hs @@ -6,8 +6,8 @@ module Propellor.Property.SiteSpecific.JoeySites where import Propellor import qualified Propellor.Property.Apt as Apt -oldUseNetshellBox :: Property -oldUseNetshellBox = check (not <$> Apt.isInstalled "oldusenet") $ +oldUseNetShellBox :: Property +oldUseNetShellBox = check (not <$> Apt.isInstalled "oldusenet") $ propertyList ("olduse.net shellbox") [ 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" diff --git a/Propellor/Types.hs b/Propellor/Types.hs index 6a1c888..e6e0212 100644 --- a/Propellor/Types.hs +++ b/Propellor/Types.hs @@ -1,7 +1,33 @@ {-# LANGUAGE PackageImports #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE ExistentialQuantification #-} -module Propellor.Types where +module Propellor.Types + ( Host(..) + , Attr + , HostName + , UserName + , GroupName + , Propellor(..) + , Property(..) + , RevertableProperty(..) + , AttrProperty(..) + , IsProp + , describe + , toProp + , getAttr + , requires + , Desc + , Result(..) + , System(..) + , Distribution(..) + , DebianSuite(..) + , Release + , Architecture + , ActionResult(..) + , CmdLine(..) + , PrivDataField(..) + ) where import Data.Monoid import Control.Applicative @@ -9,11 +35,26 @@ import System.Console.ANSI import "mtl" Control.Monad.Reader import "MonadCatchIO-transformers" Control.Monad.CatchIO -type HostName = String -type GroupName = String -type UserName = String +import Propellor.Types.Attr --- | The core data type of Propellor, this reprecents a property +data Host = Host [Property] (Attr -> Attr) + +type UserName = String +type GroupName = String + +-- | Propellor's monad provides read-only access to attributes of the +-- system. +newtype Propellor p = Propellor { runWithAttr :: ReaderT Attr IO p } + deriving + ( Monad + , Functor + , Applicative + , MonadReader Attr + , MonadIO + , MonadCatchIO + ) + +-- | The core data type of Propellor, this represents a property -- that the system should have, and an action to ensure it has the -- property. data Property = Property @@ -25,28 +66,8 @@ data Property = Property -- | A property that can be reverted. data RevertableProperty = RevertableProperty Property Property --- | Propellor's monad provides read-only access to attributes of the --- system. -newtype Propellor p = Propellor { runWithHostAttr :: ReaderT HostAttr IO p } - deriving - ( Monad - , Functor - , Applicative - , MonadReader HostAttr - , MonadIO - , MonadCatchIO - ) - --- | The attributes of a system. For example, its hostname. -newtype HostAttr = HostAttr - { _hostname :: HostName - } - -mkHostAttr :: HostName -> HostAttr -mkHostAttr = HostAttr - -getHostName :: Propellor HostName -getHostName = asks _hostname +-- | A property that affects the Attr. +data AttrProperty = forall p. IsProp p => AttrProperty p (Attr -> Attr) class IsProp p where -- | Sets description. @@ -55,6 +76,7 @@ class IsProp p where -- | Indicates that the first property can only be satisfied -- once the second one is. requires :: p -> Property -> p + getAttr :: p -> (Attr -> Attr) instance IsProp Property where describe p d = p { propertyDesc = d } @@ -64,6 +86,7 @@ instance IsProp Property where case r of FailedChange -> return FailedChange _ -> propertySatisfy x + getAttr _ = id instance IsProp RevertableProperty where -- | Sets the description of both sides. @@ -72,6 +95,13 @@ instance IsProp RevertableProperty where toProp (RevertableProperty p1 _) = p1 (RevertableProperty p1 p2) `requires` y = RevertableProperty (p1 `requires` y) p2 + getAttr _ = id + +instance IsProp AttrProperty where + describe (AttrProperty p a) d = AttrProperty (describe p d) a + toProp (AttrProperty p _) = toProp p + (AttrProperty p a) `requires` y = AttrProperty (p `requires` y) a + getAttr (AttrProperty _ a) = a type Desc = String diff --git a/Propellor/Types/Attr.hs b/Propellor/Types/Attr.hs new file mode 100644 index 0000000..20e5e63 --- /dev/null +++ b/Propellor/Types/Attr.hs @@ -0,0 +1,16 @@ +module Propellor.Types.Attr where + +import qualified Data.Set as S + +-- | The attributes of a host. For example, its hostname. +data Attr = Attr + { _hostname :: HostName + , _cnames :: S.Set Domain + } + deriving (Eq, Show) + +newAttr :: HostName -> Attr +newAttr hn = Attr hn S.empty + +type HostName = String +type Domain = String diff --git a/TODO b/TODO index 0cc8db1..a203169 100644 --- a/TODO +++ b/TODO @@ -3,8 +3,8 @@ but only once despite many config changes being made to satisfy properties. onChange is a poor substitute. * Currently only Debian and derivatives are supported by most Properties. - One way to improve that would be to parameterize Properties with a - Distribution witness. + This could be improved by making the Distribution of the system part + of its HostAttr. * Display of docker container properties is a bit wonky. It always says they are unchanged even when they changed and triggered a reprovision. diff --git a/config-joey.hs b/config-joey.hs index d1a3323..92aa909 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -20,76 +20,68 @@ import qualified Propellor.Property.Git as Git import qualified Propellor.Property.SiteSpecific.GitHome as GitHome import qualified Propellor.Property.SiteSpecific.GitAnnexBuilder as GitAnnexBuilder import qualified Propellor.Property.SiteSpecific.JoeySites as JoeySites -import Data.List -main :: IO () -main = defaultMain [host, Docker.containerProperties container] - --- | This is where the system's HostName, either as returned by uname --- or one specified on the command line, is converted into a list of --- Properties for that system. --- --- Edit this to configure propellor! -host :: HostName -> Maybe [Property] --- Clam is a tor bridge, and an olduse.net shellbox and other fun stuff. -host "clam.kitenet.net" = Just $ withSystemd $ props - & cleanCloudAtCost - & standardSystem Unstable - & Apt.unattendedUpgrades - & Network.ipv6to4 - & Apt.installed ["git-annex", "mtr"] - & Tor.isBridge - & JoeySites.oldUseNetshellBox - & Docker.docked container "openid-provider" - `requires` Apt.installed ["ntp"] - & Docker.docked container "ancient-kitenet" - & Docker.configured - & Docker.garbageCollected `period` Daily --- Orca is the main git-annex build box. -host "orca.kitenet.net" = Just $ props -- no systemd due to #726375 - & standardSystem Unstable - & Hostname.sane - & Apt.unattendedUpgrades - & Docker.configured - & Apt.buildDep ["git-annex"] `period` Daily - & Docker.docked container "amd64-git-annex-builder" - & Docker.docked container "i386-git-annex-builder" - ! Docker.docked container "armel-git-annex-builder-companion" - ! Docker.docked container "armel-git-annex-builder" - & Docker.garbageCollected `period` Daily --- Diatom is my downloads and git repos server, and secondary dns server. -host "diatom.kitenet.net" = Just $ props - & standardSystem Stable - & Hostname.sane - & Apt.unattendedUpgrades - & Apt.serviceInstalledRunning "ntp" - & Dns.zones myDnsSecondary - & Apt.serviceInstalledRunning "apache2" - & Apt.installed ["git", "git-annex", "rsync"] - & Apt.buildDep ["git-annex"] `period` Daily - & Git.daemonRunning "/srv/git" - & File.ownerGroup "/srv/git" "joey" "joey" - -- git repos restore (how?) - -- family annex needs family members to have accounts, - -- ssh host key etc.. finesse? - -- (also should upgrade git-annex-shell for it..) - -- kgb installation and setup - -- ssh keys for branchable and github repo hooks - -- gitweb - -- downloads.kitenet.net setup (including ssh key to turtle) --- My laptop -host "darkstar.kitenet.net" = Just $ props - & Docker.configured - & Apt.buildDep ["git-annex"] `period` Daily - --- add more hosts here... ---host "foo.example.com" = -host _ = Nothing +hosts :: [Host] +hosts = + [ host "clam.kitenet.net" + & cleanCloudAtCost + & standardSystem Unstable + & Apt.unattendedUpgrades + & Network.ipv6to4 + & Tor.isBridge + & Docker.configured + & cname "shell.olduse.net" + `requires` JoeySites.oldUseNetShellBox + & "openid.kitenet.net" + `cnameFor` Docker.docked container + `requires` Apt.installed ["ntp"] + & "ancient.kitenet.net" + `cnameFor` Docker.docked container + & Docker.garbageCollected `period` Daily + & Apt.installed ["git-annex", "mtr", "screen"] + -- Orca is the main git-annex build box. + , host "orca.kitenet.net" + & standardSystem Unstable + & Hostname.sane + & Apt.unattendedUpgrades + & Docker.configured + & Docker.docked container "amd64-git-annex-builder" + & Docker.docked container "i386-git-annex-builder" + ! Docker.docked container "armel-git-annex-builder-companion" + ! Docker.docked container "armel-git-annex-builder" + & Docker.garbageCollected `period` Daily + & Apt.buildDep ["git-annex"] `period` Daily + -- Important stuff that needs not too much memory or CPU. + , host "diatom.kitenet.net" + & standardSystem Stable + & Hostname.sane + & Apt.unattendedUpgrades + & Apt.serviceInstalledRunning "ntp" + & Dns.zones myDnsSecondary + & Apt.serviceInstalledRunning "apache2" + & Apt.installed ["git", "git-annex", "rsync"] + & Apt.buildDep ["git-annex"] `period` Daily + & Git.daemonRunning "/srv/git" + & File.ownerGroup "/srv/git" "joey" "joey" + -- git repos restore (how?) + -- family annex needs family members to have accounts, + -- ssh host key etc.. finesse? + -- (also should upgrade git-annex-shell for it..) + -- kgb installation and setup + -- ssh keys for branchable and github repo hooks + -- gitweb + -- downloads.kitenet.net setup (including ssh key to turtle) + -- My laptop + , host "darkstar.kitenet.net" + & Docker.configured + & Apt.buildDep ["git-annex"] `period` Daily + ] -- | This is where Docker containers are set up. A container -- can vary by hostname where it's used, or be the same everywhere. container :: HostName -> Docker.ContainerName -> Maybe (Docker.Container) container _parenthost name +{- -- Simple web server, publishing the outside host's /var/www | name == "webserver" = Just $ standardContainer Stable "amd64" [ Docker.publish "8080:80" @@ -148,7 +140,7 @@ container _parenthost name & GitAnnexBuilder.builder arch "15 * * * *" True & Apt.unattendedUpgrades ] - +-} | otherwise = Nothing -- | Docker images I prefer to use. @@ -159,7 +151,7 @@ image _ = "debian-stable-official" -- does not currently exist! -- This is my standard system setup standardSystem :: DebianSuite -> Property -standardSystem suite = propertyList "standard system" $ props +standardSystem suite = template "standard system" $ props & Apt.stdSourcesList suite `onChange` Apt.upgrade & Apt.installed ["etckeeper"] & Apt.installed ["ssh"] @@ -179,9 +171,7 @@ standardSystem suite = propertyList "standard system" $ props & Apt.removed ["exim4", "exim4-daemon-light", "exim4-config", "exim4-base"] `onChange` Apt.autoRemove -withSystemd :: [Property] -> [Property] -withSystemd ps = ps ++ [Apt.installed ["systemd-sysv"] `onChange` Reboot.now] - +{- -- This is my standard container setup, featuring automatic upgrades. standardContainer :: DebianSuite -> Architecture -> [Docker.Containerized Property] -> Docker.Container standardContainer suite arch ps = Docker.containerFrom @@ -190,6 +180,7 @@ standardContainer suite arch ps = Docker.containerFrom & Apt.stdSourcesList suite & Apt.unattendedUpgrades ] ++ ps +-} -- Clean up a system as installed by cloudatcost.com cleanCloudAtCost :: Property @@ -218,3 +209,6 @@ myDnsSecondary = where master = ["80.68.85.49", "2001:41c8:125:49::10"] -- wren branchablemaster = ["66.228.46.55", "2600:3c03::f03c:91ff:fedf:c0e5"] + +main :: IO () +main = defaultMain hosts --, Docker.containerProperties container] diff --git a/propellor.cabal b/propellor.cabal index 0c7e349..5497cc6 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -88,12 +88,14 @@ Library Propellor.Property.SiteSpecific.GitHome Propellor.Property.SiteSpecific.JoeySites Propellor.Property.SiteSpecific.GitAnnexBuilder + Propellor.Attr Propellor.Message Propellor.PrivData Propellor.Engine Propellor.Exception Propellor.Types Other-Modules: + Propellor.Types.Attr Propellor.CmdLine Propellor.SimpleSh Propellor.Property.Docker.Shim From 839e60bbcedf99efb7ec7fc8330585006ea1f222 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 23:20:12 -0400 Subject: [PATCH 73/88] propellor spin --- Propellor/Property/Docker.hs | 160 +++++++++++++++-------------------- Propellor/Types/Attr.hs | 16 +++- config-joey.hs | 157 ++++++++++++++++------------------ config-simple.hs | 56 ++++++------ 4 files changed, 182 insertions(+), 207 deletions(-) diff --git a/Propellor/Property/Docker.hs b/Propellor/Property/Docker.hs index 3828535..edf12c2e 100644 --- a/Propellor/Property/Docker.hs +++ b/Propellor/Property/Docker.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE RankNTypes, BangPatterns #-} +{-# LANGUAGE BangPatterns #-} -- | Docker support for propellor -- @@ -9,6 +9,7 @@ module Propellor.Property.Docker where import Propellor import Propellor.SimpleSh +import Propellor.Types.Attr import qualified Propellor.Property.File as File import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.Docker.Shim as Shim @@ -32,6 +33,25 @@ configured = Property "docker configured" go `requires` installed installed :: Property installed = Apt.installed ["docker.io"] +-- | A short descriptive name for a container. +-- Should not contain whitespace or other unusual characters, +-- only [a-zA-Z0-9_-] are allowed +type ContainerName = String + +-- | Starts accumulating the properties of a Docker container. +-- +-- > container "web-server" "debian" +-- > & publish "80:80" +-- > & Apt.installed {"apache2"] +-- > & ... +container :: ContainerName -> Image -> Host +container cn image = Host [] (\_ -> attr) + where + attr = (newAttr (cn2hn cn)) { _dockerImage = Just image } + +cn2hn :: ContainerName -> HostName +cn2hn cn = cn ++ ".docker" + -- | Ensures that a docker container is set up and running. The container -- has its own Properties which are handled by running propellor -- inside the container. @@ -39,24 +59,24 @@ installed = Apt.installed ["docker.io"] -- Reverting this property ensures that the container is stopped and -- removed. docked - :: (HostName -> ContainerName -> Maybe (Container)) + :: [Host] -> ContainerName -> RevertableProperty -docked findc cn = RevertableProperty (go "docked" setup) (go "undocked" teardown) +docked hosts cn = RevertableProperty (go "docked" setup) (go "undocked" teardown) where go desc a = Property (desc ++ " " ++ cn) $ do hn <- getHostName let cid = ContainerId hn cn - ensureProperties [findContainer findc hn cn $ a cid] + ensureProperties [findContainer hosts cid cn $ a cid] - setup cid (Container image containerprops) = + setup cid (Container image runparams) = provisionContainer cid `requires` - runningContainer cid image containerprops + runningContainer cid image runparams `requires` installed - teardown cid (Container image _) = + teardown cid (Container image _runparams) = combineProperties ("undocked " ++ fromContainerId cid) [ stoppedContainer cid , Property ("cleaned up " ++ fromContainerId cid) $ @@ -67,20 +87,33 @@ docked findc cn = RevertableProperty (go "docked" setup) (go "undocked" teardown ] findContainer - :: (HostName -> ContainerName -> Maybe (Container)) - -> HostName + :: [Host] + -> ContainerId -> ContainerName -> (Container -> Property) -> Property -findContainer findc hn cn mk = case findc hn cn of +findContainer hosts cid cn mk = case findHost hosts (cn2hn cn) of Nothing -> cantfind - Just container -> mk container + Just h -> maybe cantfind mk (mkContainer cid h) where - cid = ContainerId hn cn - cantfind = containerDesc (ContainerId hn cn) $ Property "" $ do - liftIO $ warningMessage $ "missing definition for docker container \"" ++ fromContainerId cid + cantfind = containerDesc cid $ Property "" $ do + liftIO $ warningMessage $ + "missing definition for docker container \"" ++ cn2hn cn return FailedChange +mkContainer :: ContainerId -> Host -> Maybe Container +mkContainer cid@(ContainerId hn _cn) h = Container + <$> _dockerImage attr + <*> pure (map (\a -> a hn) (_dockerRunParams attr)) + where + attr = hostAttr h' + h' = h + -- expose propellor directory inside the container + & volume (localdir++":"++localdir) + -- name the container in a predictable way so we + -- and the user can easily find it later + & name (fromContainerId cid) + -- | Causes *any* docker images that are not in use by running containers to -- be deleted. And deletes any containers that propellor has set up -- before that are not currently running. Does not delete any containers @@ -98,30 +131,7 @@ garbageCollected = propertyList "docker garbage collected" gcimages = Property "docker images garbage collected" $ do liftIO $ report <$> (mapM removeImage =<< listImages) --- | Pass to defaultMain to add docker containers. --- You need to provide the function mapping from --- HostName and ContainerName to the Container to use. -containerProperties - :: (HostName -> ContainerName -> Maybe (Container)) - -> (HostName -> Maybe [Property]) -containerProperties findcontainer = \h -> case toContainerId h of - Nothing -> Nothing - Just cid@(ContainerId hn cn) -> - case findcontainer hn cn of - Nothing -> Nothing - Just (Container _ cprops) -> - Just $ map (containerDesc cid) $ - fromContainerized cprops - --- | This type is used to configure a docker container. --- It has an image, and a list of Properties, but these --- properties are Containerized; they can specify --- things about the container's configuration, in --- addition to properties of the system inside the --- container. -data Container = Container Image [Containerized Property] - -data Containerized a = Containerized [HostName -> RunParam] a +data Container = Container Image [RunParam] -- | Parameters to pass to `docker run` when creating a container. type RunParam = String @@ -129,62 +139,50 @@ type RunParam = String -- | A docker image, that can be used to run a container. type Image = String --- | A short descriptive name for a container. --- Should not contain whitespace or other unusual characters, --- only [a-zA-Z0-9_.-] are allowed -type ContainerName = String - --- | Lift a Property to apply inside a container. -inside1 :: Property -> Containerized Property -inside1 = Containerized [] - -inside :: [Property] -> Containerized Property -inside = Containerized [] . combineProperties "provision" - -- | Set custom dns server for container. -dns :: String -> Containerized Property +dns :: String -> AttrProperty dns = runProp "dns" -- | Set container host name. -hostname :: String -> Containerized Property +hostname :: String -> AttrProperty hostname = runProp "hostname" -- | Set name for container. (Normally done automatically.) -name :: String -> Containerized Property +name :: String -> AttrProperty name = runProp "name" -- | Publish a container's port to the host -- (format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort) -publish :: String -> Containerized Property +publish :: String -> AttrProperty publish = runProp "publish" -- | Username or UID for container. -user :: String -> Containerized Property +user :: String -> AttrProperty user = runProp "user" -- | Mount a volume -- Create a bind mount with: [host-dir]:[container-dir]:[rw|ro] -- With just a directory, creates a volume in the container. -volume :: String -> Containerized Property +volume :: String -> AttrProperty volume = runProp "volume" -- | Mount a volume from the specified container into the current -- container. -volumes_from :: ContainerName -> Containerized Property +volumes_from :: ContainerName -> AttrProperty volumes_from cn = genProp "volumes-from" $ \hn -> fromContainerId (ContainerId hn cn) -- | Work dir inside the container. -workdir :: String -> Containerized Property +workdir :: String -> AttrProperty workdir = runProp "workdir" -- | Memory limit for container. --Format: , where unit = b, k, m or g -memory :: String -> Containerized Property +memory :: String -> AttrProperty memory = runProp "memory" -- | Link with another container on the same host. -link :: ContainerName -> ContainerAlias -> Containerized Property +link :: ContainerName -> ContainerAlias -> AttrProperty link linkwith alias = genProp "link" $ \hn -> fromContainerId (ContainerId hn linkwith) ++ ":" ++ alias @@ -203,16 +201,6 @@ data ContainerId = ContainerId HostName ContainerName data ContainerIdent = ContainerIdent Image HostName ContainerName [RunParam] deriving (Read, Show, Eq) -getRunParams :: HostName -> [Containerized a] -> [RunParam] -getRunParams hn l = concatMap get l - where - get (Containerized ps _) = map (\a -> a hn ) ps - -fromContainerized :: forall a. [Containerized a] -> [a] -fromContainerized l = map get l - where - get (Containerized _ a) = a - ident2id :: ContainerIdent -> ContainerId ident2id (ContainerIdent _ hn cn _) = ContainerId hn cn @@ -233,16 +221,13 @@ fromContainerId (ContainerId hn cn) = cn++"."++hn++myContainerSuffix myContainerSuffix :: String myContainerSuffix = ".propellor" -containerFrom :: Image -> [Containerized Property] -> Container -containerFrom = Container - containerDesc :: ContainerId -> Property -> Property containerDesc cid p = p `describe` desc where desc = "[" ++ fromContainerId cid ++ "] " ++ propertyDesc p -runningContainer :: ContainerId -> Image -> [Containerized Property] -> Property -runningContainer cid@(ContainerId hn cn) image containerprops = containerDesc cid $ Property "running" $ do +runningContainer :: ContainerId -> Image -> [RunParam] -> Property +runningContainer cid@(ContainerId hn cn) image runps = containerDesc cid $ Property "running" $ do l <- liftIO $ listContainers RunningContainers if cid `elem` l then do @@ -275,14 +260,6 @@ runningContainer cid@(ContainerId hn cn) image containerprops = containerDesc ci extractident :: [Resp] -> Maybe ContainerIdent extractident = headMaybe . catMaybes . map readish . catMaybes . map getStdout - runps = getRunParams hn $ containerprops ++ - -- expose propellor directory inside the container - [ volume (localdir++":"++localdir) - -- name the container in a predictable way so we - -- and the user can easily find it later - , name (fromContainerId cid) - ] - go img = do liftIO $ do clearProvisionedFlag cid @@ -425,17 +402,18 @@ listContainers status = listImages :: IO [Image] listImages = lines <$> readProcess dockercmd ["images", "--all", "--quiet"] -runProp :: String -> RunParam -> Containerized Property -runProp field val = Containerized - [\_ -> "--" ++ param] - (Property (param) (return NoChange)) +runProp :: String -> RunParam -> AttrProperty +runProp field val = AttrProperty prop $ \attr -> + attr { _dockerRunParams = _dockerRunParams attr ++ [\_ -> "--"++param] } where param = field++"="++val + prop = Property (param) (return NoChange) -genProp :: String -> (HostName -> RunParam) -> Containerized Property -genProp field mkval = Containerized - [\h -> "--" ++ field ++ "=" ++ mkval h] - (Property field (return NoChange)) +genProp :: String -> (HostName -> RunParam) -> AttrProperty +genProp field mkval = AttrProperty prop $ \attr -> + attr { _dockerRunParams = _dockerRunParams attr ++ [\hn -> "--"++field++"=" ++ mkval hn] } + where + prop = Property field (return NoChange) -- | The ContainerIdent of a container is written to -- /.propellor-ident inside it. This can be checked to see if diff --git a/Propellor/Types/Attr.hs b/Propellor/Types/Attr.hs index 20e5e63..7016172 100644 --- a/Propellor/Types/Attr.hs +++ b/Propellor/Types/Attr.hs @@ -6,11 +6,23 @@ import qualified Data.Set as S data Attr = Attr { _hostname :: HostName , _cnames :: S.Set Domain + + , _dockerImage :: Maybe String + , _dockerRunParams :: [HostName -> String] } - deriving (Eq, Show) + +instance Eq Attr where + x == y = and + [ _hostname x == _hostname y + , _cnames x == _cnames y + + , _dockerImage x == _dockerImage y + , let simpl v = map (\a -> a "") (_dockerRunParams v) + in simpl x == simpl y + ] newAttr :: HostName -> Attr -newAttr hn = Attr hn S.empty +newAttr hn = Attr hn S.empty Nothing [] type HostName = String type Domain = String diff --git a/config-joey.hs b/config-joey.hs index 92aa909..093ed8a 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -11,7 +11,7 @@ import qualified Propellor.Property.Cron as Cron import qualified Propellor.Property.Sudo as Sudo import qualified Propellor.Property.User as User import qualified Propellor.Property.Hostname as Hostname -import qualified Propellor.Property.Reboot as Reboot +--import qualified Propellor.Property.Reboot as Reboot import qualified Propellor.Property.Tor as Tor import qualified Propellor.Property.Dns as Dns import qualified Propellor.Property.OpenId as OpenId @@ -23,7 +23,13 @@ import qualified Propellor.Property.SiteSpecific.JoeySites as JoeySites hosts :: [Host] hosts = - [ host "clam.kitenet.net" + -- My laptop + [ host "darkstar.kitenet.net" + & Docker.configured + & Apt.buildDep ["git-annex"] `period` Daily + + -- Nothing super-important lives here. + , host "clam.kitenet.net" & cleanCloudAtCost & standardSystem Unstable & Apt.unattendedUpgrades @@ -31,26 +37,31 @@ hosts = & Tor.isBridge & Docker.configured & cname "shell.olduse.net" - `requires` JoeySites.oldUseNetShellBox - & "openid.kitenet.net" - `cnameFor` Docker.docked container + & JoeySites.oldUseNetShellBox + + & cname "openid.kitenet.net" + & Docker.docked hosts "openid-provider" `requires` Apt.installed ["ntp"] - & "ancient.kitenet.net" - `cnameFor` Docker.docked container + + & cname "ancient.kitenet.net" + & Docker.docked hosts "ancient-kitenet" + & Docker.garbageCollected `period` Daily & Apt.installed ["git-annex", "mtr", "screen"] + -- Orca is the main git-annex build box. , host "orca.kitenet.net" & standardSystem Unstable & Hostname.sane & Apt.unattendedUpgrades & Docker.configured - & Docker.docked container "amd64-git-annex-builder" - & Docker.docked container "i386-git-annex-builder" - ! Docker.docked container "armel-git-annex-builder-companion" - ! Docker.docked container "armel-git-annex-builder" + & Docker.docked hosts "amd64-git-annex-builder" + & Docker.docked hosts "i386-git-annex-builder" + ! Docker.docked hosts "armel-git-annex-builder-companion" + ! Docker.docked hosts "armel-git-annex-builder" & Docker.garbageCollected `period` Daily & Apt.buildDep ["git-annex"] `period` Daily + -- Important stuff that needs not too much memory or CPU. , host "diatom.kitenet.net" & standardSystem Stable @@ -71,83 +82,60 @@ hosts = -- ssh keys for branchable and github repo hooks -- gitweb -- downloads.kitenet.net setup (including ssh key to turtle) - -- My laptop - , host "darkstar.kitenet.net" - & Docker.configured - & Apt.buildDep ["git-annex"] `period` Daily - ] --- | This is where Docker containers are set up. A container --- can vary by hostname where it's used, or be the same everywhere. -container :: HostName -> Docker.ContainerName -> Maybe (Docker.Container) -container _parenthost name -{- + -------------------------------------------------------------------- + -- Docker Containers ----------------------------------- \o/ ----- + -------------------------------------------------------------------- + -- Simple web server, publishing the outside host's /var/www - | name == "webserver" = Just $ standardContainer Stable "amd64" - [ Docker.publish "8080:80" - , Docker.volume "/var/www:/var/www" - , Docker.inside $ props - & Apt.serviceInstalledRunning "apache2" - ] + , standardContainer "webserver" Stable "amd64" + & Docker.publish "8080:80" + & Docker.volume "/var/www:/var/www" + & Apt.serviceInstalledRunning "apache2" -- My own openid provider. Uses php, so containerized for security -- and administrative sanity. - | name == "openid-provider" = Just $ standardContainer Stable "amd64" - [ Docker.publish "8081:80" - , Docker.inside $ props - & OpenId.providerFor ["joey", "liw"] - "openid.kitenet.net:8081" - ] + , standardContainer "openid-provider" Stable "amd64" + & Docker.publish "8081:80" + & OpenId.providerFor ["joey", "liw"] + "openid.kitenet.net:8081" - | name == "ancient-kitenet" = Just $ standardContainer Stable "amd64" - [ Docker.publish "1994:80" - , Docker.inside $ props - & Apt.serviceInstalledRunning "apache2" - & Apt.installed ["git"] - & scriptProperty - [ "cd /var/" - , "rm -rf www" - , "git clone git://git.kitenet.net/kitewiki www" - , "cd www" - , "git checkout remotes/origin/old-kitenet.net" - ] `flagFile` "/var/www/blastfromthepast.html" - ] + , standardContainer "ancient-kitenet" Stable "amd64" + & Docker.publish "1994:80" + & Apt.serviceInstalledRunning "apache2" + & Apt.installed ["git"] + & scriptProperty + [ "cd /var/" + , "rm -rf www" + , "git clone git://git.kitenet.net/kitewiki www" + , "cd www" + , "git checkout remotes/origin/old-kitenet.net" + ] `flagFile` "/var/www/blastfromthepast.html" + -- git-annex autobuilder containers + , gitAnnexBuilder "amd64" 15 + , gitAnnexBuilder "i386" 45 -- armel builder has a companion container that run amd64 and -- runs the build first to get TH splices. They share a home -- directory, and need to have the same versions of all haskell -- libraries installed. - | name == "armel-git-annex-builder-companion" = Just $ Docker.containerFrom + , Docker.container "armel-git-annex-builder-companion" (image $ System (Debian Unstable) "amd64") - [ Docker.volume GitAnnexBuilder.homedir - , Docker.inside $ props - & Apt.unattendedUpgrades - ] - | name == "armel-git-annex-builder" = Just $ Docker.containerFrom + & Docker.volume GitAnnexBuilder.homedir + & Apt.unattendedUpgrades + , Docker.container "armel-git-annex-builder" (image $ System (Debian Unstable) "armel") - [ Docker.link (name ++ "-companion") "companion" - , Docker.volumes_from (name ++ "-companion") - , Docker.inside $ props --- & GitAnnexBuilder.builder "armel" "15 * * * *" True - & Apt.unattendedUpgrades - ] - - | "-git-annex-builder" `isSuffixOf` name = - let arch = takeWhile (/= '-') name - in Just $ Docker.containerFrom - (image $ System (Debian Unstable) arch) - [ Docker.inside $ props - & GitAnnexBuilder.builder arch "15 * * * *" True - & Apt.unattendedUpgrades - ] --} - | otherwise = Nothing + & Docker.link "armel-git-annex-builder-companion" "companion" + & Docker.volumes_from "armel-git-annex-builder-companion" +-- & GitAnnexBuilder.builder "armel" "15 * * * *" True + & Apt.unattendedUpgrades + ] --- | Docker images I prefer to use. -image :: System -> Docker.Image -image (System (Debian Unstable) arch) = "joeyh/debian-unstable-" ++ arch -image (System (Debian Stable) arch) = "joeyh/debian-stable-" ++ arch -image _ = "debian-stable-official" -- does not currently exist! +gitAnnexBuilder :: Architecture -> Int -> Host +gitAnnexBuilder arch buildminute = Docker.container (arch ++ "-git-annex-builder") + (image $ System (Debian Unstable) arch) + & GitAnnexBuilder.builder arch (show buildminute ++ " * * * *") True + & Apt.unattendedUpgrades -- This is my standard system setup standardSystem :: DebianSuite -> Property @@ -171,16 +159,19 @@ standardSystem suite = template "standard system" $ props & Apt.removed ["exim4", "exim4-daemon-light", "exim4-config", "exim4-base"] `onChange` Apt.autoRemove -{- -- This is my standard container setup, featuring automatic upgrades. -standardContainer :: DebianSuite -> Architecture -> [Docker.Containerized Property] -> Docker.Container -standardContainer suite arch ps = Docker.containerFrom - (image $ System (Debian suite) arch) $ - [ Docker.inside $ props - & Apt.stdSourcesList suite - & Apt.unattendedUpgrades - ] ++ ps --} +standardContainer :: Docker.ContainerName -> DebianSuite -> Architecture -> Host +standardContainer name suite arch = Docker.container name (image system) + & Apt.stdSourcesList suite + & Apt.unattendedUpgrades + where + system = System (Debian suite) arch + +-- | Docker images I prefer to use. +image :: System -> Docker.Image +image (System (Debian Unstable) arch) = "joeyh/debian-unstable-" ++ arch +image (System (Debian Stable) arch) = "joeyh/debian-stable-" ++ arch +image _ = "debian-stable-official" -- does not currently exist! -- Clean up a system as installed by cloudatcost.com cleanCloudAtCost :: Property diff --git a/config-simple.hs b/config-simple.hs index 8011e97..23a760c 100644 --- a/config-simple.hs +++ b/config-simple.hs @@ -16,38 +16,32 @@ import qualified Propellor.Property.User as User --import qualified Propellor.Property.Tor as Tor import qualified Propellor.Property.Docker as Docker -main :: IO () -main = defaultMain [host, Docker.containerProperties container] - --- | This is where the system's HostName, either as returned by uname --- or one specified on the command line, is converted into a list of --- Properties for that system. --- +-- The hosts propellor knows about. -- Edit this to configure propellor! -host :: HostName -> Maybe [Property] -host "mybox.example.com" = Just $ props - & Apt.stdSourcesList Unstable - `onChange` Apt.upgrade - & Apt.unattendedUpgrades - & Apt.installed ["etckeeper"] - & Apt.installed ["ssh"] - & User.hasSomePassword "root" - & Network.ipv6to4 - & File.dirExists "/var/www" - & Docker.docked container "webserver" - & Docker.garbageCollected `period` Daily - & Cron.runPropellor "30 * * * *" --- add more hosts here... ---host "foo.example.com" = -host _ = Nothing +hosts :: [Host] +hosts = + [ host "mybox.example.com" + & Apt.stdSourcesList Unstable + `onChange` Apt.upgrade + & Apt.unattendedUpgrades + & Apt.installed ["etckeeper"] + & Apt.installed ["ssh"] + & User.hasSomePassword "root" + & Network.ipv6to4 + & File.dirExists "/var/www" + & Docker.docked hosts "webserver" + & Docker.garbageCollected `period` Daily + & Cron.runPropellor "30 * * * *" --- | This is where Docker containers are set up. A container --- can vary by hostname where it's used, or be the same everywhere. -container :: HostName -> Docker.ContainerName -> Maybe (Docker.Container) -container _ "webserver" = Just $ Docker.containerFrom "joeyh/debian-unstable" - [ Docker.publish "80:80" - , Docker.volume "/var/www:/var/www" - , Docker.inside $ props + -- A generic webserver in a Docker container. + , Docker.container "webserver" "joeyh/debian-unstable" + & Docker.publish "80:80" + & Docker.volume "/var/www:/var/www" & Apt.serviceInstalledRunning "apache2" + + -- add more hosts here... + --, host "foo.example.com" = ... ] -container _ _ = Nothing + +main :: IO () +main = defaultMain hosts From 987265a91fe2103250fe9fd32aa82d416ec41936 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 23:21:18 -0400 Subject: [PATCH 74/88] propellor spin From 95fd1abad7d7e175e39bb1f59af12dc984f1d5fd Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 23:23:26 -0400 Subject: [PATCH 75/88] propellor spin From 28f74eead07f0034b011dc6df59a483b6a1aeda3 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Thu, 10 Apr 2014 23:26:05 -0400 Subject: [PATCH 76/88] propellor spin --- config-joey.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config-joey.hs b/config-joey.hs index 093ed8a..a5d0a47 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -27,6 +27,9 @@ hosts = [ host "darkstar.kitenet.net" & Docker.configured & Apt.buildDep ["git-annex"] `period` Daily + & propertyList "foo" + [ Hostname.sane + ] -- Nothing super-important lives here. , host "clam.kitenet.net" From 366fa574bf9174f4cd4b9bf67fc2fb28d107dbef Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:06:11 -0400 Subject: [PATCH 77/88] propellor spin --- config-joey.hs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index a5d0a47..4e17597 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -27,9 +27,6 @@ hosts = [ host "darkstar.kitenet.net" & Docker.configured & Apt.buildDep ["git-annex"] `period` Daily - & propertyList "foo" - [ Hostname.sane - ] -- Nothing super-important lives here. , host "clam.kitenet.net" @@ -179,8 +176,8 @@ image _ = "debian-stable-official" -- does not currently exist! -- Clean up a system as installed by cloudatcost.com cleanCloudAtCost :: Property cleanCloudAtCost = propertyList "cloudatcost cleanup" - [ Hostname.sane - , Ssh.uniqueHostKeys + -- [ Hostname.sane + [ Ssh.uniqueHostKeys , "worked around grub/lvm boot bug #743126" ==> "/etc/default/grub" `File.containsLine` "GRUB_DISABLE_LINUX_UUID=true" `onChange` cmdProperty "update-grub" [] From 64e6dac9964f5a3ffca75efac273ea23f9c6c8cd Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:07:47 -0400 Subject: [PATCH 78/88] propellor spin --- config-joey.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index 4e17597..21c0fc6 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -40,13 +40,13 @@ hosts = & JoeySites.oldUseNetShellBox & cname "openid.kitenet.net" - & Docker.docked hosts "openid-provider" - `requires` Apt.installed ["ntp"] + -- & Docker.docked hosts "openid-provider" + -- `requires` Apt.installed ["ntp"] & cname "ancient.kitenet.net" - & Docker.docked hosts "ancient-kitenet" + -- & Docker.docked hosts "ancient-kitenet" - & Docker.garbageCollected `period` Daily + -- & Docker.garbageCollected `period` Daily & Apt.installed ["git-annex", "mtr", "screen"] -- Orca is the main git-annex build box. @@ -176,8 +176,8 @@ image _ = "debian-stable-official" -- does not currently exist! -- Clean up a system as installed by cloudatcost.com cleanCloudAtCost :: Property cleanCloudAtCost = propertyList "cloudatcost cleanup" - -- [ Hostname.sane - [ Ssh.uniqueHostKeys + [ Hostname.sane + , Ssh.uniqueHostKeys , "worked around grub/lvm boot bug #743126" ==> "/etc/default/grub" `File.containsLine` "GRUB_DISABLE_LINUX_UUID=true" `onChange` cmdProperty "update-grub" [] From fd79ec71db33b3d16e4b07fba0a0d05698f79194 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:08:26 -0400 Subject: [PATCH 79/88] propellor spin --- config-joey.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index 21c0fc6..5a1e27e 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -30,8 +30,8 @@ hosts = -- Nothing super-important lives here. , host "clam.kitenet.net" - & cleanCloudAtCost - & standardSystem Unstable + -- & cleanCloudAtCost + -- & standardSystem Unstable & Apt.unattendedUpgrades & Network.ipv6to4 & Tor.isBridge From 8e96136eadd6a46462b46d09f467cf6ecb11cb68 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:08:53 -0400 Subject: [PATCH 80/88] propellor spin --- config-joey.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-joey.hs b/config-joey.hs index 5a1e27e..0e8c9a7 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -31,7 +31,7 @@ hosts = -- Nothing super-important lives here. , host "clam.kitenet.net" -- & cleanCloudAtCost - -- & standardSystem Unstable + & standardSystem Unstable & Apt.unattendedUpgrades & Network.ipv6to4 & Tor.isBridge From 4c4f702cff18ad51dd3e9c3af4604d896aa1d7ce Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:14:50 -0400 Subject: [PATCH 81/88] propellor spin --- Propellor/Property.hs | 20 -------------------- config-joey.hs | 25 +++++++++++-------------- 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/Propellor/Property.hs b/Propellor/Property.hs index ccc060f..e334bfb 100644 --- a/Propellor/Property.hs +++ b/Propellor/Property.hs @@ -119,23 +119,3 @@ infixl 1 & q = revert p infixl 1 ! - --- | Makes a propertyList of a set of properties, using the same syntax --- used by `host`. --- --- > template "my template" $ props --- & someproperty --- ! oldproperty --- --- Note that none of the properties can define Attrs, because --- they will not propigate out to the host that this is added to. --- --- Unfortunately, this is not currently enforced at the type level, so --- attempting to set an Attr in here will be run time error. -template :: Desc -> Host -> Property -template desc h@(Host ps _) - | hostAttr h == hostAttr props = propertyList desc ps - | otherwise = error $ desc ++ ": template contains Attr" - -props :: Host -props = Host [] (\_ -> hostnameless) diff --git a/config-joey.hs b/config-joey.hs index 0e8c9a7..cd0583f 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -29,9 +29,8 @@ hosts = & Apt.buildDep ["git-annex"] `period` Daily -- Nothing super-important lives here. - , host "clam.kitenet.net" - -- & cleanCloudAtCost - & standardSystem Unstable + , standardSystem "clam.kitenet.net" Unstable + & cleanCloudAtCost & Apt.unattendedUpgrades & Network.ipv6to4 & Tor.isBridge @@ -40,18 +39,17 @@ hosts = & JoeySites.oldUseNetShellBox & cname "openid.kitenet.net" - -- & Docker.docked hosts "openid-provider" - -- `requires` Apt.installed ["ntp"] + & Docker.docked hosts "openid-provider" + `requires` Apt.installed ["ntp"] & cname "ancient.kitenet.net" - -- & Docker.docked hosts "ancient-kitenet" + & Docker.docked hosts "ancient-kitenet" - -- & Docker.garbageCollected `period` Daily + & Docker.garbageCollected `period` Daily & Apt.installed ["git-annex", "mtr", "screen"] -- Orca is the main git-annex build box. - , host "orca.kitenet.net" - & standardSystem Unstable + , standardSystem "orca.kitenet.net" Unstable & Hostname.sane & Apt.unattendedUpgrades & Docker.configured @@ -63,8 +61,7 @@ hosts = & Apt.buildDep ["git-annex"] `period` Daily -- Important stuff that needs not too much memory or CPU. - , host "diatom.kitenet.net" - & standardSystem Stable + , standardSystem "diatom.kitenet.net" Stable & Hostname.sane & Apt.unattendedUpgrades & Apt.serviceInstalledRunning "ntp" @@ -137,9 +134,9 @@ gitAnnexBuilder arch buildminute = Docker.container (arch ++ "-git-annex-builder & GitAnnexBuilder.builder arch (show buildminute ++ " * * * *") True & Apt.unattendedUpgrades --- This is my standard system setup -standardSystem :: DebianSuite -> Property -standardSystem suite = template "standard system" $ props +-- This is my standard system setup. +standardSystem :: HostName -> DebianSuite -> Host +standardSystem hn suite = host hn & Apt.stdSourcesList suite `onChange` Apt.upgrade & Apt.installed ["etckeeper"] & Apt.installed ["ssh"] From 29ae27af3c59a221fa60995f435ca2e4a5c6d76e Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:35:48 -0400 Subject: [PATCH 82/88] fix attr combination --- Propellor/Property.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Propellor/Property.hs b/Propellor/Property.hs index e334bfb..3a3c1cb 100644 --- a/Propellor/Property.hs +++ b/Propellor/Property.hs @@ -10,7 +10,6 @@ import "mtl" Control.Monad.Reader import Propellor.Types import Propellor.Types.Attr -import Propellor.Attr import Propellor.Engine import Utility.Monad @@ -108,13 +107,13 @@ host hn = Host [] (\_ -> newAttr hn) -- | Adds a property to a Host -- Can add Properties, RevertableProperties, and AttrProperties (&) :: IsProp p => Host -> p -> Host -(Host ps as) & p = Host (ps ++ [toProp p]) (as . getAttr p) +(Host ps as) & p = Host (ps ++ [toProp p]) (getAttr p . as) infixl 1 & -- | Adds a property to the Host in reverted form. (!) :: Host -> RevertableProperty -> Host -(Host ps as) ! p = Host (ps ++ [toProp q]) (as . getAttr q) +(Host ps as) ! p = Host (ps ++ [toProp q]) (getAttr q . as) where q = revert p From 724f354b25275b59546c41201178380eb9369037 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:36:33 -0400 Subject: [PATCH 83/88] show instance --- Propellor/Types/Attr.hs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Propellor/Types/Attr.hs b/Propellor/Types/Attr.hs index 7016172..9e8058f 100644 --- a/Propellor/Types/Attr.hs +++ b/Propellor/Types/Attr.hs @@ -21,6 +21,14 @@ instance Eq Attr where in simpl x == simpl y ] +instance Show Attr where + show a = unlines + [ "hostname " ++ _hostname a + , "cnames " ++ show (_cnames a) + , "docker image " ++ show (_dockerImage a) + , "docker run params " ++ show (map (\a -> a "") (_dockerRunParams a)) + ] + newAttr :: HostName -> Attr newAttr hn = Attr hn S.empty Nothing [] From c1a54465c65aad4f0425493ef071169b2344e785 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:37:14 -0400 Subject: [PATCH 84/88] propellor spin From f5b27f4fa38d455a335dfb864ddc33e28c52dbc9 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:40:32 -0400 Subject: [PATCH 85/88] propellor spin --- Propellor/Types/Attr.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/Types/Attr.hs b/Propellor/Types/Attr.hs index 9e8058f..c253e32 100644 --- a/Propellor/Types/Attr.hs +++ b/Propellor/Types/Attr.hs @@ -26,7 +26,7 @@ instance Show Attr where [ "hostname " ++ _hostname a , "cnames " ++ show (_cnames a) , "docker image " ++ show (_dockerImage a) - , "docker run params " ++ show (map (\a -> a "") (_dockerRunParams a)) + , "docker run params " ++ show (map (\mk -> mk "") (_dockerRunParams a)) ] newAttr :: HostName -> Attr From 0274aec9491a4bacfc15ec302c9280a0d88046a1 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:48:37 -0400 Subject: [PATCH 86/88] propellor spin --- Propellor/Property/Docker.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Propellor/Property/Docker.hs b/Propellor/Property/Docker.hs index edf12c2e..b75b2bf 100644 --- a/Propellor/Property/Docker.hs +++ b/Propellor/Property/Docker.hs @@ -218,6 +218,9 @@ toContainerId s fromContainerId :: ContainerId -> String fromContainerId (ContainerId hn cn) = cn++"."++hn++myContainerSuffix +containerHostName :: ContainerId -> HostName +containerHostName (ContainerId _ cn) = cn + myContainerSuffix :: String myContainerSuffix = ".propellor" @@ -299,7 +302,7 @@ chain s = case toContainerId s of -- to avoid ever provisioning twice at the same time. whenM (checkProvisionedFlag cid) $ do let shim = Shim.file (localdir "propellor") (localdir shimdir cid) - unlessM (boolSystem shim [Param "--continue", Param $ show $ Chain $ fromContainerId cid]) $ + unlessM (boolSystem shim [Param "--continue", Param $ show $ Chain $ containerHostName cid]) $ warningMessage "Boot provision failed!" void $ async $ job reapzombies void $ async $ job $ simpleSh $ namedPipe cid @@ -328,7 +331,7 @@ provisionContainer cid = containerDesc cid $ Property "provision" $ liftIO $ do setProvisionedFlag cid return r where - params = ["--continue", show $ Chain $ fromContainerId cid] + params = ["--continue", show $ Chain $ containerHostName cid] go lastline (v:rest) = case v of StdoutLine s -> do From 9a32955f0ca9a09eb0375bf1f8bf624308bdab40 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:51:21 -0400 Subject: [PATCH 87/88] propellor spin --- Propellor/Property/Docker.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Propellor/Property/Docker.hs b/Propellor/Property/Docker.hs index b75b2bf..d2555ea 100644 --- a/Propellor/Property/Docker.hs +++ b/Propellor/Property/Docker.hs @@ -219,7 +219,7 @@ fromContainerId :: ContainerId -> String fromContainerId (ContainerId hn cn) = cn++"."++hn++myContainerSuffix containerHostName :: ContainerId -> HostName -containerHostName (ContainerId _ cn) = cn +containerHostName (ContainerId _ cn) = cn2hn cn myContainerSuffix :: String myContainerSuffix = ".propellor" From 47ff089f844c707eaa3ffd7255dc733721fb6adf Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 11 Apr 2014 00:58:00 -0400 Subject: [PATCH 88/88] propellor spin