summaryrefslogtreecommitdiff
path: root/CmdLine.hs
blob: a4ef3bae07ee323ccee8bc3132b48e459ca4d426 (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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
module CmdLine where

import System.Environment
import Data.List
import System.Exit

import Common
import Utility.FileMode

data CmdLine
	= Run HostName
	| Spin HostName
	| Boot HostName
	| Set HostName PrivDataField

processCmdLine :: IO CmdLine
processCmdLine = go =<< getArgs
  where
  	go ("--help":_) = usage
  	go ("--spin":h:[]) = return $ Spin h
  	go ("--boot":h:[]) = return $ Boot h
	go ("--set":h:f:[]) = case readish f of
		Just pf -> return $ Set h pf
		Nothing -> error $ "Unknown privdata field " ++ f
	go (h:[]) = return $ Run h
	go [] = do
		s <- takeWhile (/= '\n') <$> readProcess "hostname" ["-f"]
		if null s
			then error "Cannot determine hostname! Pass it on the command line."
			else return $ Run s
	go _ = usage
	
usage :: IO a
usage = do
	putStrLn $ unlines 
		[ "Usage:"
		, "  propellor"
		, "  propellor hostname"
		, "  propellor --spin hostname"
		, "  propellor --set hostname field"
		]
	exitFailure

defaultMain :: (HostName -> [Property]) -> IO ()
defaultMain getprops = go =<< processCmdLine
  where
	go (Run host) = ensureProperties (getprops host)
	go (Spin host) = spin host
	go (Boot host) = boot (getprops host)
	go (Set host field) = setPrivData host field

spin :: HostName -> IO ()
spin host = do
	url <- getUrl
	void $ boolSystem "git" [Param "commit", Param "-a", Param "-m", Param "propellor spin"]
	void $ boolSystem "git" [Param "push"]
	privdata <- gpgDecrypt (privDataFile host)
	withHandle StdinHandle createProcessSuccess
		(proc "ssh" ["root@"++host, bootstrap url]) $ \h -> do
			hPutStr h $ unlines $ map (privDataMarker ++) $ lines privdata
			hClose h
  where
	bootstrap url = shellWrap $ intercalate " && "
		[ intercalate " ; "
			[ "if [ ! -d " ++ localdir ++ " ]"
			, "then " ++ intercalate " && "
				[ "apt-get -y install git"
				, "git clone " ++ url ++ " " ++ localdir
				]
			, "fi"
			]
		, "cd " ++ localdir
		, "make pull build"
		, "./propellor --boot " ++ host
		]

boot :: [Property] -> IO ()
boot props = do
	privdata <- map (drop $ length privDataMarker ) 
		. filter (privDataMarker `isPrefixOf`) 
		. lines 
		<$> getContents
	makePrivDataDir
	writeFileProtected privDataLocal (unlines privdata)
	ensureProperties props

localdir :: FilePath
localdir = "/usr/local/propellor"

getUrl :: IO String
getUrl = fromMaybe nourl <$> getM get urls
  where
	urls = ["remote.deploy.url", "remote.origin.url"]
	nourl = error $ "Cannot find deploy url in " ++ show urls
	get u = do
		v <- catchMaybeIO $ 
			takeWhile (/= '\n') 
				<$> readProcess "git" ["config", u]
		return $ case v of
			Just url | not (null url) -> Just url
			_ -> Nothing