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
void $ gitCommit [Param "--allow-empty", Param "-a", Param "-m", Param "propellor spin"]
void $ boolSystem "git" [Param "push"]
privdata <- gpgDecrypt (privDataFile host)
withBothHandles createProcessSuccess (proc "ssh" [user, bootstrapcmd url]) $ \(toh, fromh) -> do
go url =<< gpgDecrypt (privDataFile host)
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"
case status of
HaveKeyRing -> finish
NeedKeyRing -> do
d <- w82s . BL.unpack . B64.encode
<$> BL.readFile keyring
senddata toh keyring keyringMarker d
HaveKeyRing -> noop
senddata toh (privDataFile host) privDataMarker privdata
hClose toh
-- Display remaining output.
void $ tryIO $ forever $
showremote =<< hGetLine fromh
hClose fromh
where
finish
NeedGitClone -> do
hClose toh
hClose fromh
sendGitClone host url
go url privdata
user = "root@"++host
bootstrapcmd url = shellWrap $ intercalate " && "
bootstrapcmd = shellWrap $ intercalate " && "
[ intercalate " ; "
[ "if [ ! -d " ++ localdir ++ " ]"
, "then " ++ intercalate " && "
[ "apt-get -y install git"
, "git clone " ++ url ++ " " ++ localdir
, "echo " ++ toMarked statusMarker (show NeedGitClone)
]
, "fi"
]
, "cd " ++ localdir
, "make pull build"
, "make build"
, "./propellor --boot " ++ host
]
getstatus :: Handle -> IO BootStrapStatus
getstatus h = do
l <- hGetLine h
@ -110,6 +119,7 @@ spin host = do
showremote l
getstatus h
Just status -> return status
showremote s = putStrLn s
senddata toh f marker s = do
putStr $ "Sending " ++ f ++ " (" ++ show (length s) ++ " bytes) to " ++ host ++ "..."
@ -118,7 +128,27 @@ spin host = do
hFlush toh
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)
type Marker = String

29
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.
Propellor lives in a git repository, and so to set it up it's cloned
to a system, and "make" can be used to pull down any new changes,
and compile and run propellor. This can be done by a cron job, or
a local propellor on your laptop can ssh in and run it.
Propellor lives in a git repository. You'll typically want to have
the repository checked out on a laptop, in order to make changes and push
them out to hosts. Each host will also have a clone of the repository,
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.
@ -26,9 +29,15 @@ and so it's easy to factor out things like classes of hosts as desired.
## bootstrapping and private data
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
done), and sets up and runs propellor in /usr/local/propellor
That clones the local git repository to the remote host (securely over ssh
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
into a propellor git repository in the clear, unless you want to restrict
@ -43,10 +52,10 @@ for available fields.
## using git://... securely
It's often easiest to deploy propellor to a host by cloning a git:// or
http:// repository rather than by cloning over ssh://. To avoid a MITM
It's often easiest for a remote host to use a git:// or http://
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
signed by a trusted gpg key, and refuses to deploy it otherwise.
signed by a trusted gpg key, and refuses to deploy it otherwise.
This is only done when privdata/keyring.gpg exists. To set it up: