propellor spin

This commit is contained in:
Joey Hess 2014-12-04 16:14:01 -04:00
parent f78c2f16d1
commit a2537ce16c
Failed to extract signature
3 changed files with 70 additions and 30 deletions

View File

@ -54,11 +54,11 @@ hosts = -- (o) `
testvm :: Host
testvm = host "testvm.kitenet.net"
& Chroot.provisioned (Chroot.debootstrapped (System (Debian Unstable) "amd64") Debootstrap.DefaultConfig "/new-os")
-- & OS.cleanInstall (OS.Confirmed "foo.example.com") []
-- `onChange` propertyList "fixing up after clean install"
-- [
-- ]
& os (System (Debian Unstable) "amd64")
& OS.cleanInstallOnce (OS.Confirmed "testvm.kitenet.netno")
`onChange` propertyList "fixing up after clean install"
[ OS.preserveRootSshAuthorized
]
darkstar :: Host
darkstar = host "darkstar.kitenet.net"

View File

@ -1,20 +1,19 @@
module Propellor.Property.OS (
cleanInstallOnce,
Confirmed(..),
Confirmation(..),
preserveNetworkInterfaces,
preserveRootSshAuthorized,
grubBoots,
GrubDev(..),
oldOSKernelPreserved,
kernelInstalled,
oldOSRemoved,
) where
import Propellor
import qualified Propellor.Property.Chroot as Chroot
import qualified Propellor.Property.Debootstrap as Debootstrap
import qualified Propellor.Property.File as File
import qualified Propellor.Property.Ssh as Ssh
import Utility.FileMode
import qualified Propellor.Property.User as User
-- | Replaces whatever OS was installed before with a clean installation
-- of the OS that the Host is configured to have.
@ -22,8 +21,10 @@ import Utility.FileMode
-- This can replace one Linux distribution with different one.
-- But, it can also fail and leave the system in an unbootable state.
--
-- The files from the old os will be left in /old-os
--
-- To avoid this property being accidentially used, you have to provide
-- a Confirmed containing the name of the host that you intend to apply the
-- a Confirmation containing the name of the host that you intend to apply the
-- property to.
--
-- This property only runs once. The cleanly installed system will have
@ -35,53 +36,73 @@ import Utility.FileMode
-- working system. For example:
--
-- > & os (System (Debian Unstable) "amd64")
-- > & cleanInstall (Confirmed "foo.example.com") [BackupOldOS, UseOldKernel]
-- > & cleanInstall (Confirmed "foo.example.com")
-- > `onChange` propertyList "fixing up after clean install"
-- > [ preserveNetworkInterfaces
-- > , preserverRootSshAuthorized
-- > , oldOSKernelPreserved
-- > -- , kernelInstalled
-- > -- , grubBoots "hd0"
-- > -- , oldOsRemoved
-- > ]
-- > & Apt.installed ["ssh"]
-- > & User.hasSomePassword "root"
-- > & User.accountFor "joey"
-- > & User.hasSomePassword "joey"
-- > -- rest of system properties here
cleanInstallOnce :: Confirmed -> [Tweak] -> Property
cleanInstallOnce confirmed tweaks = check (not <$> doesFileExist flagfile) $
property "OS cleanly installed" $ do
checkConfirmed confirmed
cleanInstallOnce :: Confirmation -> Property
cleanInstallOnce confirmation = check (not <$> doesFileExist flagfile) $
confirmed
`before`
osbootstrapped
`before`
transitioned
`before`
User.shadowConfig True
`before`
propellorbootstrapped
`before`
finalized
where
confirmed = property "clean install confirmed" $ do
checkConfirmed confirmation
return NoChange
osbootstrapped = withOS "/new-os bootstrapped" $ \o -> case o of
(Just d@(System (Debian _) _)) -> debootstrap d
(Just u@(System (Ubuntu _) _)) -> debootstrap u
_ -> error "os is not declared to be Debian or Ubuntu"
debootstrap targetos = ensureProperty $ toProp $
Debootstrap.built "/new-os" targetos Debootstrap.DefaultConfig
transitioned = property "/new-os moved into place" $
error "TODO"
-- debootstrap /new-os chroot, but don't run propellor
-- inside the chroot.
-- unmount all mounts
-- move all directories to /old-os,
-- except for /boot and /lib/modules when UseOldKernel
-- (or, delete when not BackupOldOS)
-- move /new-os to /
-- touch flagfile
propellorbootstrapped = property "propellor re-debootstrapped in new os" $
error "TODO"
-- re-bootstrap propellor in /usr/local/propellor,
-- (using git repo bundle, privdata file, and possibly
-- git repo url, which all need to be arranged to
-- be present in /old-os's /usr/local/propellor)
-- enable shadow passwords (to avoid foot-shooting)
-- return MadeChange
where
finalized = property "clean install finalized" $ do
liftIO $ writeFile flagfile ""
return MadeChange
flagfile = "/etc/propellor-cleaninstall"
data Confirmed = Confirmed HostName
data Confirmation = Confirmed HostName
checkConfirmed :: Confirmed -> Propellor ()
checkConfirmed :: Confirmation -> Propellor ()
checkConfirmed (Confirmed c) = do
hostname <- asks hostName
when (hostname /= c) $
errorMessage "Run with a bad confirmation, not matching hostname."
-- | Sometimes you want an almost clean install, but with some tweaks.
data Tweak
= UseOldKernel -- ^ Leave /boot and /lib/modules from old OS, so the system can boot using them as before
| BackupOldOS -- ^ Back up old OS to /old-os, to avoid losing any important files
-- /etc/network/interfaces is configured to bring up all interfaces that
-- are currently up, using the same IP addresses.
preserveNetworkInterfaces :: Property
@ -103,6 +124,13 @@ preserveRootSshAuthorized = check (doesDirectoryExist oldloc) $
kernelInstalled :: Property
kernelInstalled = undefined
-- Copies kernel images, initrds, and modules from /old-os
-- into the new system.
--
-- TODO: grub config?
oldOSKernelPreserved :: Property
oldOSKernelPreserved = undefined
-- Installs grub onto a device to boot the system.
--
-- You may want to install grub to multiple devices; eg for a system
@ -113,7 +141,7 @@ grubBoots = undefined
type GrubDev = String
-- Removes the old OS's backup from /old-os
oldOSRemoved :: Confirmed -> Property
oldOSRemoved :: Confirmation -> Property
oldOSRemoved confirmed = check (doesDirectoryExist oldOsDir) $
property "old OS backup removed" $ do
checkConfirmed confirmed

View File

@ -84,3 +84,15 @@ hasGroup user group' = check test $ cmdProperty "adduser"
`describe` unwords ["user", user, "in group", group']
where
test = not . elem group' . words <$> readProcess "groups" [user]
-- | Controls whether shadow passwords are enabled or not.
shadowConfig :: Bool -> Property
shadowConfig True = check (not <$> shadowExists) $
cmdProperty "shadowconfig" ["on"]
`describe` "shadow passwords enabled"
shadowConfig False = check shadowExists $
cmdProperty "shadowconfig" ["off"]
`describe` "shadow passwords disabled"
shadowExists :: IO Bool
shadowExists = doesFileExist "/etc/shadow"