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
		}
	}
}
Exemple #2
0
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
}