diff options
| author | Joey Hess <joeyh@joeyh.name> | 2016-06-13 17:54:19 -0400 |
|---|---|---|
| committer | Joey Hess <joeyh@joeyh.name> | 2016-06-13 17:54:19 -0400 |
| commit | ebaaa9db50691658094488d1d1956e6082305a1b (patch) | |
| tree | 3504388f8ce082306c8e31470b13f0db838837cb /src | |
| parent | 697d18c018330611d389093f494915ceae9b6406 (diff) | |
| parent | 5e65dc8abe3237fdc189d8a80d3eb3e193369011 (diff) | |
Merge remote-tracking branch 'spwhitton/reboot'
Diffstat (limited to 'src')
| -rw-r--r-- | src/Propellor/Property/HostingProvider/DigitalOcean.hs | 25 | ||||
| -rw-r--r-- | src/Propellor/Property/HostingProvider/Exoscale.hs | 32 | ||||
| -rw-r--r-- | src/Propellor/Property/Reboot.hs | 101 |
3 files changed, 133 insertions, 25 deletions
diff --git a/src/Propellor/Property/HostingProvider/DigitalOcean.hs b/src/Propellor/Property/HostingProvider/DigitalOcean.hs index c1e0ffc9..084faa36 100644 --- a/src/Propellor/Property/HostingProvider/DigitalOcean.hs +++ b/src/Propellor/Property/HostingProvider/DigitalOcean.hs @@ -7,8 +7,6 @@ import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.File as File import qualified Propellor.Property.Reboot as Reboot -import Data.List - -- | Digital Ocean does not provide any way to boot -- the kernel provided by the distribution, except using kexec. -- Without this, some old, and perhaps insecure kernel will be used. @@ -25,25 +23,4 @@ distroKernel = propertyList "digital ocean distro kernel hack" $ props [ "LOAD_KEXEC=true" , "USE_GRUB_CONFIG=true" ] `describe` "kexec configured" - & check (not <$> runningInstalledKernel) Reboot.now - `describe` "running installed kernel" - -runningInstalledKernel :: IO Bool -runningInstalledKernel = do - kernelver <- takeWhile (/= '\n') <$> readProcess "uname" ["-r"] - when (null kernelver) $ - error "failed to read uname -r" - kernelimages <- concat <$> mapM kernelsIn ["/", "/boot/"] - when (null kernelimages) $ - error "failed to find any installed kernel images" - findVersion kernelver <$> - readProcess "file" ("-L" : kernelimages) - --- | File output looks something like this, we want to unambiguously --- match the running kernel version: --- Linux kernel x86 boot executable bzImage, version 3.16-3-amd64 (debian-kernel@lists.debian.org) #1 SMP Debian 3.1, RO-rootFS, swap_dev 0x2, Normal VGA -findVersion :: String -> String -> Bool -findVersion ver s = (" version " ++ ver ++ " ") `isInfixOf` s - -kernelsIn :: FilePath -> IO [FilePath] -kernelsIn d = filter ("vmlinu" `isInfixOf`) <$> dirContents d + & Reboot.toDistroKernel diff --git a/src/Propellor/Property/HostingProvider/Exoscale.hs b/src/Propellor/Property/HostingProvider/Exoscale.hs new file mode 100644 index 00000000..c6244d46 --- /dev/null +++ b/src/Propellor/Property/HostingProvider/Exoscale.hs @@ -0,0 +1,32 @@ +-- | Maintainer: Sean Whitton <spwhitton@spwhitton.name> + +module Propellor.Property.HostingProvider.Exoscale ( + distroKernel, +) where + +import Propellor.Base +import qualified Propellor.Property.File as File +import qualified Propellor.Property.Grub as Grub +import qualified Propellor.Property.Apt as Apt +import qualified Propellor.Property.Reboot as Reboot + +-- | The current Exoshare Debian image doesn't install GRUB, so this property +-- makes sure GRUB is installed and correctly configured +-- +-- In case an old, insecure kernel is running, we check for an old kernel +-- version and reboot immediately if one is found. +-- +-- Note that we ignore anything after the first hyphen when considering whether +-- the running kernel's version is older than the Debian-supplied kernel's +-- version. +distroKernel :: Architecture -> Property DebianLike +distroKernel arch = go `flagFile` theFlagFile + where + go = combineProperties "boots distro kernel" $ props + & Apt.installed ["grub2", "linux-image-" ++ arch] + & Grub.boots "/dev/vda" + & Grub.mkConfig + -- Since we're rebooting we have to manually create the flagfile + & File.hasContent theFlagFile [""] + & Reboot.toDistroKernel + theFlagFile = "/etc/propellor-distro-kernel" diff --git a/src/Propellor/Property/Reboot.hs b/src/Propellor/Property/Reboot.hs index 5b854fa3..7733c0d2 100644 --- a/src/Propellor/Property/Reboot.hs +++ b/src/Propellor/Property/Reboot.hs @@ -1,7 +1,18 @@ -module Propellor.Property.Reboot where +module Propellor.Property.Reboot ( + now, + atEnd, + toDistroKernel, + toKernelNewerThan, +) where import Propellor.Base +import Data.List +import Data.Version +import Text.ParserCombinators.ReadP + +type KernelVersion = String + now :: Property Linux now = tightenTargets $ cmdProperty "reboot" [] `assume` MadeChange @@ -28,3 +39,91 @@ atEnd force resultok = property "scheduled reboot at end of propellor run" $ do rebootparams | force = [Param "--force"] | otherwise = [] + +-- | Reboots immediately if a kernel other than the distro-installed kernel is +-- running. +-- +-- This will only work if you have taken measures to ensure that the other +-- kernel won't just get booted again. See 'Propellor.Property.DigitalOcean' +-- for an example of how to do this. +toDistroKernel :: Property DebianLike +toDistroKernel = check (not <$> runningInstalledKernel) now + `describe` "running installed kernel" + +-- | Given a kernel version string @v@, reboots immediately if the running +-- kernel version is strictly less than @v@ and there is an installed kernel +-- version is greater than or equal to @v@. Dies if the requested kernel +-- version is not installed. +-- +-- For this to be useful, you need to have ensured that the installed kernel +-- with the highest version number is the one that will be started after a +-- reboot. +-- +-- This is useful when upgrading to a new version of Debian where you need to +-- ensure that a new enough kernel is running before ensuring other properties. +toKernelNewerThan :: KernelVersion -> Property DebianLike +toKernelNewerThan ver = + property' ("reboot to kernel newer than " ++ ver) $ \w -> do + wantV <- tryReadVersion ver + runningV <- tryReadVersion =<< liftIO runningKernelVersion + installedV <- maximum <$> + (mapM tryReadVersion =<< liftIO installedKernelVersions) + if runningV >= wantV then noChange + else if installedV >= wantV + then ensureProperty w now + -- We error out here because other properties + -- may be incorrectly ensured on a version + -- that's too old. E.g. Sbuild.built can fail + -- to add the config line `union-type=overlay` + else errorMessage ("kernel newer than " + ++ ver + ++ " not installed") + +runningInstalledKernel :: IO Bool +runningInstalledKernel = do + kernelver <- runningKernelVersion + when (null kernelver) $ + error "failed to read uname -r" + kernelimages <- installedKernelImages + when (null kernelimages) $ + error "failed to find any installed kernel images" + findVersion kernelver <$> + readProcess "file" ("-L" : kernelimages) + +runningKernelVersion :: IO KernelVersion +runningKernelVersion = takeWhile (/= '\n') <$> readProcess "uname" ["-r"] + +installedKernelImages :: IO [String] +installedKernelImages = concat <$> mapM kernelsIn ["/", "/boot/"] + +-- | File output looks something like this, we want to unambiguously +-- match the running kernel version: +-- Linux kernel x86 boot executable bzImage, version 3.16-3-amd64 (debian-kernel@lists.debian.org) #1 SMP Debian 3.1, RO-rootFS, swap_dev 0x2, Normal VGA +findVersion :: KernelVersion -> String -> Bool +findVersion ver s = (" version " ++ ver ++ " ") `isInfixOf` s + +installedKernelVersions :: IO [KernelVersion] +installedKernelVersions = do + kernelimages <- installedKernelImages + when (null kernelimages) $ + error "failed to find any installed kernel images" + imageLines <- lines <$> readProcess "file" ("-L" : kernelimages) + return $ extractKernelVersion <$> imageLines + +kernelsIn :: FilePath -> IO [FilePath] +kernelsIn d = filter ("vmlinu" `isInfixOf`) <$> dirContents d + +extractKernelVersion :: String -> KernelVersion +extractKernelVersion = + unwords . take 1 . drop 1 . dropWhile (/= "version") . words + +-- adapted from Utility.PartialPrelude.readish +readVersionMaybe :: KernelVersion -> Maybe Version +readVersionMaybe ver = case readP_to_S parseVersion ver of + ((x,_):_) -> Just x + _ -> Nothing + +tryReadVersion :: KernelVersion -> Propellor Version +tryReadVersion ver = case readVersionMaybe ver of + Just x -> return x + Nothing -> errorMessage ("couldn't parse version " ++ ver) |
