Documentation
¶
Overview ¶
Package backup creates integrity-checked, portable snapshots without copying live WAL files.
On SQLite a snapshot holds a VACUUM INTO copy (ether.db). On PostgreSQL, where file copies do not apply, it holds a logical dump of every table (database.jsonl, see store.ExportLogical) and the manifest records dialect "postgres"; a restored dump is imported into an empty database on the next start (ImportPending).
Index ¶
- Constants
- Variables
- func FilesystemWarning(ctx context.Context, dataDir string) string
- func ImportPending(ctx context.Context, db *sql.DB, dataDir string) (bool, error)
- func IsEncrypted(b []byte) bool
- func NewDecryptReader(r io.Reader, password string) (io.Reader, error)
- func NewEncryptWriter(w io.Writer, password string) (io.WriteCloser, error)
- func RestoreBundle(ctx context.Context, r io.Reader, destination string) error
- func RestoreEncrypted(ctx context.Context, r io.Reader, password, destination string) error
- type Manifest
- type Node
- type Service
- func (s *Service) Configure(ctx context.Context, v Settings) error
- func (s *Service) Create(ctx context.Context) (Snapshot, error)
- func (s *Service) Handler() http.Handler
- func (s *Service) List() ([]Snapshot, error)
- func (s *Service) Run(ctx context.Context, onError func(error))
- func (s *Service) SetCluster(n Node)
- func (s *Service) Settings(ctx context.Context) (Settings, error)
- func (s *Service) Snapshots(ctx context.Context) ([]Snapshot, error)
- type Settings
- type Snapshot
Constants ¶
const EncryptedExtension = ".etherbackup"
EncryptedExtension names exported bundles.
const LogicalDumpFile = "database.jsonl"
LogicalDumpFile is the PostgreSQL bundle's database entry. Left in a data directory by a restore, it is imported into the (empty) database at start.
Variables ¶
var ( // ErrPasswordPolicy rejects an export password that is too short. ErrPasswordPolicy = errors.New("backup password must be at least 12 characters") // ErrBadPassword is a wrong password or a damaged first chunk; the // two cannot be told apart. ErrBadPassword = errors.New("wrong password, or the backup is damaged") // ErrDamaged is an encrypted backup that fails authentication after // its first chunk, or is truncated or extended. ErrDamaged = errors.New("encrypted backup is damaged or incomplete") // ErrNotEncrypted is a file without the encrypted backup header. ErrNotEncrypted = errors.New("not an encrypted Ether backup") )
Functions ¶
func FilesystemWarning ¶
FilesystemWarning detects the mount containing dataDir on Linux and macOS.
func ImportPending ¶
ImportPending imports a restored logical dump (LogicalDumpFile in dataDir) into db, which must be freshly migrated and empty, then renames the dump so it is imported only once. It reports whether it imported.
func IsEncrypted ¶ added in v0.2.4
IsEncrypted reports whether b starts with the encrypted backup magic.
func NewDecryptReader ¶ added in v0.2.4
NewDecryptReader checks the header of an encrypted bundle, derives the key from password and authenticates the first chunk, so a wrong password fails here (ErrBadPassword) before anything is read from the bundle.
func NewEncryptWriter ¶ added in v0.2.4
NewEncryptWriter returns a writer that encrypts a bundle to w under password. Close must be called to write the final chunk; it does not close w.
func RestoreBundle ¶
RestoreBundle validates all content before atomically installing into a new directory. It deliberately refuses existing destinations and never replaces a running database.
func RestoreEncrypted ¶ added in v0.2.4
RestoreEncrypted is RestoreBundle for a password-encrypted bundle (NewEncryptWriter). Nothing is installed unless the whole file authenticates, including its final chunk and the absence of anything after it.
Types ¶
type Node ¶
type Node struct {
ID, Name string
}
Node identifies the cluster node a Service runs on.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
func (*Service) Handler ¶
Handler is the owner's backup API, mounted behind the admin API's requireAuth (which also enforces the CSRF header):
GET / bundles, schedule and a filesystem warning
POST /settings change the schedule owner, setup complete
POST / create a bundle now owner, setup complete
POST /export/{name} {"password"} → .etherbackup owner, setup complete
POST /restore stage an uploaded bundle (below)
A bundle holds config.json, the database and the credential key that unseals them, so it leaves the server only encrypted with a password the owner chooses (encrypted.go). Bundles kept in the data directory stay owner-only (0600) files.
Restore takes the raw file (application/octet-stream). An encrypted bundle needs its password in X-Ether-Backup-Password (base64 of the UTF-8 password) and is accepted wherever the admin API is: from the owner, or before setup completes from the local network (a fresh server being restored from backup). An unencrypted bundle from before encryption needs the signed-in owner and X-Ether-Backup-Legacy: plaintext. Either way the bundle is only validated and staged in a new directory; nothing replaces the running server until the operator restarts Ether from that directory.
func (*Service) SetCluster ¶
SetCluster marks the server as clustered: every bundle is recorded in the shared database with the node that holds it (so any node lists the whole cluster's backups), and the shared backup directory setting is honoured. A standalone server never calls it and keeps its bundles in its data directory only.
type Settings ¶
type Settings struct {
IntervalHours int `json:"intervalHours"`
Retain int `json:"retain"`
LastRun int64 `json:"lastRun"`
// at the same path, where scheduled and manual backups are written
// instead of the writing node's data directory. Empty keeps them on
// the node that made them.
SharedDir string `json:"sharedDir,omitempty"`
}
type Snapshot ¶
type Snapshot struct {
Name string `json:"name"`
Size int64 `json:"size"`
Created time.Time `json:"created"`
// Clustered servers only: the node that wrote the bundle and holds it
// (unless Shared), whether it is in the shared backup directory, and
// Remote when it is on another node's disk (download it there).
Node string `json:"node,omitempty"`
NodeID string `json:"nodeId,omitempty"`
Remote bool `json:"remote,omitempty"`
}