summaryrefslogtreecommitdiff
path: root/src/Propellor/Property/Spin.hs
blob: 92753aa5649b9d812dd0fbc3eac51acb5f3b39a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
{-# LANGUAGE FlexibleInstances #-}

module Propellor.Property.Spin (Spinnable(..), controller) where

import Propellor.Base
import Propellor.Spin (spin)
import Propellor.Types.CmdLine (ControllerChain(..))
import Propellor.Types.Info

-- | A class of things that can be spinned.
class Spinnable t where
	toSpin :: t -> Property NoInfo

instance Spinnable Host where
	toSpin h = property (cdesc (hostName h)) $ do
		ControllerChain cc <- getControllerChain
		if hostName h `elem` cc
			then noChange -- avoid loop
			else do
				liftIO $ spin (hostName h) Nothing (ControllerChain cc) h
				-- Don't know if the spin made a change to the
				-- remote host or not, but in any case, the
				-- local host was not changed.
				noChange

-- | Each Host in the list is spinned in turn. Does not stop on spin
-- failure; does propigate overall success/failure.
instance Spinnable [Host] where
	toSpin l = propertyList (cdesc $ unwords $ map hostName l) (map toSpin l)

-- | The Host that has this Property is in control of running propellor on
-- some other Hosts.
--
-- Making a host a controller eliminates the need to manually run
-- propellor --spin to update the controlled hosts. Each time
-- propellor is run on the controller host, it will in turn run
-- propellor on the controlled Hosts.
--
-- For example, if you have some webservers and some dnsservers,
-- and want a master that runs propellor on all of them, and only updates
-- the dnsservers once all the webservers are successfully updated:
--
-- > import Propellor
-- > import qualified Propellor.Property.Spin as Spin
-- > import qualified Propellor.Property.Cron as Cron
-- > 
-- > main = defaultMain hosts
-- >
-- > hosts = master : webservers ++ dnsservers
-- > 
-- > webservers = ...
-- > 
-- > dnsservers = ...
-- > 
-- > master = host "master.example.com"
-- >	& Cron.runPropellor
-- >	& Spin.controller dnsservers
-- >		`requires` Spin.controller webservers
--
-- Multiple controllers can control the same hosts. However, if
-- propellor is already running on a host, its controller will fail
-- to run it a second time. So, if two controllers both try to
-- control the same host at the same time, one will fail.
--
-- Chains of controllers are supported; host A can control host B which
-- controls host C. Loops of controllers are automatically prevented.
controller :: Spinnable h => h -> Property NoInfo
controller = toSpin

cdesc :: String -> Desc
cdesc n = "controller for " ++ n

getControllerChain :: Propellor ControllerChain
getControllerChain = do
	hn <- hostName <$> ask
	ControllerChain cc <- fromMaybe (ControllerChain []) . fromInfoVal <$> askInfo
	return (ControllerChain (hn:cc))