propellor spin

This commit is contained in:
Joey Hess 2014-03-31 15:40:16 -04:00
parent 36469bc07d
commit 9172b79612
Failed to extract signature
2 changed files with 65 additions and 26 deletions

View File

@ -70,38 +70,47 @@ spin host = do
url <- getUrl url <- getUrl
void $ gitCommit [Param "--allow-empty", Param "-a", Param "-m", Param "propellor spin"] void $ gitCommit [Param "--allow-empty", Param "-a", Param "-m", Param "propellor spin"]
void $ boolSystem "git" [Param "push"] void $ boolSystem "git" [Param "push"]
privdata <- gpgDecrypt (privDataFile host) go url =<< gpgDecrypt (privDataFile host)
withBothHandles createProcessSuccess (proc "ssh" [user, bootstrapcmd url]) $ \(toh, fromh) -> do where
go url privdata = withBothHandles createProcessSuccess (proc "ssh" [user, bootstrapcmd]) $ \(toh, fromh) -> do
let finish = do
senddata toh (privDataFile host) privDataMarker privdata
hClose toh
-- Display remaining output.
void $ tryIO $ forever $
showremote =<< hGetLine fromh
hClose fromh
status <- getstatus fromh `catchIO` error "protocol error" status <- getstatus fromh `catchIO` error "protocol error"
case status of case status of
HaveKeyRing -> finish
NeedKeyRing -> do NeedKeyRing -> do
d <- w82s . BL.unpack . B64.encode d <- w82s . BL.unpack . B64.encode
<$> BL.readFile keyring <$> BL.readFile keyring
senddata toh keyring keyringMarker d senddata toh keyring keyringMarker d
HaveKeyRing -> noop finish
senddata toh (privDataFile host) privDataMarker privdata NeedGitClone -> do
hClose toh hClose toh
hClose fromh
sendGitClone host url
go url privdata
-- Display remaining output.
void $ tryIO $ forever $
showremote =<< hGetLine fromh
hClose fromh
where
user = "root@"++host user = "root@"++host
bootstrapcmd url = shellWrap $ intercalate " && "
bootstrapcmd = shellWrap $ intercalate " && "
[ intercalate " ; " [ intercalate " ; "
[ "if [ ! -d " ++ localdir ++ " ]" [ "if [ ! -d " ++ localdir ++ " ]"
, "then " ++ intercalate " && " , "then " ++ intercalate " && "
[ "apt-get -y install git" [ "apt-get -y install git"
, "git clone " ++ url ++ " " ++ localdir , "echo " ++ toMarked statusMarker (show NeedGitClone)
] ]
, "fi" , "fi"
] ]
, "cd " ++ localdir , "cd " ++ localdir
, "make pull build" , "make build"
, "./propellor --boot " ++ host , "./propellor --boot " ++ host
] ]
getstatus :: Handle -> IO BootStrapStatus getstatus :: Handle -> IO BootStrapStatus
getstatus h = do getstatus h = do
l <- hGetLine h l <- hGetLine h
@ -110,6 +119,7 @@ spin host = do
showremote l showremote l
getstatus h getstatus h
Just status -> return status Just status -> return status
showremote s = putStrLn s showremote s = putStrLn s
senddata toh f marker s = do senddata toh f marker s = do
putStr $ "Sending " ++ f ++ " (" ++ show (length s) ++ " bytes) to " ++ host ++ "..." putStr $ "Sending " ++ f ++ " (" ++ show (length s) ++ " bytes) to " ++ host ++ "..."
@ -118,7 +128,27 @@ spin host = do
hFlush toh hFlush toh
putStrLn "done" putStrLn "done"
data BootStrapStatus = HaveKeyRing | NeedKeyRing sendGitClone :: HostName -> String -> IO ()
sendGitClone host url = do
putStrLn $ "Pushing git repository to " ++ host
withTmpFile "gitbundle" $ \tmp _ -> do
-- TODO: ssh connection caching, or better push method
-- with less connections.
void $ boolSystem "git" [Param "bundle", Param "create", File tmp, Param "HEAD"]
void $ boolSystem "scp" [File tmp, Param ("root@"++host++":"++remotebundle)]
void $ boolSystem "ssh" [Param ("root@"++host), Param unpackcmd]
where
remotebundle = "/usr/local/propellor.git"
unpackcmd = shellWrap $ intercalate " && "
[ "git clone " ++ remotebundle ++ " " ++ localdir
, "cd " ++ localdir
, "git checkout -b master"
, "git remote rm origin"
, "git remote add origin " ++ url
, "rm -f " ++ remotebundle
]
data BootStrapStatus = HaveKeyRing | NeedKeyRing | NeedGitClone
deriving (Read, Show, Eq) deriving (Read, Show, Eq)
type Marker = String type Marker = String

27
README
View File

@ -6,10 +6,13 @@ properties, taking action as necessary when a property is not yet met.
The design is intentionally very minimal. The design is intentionally very minimal.
Propellor lives in a git repository, and so to set it up it's cloned Propellor lives in a git repository. You'll typically want to have
to a system, and "make" can be used to pull down any new changes, the repository checked out on a laptop, in order to make changes and push
and compile and run propellor. This can be done by a cron job, or them out to hosts. Each host will also have a clone of the repository,
a local propellor on your laptop can ssh in and run it. and in that clone "make" can be used to build and run propellor.
This can be done by a cron job (which propellor can set up),
or a remote host can be triggered to update by running propellor
on your laptop: propellor --spin $host
Properties are defined using Haskell. Edit config.hs to get started. Properties are defined using Haskell. Edit config.hs to get started.
@ -26,9 +29,15 @@ and so it's easy to factor out things like classes of hosts as desired.
## bootstrapping and private data ## bootstrapping and private data
To bootstrap propellor on a new host, use: propellor --spin $host To bootstrap propellor on a new host, use: propellor --spin $host
This looks up the git repository's remote.origin.url (or remote.deploy.url
if available) and logs into the host, clones the url (if not already That clones the local git repository to the remote host (securely over ssh
done), and sets up and runs propellor in /usr/local/propellor and without needing any central server!), if it doesn't already have
a clone.
The repository on the remote host will have its origin set to the local git
repository's remote.origin.url (or remote.deploy.url if available).
This way, when propellor is run on the remote host, it can contact
whatever central git repository you're using.
Private data such as passwords, ssh private keys, etc should not be checked Private data such as passwords, ssh private keys, etc should not be checked
into a propellor git repository in the clear, unless you want to restrict into a propellor git repository in the clear, unless you want to restrict
@ -43,8 +52,8 @@ for available fields.
## using git://... securely ## using git://... securely
It's often easiest to deploy propellor to a host by cloning a git:// or It's often easiest for a remote host to use a git:// or http://
http:// repository rather than by cloning over ssh://. To avoid a MITM url to its origin repository, rather than ssh://. So, to avoid a MITM
attack, propellor checks that the top commit in the git repository is gpg attack, propellor checks that the top commit in the git repository is gpg
signed by a trusted gpg key, and refuses to deploy it otherwise. signed by a trusted gpg key, and refuses to deploy it otherwise.