better serial number offsets

This commit is contained in:
Joey Hess 2014-04-18 19:06:55 -04:00
parent 80caa6c09d
commit 8e22065def
2 changed files with 41 additions and 45 deletions

View File

@ -3,12 +3,10 @@ module Propellor.Property.Dns (
secondary, secondary,
servingZones, servingZones,
mkSOA, mkSOA,
nextSerialNumber,
incrSerialNumber,
currentSerialNumber,
writeZoneFile, writeZoneFile,
genZoneFile, nextSerialNumber,
genSOA, adjustSerialNumber,
serialNumberOffset,
) where ) where
import Propellor import Propellor
@ -19,7 +17,6 @@ import qualified Propellor.Property.Service as Service
import Utility.Applicative import Utility.Applicative
import Data.List import Data.List
import Data.Time.Clock.POSIX
namedconf :: FilePath namedconf :: FilePath
namedconf = "/etc/bind/named.conf.local" namedconf = "/etc/bind/named.conf.local"
@ -64,10 +61,18 @@ servingZones zs = hasContent namedconf (concatMap confStanza zs)
`onChange` Service.reloaded "bind9" `onChange` Service.reloaded "bind9"
-- | Generates a SOA with some fairly sane numbers in it. -- | Generates a SOA with some fairly sane numbers in it.
mkSOA :: Domain -> [Record] -> SOA --
mkSOA d rs = SOA -- The SerialNumber can be whatever serial number was used by the domain
-- before propellor started managing it. Or 0 if the domain has only ever
-- been managed by propellor.
--
-- You do not need to increment the SerialNumber when making changes!
-- Propellor will automatically add the number of commits in the git
-- repository to the SerialNumber.
mkSOA :: Domain -> SerialNumber -> [Record] -> SOA
mkSOA d sn rs = SOA
{ sDomain = AbsDomain d { sDomain = AbsDomain d
, sSerial = 1 , sSerial = sn
, sRefresh = hours 4 , sRefresh = hours 4
, sRetry = hours 1 , sRetry = hours 1
, sExpire = 2419200 -- 4 weeks , sExpire = 2419200 -- 4 weeks
@ -102,47 +107,33 @@ rValue (TXT s) = [q] ++ filter (/= q) s ++ [q]
-- | Adjusts the serial number of the zone to -- | Adjusts the serial number of the zone to
-- --
-- * Always be larger than the passed SerialNumber
-- * Always be larger than the serial number in the Zone record. -- * Always be larger than the serial number in the Zone record.
-- * Always be larger than the passed SerialNumber
nextSerialNumber :: Zone -> SerialNumber -> Zone nextSerialNumber :: Zone -> SerialNumber -> Zone
nextSerialNumber (Zone soa l) oldserial = Zone soa' l nextSerialNumber z serial = adjustSerialNumber z $ \sn -> succ $ max sn serial
where
soa' = soa { sSerial = succ $ max (sSerial soa) oldserial }
incrSerialNumber :: Zone -> Zone adjustSerialNumber :: Zone -> (SerialNumber -> SerialNumber) -> Zone
incrSerialNumber (Zone soa l) = Zone soa' l adjustSerialNumber (Zone soa l) f = Zone soa' l
where where
soa' = soa { sSerial = succ (sSerial soa) } soa' = soa { sSerial = f (sSerial soa) }
-- | Propellor uses a serial number derived from the current date and time. -- | Count the number of git commits made to the current branch.
-- serialNumberOffset :: IO SerialNumber
-- This ensures that, even if zone files are being generated on serialNumberOffset = fromIntegral . length . lines
-- multiple hosts, the serial numbers will not get out of sync between <$> readProcess "git" ["log", "--pretty=%H"]
-- them.
--
-- Since serial numbers are limited to 32 bits, the number of seconds
-- since the epoch is divided by 5. This will work until the year 2650,
-- at which point this stupid limit had better have been increased to
-- 128 bits. If we didn't divide by 5, it would only work up to 2106!
--
-- Dividing by 5 means that this number only changes once every 5 seconds.
-- If propellor is running more often than once every 5 seconds, you're
-- doing something wrong.
currentSerialNumber :: IO SerialNumber
currentSerialNumber = calc <$> getPOSIXTime
where
calc t = floor (t / 5)
-- | Write a Zone out to a to a file. -- | Write a Zone out to a to a file.
-- --
-- The serial number that is written to the file comes from larger of the -- The serial number in the Zone automatically has the serialNumberOffset
-- Zone's SOA serial number, and the last serial number used in the file. -- added to it. Also, just in case, the old serial number used in the zone
-- This ensures that serial number always increases, while also letting -- file is checked, and if it is somehow larger, its succ is used.
-- a Zone contain an existing serial number, which may be quite large.
writeZoneFile :: Zone -> FilePath -> IO () writeZoneFile :: Zone -> FilePath -> IO ()
writeZoneFile z f = do writeZoneFile z f = do
oldserial <- nextZoneFileSerialNumber f oldserial <- oldZoneFileSerialNumber f
let z' = nextSerialNumber z oldserial offset <- serialNumberOffset
let z' = nextSerialNumber
(adjustSerialNumber z (+ offset))
(succ oldserial)
writeFile f (genZoneFile z') writeFile f (genZoneFile z')
writeZonePropellorFile f z' writeZonePropellorFile f z'
@ -152,9 +143,8 @@ writeZoneFile z f = do
zonePropellorFile :: FilePath -> FilePath zonePropellorFile :: FilePath -> FilePath
zonePropellorFile f = f ++ ".serial" zonePropellorFile f = f ++ ".serial"
nextZoneFileSerialNumber :: FilePath -> IO SerialNumber oldZoneFileSerialNumber :: FilePath -> IO SerialNumber
nextZoneFileSerialNumber = maybe 1 (sSerial . zSOA . incrSerialNumber) oldZoneFileSerialNumber = maybe 0 (sSerial . zSOA) <$$> readZonePropellorFile
<$$> readZonePropellorFile
writeZonePropellorFile :: FilePath -> Zone -> IO () writeZonePropellorFile :: FilePath -> Zone -> IO ()
writeZonePropellorFile f z = writeFile (zonePropellorFile f) (show z) writeZonePropellorFile f z = writeFile (zonePropellorFile f) (show z)
@ -210,3 +200,9 @@ genSOA soa = unlines $
com :: String -> String com :: String -> String
com s = "; " ++ s com s = "; " ++ s
-- | Generates a Zone for a particular Domain from the DNS properies of all
-- hosts that propellor knows about that are in that Domain.
genZone :: [Host] -> Domain -> SOA -> Zone
genZone hosts domain soa = Zone soa zhosts
where
zhosts = undefined -- TODO

View File

@ -2,7 +2,7 @@ module Propellor.Types.Dns where
import Propellor.Types.OS (HostName) import Propellor.Types.OS (HostName)
import Foreign.C.Types import Data.Word
type Domain = String type Domain = String
@ -65,7 +65,7 @@ getIPAddr (Address addr) = Just addr
getIPAddr _ = Nothing getIPAddr _ = Nothing
-- | Bind serial numbers are unsigned, 32 bit integers. -- | Bind serial numbers are unsigned, 32 bit integers.
type SerialNumber = CInt type SerialNumber = Word32
-- | Domains in the zone file must end with a period if they are absolute. -- | Domains in the zone file must end with a period if they are absolute.
-- --