feat(stdlib): added a basic borg client custom promise
This commit is contained in:
parent
518cbed944
commit
48f2e9a2cb
5 changed files with 174 additions and 1 deletions
27
stdlib/backups/borg/borg-script-template
Normal file
27
stdlib/backups/borg/borg-script-template
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
on_exit() {
|
||||||
|
exit $?
|
||||||
|
}
|
||||||
|
trap on_exit EXIT
|
||||||
|
|
||||||
|
archiveName="%s-%s-$(date +%%Y-%%m-%%dT%%H:%%M:%%S)"
|
||||||
|
archiveSuffix=".failed"
|
||||||
|
|
||||||
|
# Run borg init if the repo doesn't exist yet
|
||||||
|
if ! borg list > /dev/null; then
|
||||||
|
borg init --encryption none
|
||||||
|
fi
|
||||||
|
(
|
||||||
|
borg create \
|
||||||
|
--compression auto,zstd \
|
||||||
|
"::${archiveName}${archiveSuffix}" \
|
||||||
|
'%s'
|
||||||
|
)
|
||||||
|
borg rename "::${archiveName}${archiveSuffix}" "${archiveName}"
|
||||||
|
|
||||||
|
borg prune \
|
||||||
|
--keep-daily=14 --keep-monthly=3 --keep-weekly=4 \
|
||||||
|
--glob-archives '%s-%s*'
|
||||||
|
borg compact
|
123
stdlib/backups/borg/client.go
Normal file
123
stdlib/backups/borg/client.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package borg
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"log/slog"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
gonf "git.adyxax.org/adyxax/gonf/v2/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed borg-script-template
|
||||||
|
var borg_script_template string
|
||||||
|
|
||||||
|
//go:embed systemd-service-template
|
||||||
|
var systemd_service_template string
|
||||||
|
|
||||||
|
//go:embed systemd-timer-template
|
||||||
|
var systemd_timer_template string
|
||||||
|
|
||||||
|
type Job struct {
|
||||||
|
hostname string
|
||||||
|
path string
|
||||||
|
privateKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type BorgClient struct {
|
||||||
|
chain []gonf.Promise
|
||||||
|
jobs map[string]*Job // name -> privateKey
|
||||||
|
path string
|
||||||
|
status gonf.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BorgClient) IfRepaired(p ...gonf.Promise) gonf.Promise {
|
||||||
|
b.chain = append(b.chain, p...)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BorgClient) Promise() *BorgClient {
|
||||||
|
gonf.MakeCustomPromise(b).Promise()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BorgClient) Resolve() {
|
||||||
|
b.status = gonf.KEPT
|
||||||
|
// borg package
|
||||||
|
switch installBorgPackage() {
|
||||||
|
case gonf.BROKEN:
|
||||||
|
b.status = gonf.BROKEN
|
||||||
|
return
|
||||||
|
case gonf.REPAIRED:
|
||||||
|
b.status = gonf.REPAIRED
|
||||||
|
}
|
||||||
|
// private key
|
||||||
|
rootDir := gonf.ModeUserGroup(0700, "root", "root")
|
||||||
|
rootRO := gonf.ModeUserGroup(0400, "root", "root")
|
||||||
|
rootRX := gonf.ModeUserGroup(0500, "root", "root")
|
||||||
|
gonf.Directory("/root/.cache/borg").DirectoriesPermissions(rootDir).Resolve()
|
||||||
|
gonf.Directory("/root/.config/borg").DirectoriesPermissions(rootDir).Resolve()
|
||||||
|
systemdSystemPath := "/etc/systemd/system/"
|
||||||
|
hostname, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Unable to find current hostname", "err", err)
|
||||||
|
hostname = "unknown"
|
||||||
|
}
|
||||||
|
for name, job := range b.jobs {
|
||||||
|
gonf.File(filepath.Join(b.path, fmt.Sprintf("%s.key", name))).
|
||||||
|
DirectoriesPermissions(rootDir).
|
||||||
|
Permissions(rootRO).Contents(job.privateKey).
|
||||||
|
Resolve()
|
||||||
|
gonf.File(filepath.Join(b.path, fmt.Sprintf("%s.sh", name))).
|
||||||
|
DirectoriesPermissions(rootDir).
|
||||||
|
Permissions(rootRX).Contents(fmt.Sprintf(borg_script_template,
|
||||||
|
hostname,
|
||||||
|
name,
|
||||||
|
job.path,
|
||||||
|
job.hostname, name)).
|
||||||
|
Resolve()
|
||||||
|
service_name := fmt.Sprintf("borgbackup-job-%s.service", name)
|
||||||
|
gonf.File(filepath.Join(systemdSystemPath, service_name)).
|
||||||
|
DirectoriesPermissions(rootDir).
|
||||||
|
Permissions(rootRO).Contents(fmt.Sprintf(systemd_service_template,
|
||||||
|
name,
|
||||||
|
job.hostname, name,
|
||||||
|
name,
|
||||||
|
name)).
|
||||||
|
Resolve()
|
||||||
|
timer_name := fmt.Sprintf("borgbackup-job-%s.timer", name)
|
||||||
|
gonf.File(filepath.Join(systemdSystemPath, timer_name)).
|
||||||
|
DirectoriesPermissions(rootDir).
|
||||||
|
Permissions(rootRO).Contents(fmt.Sprintf(systemd_timer_template, name)).
|
||||||
|
Resolve()
|
||||||
|
gonf.Service(timer_name).State("enabled", "started").Resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BorgClient) Status() gonf.Status {
|
||||||
|
return b.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func Client() *BorgClient {
|
||||||
|
return &BorgClient{
|
||||||
|
chain: nil,
|
||||||
|
jobs: make(map[string]*Job),
|
||||||
|
path: "/etc/borg/",
|
||||||
|
status: gonf.PROMISED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BorgClient) Add(name string, path string, privateKey []byte, hostname string) *BorgClient {
|
||||||
|
if _, ok := b.jobs[name]; ok {
|
||||||
|
slog.Debug("Duplicate name for BorgClient", "name", name)
|
||||||
|
panic("Duplicate name for BorgClient")
|
||||||
|
}
|
||||||
|
b.jobs[name] = &Job{
|
||||||
|
hostname: hostname,
|
||||||
|
path: path,
|
||||||
|
privateKey: privateKey,
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
|
@ -73,7 +73,6 @@ func (b *BorgServer) Resolve() {
|
||||||
case gonf.REPAIRED:
|
case gonf.REPAIRED:
|
||||||
b.status = gonf.REPAIRED
|
b.status = gonf.REPAIRED
|
||||||
}
|
}
|
||||||
// TODO init repositories? or let the borg client do it?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BorgServer) Status() gonf.Status {
|
func (b BorgServer) Status() gonf.Status {
|
||||||
|
|
15
stdlib/backups/borg/systemd-service-template
Normal file
15
stdlib/backups/borg/systemd-service-template
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[Unit]
|
||||||
|
Description=BorgBackup job %s
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Environment="BORG_REPO=ssh://borg@%s/srv/borg/%s"
|
||||||
|
Environment="BORG_RSH=ssh -i /etc/borg/%s.key"
|
||||||
|
CPUSchedulingPolicy=idle
|
||||||
|
ExecStart=/etc/borg/%s.sh
|
||||||
|
Group=root
|
||||||
|
IOSchedulingClass=idle
|
||||||
|
PrivateTmp=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ReadWritePaths=/root/.config/borg
|
||||||
|
ReadWritePaths=/root/.cache/borg
|
||||||
|
User=root
|
9
stdlib/backups/borg/systemd-timer-template
Normal file
9
stdlib/backups/borg/systemd-timer-template
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[Unit]
|
||||||
|
Description=BorgBackup job %s timer
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=daily
|
||||||
|
Persistent=false
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
Loading…
Add table
Reference in a new issue