summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoey Hess2016-06-13 17:54:19 -0400
committerJoey Hess2016-06-13 17:54:19 -0400
commitebaaa9db50691658094488d1d1956e6082305a1b (patch)
tree3504388f8ce082306c8e31470b13f0db838837cb /src
parent697d18c018330611d389093f494915ceae9b6406 (diff)
parent5e65dc8abe3237fdc189d8a80d3eb3e193369011 (diff)
Merge remote-tracking branch 'spwhitton/reboot'
Diffstat (limited to 'src')
-rw-r--r--src/Propellor/Property/HostingProvider/DigitalOcean.hs25
-rw-r--r--src/Propellor/Property/HostingProvider/Exoscale.hs32
-rw-r--r--src/Propellor/Property/Reboot.hs101
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)