forked from TrueCloudLab/restic
Refactor group-by to parse options into a struct
This commit is contained in:
parent
fa73b50b45
commit
acb40d2b94
4 changed files with 111 additions and 29 deletions
|
@ -56,7 +56,7 @@ type ForgetOptions struct {
|
||||||
Compact bool
|
Compact bool
|
||||||
|
|
||||||
// Grouping
|
// Grouping
|
||||||
GroupBy string
|
GroupBy restic.SnapshotGroupByOptions
|
||||||
DryRun bool
|
DryRun bool
|
||||||
Prune bool
|
Prune bool
|
||||||
}
|
}
|
||||||
|
@ -90,8 +90,8 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
f.BoolVarP(&forgetOptions.Compact, "compact", "c", false, "use compact output format")
|
f.BoolVarP(&forgetOptions.Compact, "compact", "c", false, "use compact output format")
|
||||||
|
forgetOptions.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true}
|
||||||
f.StringVarP(&forgetOptions.GroupBy, "group-by", "g", "host,paths", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')")
|
f.VarP(&forgetOptions.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')")
|
||||||
f.BoolVarP(&forgetOptions.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done")
|
f.BoolVarP(&forgetOptions.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done")
|
||||||
f.BoolVar(&forgetOptions.Prune, "prune", false, "automatically run the 'prune' command if snapshots have been removed")
|
f.BoolVar(&forgetOptions.Prune, "prune", false, "automatically run the 'prune' command if snapshots have been removed")
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ type SnapshotOptions struct {
|
||||||
Compact bool
|
Compact bool
|
||||||
Last bool // This option should be removed in favour of Latest.
|
Last bool // This option should be removed in favour of Latest.
|
||||||
Latest int
|
Latest int
|
||||||
GroupBy string
|
GroupBy restic.SnapshotGroupByOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
var snapshotOptions SnapshotOptions
|
var snapshotOptions SnapshotOptions
|
||||||
|
@ -54,7 +54,7 @@ func init() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
f.IntVar(&snapshotOptions.Latest, "latest", 0, "only show the last `n` snapshots for each host and path")
|
f.IntVar(&snapshotOptions.Latest, "latest", 0, "only show the last `n` snapshots for each host and path")
|
||||||
f.StringVarP(&snapshotOptions.GroupBy, "group-by", "g", "", "`group` snapshots by host, paths and/or tags, separated by comma")
|
f.VarP(&snapshotOptions.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma")
|
||||||
}
|
}
|
||||||
|
|
||||||
func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string) error {
|
func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string) error {
|
||||||
|
|
|
@ -8,6 +8,57 @@ import (
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SnapshotGroupByOptions struct {
|
||||||
|
Tag bool
|
||||||
|
Host bool
|
||||||
|
Path bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitSnapshotGroupBy(s string) (SnapshotGroupByOptions, error) {
|
||||||
|
var l SnapshotGroupByOptions
|
||||||
|
for _, option := range strings.Split(s, ",") {
|
||||||
|
switch option {
|
||||||
|
case "host", "hosts":
|
||||||
|
l.Host = true
|
||||||
|
case "path", "paths":
|
||||||
|
l.Path = true
|
||||||
|
case "tag", "tags":
|
||||||
|
l.Tag = true
|
||||||
|
case "":
|
||||||
|
default:
|
||||||
|
return SnapshotGroupByOptions{}, errors.Fatal("unknown grouping option: '" + option + "'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SnapshotGroupByOptions) String() string {
|
||||||
|
var parts []string
|
||||||
|
if l.Host {
|
||||||
|
parts = append(parts, "host")
|
||||||
|
}
|
||||||
|
if l.Path {
|
||||||
|
parts = append(parts, "paths")
|
||||||
|
}
|
||||||
|
if l.Tag {
|
||||||
|
parts = append(parts, "tags")
|
||||||
|
}
|
||||||
|
return strings.Join(parts, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SnapshotGroupByOptions) Set(s string) error {
|
||||||
|
parts, err := splitSnapshotGroupBy(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*l = parts
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SnapshotGroupByOptions) Type() string {
|
||||||
|
return "group"
|
||||||
|
}
|
||||||
|
|
||||||
// SnapshotGroupKey is the structure for identifying groups in a grouped
|
// SnapshotGroupKey is the structure for identifying groups in a grouped
|
||||||
// snapshot list. This is used by GroupSnapshots()
|
// snapshot list. This is used by GroupSnapshots()
|
||||||
type SnapshotGroupKey struct {
|
type SnapshotGroupKey struct {
|
||||||
|
@ -18,43 +69,24 @@ type SnapshotGroupKey struct {
|
||||||
|
|
||||||
// GroupSnapshots takes a list of snapshots and a grouping criteria and creates
|
// GroupSnapshots takes a list of snapshots and a grouping criteria and creates
|
||||||
// a group list of snapshots.
|
// a group list of snapshots.
|
||||||
func GroupSnapshots(snapshots Snapshots, options string) (map[string]Snapshots, bool, error) {
|
func GroupSnapshots(snapshots Snapshots, groupBy SnapshotGroupByOptions) (map[string]Snapshots, bool, error) {
|
||||||
// group by hostname and dirs
|
// group by hostname and dirs
|
||||||
snapshotGroups := make(map[string]Snapshots)
|
snapshotGroups := make(map[string]Snapshots)
|
||||||
|
|
||||||
var GroupByTag bool
|
|
||||||
var GroupByHost bool
|
|
||||||
var GroupByPath bool
|
|
||||||
GroupOptionList := strings.Split(options, ",")
|
|
||||||
|
|
||||||
for _, option := range GroupOptionList {
|
|
||||||
switch option {
|
|
||||||
case "host", "hosts":
|
|
||||||
GroupByHost = true
|
|
||||||
case "path", "paths":
|
|
||||||
GroupByPath = true
|
|
||||||
case "tag", "tags":
|
|
||||||
GroupByTag = true
|
|
||||||
case "":
|
|
||||||
default:
|
|
||||||
return nil, false, errors.Fatal("unknown grouping option: '" + option + "'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sn := range snapshots {
|
for _, sn := range snapshots {
|
||||||
// Determining grouping-keys
|
// Determining grouping-keys
|
||||||
var tags []string
|
var tags []string
|
||||||
var hostname string
|
var hostname string
|
||||||
var paths []string
|
var paths []string
|
||||||
|
|
||||||
if GroupByTag {
|
if groupBy.Tag {
|
||||||
tags = sn.Tags
|
tags = sn.Tags
|
||||||
sort.Strings(tags)
|
sort.Strings(tags)
|
||||||
}
|
}
|
||||||
if GroupByHost {
|
if groupBy.Host {
|
||||||
hostname = sn.Hostname
|
hostname = sn.Hostname
|
||||||
}
|
}
|
||||||
if GroupByPath {
|
if groupBy.Path {
|
||||||
paths = sn.Paths
|
paths = sn.Paths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,5 +102,5 @@ func GroupSnapshots(snapshots Snapshots, options string) (map[string]Snapshots,
|
||||||
snapshotGroups[string(k)] = append(snapshotGroups[string(k)], sn)
|
snapshotGroups[string(k)] = append(snapshotGroups[string(k)], sn)
|
||||||
}
|
}
|
||||||
|
|
||||||
return snapshotGroups, GroupByTag || GroupByHost || GroupByPath, nil
|
return snapshotGroups, groupBy.Tag || groupBy.Host || groupBy.Path, nil
|
||||||
}
|
}
|
||||||
|
|
50
internal/restic/snapshot_group_test.go
Normal file
50
internal/restic/snapshot_group_test.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package restic_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/restic/restic/internal/restic"
|
||||||
|
"github.com/restic/restic/internal/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGroupByOptions(t *testing.T) {
|
||||||
|
for _, exp := range []struct {
|
||||||
|
from string
|
||||||
|
opts restic.SnapshotGroupByOptions
|
||||||
|
normalized string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
from: "",
|
||||||
|
opts: restic.SnapshotGroupByOptions{},
|
||||||
|
normalized: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "host,paths",
|
||||||
|
opts: restic.SnapshotGroupByOptions{Host: true, Path: true},
|
||||||
|
normalized: "host,paths",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "host,path,tag",
|
||||||
|
opts: restic.SnapshotGroupByOptions{Host: true, Path: true, Tag: true},
|
||||||
|
normalized: "host,paths,tags",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "hosts,paths,tags",
|
||||||
|
opts: restic.SnapshotGroupByOptions{Host: true, Path: true, Tag: true},
|
||||||
|
normalized: "host,paths,tags",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
var opts restic.SnapshotGroupByOptions
|
||||||
|
test.OK(t, opts.Set(exp.from))
|
||||||
|
if !cmp.Equal(opts, exp.opts) {
|
||||||
|
t.Errorf("unexpeted opts %s", cmp.Diff(opts, exp.opts))
|
||||||
|
}
|
||||||
|
test.Equals(t, opts.String(), exp.normalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
var opts restic.SnapshotGroupByOptions
|
||||||
|
err := opts.Set("tags,invalid")
|
||||||
|
test.Assert(t, err != nil, "missing error on invalid tags")
|
||||||
|
test.Assert(t, !opts.Host && !opts.Path && !opts.Tag, "unexpected opts %s %s %s", opts.Host, opts.Path, opts.Tag)
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue