package main import ( "context" "fmt" "log" "os" "gitea.ceperka.net/rosti/incus-sentinel/http_notifier" "gitea.ceperka.net/rosti/incus-sentinel/incus" "gitea.ceperka.net/rosti/incus-sentinel/scheduler" "github.com/urfave/cli/v3" ) func main() { cmd := &cli.Command{ Name: "incus-sentinel", Usage: "Keeps an eye on sync and backups of Incus instances", Commands: []*cli.Command{ { Name: "list", Usage: "List all instances with their sync and backup settings", Flags: []cli.Flag{}, Action: func(c context.Context, cmd *cli.Command) error { i := incus.NewIncusDriver() insts, err := i.GetInstances("") if err != nil { return err } for _, inst := range insts { s := inst.Sentinel() fmt.Printf("%s\n", inst.Name) fmt.Printf(" Backup: %t (%s)\n", s.Backup, s.BackupSchedule) fmt.Printf(" Sync: %t (%s)\n", s.Sync, s.SyncSchedule) if s.Sync { fmt.Printf(" Sync Target: %s (pool: %s, suffix: %s)\n", s.SyncTargetRemote, s.SyncTargetPool, s.SyncTargetInstanceSuffix) } } return nil }, }, { Name: "list-volumes", Usage: "List all volumes with their sync and backup settings", Flags: []cli.Flag{}, Action: func(c context.Context, cmd *cli.Command) error { i := incus.NewIncusDriver() vols, err := i.GetVolumes("") if err != nil { return err } for _, vol := range vols { s := vol.Sentinel() fmt.Printf("%s/%s/%s\n", vol.Project, vol.Pool, vol.Name) fmt.Printf(" Backup: %t (%s, %s)\n", s.Backup, s.BackupMode, s.BackupSchedule) fmt.Printf(" Sync: %t (%s)\n", s.Sync, s.SyncSchedule) if s.Sync { fmt.Printf(" Sync Target: %s (pool: %s, suffix: %s)\n", s.SyncTargetRemote, s.SyncTargetPool, s.SyncTargetVolumeSuffix) } } return nil }, }, { Name: "sync", Usage: "Syncs all instances where sync is enabled", Flags: []cli.Flag{}, Action: func(c context.Context, cmd *cli.Command) error { i := incus.NewIncusDriver() insts, err := i.GetInstances("") if err != nil { return err } for _, inst := range insts { s := inst.Sentinel() if s.Sync { fmt.Println(".. syncing", inst.Name) err := i.Sync(inst.Project, inst.Name, fmt.Sprintf("%s%s", inst.Name, s.SyncTargetInstanceSuffix), s.SyncTargetRemote, s.SyncTargetPool) if err != nil { return err } } } return nil }, }, { Name: "sync-volumes", Usage: "Syncs all volumes where sync is enabled", Flags: []cli.Flag{}, Action: func(c context.Context, cmd *cli.Command) error { i := incus.NewIncusDriver() vols, err := i.GetVolumes("") if err != nil { return err } for _, vol := range vols { s := vol.Sentinel() if s.Sync { fmt.Printf(".. syncing %s/%s/%s\n", vol.Project, vol.Pool, vol.Name) targetPool := vol.Pool if s.SyncTargetPool != "" { targetPool = s.SyncTargetPool } err := i.SyncVolume(vol.Project, vol.Pool, vol.Name, s.SyncTargetRemote, targetPool, fmt.Sprintf("%s%s", vol.Name, s.SyncTargetVolumeSuffix)) if err != nil { return err } } } return nil }, }, { Name: "backup", Usage: "Backs up all instances where backup is enabled", Flags: []cli.Flag{}, Action: func(c context.Context, cmd *cli.Command) error { i := incus.NewIncusDriver() insts, err := i.GetInstances("") if err != nil { return err } for _, inst := range insts { s := inst.Sentinel() if s.Backup { fmt.Println(".. backing up", inst.Name) err := i.Backup(inst.Project, inst.Name, []string{}) if err != nil { return err } } } return nil }, }, { Name: "backup-volumes", Usage: "Backs up all volumes where backup is enabled", Flags: []cli.Flag{}, Action: func(c context.Context, cmd *cli.Command) error { i := incus.NewIncusDriver() vols, err := i.GetVolumes("") if err != nil { return err } for _, vol := range vols { s := vol.Sentinel() if s.Backup { fmt.Printf(".. backing up %s/%s/%s\n", vol.Project, vol.Pool, vol.Name) if s.BackupMode == "dir" { err := i.BackupVolumeDir(vol.Project, vol.Pool, vol.Name, []string{}) if err != nil { return err } } else if s.BackupMode == "native" { err := i.BackupVolumeNative(vol.Project, vol.Pool, vol.Name, []string{}) if err != nil { return err } } else { return fmt.Errorf("invalid backup mode: %s", s.BackupMode) } } } return nil }, }, { Name: "run", Usage: "Runs the sentinel that syncs and backs up instances based on their configuration", Flags: []cli.Flag{}, Action: func(c context.Context, cmd *cli.Command) error { i := incus.NewIncusDriver() n := http_notifier.NewNotifier() s := scheduler.NewScheduler(i, n) err := s.Run() if err != nil { return err } return nil }, }, }, } if err := cmd.Run(context.Background(), os.Args); err != nil { log.Fatal(err) } }