Merge branch 'joeyconfig'

This commit is contained in:
Joey Hess 2014-06-05 16:52:45 -04:00
commit f8bad27267
15 changed files with 620 additions and 423 deletions

View File

@ -37,8 +37,9 @@ hosts = -- (o) `
-- My laptop -- My laptop
[ host "darkstar.kitenet.net" [ host "darkstar.kitenet.net"
& ipv6 "2001:4830:1600:187::2" -- sixxs tunnel & ipv6 "2001:4830:1600:187::2" -- sixxs tunnel
& Docker.configured
& Apt.buildDep ["git-annex"] `period` Daily & Apt.buildDep ["git-annex"] `period` Daily
& Docker.configured
& Docker.docked hosts "android-git-annex" & Docker.docked hosts "android-git-annex"
-- Nothing super-important lives here and mostly it's docker containers. -- Nothing super-important lives here and mostly it's docker containers.
@ -162,6 +163,24 @@ hosts = -- (o) `
& Dns.secondaryFor ["animx"] hosts "animx.eu.org" & Dns.secondaryFor ["animx"] hosts "animx.eu.org"
-- storage and backup server
, standardSystem "elephant.kitenet.net" Unstable "amd64"
& ipv4 "193.234.225.114"
& Hostname.sane
& Postfix.satellite
& Apt.unattendedUpgrades
& alias "eubackup.kitenet.net"
& Apt.installed ["obnam", "sshfs", "rsync"]
& JoeySites.githubBackup
& alias "podcatcher.kitenet.net"
& Apt.installed ["git-annex"]
& Docker.configured
! Docker.docked hosts "voltagex"
& Docker.garbageCollected `period` (Weekly (Just 1))
--' __|II| ,. --' __|II| ,.
---- __|II|II|__ ( \_,/\ ---- __|II|II|__ ( \_,/\
@ -210,6 +229,17 @@ hosts = -- (o) `
, let gitannexdir = GitAnnexBuilder.homedir </> "git-annex" , let gitannexdir = GitAnnexBuilder.homedir </> "git-annex"
in GitAnnexBuilder.androidContainer dockerImage "android-git-annex" doNothing gitannexdir in GitAnnexBuilder.androidContainer dockerImage "android-git-annex" doNothing gitannexdir
& Docker.volume ("/home/joey/src/git-annex:" ++ gitannexdir) & Docker.volume ("/home/joey/src/git-annex:" ++ gitannexdir)
-- temp for an acquantance
, standardContainer "voltagex" Stable "amd64"
& Docker.publish "22022:22"
& Docker.memory "500m"
& Docker.cpuShares 1
& Apt.serviceInstalledRunning "ssh"
& Ssh.permitRootLogin True
& Ssh.passwordAuthentication True
& User.hasSomePassword "root"
] ++ monsters ] ++ monsters
-- This is my standard system setup. -- This is my standard system setup.
@ -218,6 +248,7 @@ standardSystem hn suite arch = host hn
& os (System (Debian suite) arch) & os (System (Debian suite) arch)
& Apt.stdSourcesList suite & Apt.stdSourcesList suite
`onChange` Apt.upgrade `onChange` Apt.upgrade
& Apt.cacheCleaned
& Apt.installed ["etckeeper"] & Apt.installed ["etckeeper"]
& Apt.installed ["ssh"] & Apt.installed ["ssh"]
& GitHome.installedFor "root" & GitHome.installedFor "root"
@ -241,7 +272,9 @@ standardContainer :: Docker.ContainerName -> DebianSuite -> Architecture -> Host
standardContainer name suite arch = Docker.container name (dockerImage system) standardContainer name suite arch = Docker.container name (dockerImage system)
& os (System (Debian suite) arch) & os (System (Debian suite) arch)
& Apt.stdSourcesList suite & Apt.stdSourcesList suite
& Apt.installed ["systemd"]
& Apt.unattendedUpgrades & Apt.unattendedUpgrades
& Apt.cacheCleaned
where where
system = System (Debian suite) arch system = System (Debian suite) arch

4
debian/changelog vendored
View File

@ -2,10 +2,12 @@ propellor (0.6.0) UNRELEASED; urgency=medium
* Docker containers now propigate DNS attributes out to the host they're * Docker containers now propigate DNS attributes out to the host they're
docked in. So if a docker container sets a DNS alias, every container docked in. So if a docker container sets a DNS alias, every container
it's docked in will automatically become part of a round-robin DNS, it's docked in will automatically be added to a DNS round-robin,
if propellor is used to manage DNS for the domain. if propellor is used to manage DNS for the domain.
* Propellor's output now includes the hostname being provisioned, or * Propellor's output now includes the hostname being provisioned, or
when provisioning a docker container, the container name. when provisioning a docker container, the container name.
* Added --dump to dump out a field of a host's privdata. Useful for editing
it.
-- Joey Hess <joeyh@debian.org> Sat, 31 May 2014 16:41:56 -0400 -- Joey Hess <joeyh@debian.org> Sat, 31 May 2014 16:41:56 -0400

View File

@ -1,366 +1,372 @@
-----BEGIN PGP MESSAGE----- -----BEGIN PGP MESSAGE-----
Version: GnuPG v1 Version: GnuPG v1
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
=RrAw WmXqwysDRBaokNuT1s2JcPI1W42Id3sNDfUVSbRcASLbRqudZFT5xdAIiqzhV73y
tjkL31tNg+CmiX6heKIhB2zFFWFTIGkWaCaemOeIPZ1LT7gN+RVfLOxtkCz25s6U
Dfk4pqsolcnZxw9DirkB+4Gc4iNA1FxAgF3XyKU4VhBB0aC/k6BXwGZkmyfPMObg
Ezr6ECjHe3+5h8uAd3nLiMqLJszqzL8o902zwHN5uS8leFZtLmpWqSOAacQ+loGm
GJp5wRXZMT5eJZlkdfOM29d6fnB0XTTsnNF5q46EyJ8tcdjDv0GxCn6pNunmsKsE
uHj0ZyJrX3IzUToU+/qSYA53iBg6mX7EsHM=
=IsJI
-----END PGP MESSAGE----- -----END PGP MESSAGE-----

View File

@ -0,0 +1,22 @@
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1
hQIMA7ODiaEXBlRZAQ//SW/xJB2ymi3hEqTbJRCtmU2cbMW/mk/Mb/ZW3P0DaARv
beC10xd1fgFUCNt40JrQ5HGeq5ckG/GWEjT2J1IP3oDTPPoK40qqLYZfHYLyAFok
N59z2rh2FDwsnd7ordI6SAfuyd5Z5v+SF800tIjOuy5Byjw8n3QkrpnLdXhBZU7k
8EP94+/z1vWOkumVqiFRarqbgBwoAbVv0dnp5/CHqGPl83P/JSGrjMMxbcaZ/gfA
ACbX/jq7nHwhYRRkDAsHKZ2IqD48r14ddtoLdXhATUDJBFUEEGoAVOvbCigLzjQE
yXeQ7H3kZ7yVy6iWhaRyKtsCy3rfApzbjleYTisT9cLlVUC0z+kitgcIpcWKF286
ezWaM8qcyuXXsDmr0e0QLAN2gbJMNrM2D2mgULMKnYT2h72/raQOiUOxgWJgyo1U
ybzEVRmF9YWQXrzZI66FEK7e94IPK68JMNEFVc3brrhNi6zNfpTck5Ut5R9pa41L
u8oIqOZ11GTFhZ2COhlAV+Ud5ihXoGKa4HtqsJFrOsonNz9i1J2kw+2vhsZ2lG7X
ILVeFmDnWNfOv1ONe/eO4ZfRO7BdA3EovYCr8CF5zN9WI5y09DWnHg+3Qm90rHMo
ytZoevIpNpkHN+g0u+rNs2qIm4F2E2oI9IwjrI2oo/0tZbwfclICu4ed6e5oqu3S
wFcBmuO0P9zst5bmgozQaMUKgTKidKDtg8G33AqICRttwDr++JR0mCTKB8fW7KBu
R07VTpxd5RcXww8OFZYjf7UBNO1z98prBiJC+gUP4jccZD8zeahxqZ908CHMx5sf
Pb2VGT7X/wZExy6Ek9GRSpA0gGGCWDc9ITmRVHvXWvbxP0F/zzuYhggxJFGoSgw/
7fWq05dfI8pKaOeBlzF/XsrtrLJO3yZVEw17eodxa/KEkAbP+Ze3Qfmurg0UMbut
2y0GsYzpSpzWGoBtYigXtVYDveeohzPu8jL01NbfmkNDp6FSjkm31E4sV7ipe5M7
cvzDXh7in4RBO1znFwzGccDeNT2d8t9Pf+up9wxs7+EvsKwA2hRBLfg=
=AdUG
-----END PGP MESSAGE-----

View File

@ -15,12 +15,15 @@ import Control.Applicative
pureAttrProperty :: Desc -> Attr -> Property pureAttrProperty :: Desc -> Attr -> Property
pureAttrProperty desc = Property ("has " ++ desc) (return NoChange) pureAttrProperty desc = Property ("has " ++ desc) (return NoChange)
askAttr :: (Attr -> Val a) -> Propellor (Maybe a)
askAttr f = asks (fromVal . f . hostAttr)
os :: System -> Property os :: System -> Property
os system = pureAttrProperty ("Operating " ++ show system) $ os system = pureAttrProperty ("Operating " ++ show system) $
mempty { _os = Just system } mempty { _os = Val system }
getOS :: Propellor (Maybe System) getOS :: Propellor (Maybe System)
getOS = asks (_os . hostAttr) getOS = askAttr _os
-- | Indidate that a host has an A record in the DNS. -- | Indidate that a host has an A record in the DNS.
-- --
@ -34,6 +37,11 @@ ipv6 :: String -> Property
ipv6 = addDNS . Address . IPv6 ipv6 = addDNS . Address . IPv6
-- | Indicates another name for the host in the DNS. -- | Indicates another name for the host in the DNS.
--
-- When the host's ipv4/ipv6 addresses are known, the alias is set up
-- to use their address, rather than using a CNAME. This avoids various
-- problems with CNAMEs, and also means that when multiple hosts have the
-- same alias, a DNS round-robin is automatically set up.
alias :: Domain -> Property alias :: Domain -> Property
alias = addDNS . CNAME . AbsDomain alias = addDNS . CNAME . AbsDomain
@ -55,10 +63,10 @@ addDNS r = pureAttrProperty (rdesc r) $
sshPubKey :: String -> Property sshPubKey :: String -> Property
sshPubKey k = pureAttrProperty ("ssh pubkey known") $ sshPubKey k = pureAttrProperty ("ssh pubkey known") $
mempty { _sshPubKey = Just k } mempty { _sshPubKey = Val k }
getSshPubKey :: Propellor (Maybe String) getSshPubKey :: Propellor (Maybe String)
getSshPubKey = asks (_sshPubKey . hostAttr) getSshPubKey = askAttr _sshPubKey
hostMap :: [Host] -> M.Map HostName Host hostMap :: [Host] -> M.Map HostName Host
hostMap l = M.fromList $ zip (map hostName l) l hostMap l = M.fromList $ zip (map hostName l) l

View File

@ -27,6 +27,7 @@ usage = do
, " propellor hostname" , " propellor hostname"
, " propellor --spin hostname" , " propellor --spin hostname"
, " propellor --set hostname field" , " propellor --set hostname field"
, " propellor --dump hostname field"
, " propellor --add-key keyid" , " propellor --add-key keyid"
] ]
exitFailure exitFailure
@ -38,9 +39,8 @@ processCmdLine = go =<< getArgs
go ("--spin":h:[]) = return $ Spin h go ("--spin":h:[]) = return $ Spin h
go ("--boot":h:[]) = return $ Boot h go ("--boot":h:[]) = return $ Boot h
go ("--add-key":k:[]) = return $ AddKey k go ("--add-key":k:[]) = return $ AddKey k
go ("--set":h:f:[]) = case readish f of go ("--set":h:f:[]) = withprivfield f (return . Set h)
Just pf -> return $ Set h pf go ("--dump":h:f:[]) = withprivfield f (return . Dump h)
Nothing -> errorMessage $ "Unknown privdata field " ++ f
go ("--continue":s:[]) = case readish s of go ("--continue":s:[]) = case readish s of
Just cmdline -> return $ Continue cmdline Just cmdline -> return $ Continue cmdline
Nothing -> errorMessage "--continue serialization failure" Nothing -> errorMessage "--continue serialization failure"
@ -56,6 +56,10 @@ processCmdLine = go =<< getArgs
else return $ Run s else return $ Run s
go _ = usage go _ = usage
withprivfield s f = case readish s of
Just pf -> f pf
Nothing -> errorMessage $ "Unknown privdata field " ++ s
defaultMain :: [Host] -> IO () defaultMain :: [Host] -> IO ()
defaultMain hostlist = do defaultMain hostlist = do
DockerShim.cleanEnv DockerShim.cleanEnv
@ -66,6 +70,7 @@ defaultMain hostlist = do
where where
go _ (Continue cmdline) = go False cmdline go _ (Continue cmdline) = go False cmdline
go _ (Set hn field) = setPrivData hn field go _ (Set hn field) = setPrivData hn field
go _ (Dump hn field) = dumpPrivData hn field
go _ (AddKey keyid) = addKey keyid go _ (AddKey keyid) = addKey keyid
go _ (Chain hn) = withhost hn $ \h -> do go _ (Chain hn) = withhost hn $ \h -> do
r <- runPropellor h $ ensureProperties $ hostProperties h r <- runPropellor h $ ensureProperties $ hostProperties h

View File

@ -49,7 +49,7 @@ setPrivData host field = do
value <- chomp <$> hGetContentsStrict stdin value <- chomp <$> hGetContentsStrict stdin
makePrivDataDir makePrivDataDir
let f = privDataFile host let f = privDataFile host
m <- fromMaybe M.empty . readish <$> gpgDecrypt f m <- decryptPrivData host
let m' = M.insert field value m let m' = M.insert field value m
gpgEncrypt f (show m') gpgEncrypt f (show m')
putStrLn "Private data set." putStrLn "Private data set."
@ -59,6 +59,16 @@ setPrivData host field = do
| end s == "\n" = chomp (beginning s) | end s == "\n" = chomp (beginning s)
| otherwise = s | otherwise = s
dumpPrivData :: HostName -> PrivDataField -> IO ()
dumpPrivData host field = go . M.lookup field =<< decryptPrivData host
where
go Nothing = error "Requested privdata is not set."
go (Just s) = putStrLn s
decryptPrivData :: HostName -> IO (M.Map PrivDataField String)
decryptPrivData host = fromMaybe M.empty . readish
<$> gpgDecrypt (privDataFile host)
makePrivDataDir :: IO () makePrivDataDir :: IO ()
makePrivDataDir = createDirectoryIfMissing False privDataDir makePrivDataDir = createDirectoryIfMissing False privDataDir

View File

@ -254,3 +254,9 @@ trustsKey k = RevertableProperty trust untrust
hPutStr h (pubkey k) hPutStr h (pubkey k)
hClose h hClose h
nukeFile $ f ++ "~" -- gpg dropping nukeFile $ f ++ "~" -- gpg dropping
-- | Cleans apt's cache of downloaded packages to avoid using up disk
-- space.
cacheCleaned :: Property
cacheCleaned = cmdProperty "apt-get" ["clean"]
`describe` "apt cache cleaned"

View File

@ -339,7 +339,7 @@ genZone hosts zdomain soa =
, map hostrecords inzdomain , map hostrecords inzdomain
, map addcnames (M.elems m) , map addcnames (M.elems m)
] ]
in (Zone zdomain soa (nub zhosts), warnings) in (Zone zdomain soa (simplify zhosts), warnings)
where where
m = hostMap hosts m = hostMap hosts
-- Known hosts with hostname located in the zone's domain. -- Known hosts with hostname located in the zone's domain.
@ -390,6 +390,17 @@ genZone hosts zdomain soa =
l = zip (repeat $ AbsDomain $ hostName h) l = zip (repeat $ AbsDomain $ hostName h)
(S.toList $ S.filter (\r -> isNothing (getIPAddr r) && isNothing (getCNAME r)) (_dns attr)) (S.toList $ S.filter (\r -> isNothing (getIPAddr r) && isNothing (getCNAME r)) (_dns attr))
-- Simplifies the list of hosts. Remove duplicate entries.
-- Also, filter out any CHAMES where the same domain has an
-- IP address, since that's not legal.
simplify :: [(BindDomain, Record)] -> [(BindDomain, Record)]
simplify l = nub $ filter (not . dupcname ) l
where
dupcname (d, CNAME _) | any (matchingaddr d) l = True
dupcname _ = False
matchingaddr d (d', (Address _)) | d == d' = True
matchingaddr _ _ = False
inDomain :: Domain -> BindDomain -> Bool inDomain :: Domain -> BindDomain -> Bool
inDomain domain (AbsDomain d) = domain == d || ('.':domain) `isSuffixOf` d inDomain domain (AbsDomain d) = domain == d || ('.':domain) `isSuffixOf` d
inDomain _ _ = False -- can't tell, so assume not inDomain _ _ = False -- can't tell, so assume not

View File

@ -5,7 +5,33 @@
-- The existance of a docker container is just another Property of a system, -- The existance of a docker container is just another Property of a system,
-- which propellor can set up. See config.hs for an example. -- which propellor can set up. See config.hs for an example.
module Propellor.Property.Docker where module Propellor.Property.Docker (
-- * Host properties
installed,
configured,
container,
docked,
memoryLimited,
garbageCollected,
Image,
ContainerName,
-- * Container configuration
dns,
hostname,
name,
publish,
expose,
user,
volume,
volumes_from,
workdir,
memory,
cpuShares,
link,
ContainerAlias,
-- * Internal use
chain,
) where
import Propellor import Propellor
import Propellor.SimpleSh import Propellor.SimpleSh
@ -16,24 +42,24 @@ import qualified Propellor.Property.Docker.Shim as Shim
import Utility.SafeCommand import Utility.SafeCommand
import Utility.Path import Utility.Path
import Control.Concurrent.Async import Control.Concurrent.Async hiding (link)
import System.Posix.Directory import System.Posix.Directory
import System.Posix.Process import System.Posix.Process
import Data.List import Data.List
import Data.List.Utils import Data.List.Utils
import qualified Data.Set as S import qualified Data.Set as S
installed :: Property
installed = Apt.installed ["docker.io"]
-- | Configures docker with an authentication file, so that images can be -- | Configures docker with an authentication file, so that images can be
-- pushed to index.docker.io. -- pushed to index.docker.io. Optional.
configured :: Property configured :: Property
configured = property "docker configured" go `requires` installed configured = property "docker configured" go `requires` installed
where where
go = withPrivData DockerAuthentication $ \cfg -> ensureProperty $ go = withPrivData DockerAuthentication $ \cfg -> ensureProperty $
"/root/.dockercfg" `File.hasContent` (lines cfg) "/root/.dockercfg" `File.hasContent` (lines cfg)
installed :: Property
installed = Apt.installed ["docker.io"]
-- | A short descriptive name for a container. -- | A short descriptive name for a container.
-- Should not contain whitespace or other unusual characters, -- Should not contain whitespace or other unusual characters,
-- only [a-zA-Z0-9_-] are allowed -- only [a-zA-Z0-9_-] are allowed
@ -48,15 +74,17 @@ type ContainerName = String
container :: ContainerName -> Image -> Host container :: ContainerName -> Image -> Host
container cn image = Host hn [] attr container cn image = Host hn [] attr
where where
attr = mempty { _dockerImage = Just image } attr = dockerAttr $ mempty { _dockerImage = Val image }
hn = cn2hn cn hn = cn2hn cn
cn2hn :: ContainerName -> HostName cn2hn :: ContainerName -> HostName
cn2hn cn = cn ++ ".docker" cn2hn cn = cn ++ ".docker"
-- | Ensures that a docker container is set up and running. The container -- | Ensures that a docker container is set up and running, finding
-- has its own Properties which are handled by running propellor -- its configuration in the passed list of hosts.
-- inside the container. --
-- The container has its own Properties which are handled by running
-- propellor inside the container.
-- --
-- Additionally, the container can have DNS attributes, such as a CNAME. -- Additionally, the container can have DNS attributes, such as a CNAME.
-- These become attributes of the host(s) it's docked in. -- These become attributes of the host(s) it's docked in.
@ -116,10 +144,10 @@ findContainer mhost cid cn mk = case mhost of
mkContainer :: ContainerId -> Host -> Maybe Container mkContainer :: ContainerId -> Host -> Maybe Container
mkContainer cid@(ContainerId hn _cn) h = Container mkContainer cid@(ContainerId hn _cn) h = Container
<$> _dockerImage attr <$> fromVal (_dockerImage attr)
<*> pure (map (\a -> a hn) (_dockerRunParams attr)) <*> pure (map (\a -> a hn) (_dockerRunParams attr))
where where
attr = hostAttr h' attr = _dockerattr $ hostAttr h'
h' = h h' = h
-- expose propellor directory inside the container -- expose propellor directory inside the container
& volume (localdir++":"++localdir) & volume (localdir++":"++localdir)
@ -144,6 +172,20 @@ garbageCollected = propertyList "docker garbage collected"
gcimages = property "docker images garbage collected" $ do gcimages = property "docker images garbage collected" $ do
liftIO $ report <$> (mapM removeImage =<< listImages) liftIO $ report <$> (mapM removeImage =<< listImages)
-- | Configures the kernel to respect docker memory limits.
--
-- This assumes the system boots using grub 2. And that you don't need any
-- other GRUB_CMDLINE_LINUX_DEFAULT settings.
--
-- Only takes effect after reboot. (Not automated.)
memoryLimited :: Property
memoryLimited = "/etc/default/grub" `File.containsLine` cfg
`describe` "docker memory limited"
`onChange` cmdProperty "update-grub" []
where
cmdline = "cgroup_enable=memory swapaccount=1"
cfg = "GRUB_CMDLINE_LINUX_DEFAULT=\""++cmdline++"\""
data Container = Container Image [RunParam] data Container = Container Image [RunParam]
-- | Parameters to pass to `docker run` when creating a container. -- | Parameters to pass to `docker run` when creating a container.
@ -194,10 +236,20 @@ workdir :: String -> Property
workdir = runProp "workdir" workdir = runProp "workdir"
-- | Memory limit for container. -- | Memory limit for container.
--Format: <number><optional unit>, where unit = b, k, m or g -- Format: <number><optional unit>, where unit = b, k, m or g
--
-- Note: Only takes effect when the host has the memoryLimited property
-- enabled.
memory :: String -> Property memory :: String -> Property
memory = runProp "memory" memory = runProp "memory"
-- | CPU shares (relative weight).
--
-- By default, all containers run at the same priority, but you can tell
-- the kernel to give more CPU time to a container using this property.
cpuShares :: Int -> Property
cpuShares = runProp "cpu-shares" . show
-- | Link with another container on the same host. -- | Link with another container on the same host.
link :: ContainerName -> ContainerAlias -> Property link :: ContainerName -> ContainerAlias -> Property
link linkwith calias = genProp "link" $ \hn -> link linkwith calias = genProp "link" $ \hn ->
@ -218,9 +270,6 @@ data ContainerId = ContainerId HostName ContainerName
data ContainerIdent = ContainerIdent Image HostName ContainerName [RunParam] data ContainerIdent = ContainerIdent Image HostName ContainerName [RunParam]
deriving (Read, Show, Eq) deriving (Read, Show, Eq)
ident2id :: ContainerIdent -> ContainerId
ident2id (ContainerIdent _ hn cn _) = ContainerId hn cn
toContainerId :: String -> Maybe ContainerId toContainerId :: String -> Maybe ContainerId
toContainerId s toContainerId s
| myContainerSuffix `isSuffixOf` s = case separate (== '.') (desuffix s) of | myContainerSuffix `isSuffixOf` s = case separate (== '.') (desuffix s) of
@ -420,15 +469,18 @@ listImages :: IO [Image]
listImages = lines <$> readProcess dockercmd ["images", "--all", "--quiet"] listImages = lines <$> readProcess dockercmd ["images", "--all", "--quiet"]
runProp :: String -> RunParam -> Property runProp :: String -> RunParam -> Property
runProp field val = pureAttrProperty (param) $ runProp field val = pureAttrProperty (param) $ dockerAttr $
mempty { _dockerRunParams = [\_ -> "--"++param] } mempty { _dockerRunParams = [\_ -> "--"++param] }
where where
param = field++"="++val param = field++"="++val
genProp :: String -> (HostName -> RunParam) -> Property genProp :: String -> (HostName -> RunParam) -> Property
genProp field mkval = pureAttrProperty field $ genProp field mkval = pureAttrProperty field $ dockerAttr $
mempty { _dockerRunParams = [\hn -> "--"++field++"=" ++ mkval hn] } mempty { _dockerRunParams = [\hn -> "--"++field++"=" ++ mkval hn] }
dockerAttr :: DockerAttr -> Attr
dockerAttr a = mempty { _dockerattr = a }
-- | The ContainerIdent of a container is written to -- | The ContainerIdent of a container is written to
-- /.propellor-ident inside it. This can be checked to see if -- /.propellor-ident inside it. This can be checked to see if
-- the container has the same ident later. -- the container has the same ident later.

View File

@ -38,8 +38,12 @@ data NumClients = OnlyClient | MultipleClients
-- --
-- How awesome is that? -- How awesome is that?
backup :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Property backup :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Property
backup dir crontimes params numclients = cronjob `describe` desc backup dir crontimes params numclients = backup' dir crontimes params numclients
`requires` restored dir params `requires` restored dir params
-- | Does a backup, but does not automatically restore.
backup' :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Property
backup' dir crontimes params numclients = cronjob `describe` desc
where where
desc = dir ++ " backed up by obnam" desc = dir ++ " backed up by obnam"
cronjob = Cron.niceJob ("obnam_backup" ++ dir) crontimes "root" "/" $ cronjob = Cron.niceJob ("obnam_backup" ++ dir) crontimes "root" "/" $

View File

@ -99,7 +99,9 @@ cabalDeps = flagFile go cabalupdated
standardAutoBuilderContainer :: (System -> Docker.Image) -> Architecture -> Int -> TimeOut -> Host standardAutoBuilderContainer :: (System -> Docker.Image) -> Architecture -> Int -> TimeOut -> Host
standardAutoBuilderContainer dockerImage arch buildminute timeout = Docker.container (arch ++ "-git-annex-builder") standardAutoBuilderContainer dockerImage arch buildminute timeout = Docker.container (arch ++ "-git-annex-builder")
(dockerImage $ System (Debian Unstable) arch) (dockerImage $ System (Debian Unstable) arch)
& os (System (Debian Unstable) arch)
& Apt.stdSourcesList Unstable & Apt.stdSourcesList Unstable
& Apt.installed ["systemd"]
& Apt.unattendedUpgrades & Apt.unattendedUpgrades
& buildDepsApt & buildDepsApt
& autobuilder (show buildminute ++ " * * * *") timeout True & autobuilder (show buildminute ++ " * * * *") timeout True
@ -115,7 +117,9 @@ androidAutoBuilderContainer dockerImage crontimes timeout =
androidContainer :: (System -> Docker.Image) -> Docker.ContainerName -> Property -> FilePath -> Host androidContainer :: (System -> Docker.Image) -> Docker.ContainerName -> Property -> FilePath -> Host
androidContainer dockerImage name setupgitannexdir gitannexdir = Docker.container name androidContainer dockerImage name setupgitannexdir gitannexdir = Docker.container name
(dockerImage $ System (Debian Stable) "i386") (dockerImage $ System (Debian Stable) "i386")
& os (System (Debian Stable) "i386")
& Apt.stdSourcesList Stable & Apt.stdSourcesList Stable
& Apt.installed ["systemd"]
& User.accountFor builduser & User.accountFor builduser
& File.dirExists gitbuilderdir & File.dirExists gitbuilderdir
& File.ownerGroup homedir builduser builduser & File.ownerGroup homedir builduser builduser
@ -140,7 +144,9 @@ androidContainer dockerImage name setupgitannexdir gitannexdir = Docker.containe
armelCompanionContainer :: (System -> Docker.Image) -> Host armelCompanionContainer :: (System -> Docker.Image) -> Host
armelCompanionContainer dockerImage = Docker.container "armel-git-annex-builder-companion" armelCompanionContainer dockerImage = Docker.container "armel-git-annex-builder-companion"
(dockerImage $ System (Debian Unstable) "amd64") (dockerImage $ System (Debian Unstable) "amd64")
& os (System (Debian Unstable) "amd64")
& Apt.stdSourcesList Unstable & Apt.stdSourcesList Unstable
& Apt.installed ["systemd"]
& Apt.unattendedUpgrades & Apt.unattendedUpgrades
-- This volume is shared with the armel builder. -- This volume is shared with the armel builder.
& Docker.volume gitbuilderdir & Docker.volume gitbuilderdir
@ -156,8 +162,10 @@ armelCompanionContainer dockerImage = Docker.container "armel-git-annex-builder-
armelAutoBuilderContainer :: (System -> Docker.Image) -> Cron.CronTimes -> TimeOut -> Host armelAutoBuilderContainer :: (System -> Docker.Image) -> Cron.CronTimes -> TimeOut -> Host
armelAutoBuilderContainer dockerImage crontimes timeout = Docker.container "armel-git-annex-builder" armelAutoBuilderContainer dockerImage crontimes timeout = Docker.container "armel-git-annex-builder"
(dockerImage $ System (Debian Unstable) "armel") (dockerImage $ System (Debian Unstable) "armel")
& os (System (Debian Unstable) "armel")
& Apt.stdSourcesList Unstable & Apt.stdSourcesList Unstable
& Apt.unattendedUpgrades & Apt.unattendedUpgrades
& Apt.installed ["systemd"]
& Apt.installed ["openssh-client"] & Apt.installed ["openssh-client"]
& Docker.link "armel-git-annex-builder-companion" "companion" & Docker.link "armel-git-annex-builder-companion" "companion"
& Docker.volumes_from "armel-git-annex-builder-companion" & Docker.volumes_from "armel-git-annex-builder-companion"

View File

@ -330,3 +330,11 @@ kiteShellBox = propertyList "kitenet.net shellinabox"
`onChange` Service.restarted "shellinabox" `onChange` Service.restarted "shellinabox"
, Service.running "shellinabox" , Service.running "shellinabox"
] ]
githubBackup :: Property
githubBackup = propertyList "github-backup box"
[ Apt.installed ["github-backup", "moreutils"]
, let f = "/home/joey/.github-keys"
in File.hasPrivContent f
`onChange` File.ownerGroup f "joey" "joey"
]

View File

@ -40,6 +40,7 @@ data Host = Host
, hostProperties :: [Property] , hostProperties :: [Property]
, hostAttr :: Attr , hostAttr :: Attr
} }
deriving (Show)
-- | Propellor's monad provides read-only access to the host it's running -- | Propellor's monad provides read-only access to the host it's running
-- on, including its attributes. -- on, including its attributes.
@ -64,6 +65,9 @@ data Property = Property
-- ^ a property can set an attribute of the host that has the property. -- ^ a property can set an attribute of the host that has the property.
} }
instance Show Property where
show = propertyDesc
-- | A property that can be reverted. -- | A property that can be reverted.
data RevertableProperty = RevertableProperty Property Property data RevertableProperty = RevertableProperty Property Property
@ -132,6 +136,7 @@ data CmdLine
| Spin HostName | Spin HostName
| Boot HostName | Boot HostName
| Set HostName PrivDataField | Set HostName PrivDataField
| Dump HostName PrivDataField
| AddKey String | AddKey String
| Continue CmdLine | Continue CmdLine
| Chain HostName | Chain HostName

View File

@ -8,42 +8,22 @@ import Data.Monoid
-- | The attributes of a host. -- | The attributes of a host.
data Attr = Attr data Attr = Attr
{ _os :: Maybe System { _os :: Val System
, _sshPubKey :: Maybe String , _sshPubKey :: Val String
, _dns :: S.Set Dns.Record , _dns :: S.Set Dns.Record
, _namedconf :: Dns.NamedConfMap , _namedconf :: Dns.NamedConfMap
, _dockerattr :: DockerAttr
, _dockerImage :: Maybe String
, _dockerRunParams :: [HostName -> String]
} }
deriving (Eq)
instance Eq Attr where
x == y = and
[ _os x == _os y
, _dns x == _dns y
, _namedconf x == _namedconf y
, _sshPubKey x == _sshPubKey y
, _dockerImage x == _dockerImage y
, let simpl v = map (\a -> a "") (_dockerRunParams v)
in simpl x == simpl y
]
instance Monoid Attr where instance Monoid Attr where
mempty = Attr Nothing Nothing mempty mempty Nothing mempty mempty = Attr mempty mempty mempty mempty mempty
mappend old new = Attr mappend old new = Attr
{ _os = case _os new of { _os = _os old <> _os new
Just v -> Just v , _sshPubKey = _sshPubKey old <> _sshPubKey new
Nothing -> _os old , _dns = _dns old <> _dns new
, _sshPubKey = case _sshPubKey new of , _namedconf = _namedconf old <> _namedconf new
Just v -> Just v , _dockerattr = _dockerattr old <> _dockerattr new
Nothing -> _sshPubKey old
, _dns = _dns new <> _dns old
, _namedconf = _namedconf new <> _namedconf old
, _dockerImage = case _dockerImage new of
Just v -> Just v
Nothing -> _dockerImage old
, _dockerRunParams = _dockerRunParams old <> _dockerRunParams new
} }
instance Show Attr where instance Show Attr where
@ -52,6 +32,43 @@ instance Show Attr where
, "sshPubKey " ++ show (_sshPubKey a) , "sshPubKey " ++ show (_sshPubKey a)
, "dns " ++ show (_dns a) , "dns " ++ show (_dns a)
, "namedconf " ++ show (_namedconf a) , "namedconf " ++ show (_namedconf a)
, "docker image " ++ show (_dockerImage a) , show (_dockerattr a)
]
data Val a = Val a | NoVal
deriving (Eq, Show)
instance Monoid (Val a) where
mempty = NoVal
mappend old new = case new of
NoVal -> old
_ -> new
fromVal :: Val a -> Maybe a
fromVal (Val a) = Just a
fromVal NoVal = Nothing
data DockerAttr = DockerAttr
{ _dockerImage :: Val String
, _dockerRunParams :: [HostName -> String]
}
instance Eq DockerAttr where
x == y = and
[ _dockerImage x == _dockerImage y
, let simpl v = map (\a -> a "") (_dockerRunParams v)
in simpl x == simpl y
]
instance Monoid DockerAttr where
mempty = DockerAttr mempty mempty
mappend old new = DockerAttr
{ _dockerImage = _dockerImage old <> _dockerImage new
, _dockerRunParams = _dockerRunParams old <> _dockerRunParams new
}
instance Show DockerAttr where
show a = unlines
[ "docker image " ++ show (_dockerImage a)
, "docker run params " ++ show (map (\mk -> mk "") (_dockerRunParams a)) , "docker run params " ++ show (map (\mk -> mk "") (_dockerRunParams a))
] ]