From ef8c1d22cd6f4957f9d169338899d5282698bc25 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 5 Dec 2014 15:30:34 -0400 Subject: [PATCH] propellor spin --- config-joey.hs | 10 ++++++- debian/changelog | 1 + src/Propellor/Property/Grub.hs | 42 ++++++++++++++++++++++++-- src/Propellor/Property/OS.hs | 54 +++++++++++++++++++--------------- 4 files changed, 81 insertions(+), 26 deletions(-) diff --git a/config-joey.hs b/config-joey.hs index a1ea21d..0e46cb5 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -57,8 +57,16 @@ testvm = host "testvm.kitenet.net" & os (System (Debian Unstable) "amd64") & OS.cleanInstallOnce (OS.Confirmed "testvm.kitenet.net") `onChange` propertyList "fixing up after clean install" - [ OS.preserveRootSshAuthorized + [ User.shadowConfig True + , OS.preserveRootSshAuthorized + , OS.preserveResolvConf + , Grub.boots "/dev/sda" + `requires` Grub.installed Grub.PC ] + & Hostname.sane + & Hostname.searchDomain + & Apt.installed ["linux-image-amd64"] + & Apt.installed ["ssh"] darkstar :: Host darkstar = host "darkstar.kitenet.net" diff --git a/debian/changelog b/debian/changelog index 079a737..7ee1198 100644 --- a/debian/changelog +++ b/debian/changelog @@ -17,6 +17,7 @@ propellor (1.1.0) UNRELEASED; urgency=medium to do an in-place conversion from Fedora to Debian. Use with caution! * Added group-related properties. Thanks, Félix Sipma. * Added Git.barerepo. Thanks, Félix Sipma. + * Added Grub.installed and Grub.boots properties. * hasSomePassword and hasPassword now default to using the name of the host as the Context for the password. To specify a different context, use hasSomePassword' and hasPassword' (API change) diff --git a/src/Propellor/Property/Grub.hs b/src/Propellor/Property/Grub.hs index 841861f..00592d0 100644 --- a/src/Propellor/Property/Grub.hs +++ b/src/Propellor/Property/Grub.hs @@ -7,8 +7,46 @@ import qualified Propellor.Property.Apt as Apt -- | Eg, hd0,0 or xen/xvda1 type GrubDevice = String +-- | Eg, /dev/sda +type OSDevice = String + type TimeoutSecs = Int +-- | Types of machines that grub can boot. +data BIOS = PC | EFI64 | EFI32 | Coreboot | Xen + +-- | Installs the grub package. This does not make grub be used as the +-- bootloader. +-- +-- This includes running update-grub, so that the grub boot menu is +-- created. It will be automatically updated when kernel packages are +-- installed. +installed :: BIOS -> Property +installed bios = + Apt.installed [pkg] `describe` "grub package installed" + `before` + cmdProperty "update-grub" [] + where + pkg = case bios of + PC -> "grub-pc" + EFI64 -> "grub-efi-amd64" + EFI32 -> "grub-efi-ia32" + Coreboot -> "grub-coreboot" + Xen -> "grub-xen" + +-- | Installs grub onto a device, so the system can boot from that device. +-- +-- You may want to install grub to multiple devices; eg for a system +-- that uses software RAID. +-- +-- Note that this property does not check if grub is already installed +-- on the device; it always does the work to reinstall it. It's a good idea +-- to arrange for this property to only run once, by eg making it be run +-- onChange after OS.cleanInstallOnce. +boots :: OSDevice -> Property +boots dev = cmdProperty "grub-install" [dev] + `describe` ("grub boots " ++ dev) + -- | Use PV-grub chaining to boot -- -- Useful when the VPS's pv-grub is too old to boot a modern kernel image. @@ -31,8 +69,8 @@ chainPVGrub rootdev bootdev timeout = combineProperties desc ] , "/boot/load.cf" `File.hasContent` [ "configfile (" ++ bootdev ++ ")/boot/grub/grub.cfg" ] - , Apt.installed ["grub-xen"] - , flagFile (scriptProperty ["update-grub; grub-mkimage --prefix '(" ++ bootdev ++ ")/boot/grub' -c /boot/load.cf -O x86_64-xen /usr/lib/grub/x86_64-xen/*.mod > /boot/xen-shim"]) "/boot/xen-shim" + , installed Xen + , flagFile (scriptProperty ["grub-mkimage --prefix '(" ++ bootdev ++ ")/boot/grub' -c /boot/load.cf -O x86_64-xen /usr/lib/grub/x86_64-xen/*.mod > /boot/xen-shim"]) "/boot/xen-shim" `describe` "/boot-xen-shim" ] where diff --git a/src/Propellor/Property/OS.hs b/src/Propellor/Property/OS.hs index 144b17f..30f8c4b 100644 --- a/src/Propellor/Property/OS.hs +++ b/src/Propellor/Property/OS.hs @@ -2,18 +2,16 @@ module Propellor.Property.OS ( cleanInstallOnce, Confirmation(..), preserveNetworkInterfaces, + preserveResolvConf, preserveRootSshAuthorized, - grubBoots, - GrubDev, rebootForced, - kernelInstalled, oldOSRemoved, ) where 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 Propellor.Property.Mount import Propellor.Property.Chroot.Util (stdPATH) import Utility.SafeCommand @@ -47,12 +45,17 @@ import Control.Exception (throw) -- > & os (System (Debian Unstable) "amd64") -- > & cleanInstallOnce (Confirmed "foo.example.com") -- > `onChange` propertyList "fixing up after clean install" --- > [ preserveNetworkInterfaces +-- > [ User.shadowConfig True +-- > , preserveNetworkInterfaces +-- > , preserveResolvConf -- > , preserverRootSshAuthorized --- > -- , kernelInstalled --- > -- , grubBoots "hd0" +-- > , Apt.update +-- > -- , Grub.boots "/dev/sda" +-- > -- `requires` Grub.installed Grub.PC -- > -- , oldOsRemoved (Confirmed "foo.example.com") -- > ] +-- > & Hostname.sane +-- > & Apt.installed ["linux-image-amd64"] -- > & Apt.installed ["ssh"] -- > & User.hasSomePassword "root" -- > & User.accountFor "joey" @@ -67,8 +70,6 @@ cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $ `requires` propellorbootstrapped `requires` - User.shadowConfig True - `requires` flipped `requires` osbootstrapped @@ -96,8 +97,10 @@ cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $ massRename (renamesout ++ renamesin) removeDirectoryRecursive newOSDir - -- Prepare environment for running additional properties. + -- Prepare environment for running additional properties, + -- overriding old OS's environment. void $ setEnv "PATH" stdPATH True + void $ unsetEnv "LANG" -- Remount /dev, so that block devices etc are -- available for other properties to use. @@ -105,6 +108,14 @@ cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $ warningMessage $ "failed mounting /dev using " ++ devfstype ++ "; falling back to MAKEDEV generic" void $ boolSystem "sh" [Param "-c", Param "cd /dev && /sbin/MAKEDEV generic"] + -- Mount /sys too, needed by eg, grub-mkconfig. + unlessM (mount "sysfs" "sysfs" "/sys") $ + warningMessage "failed mounting /sys" + + -- And /dev/pts, used by apt. + unlessM (mount "devpts" "devpts" "/dev/pts") $ + warningMessage "failed mounting /dev/pts" + liftIO $ writeFile flagfile "" return MadeChange @@ -164,6 +175,16 @@ confirmed desc (Confirmed c) = property desc $ do preserveNetworkInterfaces :: Property preserveNetworkInterfaces = undefined +-- | /etc/resolv.conf is copied the from the old OS +preserveResolvConf :: Property +preserveResolvConf = check (fileExist oldloc) $ + property (newloc ++ " copied from old OS") $ do + ls <- liftIO $ lines <$> readFile oldloc + ensureProperty $ newloc `File.hasContent` ls + where + newloc = "/etc/resolv.conf" + oldloc = oldOSDir ++ newloc + -- | Root's .ssh/authorized_keys has added to it any ssh keys that -- were authorized in the old OS. Any other contents of the file are -- retained. @@ -176,19 +197,6 @@ preserveRootSshAuthorized = check (fileExist oldloc) $ newloc = "/root/.ssh/authorized_keys" oldloc = oldOSDir ++ newloc --- | Installs an appropriate kernel from the OS distribution. -kernelInstalled :: Property -kernelInstalled = undefined - --- | Installs grub onto a device to boot the system. --- --- You may want to install grub to multiple devices; eg for a system --- that uses software RAID. -grubBoots :: GrubDev -> Property -grubBoots = undefined - -type GrubDev = String - -- | Forces an immediate reboot, without contacting the init system. -- -- Can be used after cleanInstallOnce.