inital code for snapshoting on cron

This commit is contained in:
2022-12-19 07:16:24 -07:00
parent 965af25890
commit 3c0d373e6e
10 changed files with 338 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
build:
go build .
package: build
mkdir -p snapz-1
cp snapzd snapz-1
cp systemd/snapzd.service snapz-1/snapzd.service
tar -czvf snapzd.tar.gz snapz-1
makepkg: package
makepkg -f --skipinteg
pacinstall: makepkg
sudo pacman -U snapz-1-1-x86_64.pkg.tar.zst
clean:
rm snapzd
rm -r snapz-1
rm snapzd.tar.gz
rm snapz-1-1-x86_64.pkg.tar.zst
rm -r src
rm -r pkg
+52
View File
@@ -0,0 +1,52 @@
# This is an example PKGBUILD file. Use this as a start to creating your own,
# and remove these comments. For more information, see 'man PKGBUILD'.
# NOTE: Please fill out the license field for your package! If it is unknown,
# then please put 'unknown'.
# Maintainer: Your Name <youremail@domain.com>
pkgname=snapz
pkgver=1
pkgrel=1
epoch=
pkgdesc=""
arch=('x86_64')
url=""
license=('GPL')
groups=()
depends=()
makedepends=()
checkdepends=()
optdepends=()
provides=()
conflicts=()
replaces=()
backup=()
options=()
install=
changelog=
source=("file://snapzd.tar.gz")
noextract=()
md5sums=()
validpgpkeys=()
prepare() {
echo "$PWD"
}
build() {
echo "foo"
}
check() {
cd "$pkgname-$pkgver"
}
package() {
cd "$pkgname-$pkgver"
mkdir -p "$pkgdir/usr/bin"
mkdir -p "$pkgdir/etc/snapz/"
cp snapzd "$pkgdir/usr/bin"
mkdir -p "$pkgdir/usr/lib/systemd/system/"
cp snapzd.service "$pkgdir/usr/lib/systemd/system/"
}
+60
View File
@@ -0,0 +1,60 @@
package configuration
import (
"os"
"path/filepath"
"github.com/pelletier/go-toml/v2"
"go.uber.org/zap"
)
const (
SNAPZ_CONFIG_ROOT = "/etc/snapz"
SNAPZ_JOBS_FILE = "jobs.toml"
SNAPZ_DATASET_DIRECTORY = "datasets.toml"
)
/*
[[job]]
name = "example"
description = "example job"
cron = "* /1 * * * *"
recursive = true
*/
type JobConfig struct {
Name string `toml:"name"`
Description string `toml:"description"`
Cron string `toml:"cron"`
Dataset string `toml:"dataset"`
Recursive bool `toml:"recusrive"`
}
type JobsConfig struct {
Jobs []JobConfig `toml:"job"`
}
func getConfigPaths(filename string) []string {
global_file_path := filepath.Join(SNAPZ_CONFIG_ROOT, filename)
return []string{global_file_path}
}
func GetJobs() ([]JobConfig, error) {
job_file_paths := getConfigPaths(SNAPZ_JOBS_FILE)
jobs := make([]JobConfig, 0)
for _, file := range job_file_paths {
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var jobsConfig JobsConfig
err = toml.Unmarshal(data, &jobsConfig)
if err != nil {
return nil, err
}
zap.S().Infof("adding %v jobs to queue", len(jobsConfig.Jobs))
jobs = append(jobs, jobsConfig.Jobs...)
}
return jobs, nil
}
+62
View File
@@ -0,0 +1,62 @@
package configuration
import (
"io/ioutil"
"time"
"github.com/go-co-op/gocron"
"gopkg.in/yaml.v3"
)
type Schedule struct {
Every int
Unit string
At []string
}
func (s Schedule) ToGocron() *gocron.Scheduler {
out := gocron.NewScheduler(time.Local)
out = out.Every(s.Every)
switch s.Unit {
case "Day":
out = out.Day()
case "Hour":
out = out.Hour()
default:
return nil
}
for _, at := range s.At {
out = out.At(at)
}
return out
}
type SnapJob struct {
Name string `yaml:"name"`
DatasetPath string `yaml:"dataset_path"`
Description string `yaml:"descriptions"`
Recursive bool `yaml:"recursive"`
Schedules []Schedule `yaml:"schedules"`
Cron string `yaml:"cron"`
}
func LoadSnap(path string) (*SnapJob, error) {
configFile, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var jobInfo SnapJob
err = yaml.Unmarshal(configFile, &jobInfo)
if err != nil {
return nil, err
}
return &jobInfo, nil
}
+6
View File
@@ -0,0 +1,6 @@
name: "SnapshotUserdata"
description: "Daily snapshot of userdata/home for backup to network storage"
dataset_path: "userdata/home"
recursive: true
cron: "*/1 * * * *"
# Makes a snapshot once a day at 8:00PM local time named autosnap@<RFC3339>
+16
View File
@@ -0,0 +1,16 @@
module git.ohea.xyz/spaceman/snapzd
go 1.19
require github.com/go-co-op/gocron v1.18.0
require (
github.com/mistifyio/go-zfs v2.1.1+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+34
View File
@@ -0,0 +1,34 @@
git.ohea.xyz/golang/config v0.0.0-20221002005232-8a901413a8b0 h1:a8ygEuzmqFDxXmf+e1IseDKBcAtkaIwfL3k4PIVVVr8=
git.ohea.xyz/golang/config v0.0.0-20221002005232-8a901413a8b0/go.mod h1:86PbXJ2WdqQ+3hYqrnv3ukgKNRK9nQfThnlY03FAO0g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-co-op/gocron v1.18.0 h1:SxTyJ5xnSN4byCq7b10LmmszFdxQlSQJod8s3gbnXxA=
github.com/go-co-op/gocron v1.18.0/go.mod h1:sD/a0Aadtw5CpflUJ/lpP9Vfdk979Wl1Sg33HPHg0FY=
github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJdNZo6oqSENd4eW8=
github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+7
View File
@@ -0,0 +1,7 @@
[[job]]
name = "dailysnap"
description = "snapshot home dir daily"
cron = "* * * * *"
dataset = "userdata/home"
rescursive = false
+69
View File
@@ -0,0 +1,69 @@
package main
import (
"fmt"
"os"
"time"
"git.ohea.xyz/spaceman/snapzd/configuration"
"github.com/go-co-op/gocron"
"github.com/mistifyio/go-zfs"
"go.uber.org/zap"
)
func CreateSnapshotJob(job_info *configuration.JobConfig) func() {
return func() {
fs, err := zfs.Filesystems(job_info.Dataset)
if err != nil {
zap.S().Errorf("failed to start job %v: %v", job_info.Name, err)
return
}
if len(fs) != 1 {
zap.S().Errorf("dataset path returned %v datasets. Expected 1", len(fs))
return
}
to_snapshot := fs[0]
snaptime := time.Now()
snapname := fmt.Sprintf("snapz.%s.%s", job_info.Name, snaptime.Format(time.RFC3339))
snapshot, err := to_snapshot.Snapshot(snapname, job_info.Recursive)
if err != nil {
zap.S().Errorf("failed to create snapshot: %v: ", snapname, err)
return
}
zap.S().Infof("created snapshot: %v", snapshot.Name)
}
}
func configureLogging() error {
logger, err := zap.NewDevelopment()
if err != nil {
return err
}
zap.ReplaceGlobals(logger)
return nil
}
func main() {
err := configureLogging()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to initalize logging, can't continue: %v", err)
return
}
zap.S().Info("begining conifg scan")
jobs, err := configuration.GetJobs()
if err != nil {
zap.S().Fatalf("failed to read configuration: %v", err)
}
scheduler := gocron.NewScheduler(time.Local)
for _, job := range jobs {
zap.S().Infof("discovered job: %v", job.Name)
zap.S().Infof("adding job. Name: %v, Dataset: %v, Cron: %v", job.Name, job.Dataset, job.Cron)
scheduler.Cron(job.Cron).SingletonMode().Tag(job.Name).Do(CreateSnapshotJob(&job))
}
scheduler.StartBlocking()
}
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=snapz snapshot daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/snapzd
[Install]
WantedBy=multi-user.target