From c97dd0d7088fa981f762070e06fc8058ab04cdbd Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 5 Dec 2014 19:33:34 -0400 Subject: spelling typo --- src/Propellor/Types/OS.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Propellor/Types/OS.hs b/src/Propellor/Types/OS.hs index 72e3d764..8b3cd0fd 100644 --- a/src/Propellor/Types/OS.hs +++ b/src/Propellor/Types/OS.hs @@ -15,7 +15,7 @@ import Network.BSD (HostName) type UserName = String type GroupName = String --- | High level descritption of a operating system. +-- | High level description of a operating system. data System = System Distribution Architecture deriving (Show, Eq) -- cgit v1.3-2-g0d8e From fcff7762e395378791f01c9ea8507b41a4d7d501 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 6 Dec 2014 06:34:32 -0400 Subject: endAction can be used to register an action to run once propellor has successfully run on a host. --- debian/changelog | 2 ++ src/Propellor/Engine.hs | 37 +++++++++++++++++++++++++++++-------- src/Propellor/Property.hs | 11 ++++++++--- src/Propellor/Types.hs | 15 ++++++++++++--- 4 files changed, 51 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/debian/changelog b/debian/changelog index 4bb387ce..a2b357ae 100644 --- a/debian/changelog +++ b/debian/changelog @@ -28,6 +28,8 @@ propellor (1.1.0) UNRELEASED; urgency=medium * propellor.debug can be set in the git config to enable more persistent debugging output. * Run apt-cache policy with LANG=C so it works on other locales. + * endAction can be used to register an action to run once propellor + has successfully run on a host. -- Joey Hess Sat, 22 Nov 2014 00:12:35 -0400 diff --git a/src/Propellor/Engine.hs b/src/Propellor/Engine.hs index 81cc2397..310f4c84 100644 --- a/src/Propellor/Engine.hs +++ b/src/Propellor/Engine.hs @@ -7,7 +7,7 @@ import System.IO import Data.Monoid import Control.Applicative import System.Console.ANSI -import "mtl" Control.Monad.Reader +import "mtl" Control.Monad.RWS.Strict import Control.Exception (bracket) import System.PosixCompat import System.Posix.IO @@ -22,21 +22,37 @@ import Utility.Exception import Utility.PartialPrelude import Utility.Monad -runPropellor :: Host -> Propellor a -> IO a -runPropellor host a = runReaderT (runWithHost a) host - +-- | Gets the Properties of a Host, and ensures them all, +-- with nice display of what's being done. mainProperties :: Host -> IO () mainProperties host = do - r <- runPropellor host $ + ret <- runPropellor host $ ensureProperties [Property "overall" (ensureProperties $ hostProperties host) mempty] h <- mkMessageHandle whenConsole h $ setTitle "propellor: done" hFlush stdout - case r of + case ret of FailedChange -> exitWith (ExitFailure 1) _ -> exitWith ExitSuccess +-- | Runs a Propellor action with the specified host. +-- +-- If the Result is not FailedChange, any EndActions +-- that were accumulated while running the action +-- are then also run. +runPropellor :: Host -> Propellor Result -> IO Result +runPropellor host a = do + (ret, _s, endactions) <- runRWST (runWithHost a) host () + endrets <- mapM (runEndAction host) endactions + return $ mconcat (ret:endrets) + +runEndAction :: Host -> EndAction -> IO Result +runEndAction host (EndAction desc a) = actionMessageOn (hostName host) desc $ do + (ret, _s, _) <- runRWST (runWithHost (catchPropellor a)) host () + return ret + +-- | Ensures a list of Properties, with a display of each as it runs. ensureProperties :: [Property] -> Propellor Result ensureProperties ps = ensure ps NoChange where @@ -46,6 +62,8 @@ ensureProperties ps = ensure ps NoChange r <- actionMessageOn hn (propertyDesc l) (ensureProperty l) ensure ls (r <> rs) +-- | For when code running in the Propellor monad needs to ensure a +-- Property. ensureProperty :: Property -> Propellor Result ensureProperty = catchPropellor . propertySatisfy @@ -55,8 +73,11 @@ ensureProperty = catchPropellor . propertySatisfy fromHost :: [Host] -> HostName -> Propellor a -> Propellor (Maybe a) fromHost l hn getter = case findHost l hn of Nothing -> return Nothing - Just h -> liftIO $ Just <$> - runReaderT (runWithHost getter) h + Just h -> do + (ret, _s, runlog) <- liftIO $ + runRWST (runWithHost getter) h () + tell runlog + return (Just ret) onlyProcess :: FilePath -> IO a -> IO a onlyProcess lockfile a = bracket lock unlock (const a) diff --git a/src/Propellor/Property.hs b/src/Propellor/Property.hs index 6ace5e4e..1533471e 100644 --- a/src/Propellor/Property.hs +++ b/src/Propellor/Property.hs @@ -7,7 +7,7 @@ import System.FilePath import Control.Monad import Data.Monoid import Control.Monad.IfElse -import "mtl" Control.Monad.Reader +import "mtl" Control.Monad.RWS.Strict import Propellor.Types import Propellor.Info @@ -131,11 +131,11 @@ boolProperty desc a = property desc $ ifM (liftIO a) revert :: RevertableProperty -> RevertableProperty revert (RevertableProperty p1 p2) = RevertableProperty p2 p1 --- Changes the action that is performed to satisfy a property. +-- | Changes the action that is performed to satisfy a property. adjustProperty :: Property -> (Propellor Result -> Propellor Result) -> Property adjustProperty p f = p { propertySatisfy = f (propertySatisfy p) } --- Combines the Info of two properties. +-- | Combines the Info of two properties. combineInfo :: (IsProp p, IsProp q) => p -> q -> Info combineInfo p q = getInfo p <> getInfo q @@ -147,3 +147,8 @@ makeChange a = liftIO a >> return MadeChange noChange :: Propellor Result noChange = return NoChange + +-- | Registers an action that should be run at the very end, +-- and only when all configured Properties of the host succeed. +endAction :: Desc -> Propellor Result -> Propellor () +endAction desc a = tell [EndAction desc a] diff --git a/src/Propellor/Types.hs b/src/Propellor/Types.hs index 2f51b3e4..64cb5fbb 100644 --- a/src/Propellor/Types.hs +++ b/src/Propellor/Types.hs @@ -23,6 +23,8 @@ module Propellor.Types , SshKeyType(..) , Val(..) , fromVal + , RunLog + , EndAction(..) , module Propellor.Types.OS , module Propellor.Types.Dns ) where @@ -31,7 +33,7 @@ import Data.Monoid import Control.Applicative import System.Console.ANSI import System.Posix.Types -import "mtl" Control.Monad.Reader +import "mtl" Control.Monad.RWS.Strict import "MonadCatchIO-transformers" Control.Monad.CatchIO import qualified Data.Set as S import qualified Propellor.Types.Dns as Dns @@ -52,13 +54,14 @@ data Host = Host deriving (Show) -- | Propellor's monad provides read-only access to info about the host --- it's running on. -newtype Propellor p = Propellor { runWithHost :: ReaderT Host IO p } +-- it's running on, and a writer to accumulate logs about the run. +newtype Propellor p = Propellor { runWithHost :: RWST Host RunLog () IO p } deriving ( Monad , Functor , Applicative , MonadReader Host + , MonadWriter RunLog , MonadIO , MonadCatchIO ) @@ -197,3 +200,9 @@ instance Monoid (Val a) where fromVal :: Val a -> Maybe a fromVal (Val a) = Just a fromVal NoVal = Nothing + +type RunLog = [EndAction] + +-- | An action that Propellor runs at the end, after trying to satisfy all +-- properties. +data EndAction = EndAction Desc (Propellor Result) -- cgit v1.3-2-g0d8e From 62697c7b7f7ba2d39bfad632f1cf720c9805bdd2 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 6 Dec 2014 06:41:21 -0400 Subject: reboot at end of propellor run when clean os has been installed --- src/Propellor/Property/OS.hs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/Propellor/Property/OS.hs b/src/Propellor/Property/OS.hs index 22414bb6..e84490fd 100644 --- a/src/Propellor/Property/OS.hs +++ b/src/Propellor/Property/OS.hs @@ -4,7 +4,6 @@ module Propellor.Property.OS ( preserveNetworkInterfaces, preserveResolvConf, preserveRootSshAuthorized, - rebootForced, oldOSRemoved, ) where @@ -35,8 +34,9 @@ import Control.Exception (throw) -- -- The files from the old os will be left in /old-os -- --- TODO: A forced reboot should be schedued to run after propellor finishes --- ensuring all properties of the host. +-- After the OS is installed, and if all properties of the host have +-- been successfully satisfied, the host will be rebooted to properly load +-- the new OS. -- -- You will typically want to run some more properties after the clean -- install succeeds, to bootstrap from the cleanly installed system to @@ -125,7 +125,6 @@ cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $ unlessM (mount "devpts" "devpts" "/dev/pts") $ warningMessage "failed mounting /dev/pts" - liftIO $ writeFile flagfile "" return MadeChange propellorbootstrapped = property "propellor re-debootstrapped in new os" $ @@ -136,9 +135,14 @@ cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $ -- be present in /old-os's /usr/local/propellor) -- TODO - -- Ensure that MadeChange is returned by the overall property, - -- so that anything hooking in onChange will run afterwards. - finalized = property "clean OS installed" $ return MadeChange + finalized = property "clean OS installed" $ do + liftIO $ writeFile flagfile "" + endAction "rebooting into new OS" $ liftIO $ + ifM (boolSystem "reboot" [ Param "--force" ]) + ( return MadeChange + , return FailedChange + ) + return MadeChange flagfile = "/etc/propellor-cleaninstall" @@ -206,12 +210,6 @@ preserveRootSshAuthorized = check (fileExist oldloc) $ newloc = "/root/.ssh/authorized_keys" oldloc = oldOSDir ++ newloc --- | Forces an immediate reboot, without contacting the init system. --- --- Can be used after cleanInstallOnce. -rebootForced :: Property -rebootForced = cmdProperty "reboot" [ "--force" ] - -- Removes the old OS's backup from /old-os oldOSRemoved :: Confirmation -> Property oldOSRemoved confirmation = check (doesDirectoryExist oldOSDir) $ -- cgit v1.3-2-g0d8e From 29442f222ec2342c6dea7036e80e15eefa4158c0 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 6 Dec 2014 13:21:19 -0400 Subject: Reboot.atEnd --- config-simple.hs | 1 - src/Propellor/Engine.hs | 12 ++++++------ src/Propellor/Property.hs | 3 +-- src/Propellor/Property/OS.hs | 8 +++----- src/Propellor/Property/Reboot.hs | 28 +++++++++++++++++++++++++--- src/Propellor/Types.hs | 4 ++-- 6 files changed, 37 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/config-simple.hs b/config-simple.hs index c03149ed..95c2fe18 100644 --- a/config-simple.hs +++ b/config-simple.hs @@ -12,7 +12,6 @@ import qualified Propellor.Property.Cron as Cron --import qualified Propellor.Property.Sudo as Sudo import qualified Propellor.Property.User as User --import qualified Propellor.Property.Hostname as Hostname ---import qualified Propellor.Property.Reboot as Reboot --import qualified Propellor.Property.Tor as Tor import qualified Propellor.Property.Docker as Docker diff --git a/src/Propellor/Engine.hs b/src/Propellor/Engine.hs index 310f4c84..44b10cab 100644 --- a/src/Propellor/Engine.hs +++ b/src/Propellor/Engine.hs @@ -43,13 +43,13 @@ mainProperties host = do -- are then also run. runPropellor :: Host -> Propellor Result -> IO Result runPropellor host a = do - (ret, _s, endactions) <- runRWST (runWithHost a) host () - endrets <- mapM (runEndAction host) endactions - return $ mconcat (ret:endrets) + (res, _s, endactions) <- runRWST (runWithHost a) host () + endres <- mapM (runEndAction host res) endactions + return $ mconcat (res:endres) -runEndAction :: Host -> EndAction -> IO Result -runEndAction host (EndAction desc a) = actionMessageOn (hostName host) desc $ do - (ret, _s, _) <- runRWST (runWithHost (catchPropellor a)) host () +runEndAction :: Host -> Result -> EndAction -> IO Result +runEndAction host res (EndAction desc a) = actionMessageOn (hostName host) desc $ do + (ret, _s, _) <- runRWST (runWithHost (catchPropellor (a res))) host () return ret -- | Ensures a list of Properties, with a display of each as it runs. diff --git a/src/Propellor/Property.hs b/src/Propellor/Property.hs index 1533471e..6371cc09 100644 --- a/src/Propellor/Property.hs +++ b/src/Propellor/Property.hs @@ -149,6 +149,5 @@ noChange :: Propellor Result noChange = return NoChange -- | Registers an action that should be run at the very end, --- and only when all configured Properties of the host succeed. -endAction :: Desc -> Propellor Result -> Propellor () +endAction :: Desc -> (Result -> Propellor Result) -> Propellor () endAction desc a = tell [EndAction desc a] diff --git a/src/Propellor/Property/OS.hs b/src/Propellor/Property/OS.hs index e84490fd..2185471d 100644 --- a/src/Propellor/Property/OS.hs +++ b/src/Propellor/Property/OS.hs @@ -11,6 +11,7 @@ import Propellor import qualified Propellor.Property.Debootstrap as Debootstrap import qualified Propellor.Property.Ssh as Ssh import qualified Propellor.Property.File as File +import qualified Propellor.Property.Reboot as Reboot import Propellor.Property.Mount import Propellor.Property.Chroot.Util (stdPATH) import Utility.SafeCommand @@ -67,6 +68,8 @@ cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $ go = finalized `requires` + Reboot.atEnd True (/= FailedChange) + `requires` propellorbootstrapped `requires` flipped @@ -137,11 +140,6 @@ cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $ finalized = property "clean OS installed" $ do liftIO $ writeFile flagfile "" - endAction "rebooting into new OS" $ liftIO $ - ifM (boolSystem "reboot" [ Param "--force" ]) - ( return MadeChange - , return FailedChange - ) return MadeChange flagfile = "/etc/propellor-cleaninstall" diff --git a/src/Propellor/Property/Reboot.hs b/src/Propellor/Property/Reboot.hs index 25e53159..3a725838 100644 --- a/src/Propellor/Property/Reboot.hs +++ b/src/Propellor/Property/Reboot.hs @@ -1,7 +1,29 @@ module Propellor.Property.Reboot where import Propellor +import Utility.SafeCommand -now :: Property -now = cmdProperty "reboot" [] - `describe` "reboot now" +-- | Schedules a reboot at the end of the current propellor run. +-- +-- The Result code of the endire propellor run can be checked; +-- the reboot proceeds only if the function returns True. +-- +-- The reboot can be forced to run, which bypasses the init system. Useful +-- if the init system might not be running for some reason. +atEnd :: Bool -> (Result -> Bool) -> Property +atEnd force resultok = property "scheduled reboot at end of propellor run" $ do + endAction "rebooting" atend + return NoChange + where + atend r + | resultok r = liftIO $ + ifM (boolSystem "reboot" rebootparams) + ( return MadeChange + , return FailedChange + ) + | otherwise = do + warningMessage "Not rebooting, due to status of propellor run." + return FailedChange + rebootparams + | force = [Param "--force"] + | otherwise = [] diff --git a/src/Propellor/Types.hs b/src/Propellor/Types.hs index 64cb5fbb..f349a29a 100644 --- a/src/Propellor/Types.hs +++ b/src/Propellor/Types.hs @@ -204,5 +204,5 @@ fromVal NoVal = Nothing type RunLog = [EndAction] -- | An action that Propellor runs at the end, after trying to satisfy all --- properties. -data EndAction = EndAction Desc (Propellor Result) +-- properties. It's passed the combined Result of the entire Propellor run. +data EndAction = EndAction Desc (Result -> Propellor Result) -- cgit v1.3-2-g0d8e From bdbf787e2cf1123429491da5f06f46a8dc8774f6 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 6 Dec 2014 13:32:53 -0400 Subject: keep and use Reboot.now --- src/Propellor/Property/HostingProvider/DigitalOcean.hs | 6 +++--- src/Propellor/Property/Reboot.hs | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/Propellor/Property/HostingProvider/DigitalOcean.hs b/src/Propellor/Property/HostingProvider/DigitalOcean.hs index 32165d48..4d2534ec 100644 --- a/src/Propellor/Property/HostingProvider/DigitalOcean.hs +++ b/src/Propellor/Property/HostingProvider/DigitalOcean.hs @@ -5,6 +5,7 @@ module Propellor.Property.HostingProvider.DigitalOcean ( import Propellor import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.File as File +import qualified Propellor.Property.Reboot as Reboot import Data.List @@ -24,9 +25,8 @@ distroKernel = propertyList "digital ocean distro kernel hack" [ "LOAD_KEXEC=true" , "USE_GRUB_CONFIG=true" ] `describe` "kexec configured" - , check (not <$> runningInstalledKernel) - (cmdProperty "reboot" []) - `describe` "running installed kernel" + , check (not <$> runningInstalledKernel) Reboot.now + `describe` "running installed kernel" ] runningInstalledKernel :: IO Bool diff --git a/src/Propellor/Property/Reboot.hs b/src/Propellor/Property/Reboot.hs index 3a725838..c2628689 100644 --- a/src/Propellor/Property/Reboot.hs +++ b/src/Propellor/Property/Reboot.hs @@ -3,6 +3,10 @@ module Propellor.Property.Reboot where import Propellor import Utility.SafeCommand +now :: Property +now = cmdProperty "reboot" [] + `describe` "reboot now" + -- | Schedules a reboot at the end of the current propellor run. -- -- The Result code of the endire propellor run can be checked; -- cgit v1.3-2-g0d8e From 7983046cd593142373aefd23dee6c1d84400cf1b Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 6 Dec 2014 15:28:30 -0400 Subject: update --- src/Propellor/Property/OS.hs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Propellor/Property/OS.hs b/src/Propellor/Property/OS.hs index 2185471d..2720b476 100644 --- a/src/Propellor/Property/OS.hs +++ b/src/Propellor/Property/OS.hs @@ -1,7 +1,7 @@ module Propellor.Property.OS ( cleanInstallOnce, Confirmation(..), - preserveNetworkInterfaces, + preserveNetwork, preserveResolvConf, preserveRootSshAuthorized, oldOSRemoved, @@ -46,7 +46,7 @@ import Control.Exception (throw) -- > & os (System (Debian Unstable) "amd64") -- > & cleanInstallOnce (Confirmed "foo.example.com") -- > `onChange` propertyList "fixing up after clean install" --- > [ preserveNetworkInterfaces +-- > [ preserveNetwork -- > , preserveResolvConf -- > , preserverRootSshAuthorized -- > , Apt.update @@ -181,10 +181,11 @@ confirmed desc (Confirmed c) = property desc $ do return FailedChange else return NoChange --- | /etc/network/interfaces is configured to bring up all interfaces that --- are currently up, using the same IP addresses. -preserveNetworkInterfaces :: Property -preserveNetworkInterfaces = undefined -- TODO +-- | /etc/network/interfaces is configured to bring up the network +-- interface that currently has a default route configured, using +-- the same (static) IP address. +preserveNetwork :: Property +preserveNetwork = undefined -- TODO -- | /etc/resolv.conf is copied the from the old OS preserveResolvConf :: Property -- cgit v1.3-2-g0d8e From 52a83a8911ebfa01224be7f378d76ecc403c8119 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 6 Dec 2014 15:39:02 -0400 Subject: enable shadow passwords I have seen weird almost boot failures when they're disabled, including a segfault. --- src/Propellor/Property/OS.hs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/Propellor/Property/OS.hs b/src/Propellor/Property/OS.hs index 2720b476..6d55072f 100644 --- a/src/Propellor/Property/OS.hs +++ b/src/Propellor/Property/OS.hs @@ -10,6 +10,7 @@ module Propellor.Property.OS ( import Propellor import qualified Propellor.Property.Debootstrap as Debootstrap import qualified Propellor.Property.Ssh as Ssh +import qualified Propellor.Property.User as User import qualified Propellor.Property.File as File import qualified Propellor.Property.Reboot as Reboot import Propellor.Property.Mount @@ -68,6 +69,10 @@ cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $ go = finalized `requires` + -- easy to forget and system may not boot without shadow pw! + User.shadowConfig True + `requires` + -- reboot at end if the rest of the propellor run succeeds Reboot.atEnd True (/= FailedChange) `requires` propellorbootstrapped -- cgit v1.3-2-g0d8e