99 lines
4.1 KiB
Go
99 lines
4.1 KiB
Go
// Package backup implements the WorldArchiver abstraction (spec §19). The
|
|
// reaper and the restore endpoint speak only to the interface and never learn
|
|
// whether the backend is a tar file, a VolumeSnapshot, or a Longhorn backup —
|
|
// ArchiveRef is deliberately opaque.
|
|
package backup
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// ArchiveRef is an opaque handle to a stored world archive. Depending on the
|
|
// backend it may be a tar path, a VolumeSnapshot name, or a Longhorn backup URL.
|
|
type ArchiveRef string
|
|
|
|
// WorldArchiver archives, restores, and deletes a server's world. The signature
|
|
// is intentionally "archive a world" rather than "write bytes": snapshot
|
|
// backends (VolumeSnapshot/Longhorn) cannot produce an io.Reader — they create
|
|
// K8s objects referencing the source PVC (spec §19).
|
|
type WorldArchiver interface {
|
|
// Archive captures the world living on pvc for server. It returns only once
|
|
// the archive is complete and durable: a failed or interrupted Archive leaves
|
|
// nothing that could be mistaken for a finished archive.
|
|
Archive(ctx context.Context, server, pvc string) (Archived, error)
|
|
// Restore writes a previously archived world into targetPVC.
|
|
Restore(ctx context.Context, ref ArchiveRef, targetPVC string) error
|
|
// Delete removes the archive identified by ref.
|
|
Delete(ctx context.Context, ref ArchiveRef) error
|
|
}
|
|
|
|
// Archived is what one Archive call stored.
|
|
type Archived struct {
|
|
Ref ArchiveRef
|
|
Size int64 // stored bytes
|
|
// SHA256 is the hex digest of the stored archive, "" when the backend keeps
|
|
// none. world_backups.sha256 records it, so a later Verify tells an archive
|
|
// that rotted on disk from a good one.
|
|
SHA256 string
|
|
// Skipped lists the world entries the archive leaves out (symbolic links,
|
|
// devices, sockets, named pipes), relative to the world root.
|
|
Skipped []string
|
|
}
|
|
|
|
// ErrCorrupt marks an archive that cannot be read back in full, or whose bytes
|
|
// no longer match the checksum recorded for it. It is the only Verify error that
|
|
// condemns the archive: any other (a mount that is not there, a cancelled
|
|
// context) says nothing about it.
|
|
var ErrCorrupt = errors.New("backup: archive corrupt")
|
|
|
|
// Verifier is implemented by backends that can read a stored archive back end to
|
|
// end. Verify returns the archive's SHA256 as read; want, when not "", is the
|
|
// digest it must match (an archive recorded before checksums were kept has
|
|
// none, and its read-back establishes one).
|
|
type Verifier interface {
|
|
Verify(ctx context.Context, ref ArchiveRef, want string) (sha256 string, err error)
|
|
}
|
|
|
|
// Sweeper is implemented by backends that can find what interrupted archives
|
|
// leave behind.
|
|
type Sweeper interface {
|
|
// Sweep removes every unfinished archive last written before partialBefore.
|
|
// A finished archive that live does not claim (its record was never inserted)
|
|
// is removed once it was last written before orphanBefore, and reported and
|
|
// kept until then.
|
|
Sweep(ctx context.Context, live func(ArchiveRef) bool, partialBefore, orphanBefore time.Time) (Swept, error)
|
|
}
|
|
|
|
// Swept is what one Sweep found.
|
|
type Swept struct {
|
|
Removed []string // paths removed
|
|
// Orphans are finished archives no record claims that are still young enough
|
|
// to keep. A backup whose record insert failed looks like this, and so do the
|
|
// archives of a store brought back before the database that records them:
|
|
// removing them early could throw away the only copy of a world.
|
|
Orphans []string
|
|
OrphanBytes int64
|
|
}
|
|
|
|
// PVCResolver maps a PVC name to the local filesystem path where it is mounted.
|
|
// In production the reaper Job mounts the source/backup PVCs and supplies a
|
|
// resolver over those mount points; tests supply temp dirs.
|
|
type PVCResolver func(pvc string) (string, error)
|
|
|
|
// StaticResolver resolves PVC names from a fixed map, erroring on unknown names.
|
|
func StaticResolver(paths map[string]string) PVCResolver {
|
|
return func(pvc string) (string, error) {
|
|
if p, ok := paths[pvc]; ok {
|
|
return p, nil
|
|
}
|
|
return "", &UnknownPVCError{PVC: pvc}
|
|
}
|
|
}
|
|
|
|
// UnknownPVCError is returned when a resolver cannot map a PVC name.
|
|
type UnknownPVCError struct{ PVC string }
|
|
|
|
func (e *UnknownPVCError) Error() string { return "backup: unknown pvc " + e.PVC }
|