Merge branch 'joeyconfig'
Conflicts: Propellor/Property/Docker.hs
This commit is contained in:
commit
634cf61c79
|
@ -8,9 +8,12 @@ import System.Log.Formatter
|
||||||
import System.Log.Handler (setFormatter, LogHandler)
|
import System.Log.Handler (setFormatter, LogHandler)
|
||||||
import System.Log.Handler.Simple
|
import System.Log.Handler.Simple
|
||||||
import System.PosixCompat
|
import System.PosixCompat
|
||||||
|
import Control.Exception (bracket)
|
||||||
|
import System.Posix.IO
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import qualified Propellor.Property.Docker as Docker
|
import qualified Propellor.Property.Docker as Docker
|
||||||
|
import qualified Propellor.Property.Docker.Shim as DockerShim
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
import Utility.SafeCommand
|
import Utility.SafeCommand
|
||||||
|
|
||||||
|
@ -53,6 +56,7 @@ processCmdLine = go =<< getArgs
|
||||||
|
|
||||||
defaultMain :: [HostName -> Maybe [Property]] -> IO ()
|
defaultMain :: [HostName -> Maybe [Property]] -> IO ()
|
||||||
defaultMain getprops = do
|
defaultMain getprops = do
|
||||||
|
DockerShim.cleanEnv
|
||||||
checkDebugMode
|
checkDebugMode
|
||||||
cmdline <- processCmdLine
|
cmdline <- processCmdLine
|
||||||
debug ["command line: ", show cmdline]
|
debug ["command line: ", show cmdline]
|
||||||
|
@ -69,14 +73,26 @@ defaultMain getprops = do
|
||||||
go True cmdline = updateFirst cmdline $ go False cmdline
|
go True cmdline = updateFirst cmdline $ go False cmdline
|
||||||
go False (Spin host) = withprops host $ const $ spin host
|
go False (Spin host) = withprops host $ const $ spin host
|
||||||
go False (Run host) = ifM ((==) 0 <$> getRealUserID)
|
go False (Run host) = ifM ((==) 0 <$> getRealUserID)
|
||||||
( withprops host ensureProperties
|
( onlyProcess $ withprops host ensureProperties
|
||||||
, go True (Spin host)
|
, go True (Spin host)
|
||||||
)
|
)
|
||||||
go False (Boot host) = withprops host $ boot
|
go False (Boot host) = onlyProcess $ withprops host $ boot
|
||||||
|
|
||||||
withprops host a = maybe (unknownhost host) a $
|
withprops host a = maybe (unknownhost host) a $
|
||||||
headMaybe $ catMaybes $ map (\get -> get host) getprops
|
headMaybe $ catMaybes $ map (\get -> get host) getprops
|
||||||
|
|
||||||
|
onlyProcess :: IO a -> IO a
|
||||||
|
onlyProcess a = bracket lock unlock (const a)
|
||||||
|
where
|
||||||
|
lock = do
|
||||||
|
l <- createFile lockfile stdFileMode
|
||||||
|
setLock l (WriteLock, AbsoluteSeek, 0, 0)
|
||||||
|
`catchIO` const alreadyrunning
|
||||||
|
return l
|
||||||
|
unlock = closeFd
|
||||||
|
alreadyrunning = error "Propellor is already running on this host!"
|
||||||
|
lockfile = localdir </> ".lock"
|
||||||
|
|
||||||
unknownhost :: HostName -> IO a
|
unknownhost :: HostName -> IO a
|
||||||
unknownhost h = errorMessage $ unlines
|
unknownhost h = errorMessage $ unlines
|
||||||
[ "Unknown host: " ++ h
|
[ "Unknown host: " ++ h
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{-# LANGUAGE RankNTypes #-}
|
{-# LANGUAGE RankNTypes, BangPatterns #-}
|
||||||
|
|
||||||
-- | Docker support for propellor
|
-- | Docker support for propellor
|
||||||
--
|
--
|
||||||
|
@ -17,6 +17,7 @@ import Utility.Path
|
||||||
|
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
import System.Posix.Directory
|
import System.Posix.Directory
|
||||||
|
import System.Posix.Process
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.List.Utils
|
import Data.List.Utils
|
||||||
|
|
||||||
|
@ -166,7 +167,7 @@ volume = runProp "volume"
|
||||||
-- | Mount a volume from the specified container into the current
|
-- | Mount a volume from the specified container into the current
|
||||||
-- container.
|
-- container.
|
||||||
volumes_from :: ContainerName -> Containerized Property
|
volumes_from :: ContainerName -> Containerized Property
|
||||||
volumes_from cn = genProp "volumes-rom" $ \hn ->
|
volumes_from cn = genProp "volumes-from" $ \hn ->
|
||||||
fromContainerId (ContainerId hn cn)
|
fromContainerId (ContainerId hn cn)
|
||||||
|
|
||||||
-- | Work dir inside the container.
|
-- | Work dir inside the container.
|
||||||
|
@ -241,24 +242,34 @@ runningContainer cid@(ContainerId hn cn) image containerprops = containerDesc ci
|
||||||
l <- listContainers RunningContainers
|
l <- listContainers RunningContainers
|
||||||
if cid `elem` l
|
if cid `elem` l
|
||||||
then do
|
then do
|
||||||
|
-- Check if the ident has changed; if so the
|
||||||
|
-- parameters of the container differ and it must
|
||||||
|
-- be restarted.
|
||||||
runningident <- getrunningident
|
runningident <- getrunningident
|
||||||
if (ident2id <$> runningident) == Just (ident2id ident)
|
if runningident == Just ident
|
||||||
then return NoChange
|
then return NoChange
|
||||||
else do
|
else do
|
||||||
void $ stopContainer cid
|
void $ stopContainer cid
|
||||||
oldimage <- fromMaybe image <$> commitContainer cid
|
restartcontainer
|
||||||
void $ removeContainer cid
|
else ifM (elem cid <$> listContainers AllContainers)
|
||||||
go oldimage
|
( restartcontainer
|
||||||
else do
|
, go image
|
||||||
whenM (elem cid <$> listContainers AllContainers) $ do
|
)
|
||||||
void $ removeContainer cid
|
|
||||||
go image
|
|
||||||
where
|
where
|
||||||
ident = ContainerIdent image hn cn runps
|
ident = ContainerIdent image hn cn runps
|
||||||
|
|
||||||
getrunningident = catchDefaultIO Nothing $
|
restartcontainer = do
|
||||||
simpleShClient (namedPipe cid) "cat" [propellorIdent] $
|
oldimage <- fromMaybe image <$> commitContainer cid
|
||||||
pure . headMaybe . catMaybes . map readish . catMaybes . map getStdout
|
void $ removeContainer cid
|
||||||
|
go oldimage
|
||||||
|
|
||||||
|
getrunningident :: IO (Maybe ContainerIdent)
|
||||||
|
getrunningident = simpleShClient (namedPipe cid) "cat" [propellorIdent] $ \rs -> do
|
||||||
|
let !v = extractident rs
|
||||||
|
return v
|
||||||
|
|
||||||
|
extractident :: [Resp] -> Maybe ContainerIdent
|
||||||
|
extractident = headMaybe . catMaybes . map readish . catMaybes . map getStdout
|
||||||
|
|
||||||
runps = getRunParams hn $ containerprops ++
|
runps = getRunParams hn $ containerprops ++
|
||||||
-- expose propellor directory inside the container
|
-- expose propellor directory inside the container
|
||||||
|
@ -280,6 +291,9 @@ runningContainer cid@(ContainerId hn cn) image containerprops = containerDesc ci
|
||||||
-- | Called when propellor is running inside a docker container.
|
-- | Called when propellor is running inside a docker container.
|
||||||
-- The string should be the container's ContainerId.
|
-- The string should be the container's ContainerId.
|
||||||
--
|
--
|
||||||
|
-- This process is effectively init inside the container.
|
||||||
|
-- It even needs to wait on zombie processes!
|
||||||
|
--
|
||||||
-- Fork a thread to run the SimpleSh server in the background.
|
-- Fork a thread to run the SimpleSh server in the background.
|
||||||
-- In the foreground, run an interactive bash (or sh) shell,
|
-- In the foreground, run an interactive bash (or sh) shell,
|
||||||
-- so that the user can interact with it when attached to the container.
|
-- so that the user can interact with it when attached to the container.
|
||||||
|
@ -305,13 +319,17 @@ chain s = case toContainerId s of
|
||||||
let shim = Shim.file (localdir </> "propellor") (localdir </> shimdir cid)
|
let shim = Shim.file (localdir </> "propellor") (localdir </> shimdir cid)
|
||||||
unlessM (boolSystem shim [Param "--continue", Param $ show $ Chain $ fromContainerId cid]) $
|
unlessM (boolSystem shim [Param "--continue", Param $ show $ Chain $ fromContainerId cid]) $
|
||||||
warningMessage "Boot provision failed!"
|
warningMessage "Boot provision failed!"
|
||||||
void $ async $ simpleSh $ namedPipe cid
|
void $ async $ job reapzombies
|
||||||
forever $ do
|
void $ async $ job $ simpleSh $ namedPipe cid
|
||||||
void $ ifM (inPath "bash")
|
job $ do
|
||||||
|
void $ tryIO $ ifM (inPath "bash")
|
||||||
( boolSystem "bash" [Param "-l"]
|
( boolSystem "bash" [Param "-l"]
|
||||||
, boolSystem "/bin/sh" []
|
, boolSystem "/bin/sh" []
|
||||||
)
|
)
|
||||||
putStrLn "Container is still running. Press ^P^Q to detach."
|
putStrLn "Container is still running. Press ^P^Q to detach."
|
||||||
|
where
|
||||||
|
job = forever . void . tryIO
|
||||||
|
reapzombies = void $ getAnyProcessStatus True False
|
||||||
|
|
||||||
-- | Once a container is running, propellor can be run inside
|
-- | Once a container is running, propellor can be run inside
|
||||||
-- it to provision it.
|
-- it to provision it.
|
||||||
|
@ -343,7 +361,7 @@ provisionContainer cid = containerDesc cid $ Property "provision" $ do
|
||||||
hPutStrLn stderr s
|
hPutStrLn stderr s
|
||||||
hFlush stderr
|
hFlush stderr
|
||||||
go Nothing rest
|
go Nothing rest
|
||||||
Done _ -> ret lastline
|
Done -> ret lastline
|
||||||
go lastline [] = ret lastline
|
go lastline [] = ret lastline
|
||||||
|
|
||||||
ret lastline = return $ fromMaybe FailedChange $
|
ret lastline = return $ fromMaybe FailedChange $
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
--
|
--
|
||||||
-- Note: This is currently Debian specific, due to glibcLibs.
|
-- Note: This is currently Debian specific, due to glibcLibs.
|
||||||
|
|
||||||
module Propellor.Property.Docker.Shim (setup, file) where
|
module Propellor.Property.Docker.Shim (setup, cleanEnv, file) where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.LinuxMkLibs
|
import Utility.LinuxMkLibs
|
||||||
|
@ -44,6 +44,9 @@ setup propellorbin dest = do
|
||||||
modifyFileMode shim (addModes executeModes)
|
modifyFileMode shim (addModes executeModes)
|
||||||
return shim
|
return shim
|
||||||
|
|
||||||
|
cleanEnv :: IO ()
|
||||||
|
cleanEnv = void $ unsetEnv "GCONV_PATH"
|
||||||
|
|
||||||
file :: FilePath -> FilePath -> FilePath
|
file :: FilePath -> FilePath -> FilePath
|
||||||
file propellorbin dest = dest </> takeFileName propellorbin
|
file propellorbin dest = dest </> takeFileName propellorbin
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,14 @@ import Propellor.Property.Cron (CronTimes)
|
||||||
builduser :: UserName
|
builduser :: UserName
|
||||||
builduser = "builder"
|
builduser = "builder"
|
||||||
|
|
||||||
|
homedir :: FilePath
|
||||||
|
homedir = "/home/builder"
|
||||||
|
|
||||||
|
gitbuilderdir :: FilePath
|
||||||
|
gitbuilderdir = homedir </> "gitbuilder"
|
||||||
|
|
||||||
builddir :: FilePath
|
builddir :: FilePath
|
||||||
builddir = "gitbuilder"
|
builddir = gitbuilderdir </> "build"
|
||||||
|
|
||||||
builder :: Architecture -> CronTimes -> Bool -> Property
|
builder :: Architecture -> CronTimes -> Bool -> Property
|
||||||
builder arch crontimes rsyncupload = combineProperties "gitannexbuilder"
|
builder arch crontimes rsyncupload = combineProperties "gitannexbuilder"
|
||||||
|
@ -20,26 +26,22 @@ builder arch crontimes rsyncupload = combineProperties "gitannexbuilder"
|
||||||
"liblockfile-simple-perl", "cabal-install", "vim", "less"]
|
"liblockfile-simple-perl", "cabal-install", "vim", "less"]
|
||||||
, serviceRunning "cron" `requires` Apt.installed ["cron"]
|
, serviceRunning "cron" `requires` Apt.installed ["cron"]
|
||||||
, User.accountFor builduser
|
, User.accountFor builduser
|
||||||
, check (lacksdir builddir) $ userScriptProperty builduser
|
, check (not <$> doesDirectoryExist gitbuilderdir) $ userScriptProperty builduser
|
||||||
[ "git clone git://git.kitenet.net/gitannexbuilder " ++ builddir
|
[ "git clone git://git.kitenet.net/gitannexbuilder " ++ gitbuilderdir
|
||||||
, "cd " ++ builddir
|
, "cd " ++ gitbuilderdir
|
||||||
, "git checkout " ++ arch
|
, "git checkout " ++ arch
|
||||||
]
|
]
|
||||||
`describe` "gitbuilder setup"
|
`describe` "gitbuilder setup"
|
||||||
, check (lacksdir $ builddir </> "build") $ userScriptProperty builduser
|
, check (not <$> doesDirectoryExist builddir) $ userScriptProperty builduser
|
||||||
[ "cd " ++ builddir
|
[ "git clone git://git-annex.branchable.com/ " ++ builddir
|
||||||
, "git clone git://git-annex.branchable.com/ build"
|
|
||||||
]
|
]
|
||||||
, Property "git-annex source build deps installed" $ do
|
, "git-annex source build deps installed" ==> Apt.buildDepIn builddir
|
||||||
d <- homedir
|
, Cron.niceJob "gitannexbuilder" crontimes builduser gitbuilderdir "git pull ; ./autobuild"
|
||||||
ensureProperty $ Apt.buildDepIn (d </> builddir </> "build")
|
|
||||||
, Cron.niceJob "gitannexbuilder" crontimes builduser ("~/" ++ builddir) "git pull ; ./autobuild"
|
|
||||||
-- The builduser account does not have a password set,
|
-- The builduser account does not have a password set,
|
||||||
-- instead use the password privdata to hold the rsync server
|
-- instead use the password privdata to hold the rsync server
|
||||||
-- password used to upload the built image.
|
-- password used to upload the built image.
|
||||||
, Property "rsync password" $ do
|
, Property "rsync password" $ do
|
||||||
d <- homedir
|
let f = homedir </> "rsyncpassword"
|
||||||
let f = d </> "rsyncpassword"
|
|
||||||
if rsyncupload
|
if rsyncupload
|
||||||
then withPrivData (Password builduser) $ \p -> do
|
then withPrivData (Password builduser) $ \p -> do
|
||||||
oldp <- catchDefaultIO "" $ readFileStrict f
|
oldp <- catchDefaultIO "" $ readFileStrict f
|
||||||
|
@ -52,8 +54,3 @@ builder arch crontimes rsyncupload = combineProperties "gitannexbuilder"
|
||||||
, makeChange $ writeFile f "no password configured"
|
, makeChange $ writeFile f "no password configured"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
where
|
|
||||||
homedir = fromMaybe ("/home/" ++ builduser) <$> User.homedir builduser
|
|
||||||
lacksdir d = do
|
|
||||||
h <- homedir
|
|
||||||
not <$> doesDirectoryExist (h </> d)
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import Network.Socket
|
||||||
import Control.Concurrent.Chan
|
import Control.Concurrent.Chan
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
import System.Process (std_in, std_out, std_err)
|
import System.Process (std_in, std_out, std_err)
|
||||||
import System.Exit
|
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
import Utility.FileMode
|
import Utility.FileMode
|
||||||
|
@ -18,7 +17,7 @@ import Utility.ThreadScheduler
|
||||||
data Cmd = Cmd String [String]
|
data Cmd = Cmd String [String]
|
||||||
deriving (Read, Show)
|
deriving (Read, Show)
|
||||||
|
|
||||||
data Resp = StdoutLine String | StderrLine String | Done ExitCode
|
data Resp = StdoutLine String | StderrLine String | Done
|
||||||
deriving (Read, Show)
|
deriving (Read, Show)
|
||||||
|
|
||||||
simpleSh :: FilePath -> IO ()
|
simpleSh :: FilePath -> IO ()
|
||||||
|
@ -49,7 +48,7 @@ simpleSh namedpipe = do
|
||||||
v <- readChan chan
|
v <- readChan chan
|
||||||
hPutStrLn h (show v)
|
hPutStrLn h (show v)
|
||||||
case v of
|
case v of
|
||||||
Done _ -> noop
|
Done -> noop
|
||||||
_ -> runwriter
|
_ -> runwriter
|
||||||
writer <- async runwriter
|
writer <- async runwriter
|
||||||
|
|
||||||
|
@ -59,7 +58,9 @@ simpleSh namedpipe = do
|
||||||
(mkreader StdoutLine outh)
|
(mkreader StdoutLine outh)
|
||||||
(mkreader StderrLine errh)
|
(mkreader StderrLine errh)
|
||||||
|
|
||||||
writeChan chan . Done =<< waitForProcess pid
|
void $ tryIO $ waitForProcess pid
|
||||||
|
|
||||||
|
writeChan chan Done
|
||||||
|
|
||||||
wait writer
|
wait writer
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ host hostname@"clam.kitenet.net" = standardSystem Unstable $ props
|
||||||
& Tor.isBridge
|
& Tor.isBridge
|
||||||
& JoeySites.oldUseNetshellBox
|
& JoeySites.oldUseNetshellBox
|
||||||
& Docker.configured
|
& Docker.configured
|
||||||
! Docker.docked container hostname "amd64-git-annex-builder"
|
|
||||||
& Docker.garbageCollected
|
& Docker.garbageCollected
|
||||||
-- Orca is the main git-annex build box.
|
-- Orca is the main git-annex build box.
|
||||||
host hostname@"orca.kitenet.net" = standardSystem Unstable $ props
|
host hostname@"orca.kitenet.net" = standardSystem Unstable $ props
|
||||||
|
@ -46,6 +45,8 @@ host hostname@"orca.kitenet.net" = standardSystem Unstable $ props
|
||||||
& Apt.buildDep ["git-annex"]
|
& Apt.buildDep ["git-annex"]
|
||||||
& Docker.docked container hostname "amd64-git-annex-builder"
|
& Docker.docked container hostname "amd64-git-annex-builder"
|
||||||
& Docker.docked container hostname "i386-git-annex-builder"
|
& Docker.docked container hostname "i386-git-annex-builder"
|
||||||
|
& Docker.docked container hostname "armel-git-annex-builder-companion"
|
||||||
|
& Docker.docked container hostname "armel-git-annex-builder"
|
||||||
& Docker.garbageCollected
|
& Docker.garbageCollected
|
||||||
-- My laptop
|
-- My laptop
|
||||||
host _hostname@"darkstar.kitenet.net" = Just $ props
|
host _hostname@"darkstar.kitenet.net" = Just $ props
|
||||||
|
@ -67,11 +68,29 @@ container _host name
|
||||||
& serviceRunning "apache2"
|
& serviceRunning "apache2"
|
||||||
`requires` Apt.installed ["apache2"]
|
`requires` Apt.installed ["apache2"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- armel builder has a companion container that run amd64 and
|
||||||
|
-- runs the build first to get TH splices. They share a home
|
||||||
|
-- directory, and need to have the same versions of all haskell
|
||||||
|
-- libraries installed.
|
||||||
|
| name == "armel-git-annex-builder-companion" = Just $ Docker.containerFrom
|
||||||
|
(image $ System (Debian Unstable) "amd64")
|
||||||
|
[ Docker.volume GitAnnexBuilder.homedir
|
||||||
|
]
|
||||||
|
| name == "armel-git-annex-builder" = Just $ Docker.containerFrom
|
||||||
|
(image $ System (Debian Unstable) "armel")
|
||||||
|
[ Docker.link (name ++ "-companion") "companion"
|
||||||
|
, Docker.volumes_from (name ++ "-companion")
|
||||||
|
, Docker.inside $ props
|
||||||
|
-- & GitAnnexBuilder.builder "armel" "15 * * * *" True
|
||||||
|
]
|
||||||
|
|
||||||
| "-git-annex-builder" `isSuffixOf` name =
|
| "-git-annex-builder" `isSuffixOf` name =
|
||||||
let arch = takeWhile (/= '-') name
|
let arch = takeWhile (/= '-') name
|
||||||
in Just $ Docker.containerFrom
|
in Just $ Docker.containerFrom
|
||||||
(image $ System (Debian Unstable) arch)
|
(image $ System (Debian Unstable) arch)
|
||||||
[ Docker.inside $ props & GitAnnexBuilder.builder arch "15 * * * *" True ]
|
[ Docker.inside $ props & GitAnnexBuilder.builder arch "15 * * * *" True ]
|
||||||
|
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
|
|
||||||
-- | Docker images I prefer to use.
|
-- | Docker images I prefer to use.
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
propellor (0.2.3) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* docker: Fix laziness bug that caused running containers to be
|
||||||
|
unnecessarily stopped and committed.
|
||||||
|
* Add locking so only one propellor can run at a time on a host.
|
||||||
|
* docker: When running as effective init inside container, wait on zombies.
|
||||||
|
|
||||||
|
-- Joey Hess <joeyh@debian.org> Fri, 04 Apr 2014 15:58:03 -0400
|
||||||
|
|
||||||
propellor (0.2.2) unstable; urgency=medium
|
propellor (0.2.2) unstable; urgency=medium
|
||||||
|
|
||||||
* Now supports provisioning docker containers with architecture/libraries
|
* Now supports provisioning docker containers with architecture/libraries
|
||||||
|
|
Loading…
Reference in New Issue