Read quarantine evidence back when planning a seed
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful

This commit is contained in:
2026-09-19 23:14:26 +10:00
parent 6730b8bcb2
commit 949662a334
3 changed files with 380 additions and 13 deletions
+73 -9
View File
@@ -23,6 +23,11 @@ type ZoneDiskState struct {
JournalBegin int64
JournalEnd int64
JournalOK bool
// OrphanSerial is the furthest serial recorded in a quarantine sibling on
// disk. It is the only record of how far the zone had advanced once a
// transition is interrupted between the renames and the new file landing.
OrphanSerial int64
OrphanSerialOK bool
}
// SeedPlan is the decision taken before writing a skeleton zone file.
@@ -92,12 +97,52 @@ func highestSerial(st ZoneDiskState) (int64, bool) {
if st.Journal && st.JournalOK && (!known || serialLT(h, st.JournalEnd)) {
h, known = st.JournalEnd, true
}
if st.OrphanSerialOK && (!known || serialLT(h, st.OrphanSerial)) {
h, known = st.OrphanSerial, true
}
return h, known
}
func quarantineSuffix(st ZoneDiskState) string {
h, _ := highestSerial(st)
return fmt.Sprintf(".orphaned-%d", h)
return quarantineMarker + strconv.FormatInt(h, 10)
}
// parseOrphanSerial reads the serial back out of a name moveAside produced,
// with or without the counter it appends when the destination is taken.
func parseOrphanSerial(name string) (int64, bool) {
i := strings.LastIndex(name, quarantineMarker)
if i < 0 {
return 0, false
}
digits := name[i+len(quarantineMarker):]
n := 0
for n < len(digits) && digits[n] >= '0' && digits[n] <= '9' {
n++
}
if n == 0 {
return 0, false
}
serial, err := strconv.ParseUint(digits[:n], 10, 32)
if err != nil {
return 0, false
}
return int64(serial), true
}
func orphanSerial(names []string) (int64, bool) {
var h int64
var known bool
for _, name := range names {
s, ok := parseOrphanSerial(name)
if !ok {
continue
}
if !known || serialLT(h, s) {
h, known = s, true
}
}
return h, known
}
func nextSerial(st ZoneDiskState) int64 {
@@ -199,10 +244,14 @@ func beUint32(b []byte) uint32 {
// stdout that was truncated, empty or partially written is rejected rather than
// read as "fresh install".
const (
probeBegin = "zonestate-begin-v1"
probeEnd = "zonestate-end-v1"
probeHeadOpen = "head<<"
probeHeadShut = ">>head"
probeBegin = "zonestate-begin-v1"
probeEnd = "zonestate-end-v1"
probeHeadOpen = "head<<"
probeHeadShut = ">>head"
probeOrphanOpen = "orphans<<"
probeOrphanShut = ">>orphans"
// quarantineMarker joins a preserved file to the serial it was holding.
quarantineMarker = ".orphaned-"
)
// zoneStateProbe reads only the head of the zone file: the SOA is the first
@@ -215,6 +264,10 @@ func zoneStateProbe(path string) string {
zone, probeHeadOpen, zone, probeHeadShut),
fmt.Sprintf("if [ -f %s ]; then printf 'journal=1\\n'; hdr=$(od -An -v -tx1 -N32 %s) || exit 1; printf 'jnl=%%s\\n' \"$(printf '%%s' \"$hdr\" | tr -d ' \\n')\"; else printf 'journal=0\\n'; fi",
jnl, jnl),
// The quarantine siblings outlive the files they replaced, so they are
// the floor a reseed must stay above after an interrupted transition.
fmt.Sprintf("printf '%s\\n'\nfor f in %s* %s*; do if [ -e \"$f\" ]; then printf '%%s\\n' \"$f\"; fi; done\nprintf '%s\\n'",
probeOrphanOpen, shellQuote(path+quarantineMarker), shellQuote(JournalPath(path)+quarantineMarker), probeOrphanShut),
fmt.Sprintf("printf '%s\\n'", probeEnd),
}, "\n")
}
@@ -223,9 +276,9 @@ func zoneStateProbe(path string) string {
// half-written or empty probe must never be mistaken for an empty filesystem.
func parseZoneDiskState(out string) (ZoneDiskState, bool) {
var st ZoneDiskState
var head []string
var sawBegin, sawEnd, inHead, headShut bool
var zoneDecls, journalDecls, headerDecls int
var head, orphans []string
var sawBegin, sawEnd, inHead, headShut, inOrphans, orphansShut bool
var zoneDecls, journalDecls, headerDecls, orphanDecls int
for _, line := range strings.Split(out, "\n") {
switch {
@@ -233,12 +286,20 @@ func parseZoneDiskState(out string) (ZoneDiskState, bool) {
inHead, headShut = false, true
case inHead:
head = append(head, line)
case inOrphans && line == probeOrphanShut:
inOrphans, orphansShut = false, true
case inOrphans:
if line != "" {
orphans = append(orphans, line)
}
case line == probeBegin:
sawBegin = true
case line == probeEnd:
sawEnd = true
case line == probeHeadOpen:
inHead = true
case line == probeOrphanOpen:
inOrphans, orphanDecls = true, orphanDecls+1
case line == "zonefile=1":
st.ZoneFile = true
zoneDecls++
@@ -258,7 +319,9 @@ func parseZoneDiskState(out string) (ZoneDiskState, bool) {
}
switch {
case !sawBegin || !sawEnd || inHead:
case !sawBegin || !sawEnd || inHead || inOrphans:
return ZoneDiskState{}, false
case orphanDecls != 1 || !orphansShut:
return ZoneDiskState{}, false
case zoneDecls != 1 || journalDecls != 1:
return ZoneDiskState{}, false
@@ -273,6 +336,7 @@ func parseZoneDiskState(out string) (ZoneDiskState, bool) {
if st.ZoneFile {
st.ZoneSerial, st.ZoneSerialOK = ParseZoneSerial(strings.Join(head, "\n"))
}
st.OrphanSerial, st.OrphanSerialOK = orphanSerial(orphans)
return st, true
}