func TestApplyPolicy(t *testing.T) { for i, p := range expireTests { keep, remove := restic.ApplyPolicy(testExpireSnapshots, p) t.Logf("test %d: returned keep %v, remove %v (of %v) expired snapshots for policy %v", i, len(keep), len(remove), len(testExpireSnapshots), p) if len(keep)+len(remove) != len(testExpireSnapshots) { t.Errorf("test %d: len(keep)+len(remove) = %d != len(testExpireSnapshots) = %d", i, len(keep)+len(remove), len(testExpireSnapshots)) } if p.Sum() > 0 && len(keep) > p.Sum() { t.Errorf("not enough snapshots removed: policy allows %v snapshots to remain, but ended up with %v", p.Sum(), len(keep)) } for _, sn := range keep { t.Logf("test %d: keep snapshot at %v %s\n", i, sn.Time, sn.Tags) } for _, sn := range remove { t.Logf("test %d: forget snapshot at %v %s\n", i, sn.Time, sn.Tags) } goldenFilename := filepath.Join("testdata", fmt.Sprintf("policy_keep_snapshots_%d", i)) if *updateGoldenFiles { buf, err := json.MarshalIndent(keep, "", " ") if err != nil { t.Fatalf("error marshaling result: %v", err) } if err = ioutil.WriteFile(goldenFilename, buf, 0644); err != nil { t.Fatalf("unable to update golden file: %v", err) } } buf, err := ioutil.ReadFile(goldenFilename) if err != nil { t.Errorf("error loading golden file %v: %v", goldenFilename, err) continue } var want restic.Snapshots err = json.Unmarshal(buf, &want) if !reflect.DeepEqual(keep, want) { t.Errorf("test %v: wrong result, want:\n %v\ngot:\n %v", i, want, keep) continue } } }
func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { repo, err := OpenRepository(gopts) if err != nil { return err } lock, err := lockRepoExclusive(repo) defer unlockRepo(lock) if err != nil { return err } err = repo.LoadIndex() if err != nil { return err } // first, process all snapshot IDs given as arguments for _, s := range args { id, err := restic.FindSnapshot(repo, s) if err != nil { return err } if !opts.DryRun { err = repo.Backend().Remove(restic.SnapshotFile, id.String()) if err != nil { return err } Verbosef("removed snapshot %v\n", id.Str()) } else { Verbosef("would removed snapshot %v\n", id.Str()) } } policy := restic.ExpirePolicy{ Last: opts.Last, Hourly: opts.Hourly, Daily: opts.Daily, Weekly: opts.Weekly, Monthly: opts.Monthly, Yearly: opts.Yearly, Tags: opts.KeepTags, } if policy.Empty() { return nil } // then, load all remaining snapshots snapshots, err := restic.LoadAllSnapshots(repo) if err != nil { return err } // group by hostname and dirs type key struct { Hostname string Dirs string } snapshotGroups := make(map[key]restic.Snapshots) for _, sn := range snapshots { if opts.Hostname != "" && sn.Hostname != opts.Hostname { continue } if !sn.HasTags(opts.Tags) { continue } k := key{Hostname: sn.Hostname, Dirs: strings.Join(sn.Paths, ":")} list := snapshotGroups[k] list = append(list, sn) snapshotGroups[k] = list } for key, snapshotGroup := range snapshotGroups { Printf("snapshots for host %v, directories %v:\n\n", key.Hostname, key.Dirs) keep, remove := restic.ApplyPolicy(snapshotGroup, policy) Printf("keep %d snapshots:\n", len(keep)) printSnapshots(globalOptions.stdout, keep) Printf("\n") Printf("remove %d snapshots:\n", len(remove)) printSnapshots(globalOptions.stdout, remove) Printf("\n") if !opts.DryRun { for _, sn := range remove { err = repo.Backend().Remove(restic.SnapshotFile, sn.ID().String()) if err != nil { return err } } } } return nil }