2021-10-16 15:39:54 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2023-06-29 22:58:24 +02:00
|
|
|
"errors"
|
2021-10-16 15:39:54 +02:00
|
|
|
"regexp"
|
2023-06-29 22:58:24 +02:00
|
|
|
"strings"
|
2021-10-16 15:39:54 +02:00
|
|
|
"time"
|
2023-06-29 22:58:24 +02:00
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
2021-10-16 15:39:54 +02:00
|
|
|
)
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
type App struct {
|
|
|
|
name string
|
|
|
|
schedule map[string]struct{}
|
|
|
|
sources []Addr
|
|
|
|
destinations []Addr
|
|
|
|
before map[string]Addr
|
|
|
|
after map[string]Addr
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
func (c *Config) NewApp(name string, sources, destinations, schedule []string, before, after map[string]string) (*App, error) {
|
|
|
|
log.WithFields(log.Fields{"name": name}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"name": name}).Debugf("done")
|
2021-10-16 15:39:54 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
a := &App{
|
|
|
|
name: name,
|
|
|
|
sources: make([]Addr, 0),
|
|
|
|
destinations: make([]Addr, 0),
|
|
|
|
schedule: make(map[string]struct{}, 0),
|
|
|
|
before: make(map[string]Addr),
|
|
|
|
after: make(map[string]Addr),
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, v := range sources {
|
|
|
|
src := Addr(v)
|
|
|
|
if src.Box() == "" {
|
|
|
|
err := errors.New("source box incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if _, ok := c.box[src.Box()]; !ok {
|
|
|
|
err := errors.New("source box doesn't exist")
|
|
|
|
log.WithFields(log.Fields{"app": name, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if src.Path() == "" {
|
|
|
|
err := errors.New("source path incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-11-14 10:20:44 +01:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
a.sources = append(a.sources, src)
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, v := range destinations {
|
|
|
|
dest := Addr(v)
|
|
|
|
if dest.Box() == "" {
|
|
|
|
err := errors.New("destination box incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if _, ok := c.box[dest.Box()]; !ok {
|
|
|
|
err := errors.New("destination box doesn't exist")
|
|
|
|
log.WithFields(log.Fields{"app": name, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if dest.Path() == "" {
|
|
|
|
err := errors.New("destination path incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
a.destinations = append(a.destinations, dest)
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, v := range schedule {
|
|
|
|
switch strings.ToLower(v) {
|
|
|
|
case "hourly":
|
|
|
|
a.schedule["hourly"] = struct{}{}
|
|
|
|
case "daily":
|
|
|
|
a.schedule["daily"] = struct{}{}
|
|
|
|
case "weekly":
|
|
|
|
a.schedule["weekly"] = struct{}{}
|
|
|
|
case "monthly":
|
|
|
|
a.schedule["monthly"] = struct{}{}
|
|
|
|
case "yearly":
|
|
|
|
a.schedule["yearly"] = struct{}{}
|
|
|
|
default:
|
|
|
|
err := errors.New("schedule incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for k, v := range before {
|
|
|
|
if _, err := regexp.Compile(k); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": k, "addr": v, "call": "regexp.Compile", "attr": k, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
script := Addr(v)
|
|
|
|
if script.Box() == "" {
|
|
|
|
err := errors.New("before box incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": k, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
if script.Path() == "" {
|
|
|
|
err := errors.New("before path incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": k, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if _, ok := a.before[k]; ok {
|
|
|
|
err := errors.New("before already exists")
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": k, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
|
|
|
|
a.before[k] = script
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for k, v := range after {
|
|
|
|
if _, err := regexp.Compile(k); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": k, "addr": v, "call": "regexp.Compile", "attr": k, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
script := Addr(v)
|
|
|
|
if script.Box() == "" {
|
|
|
|
err := errors.New("after box incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": k, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if script.Path() == "" {
|
|
|
|
err := errors.New("after path incorrect")
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": k, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if _, ok := a.before[k]; ok {
|
|
|
|
err := errors.New("after already exists")
|
|
|
|
log.WithFields(log.Fields{"app": name, "schedule": k, "addr": v, "error": err}).Errorf("")
|
|
|
|
return nil, err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
a.after[k] = script
|
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
return a, nil
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
func (a *App) Cleanup(now time.Time) error {
|
|
|
|
log.WithFields(log.Fields{"app": a.name}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"app": a.name}).Debugf("done")
|
2021-10-16 15:39:54 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, src := range a.sources {
|
|
|
|
for _, s := range cfg.box[src.Box()].zfs.filesystems[src.Path()].snapshots {
|
2023-07-01 00:15:31 +02:00
|
|
|
if !s.Valid() {
|
|
|
|
if err := s.Delete(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": src.Box(), "snapshot": s.String(), "call": "Delete", "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if expired, err := s.Expired(now); err != nil {
|
2023-06-29 22:58:24 +02:00
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": src.Box(), "snapshot": s.String(), "call": "Expired", "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
} else if expired {
|
|
|
|
if err := s.Delete(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": src.Box(), "snapshot": s.String(), "call": "Delete", "error": err}).Errorf("")
|
|
|
|
return err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, dest := range a.destinations {
|
2023-07-31 10:34:14 +02:00
|
|
|
if cfg.box[dest.Box()].online {
|
2023-07-31 10:46:02 +02:00
|
|
|
dest2 := dest.Append("/" + src.Box() + "/" + src.Path())
|
|
|
|
for _, s := range cfg.box[dest2.Box()].zfs.filesystems[dest2.Path()].snapshots {
|
2023-07-31 10:34:14 +02:00
|
|
|
if !s.Valid() {
|
|
|
|
if err := s.Delete(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": src.Box(), "snapshot": s.String(), "call": "Delete", "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if expired, err := s.Expired(now); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": dest.Box(), "snapshot": s.String(), "call": "Expired", "error": err}).Errorf("")
|
2023-06-29 22:58:24 +02:00
|
|
|
return err
|
2023-07-31 10:34:14 +02:00
|
|
|
} else if expired {
|
|
|
|
if err := s.Delete(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": src.Box(), "snapshot": s.String(), "call": "Delete", "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
}
|
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
return nil
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
func (a *App) SanityCheck() error {
|
|
|
|
log.WithFields(log.Fields{"app": a.name}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"app": a.name}).Debugf("done")
|
2021-10-16 15:39:54 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, src := range a.sources {
|
|
|
|
b := cfg.box[src.Box()]
|
|
|
|
if !b.online {
|
|
|
|
err := errors.New("source box offline")
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "box": src.Box(), "error": err}).Errorf("")
|
|
|
|
return err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if _, ok := b.zfs.filesystems[src.Path()]; !ok {
|
|
|
|
err := errors.New("source path doesn't exist")
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "box": src.Box(), "path": src.Path(), "error": err}).Errorf("")
|
|
|
|
return err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2023-06-29 23:13:20 +02:00
|
|
|
|
|
|
|
for _, s := range b.zfs.filesystems[src.Path()].snapshots {
|
|
|
|
if !s.Valid() {
|
|
|
|
if err := s.Delete(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "box": src.Box(), "snapshot": s.String(), "call": "Delete", "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
onlineDestinations := 0
|
|
|
|
for _, dest := range a.destinations {
|
2023-06-29 23:13:20 +02:00
|
|
|
b := cfg.box[dest.Box()]
|
|
|
|
if b.online {
|
2023-06-29 22:58:24 +02:00
|
|
|
onlineDestinations++
|
2023-06-29 23:13:20 +02:00
|
|
|
for _, src := range a.sources {
|
|
|
|
dest2 := dest.Append("/" + src.Box() + "/" + src.Path())
|
2023-07-01 22:34:04 +02:00
|
|
|
if fs, ok := b.zfs.filesystems[dest2.Path()]; ok {
|
|
|
|
for _, s := range fs.snapshots {
|
|
|
|
if !s.Valid() {
|
|
|
|
if err := s.Delete(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "box": src.Box(), "snapshot": s.String(), "call": "Delete", "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
2023-06-29 23:13:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
if onlineDestinations == 0 {
|
|
|
|
err := errors.New("no destination box online")
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "error": err}).Errorf("")
|
|
|
|
return err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
return nil
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
func (a *App) RunSchedule(schedule string, now time.Time) error {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now}).Debugf("done")
|
2021-10-16 15:39:54 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
snapshotName := SnapshotName(schedule, now)
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now, "snapshot": snapshotName}).Debugf("snapshot name")
|
2021-10-16 15:39:54 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if err := a.RunBefore(schedule); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now, "call": "RunBefore", "attr": schedule, "error": err}).Errorf("")
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, src := range a.sources {
|
|
|
|
srcFs := cfg.box[src.Box()].zfs.filesystems[src.Path()]
|
|
|
|
if _, err := srcFs.TakeSnapshot(snapshotName); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now, "call": "TakeSnapshot", "attr": snapshotName, "error": err}).Errorf("")
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if err := a.RunAfter(schedule); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now, "call": "RunAfter", "attr": schedule, "error": err}).Errorf("")
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
|
2021-10-18 16:13:04 +02:00
|
|
|
return nil
|
2023-06-29 22:58:24 +02:00
|
|
|
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
|
2023-08-01 09:35:45 +02:00
|
|
|
func (a *App) Run(now time.Time) (string, error) {
|
2023-06-29 22:58:24 +02:00
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"app": a.name, "now": now}).Debugf("done")
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if err := a.SanityCheck(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "call": "SanityCheck", "error": err}).Errorf("")
|
2023-08-01 09:35:45 +02:00
|
|
|
return "", err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
schedule, err := a.NextSchedule(now)
|
|
|
|
if err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "NextSchedule", "error": err}).Errorf("")
|
2023-08-01 09:35:45 +02:00
|
|
|
return "", err
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "schedule": schedule}).Debugf("schedule")
|
|
|
|
if schedule != "" {
|
|
|
|
if err := a.RunSchedule(schedule, now); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "NextSchedule", "error": err}).Errorf("")
|
2023-08-01 09:35:45 +02:00
|
|
|
return "", err
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-31 10:13:36 +02:00
|
|
|
for _, src := range a.sources {
|
|
|
|
if err := src.SetManaged(true); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "src.SetManaged", "error": err}).Errorf("")
|
2023-08-01 09:35:45 +02:00
|
|
|
return "", err
|
2023-07-31 10:13:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if err := a.Transfer(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "Transfer", "error": err}).Errorf("")
|
2023-08-01 09:35:45 +02:00
|
|
|
return "", err
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if err := a.Cleanup(now); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "Cleanup", "error": err}).Errorf("")
|
2023-08-01 09:35:45 +02:00
|
|
|
return "", err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-08-01 09:35:45 +02:00
|
|
|
return schedule, nil
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
func (a *App) NextSchedule(now time.Time) (string, error) {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"app": a.name, "now": now}).Debugf("done")
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
// get a list of all the common timestamps in sources snapshots
|
|
|
|
snapshots := make(map[string]map[time.Time]int)
|
|
|
|
for _, v := range a.sources {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": v.Box(), "path": v.Path()}).Debugf("source")
|
|
|
|
b := cfg.box[v.Box()] // we tested the boxes
|
|
|
|
|
|
|
|
fs, ok := b.zfs.filesystems[v.Path()]
|
|
|
|
if !ok {
|
|
|
|
err := errors.New("path doesn't exist")
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": v.Box(), "path": v.Path(), "error": err}).Errorf("")
|
|
|
|
return "", err
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "box": v.Box(), "path": v.Path()}).Debugf("%d snapshots", len(fs.snapshots))
|
2022-06-17 15:44:25 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, v2 := range fs.snapshots {
|
|
|
|
if s, err := v2.Schedule(); err == nil {
|
|
|
|
snapshots2, ok := snapshots[s]
|
|
|
|
if !ok {
|
|
|
|
snapshots2 = make(map[time.Time]int)
|
2022-06-17 15:44:25 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if t, err := v2.Timestamp(); err == nil {
|
|
|
|
if t.After(now) {
|
|
|
|
err := errors.New("snapshot in the future")
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "now": now, "source": v, "timestamp": t, "error": err}).Errorf("")
|
|
|
|
return "", err
|
2022-06-17 15:44:25 +02:00
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
if count, ok := snapshots2[t]; ok {
|
|
|
|
snapshots2[t] = count + 1
|
|
|
|
} else {
|
|
|
|
snapshots2[t] = 1
|
2022-06-17 15:44:25 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
|
|
|
|
snapshots[s] = snapshots2
|
2022-06-17 15:44:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
t := time.Unix(0, 0)
|
|
|
|
if _, ok := a.schedule["yearly"]; ok {
|
|
|
|
if s, ok := snapshots["yearly"]; ok {
|
|
|
|
for k, v := range s {
|
|
|
|
if k.After(t) && v == len(a.sources) {
|
|
|
|
t = k
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if t.Year() < now.Year() {
|
|
|
|
return "yearly", nil
|
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if _, ok := a.schedule["monthly"]; ok {
|
|
|
|
if s, ok := snapshots["monthly"]; ok {
|
|
|
|
for k, v := range s {
|
|
|
|
if k.After(t) && v == len(a.sources) {
|
|
|
|
t = k
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
if t.Year() < now.Year() || t.Month() != now.Month() {
|
2023-07-01 14:10:35 +02:00
|
|
|
return "monthly", nil
|
2023-06-29 22:58:24 +02:00
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if _, ok := a.schedule["weekly"]; ok {
|
|
|
|
if s, ok := snapshots["weekly"]; ok {
|
|
|
|
for k, v := range s {
|
|
|
|
if k.After(t) && v == len(a.sources) {
|
|
|
|
t = k
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ny, nw := now.ISOWeek()
|
|
|
|
ty, tw := t.ISOWeek()
|
|
|
|
if ty < ny || tw < nw {
|
2023-07-01 14:10:35 +02:00
|
|
|
return "weekly", nil
|
2023-06-29 22:58:24 +02:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if _, ok := a.schedule["daily"]; ok {
|
|
|
|
if s, ok := snapshots["daily"]; ok {
|
|
|
|
for k, v := range s {
|
|
|
|
if k.After(t) && v == len(a.sources) {
|
|
|
|
t = k
|
2022-06-17 16:03:12 +02:00
|
|
|
}
|
2022-04-16 15:30:47 +02:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
if t.Year() < now.Year() || t.Month() != now.Month() || t.Day() < now.Day() {
|
|
|
|
return "daily", nil
|
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
if _, ok := a.schedule["hourly"]; ok {
|
|
|
|
if s, ok := snapshots["hourly"]; ok {
|
|
|
|
for k, v := range s {
|
|
|
|
if k.After(t) && v == len(a.sources) {
|
|
|
|
t = k
|
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2023-06-29 22:58:24 +02:00
|
|
|
}
|
|
|
|
if t.Year() < now.Year() || t.Month() != now.Month() || t.Day() < now.Day() || t.Hour() < now.Hour() {
|
|
|
|
return "hourly", nil
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
return "", nil
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
func (a *App) RunBefore(schedule string) error {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"app": a.name, "schedule": schedule}).Debugf("done")
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for k, v := range a.before {
|
|
|
|
re := regexp.MustCompile(k)
|
|
|
|
if re.MatchString(schedule) {
|
|
|
|
if _, err := v.Exec(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "regex": k, "call": "Exec", "error": err}).Errorf("")
|
|
|
|
return err
|
2021-10-18 16:13:04 +02:00
|
|
|
}
|
|
|
|
}
|
2021-11-04 14:33:44 +01:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
return nil
|
|
|
|
}
|
2022-06-17 14:54:14 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
func (a *App) RunAfter(schedule string) error {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"app": a.name, "schedule": schedule}).Debugf("done")
|
2021-10-19 02:45:51 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
for k, v := range a.after {
|
|
|
|
re := regexp.MustCompile(k)
|
|
|
|
if re.MatchString(schedule) {
|
|
|
|
if _, err := v.Exec(); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "regex": k, "call": "Exec", "error": err}).Errorf("")
|
|
|
|
return err
|
2021-10-19 02:45:51 +02:00
|
|
|
}
|
|
|
|
}
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
return nil
|
|
|
|
}
|
2022-06-17 15:44:25 +02:00
|
|
|
|
2023-06-29 22:58:24 +02:00
|
|
|
func (a *App) Transfer() error {
|
|
|
|
log.WithFields(log.Fields{"app": a.name}).Debugf("starting")
|
|
|
|
defer log.WithFields(log.Fields{"app": a.name}).Debugf("done")
|
|
|
|
|
|
|
|
for _, src := range a.sources {
|
2023-07-31 10:46:02 +02:00
|
|
|
backedUp := false
|
2023-06-29 22:58:24 +02:00
|
|
|
for _, dest := range a.destinations {
|
2023-07-31 10:46:02 +02:00
|
|
|
dest2 := dest.Append("/" + src.Box() + "/" + src.Path())
|
|
|
|
if dest2.Online() {
|
|
|
|
if err := dest2.Mkdir(); err != nil {
|
2023-06-29 22:58:24 +02:00
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "Mkdir", "attr": dest, "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2023-07-31 10:46:02 +02:00
|
|
|
if err := TransferZfs(src, dest2); err != nil {
|
2023-06-29 22:58:24 +02:00
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "TransferZfs", "src": src, "dest": dest, "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
2023-07-31 10:46:02 +02:00
|
|
|
if err := dest2.SetManaged(true); err != nil {
|
2023-07-31 10:13:36 +02:00
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "SetManaged", "src": src, "dest": dest, "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
2023-07-31 10:46:02 +02:00
|
|
|
backedUp = true
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
|
|
|
}
|
2023-07-31 10:46:02 +02:00
|
|
|
if backedUp {
|
|
|
|
if err := src.SetBackedUp(true); err != nil {
|
|
|
|
log.WithFields(log.Fields{"app": a.name, "call": "SetBackedUp", "src": src, "error": err}).Errorf("")
|
|
|
|
return err
|
|
|
|
}
|
2023-07-31 10:13:36 +02:00
|
|
|
}
|
|
|
|
|
2021-10-16 15:39:54 +02:00
|
|
|
}
|
2021-10-18 16:13:04 +02:00
|
|
|
|
2021-10-16 15:39:54 +02:00
|
|
|
return nil
|
|
|
|
}
|