commit
78a3ffcfb9
3 changed files with 95 additions and 6 deletions
8
changelog/unreleased/issue-2184
Normal file
8
changelog/unreleased/issue-2184
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Enhancement: Add --json support to forget command
|
||||||
|
|
||||||
|
The forget command now supports the --json argument, outputting the
|
||||||
|
information about what is (or would-be) kept and removed from the
|
||||||
|
repository.
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/2184
|
||||||
|
https://github.com/restic/restic/pull/2185
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -182,7 +183,11 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !policy.Empty() {
|
if !policy.Empty() {
|
||||||
|
if !gopts.JSON {
|
||||||
Verbosef("Applying Policy: %v\n", policy)
|
Verbosef("Applying Policy: %v\n", policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonGroups []*ForgetGroup
|
||||||
|
|
||||||
for k, snapshotGroup := range snapshotGroups {
|
for k, snapshotGroup := range snapshotGroups {
|
||||||
var key key
|
var key key
|
||||||
|
@ -190,36 +195,50 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fg ForgetGroup
|
||||||
// Info
|
// Info
|
||||||
|
if !gopts.JSON {
|
||||||
Verbosef("snapshots")
|
Verbosef("snapshots")
|
||||||
|
}
|
||||||
var infoStrings []string
|
var infoStrings []string
|
||||||
if GroupByTag {
|
if GroupByTag {
|
||||||
infoStrings = append(infoStrings, "tags ["+strings.Join(key.Tags, ", ")+"]")
|
infoStrings = append(infoStrings, "tags ["+strings.Join(key.Tags, ", ")+"]")
|
||||||
|
fg.Tags = key.Tags
|
||||||
}
|
}
|
||||||
if GroupByHost {
|
if GroupByHost {
|
||||||
infoStrings = append(infoStrings, "host ["+key.Hostname+"]")
|
infoStrings = append(infoStrings, "host ["+key.Hostname+"]")
|
||||||
|
fg.Host = key.Hostname
|
||||||
}
|
}
|
||||||
if GroupByPath {
|
if GroupByPath {
|
||||||
infoStrings = append(infoStrings, "paths ["+strings.Join(key.Paths, ", ")+"]")
|
infoStrings = append(infoStrings, "paths ["+strings.Join(key.Paths, ", ")+"]")
|
||||||
|
fg.Paths = key.Paths
|
||||||
}
|
}
|
||||||
if infoStrings != nil {
|
if infoStrings != nil && !gopts.JSON {
|
||||||
Verbosef(" for (" + strings.Join(infoStrings, ", ") + ")")
|
Verbosef(" for (" + strings.Join(infoStrings, ", ") + ")")
|
||||||
}
|
}
|
||||||
|
if !gopts.JSON {
|
||||||
Verbosef(":\n\n")
|
Verbosef(":\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
keep, remove, reasons := restic.ApplyPolicy(snapshotGroup, policy)
|
keep, remove, reasons := restic.ApplyPolicy(snapshotGroup, policy)
|
||||||
|
|
||||||
if len(keep) != 0 && !gopts.Quiet {
|
if len(keep) != 0 && !gopts.Quiet && !gopts.JSON {
|
||||||
Printf("keep %d snapshots:\n", len(keep))
|
Printf("keep %d snapshots:\n", len(keep))
|
||||||
PrintSnapshots(globalOptions.stdout, keep, reasons, opts.Compact)
|
PrintSnapshots(globalOptions.stdout, keep, reasons, opts.Compact)
|
||||||
Printf("\n")
|
Printf("\n")
|
||||||
}
|
}
|
||||||
|
addJSONSnapshots(&fg.Keep, keep)
|
||||||
|
|
||||||
if len(remove) != 0 && !gopts.Quiet {
|
if len(remove) != 0 && !gopts.Quiet && !gopts.JSON {
|
||||||
Printf("remove %d snapshots:\n", len(remove))
|
Printf("remove %d snapshots:\n", len(remove))
|
||||||
PrintSnapshots(globalOptions.stdout, remove, nil, opts.Compact)
|
PrintSnapshots(globalOptions.stdout, remove, nil, opts.Compact)
|
||||||
Printf("\n")
|
Printf("\n")
|
||||||
}
|
}
|
||||||
|
addJSONSnapshots(&fg.Remove, remove)
|
||||||
|
|
||||||
|
fg.Reasons = reasons
|
||||||
|
|
||||||
|
jsonGroups = append(jsonGroups, &fg)
|
||||||
|
|
||||||
removeSnapshots += len(remove)
|
removeSnapshots += len(remove)
|
||||||
|
|
||||||
|
@ -233,6 +252,13 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gopts.JSON {
|
||||||
|
err = printJSONForget(gopts.stdout, jsonGroups)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if removeSnapshots > 0 && opts.Prune {
|
if removeSnapshots > 0 && opts.Prune {
|
||||||
|
@ -244,3 +270,28 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForgetGroup helps to print what is forgotten in JSON.
|
||||||
|
type ForgetGroup struct {
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Paths []string `json:"paths"`
|
||||||
|
Keep []Snapshot `json:"keep"`
|
||||||
|
Remove []Snapshot `json:"remove"`
|
||||||
|
Reasons []restic.KeepReason `json:"reasons"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func addJSONSnapshots(js *[]Snapshot, list restic.Snapshots) {
|
||||||
|
for _, sn := range list {
|
||||||
|
k := Snapshot{
|
||||||
|
Snapshot: sn,
|
||||||
|
ID: sn.ID(),
|
||||||
|
ShortID: sn.ID().Str(),
|
||||||
|
}
|
||||||
|
*js = append(*js, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printJSONForget(stdout io.Writer, forgets []*ForgetGroup) error {
|
||||||
|
return json.NewEncoder(stdout).Encode(forgets)
|
||||||
|
}
|
||||||
|
|
|
@ -219,6 +219,35 @@ func testRunForget(t testing.TB, gopts GlobalOptions, args ...string) {
|
||||||
rtest.OK(t, runForget(opts, gopts, args))
|
rtest.OK(t, runForget(opts, gopts, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testRunForgetJSON(t testing.TB, gopts GlobalOptions, args ...string) {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
oldJSON := gopts.JSON
|
||||||
|
gopts.stdout = buf
|
||||||
|
gopts.JSON = true
|
||||||
|
defer func() {
|
||||||
|
gopts.stdout = os.Stdout
|
||||||
|
gopts.JSON = oldJSON
|
||||||
|
}()
|
||||||
|
|
||||||
|
opts := ForgetOptions{
|
||||||
|
DryRun: true,
|
||||||
|
Last: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
rtest.OK(t, runForget(opts, gopts, args))
|
||||||
|
|
||||||
|
var forgets []*ForgetGroup
|
||||||
|
rtest.OK(t, json.Unmarshal(buf.Bytes(), &forgets))
|
||||||
|
|
||||||
|
rtest.Assert(t, len(forgets) == 1,
|
||||||
|
"Expected 1 snapshot group, got %v", len(forgets))
|
||||||
|
rtest.Assert(t, len(forgets[0].Keep) == 1,
|
||||||
|
"Expected 1 snapshot to be kept, got %v", len(forgets[0].Keep))
|
||||||
|
rtest.Assert(t, len(forgets[0].Remove) == 2,
|
||||||
|
"Expected 2 snapshots to be removed, got %v", len(forgets[0].Remove))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func testRunPrune(t testing.TB, gopts GlobalOptions) {
|
func testRunPrune(t testing.TB, gopts GlobalOptions) {
|
||||||
rtest.OK(t, runPrune(gopts))
|
rtest.OK(t, runPrune(gopts))
|
||||||
}
|
}
|
||||||
|
@ -1051,6 +1080,7 @@ func TestPrune(t *testing.T) {
|
||||||
rtest.Assert(t, len(snapshotIDs) == 3,
|
rtest.Assert(t, len(snapshotIDs) == 3,
|
||||||
"expected 3 snapshot, got %v", snapshotIDs)
|
"expected 3 snapshot, got %v", snapshotIDs)
|
||||||
|
|
||||||
|
testRunForgetJSON(t, env.gopts)
|
||||||
testRunForget(t, env.gopts, firstSnapshot[0].String())
|
testRunForget(t, env.gopts, firstSnapshot[0].String())
|
||||||
testRunPrune(t, env.gopts)
|
testRunPrune(t, env.gopts)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
|
|
Loading…
Add table
Reference in a new issue