2
2
mirror of https://github.com/octoleo/restic.git synced 2024-12-01 17:23:57 +00:00

Implement TagList and TagLists as pflag.Value

This commit is contained in:
Alexander Neumann 2017-07-09 12:45:49 +02:00
parent 7362569cf5
commit 78d090aea5
9 changed files with 86 additions and 55 deletions

View File

@ -34,7 +34,7 @@ type FindOptions struct {
ListLong bool ListLong bool
Host string Host string
Paths []string Paths []string
Tags []string Tags restic.TagLists
} }
var findOptions FindOptions var findOptions FindOptions
@ -50,7 +50,7 @@ func init() {
f.BoolVarP(&findOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode") f.BoolVarP(&findOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
f.StringVarP(&findOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given") f.StringVarP(&findOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given")
f.StringArrayVar(&findOptions.Tags, "tag", nil, "only consider snapshots which include the list of `tag,tag,...`, when no snapshot-ID is given") f.Var(&findOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot-ID is given")
f.StringArrayVar(&findOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot-ID is given") f.StringArrayVar(&findOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot-ID is given")
} }
@ -290,15 +290,13 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
ctx, cancel := context.WithCancel(gopts.ctx) ctx, cancel := context.WithCancel(gopts.ctx)
defer cancel() defer cancel()
tagLists := restic.SplitTagLists(opts.Tags)
f := &Finder{ f := &Finder{
repo: repo, repo: repo,
pat: pat, pat: pat,
out: statefulOutput{ListLong: opts.ListLong, JSON: globalOptions.JSON}, out: statefulOutput{ListLong: opts.ListLong, JSON: globalOptions.JSON},
notfound: restic.NewIDSet(), notfound: restic.NewIDSet(),
} }
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, tagLists, opts.Paths, opts.Snapshots) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, opts.Snapshots) {
if err = f.findInSnapshot(sn); err != nil { if err = f.findInSnapshot(sn); err != nil {
return err return err
} }

View File

@ -31,10 +31,10 @@ type ForgetOptions struct {
Weekly int Weekly int
Monthly int Monthly int
Yearly int Yearly int
KeepTags []string KeepTags restic.TagLists
Host string Host string
Tags []string Tags restic.TagLists
Paths []string Paths []string
GroupByTags bool GroupByTags bool
@ -55,13 +55,13 @@ func init() {
f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots") f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots")
f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots") f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots")
f.StringArrayVar(&forgetOptions.KeepTags, "keep-tag", []string{}, "keep snapshots with this `tag` (can be specified multiple times)") f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
f.BoolVarP(&forgetOptions.GroupByTags, "group-by-tags", "G", false, "Group by host,paths,tags instead of just host,paths") f.BoolVarP(&forgetOptions.GroupByTags, "group-by-tags", "G", false, "Group by host,paths,tags instead of just host,paths")
// Sadly the commonly used shortcut `H` is already used. // Sadly the commonly used shortcut `H` is already used.
f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`") f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`")
// Deprecated since 2017-03-07. // Deprecated since 2017-03-07.
f.StringVar(&forgetOptions.Host, "hostname", "", "only consider snapshots with the given `hostname` (deprecated)") f.StringVar(&forgetOptions.Host, "hostname", "", "only consider snapshots with the given `hostname` (deprecated)")
f.StringArrayVar(&forgetOptions.Tags, "tag", nil, "only consider snapshots which include this `tag` (can be specified multiple times)") f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` (can be specified multiple times)")
f.StringArrayVar(&forgetOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` (can be specified multiple times)") f.StringArrayVar(&forgetOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` (can be specified multiple times)")
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")
@ -92,7 +92,7 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
ctx, cancel := context.WithCancel(gopts.ctx) ctx, cancel := context.WithCancel(gopts.ctx)
defer cancel() defer cancel()
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(opts.Tags), opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
if len(args) > 0 { if len(args) > 0 {
// When explicit snapshots args are given, remove them immediately. // When explicit snapshots args are given, remove them immediately.
if !opts.DryRun { if !opts.DryRun {
@ -122,11 +122,6 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
return nil return nil
} }
var tagLists []restic.TagList
for _, t := range opts.KeepTags {
tagLists = append(tagLists, restic.SplitTagList(t))
}
policy := restic.ExpirePolicy{ policy := restic.ExpirePolicy{
Last: opts.Last, Last: opts.Last,
Hourly: opts.Hourly, Hourly: opts.Hourly,
@ -134,7 +129,7 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
Weekly: opts.Weekly, Weekly: opts.Weekly,
Monthly: opts.Monthly, Monthly: opts.Monthly,
Yearly: opts.Yearly, Yearly: opts.Yearly,
Tags: tagLists, Tags: opts.KeepTags,
} }
if policy.Empty() { if policy.Empty() {

View File

@ -28,7 +28,7 @@ The special snapshot-ID "latest" can be used to list files and directories of th
type LsOptions struct { type LsOptions struct {
ListLong bool ListLong bool
Host string Host string
Tags []string Tags restic.TagLists
Paths []string Paths []string
} }
@ -41,7 +41,7 @@ func init() {
flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode") flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
flags.StringVarP(&lsOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given") flags.StringVarP(&lsOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given")
flags.StringArrayVar(&lsOptions.Tags, "tag", nil, "only consider snapshots which include this `tag`, when no snapshot ID is given") flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given")
flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given") flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given")
} }
@ -80,7 +80,7 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
ctx, cancel := context.WithCancel(gopts.ctx) ctx, cancel := context.WithCancel(gopts.ctx)
defer cancel() defer cancel()
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(opts.Tags), opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
Verbosef("snapshot %s of %v at %s):\n", sn.ID().Str(), sn.Paths, sn.Time) Verbosef("snapshot %s of %v at %s):\n", sn.ID().Str(), sn.Paths, sn.Time)
if err = printTree(repo, sn.Tree, string(filepath.Separator)); err != nil { if err = printTree(repo, sn.Tree, string(filepath.Separator)); err != nil {

View File

@ -38,7 +38,7 @@ type MountOptions struct {
AllowRoot bool AllowRoot bool
AllowOther bool AllowOther bool
Host string Host string
Tags []string Tags restic.TagLists
Paths []string Paths []string
} }
@ -53,7 +53,7 @@ func init() {
mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory") mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory")
mountFlags.StringVarP(&mountOptions.Host, "host", "H", "", `only consider snapshots for this host`) mountFlags.StringVarP(&mountOptions.Host, "host", "H", "", `only consider snapshots for this host`)
mountFlags.StringArrayVar(&mountOptions.Tags, "tag", nil, "only consider snapshots which include this `tag`") mountFlags.Var(&mountOptions.Tags, "tag", "only consider snapshots which include this `taglist`")
mountFlags.StringArrayVar(&mountOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`") mountFlags.StringArrayVar(&mountOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`")
} }
@ -104,7 +104,7 @@ func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
cfg := fuse.Config{ cfg := fuse.Config{
OwnerIsRoot: opts.OwnerRoot, OwnerIsRoot: opts.OwnerRoot,
Host: opts.Host, Host: opts.Host,
Tags: restic.SplitTagLists(opts.Tags), Tags: opts.Tags,
Paths: opts.Paths, Paths: opts.Paths,
} }
root, err := fuse.NewRoot(context.TODO(), repo, cfg) root, err := fuse.NewRoot(context.TODO(), repo, cfg)

View File

@ -31,7 +31,7 @@ type RestoreOptions struct {
Target string Target string
Host string Host string
Paths []string Paths []string
Tags []string Tags restic.TagLists
} }
var restoreOptions RestoreOptions var restoreOptions RestoreOptions
@ -45,7 +45,7 @@ func init() {
flags.StringVarP(&restoreOptions.Target, "target", "t", "", "directory to extract data to") flags.StringVarP(&restoreOptions.Target, "target", "t", "", "directory to extract data to")
flags.StringVarP(&restoreOptions.Host, "host", "H", "", `only consider snapshots for this host when the snapshot ID is "latest"`) flags.StringVarP(&restoreOptions.Host, "host", "H", "", `only consider snapshots for this host when the snapshot ID is "latest"`)
flags.StringArrayVar(&restoreOptions.Tags, "tag", nil, "only consider snapshots which include this `tag` for snapshot ID \"latest\"") flags.Var(&restoreOptions.Tags, "tag", "only consider snapshots which include this `taglist` for snapshot ID \"latest\"")
flags.StringArrayVar(&restoreOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"") flags.StringArrayVar(&restoreOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"")
} }
@ -89,7 +89,7 @@ func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error {
var id restic.ID var id restic.ID
if snapshotIDString == "latest" { if snapshotIDString == "latest" {
id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, restic.SplitTagLists(opts.Tags), opts.Host) id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, opts.Tags, opts.Host)
if err != nil { if err != nil {
Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Host:%v", err, opts.Paths, opts.Host) Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Host:%v", err, opts.Paths, opts.Host)
} }

View File

@ -26,7 +26,7 @@ The "snapshots" command lists all snapshots stored in the repository.
// SnapshotOptions bundles all options for the snapshots command. // SnapshotOptions bundles all options for the snapshots command.
type SnapshotOptions struct { type SnapshotOptions struct {
Host string Host string
Tags []string Tags restic.TagLists
Paths []string Paths []string
} }
@ -37,7 +37,7 @@ func init() {
f := cmdSnapshots.Flags() f := cmdSnapshots.Flags()
f.StringVarP(&snapshotOptions.Host, "host", "H", "", "only consider snapshots for this `host`") f.StringVarP(&snapshotOptions.Host, "host", "H", "", "only consider snapshots for this `host`")
f.StringArrayVar(&snapshotOptions.Tags, "tag", nil, "only consider snapshots which include this `tag` (can be specified multiple times)") f.Var(&snapshotOptions.Tags, "tag", "only consider snapshots which include this `taglist` (can be specified multiple times)")
f.StringArrayVar(&snapshotOptions.Paths, "path", nil, "only consider snapshots for this `path` (can be specified multiple times)") f.StringArrayVar(&snapshotOptions.Paths, "path", nil, "only consider snapshots for this `path` (can be specified multiple times)")
} }
@ -59,7 +59,7 @@ func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) erro
defer cancel() defer cancel()
var list restic.Snapshots var list restic.Snapshots
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(opts.Tags), opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
list = append(list, sn) list = append(list, sn)
} }
sort.Sort(sort.Reverse(list)) sort.Sort(sort.Reverse(list))

View File

@ -31,7 +31,7 @@ When no snapshot-ID is given, all snapshots matching the host, tag and path filt
type TagOptions struct { type TagOptions struct {
Host string Host string
Paths []string Paths []string
Tags []string Tags restic.TagLists
SetTags []string SetTags []string
AddTags []string AddTags []string
RemoveTags []string RemoveTags []string
@ -48,7 +48,7 @@ func init() {
tagFlags.StringSliceVar(&tagOptions.RemoveTags, "remove", nil, "`tag` which will be removed from the existing tags (can be given multiple times)") tagFlags.StringSliceVar(&tagOptions.RemoveTags, "remove", nil, "`tag` which will be removed from the existing tags (can be given multiple times)")
tagFlags.StringVarP(&tagOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given") tagFlags.StringVarP(&tagOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given")
tagFlags.StringArrayVar(&tagOptions.Tags, "tag", nil, "only consider snapshots which include this `tag`, when no snapshot-ID is given") tagFlags.Var(&tagOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot-ID is given")
tagFlags.StringArrayVar(&tagOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot-ID is given") tagFlags.StringArrayVar(&tagOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot-ID is given")
} }
@ -123,7 +123,7 @@ func runTag(opts TagOptions, gopts GlobalOptions, args []string) error {
changeCnt := 0 changeCnt := 0
ctx, cancel := context.WithCancel(gopts.ctx) ctx, cancel := context.WithCancel(gopts.ctx)
defer cancel() defer cancel()
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(opts.Tags), opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
changed, err := changeTags(repo, sn, opts.SetTags, opts.AddTags, opts.RemoveTags) changed, err := changeTags(repo, sn, opts.SetTags, opts.AddTags, opts.RemoveTags)
if err != nil { if err != nil {
Warnf("unable to modify the tags for snapshot ID %q, ignoring: %v\n", sn.ID(), err) Warnf("unable to modify the tags for snapshot ID %q, ignoring: %v\n", sn.ID(), err)

View File

@ -3,33 +3,9 @@ package restic
import ( import (
"reflect" "reflect"
"sort" "sort"
"strings"
"time" "time"
) )
// TagList is a list of tags.
type TagList []string
// SplitTagList splits a string into a list of tags. The tags in the string
// need to be separated by commas. Whitespace is stripped around the individual
// tags.
func SplitTagList(s string) (l TagList) {
for _, t := range strings.Split(s, ",") {
l = append(l, strings.TrimSpace(t))
}
return l
}
// SplitTagLists splits a slice of strings into a slice of TagLists using
// SplitTagList.
func SplitTagLists(s []string) (l []TagList) {
l = make([]TagList, 0, len(s))
for _, t := range s {
l = append(l, SplitTagList(t))
}
return l
}
// ExpirePolicy configures which snapshots should be automatically removed. // ExpirePolicy configures which snapshots should be automatically removed.
type ExpirePolicy struct { type ExpirePolicy struct {
Last int // keep the last n snapshots Last int // keep the last n snapshots

62
src/restic/tag_list.go Normal file
View File

@ -0,0 +1,62 @@
package restic
import (
"fmt"
"strings"
)
// TagList is a list of tags.
type TagList []string
// SplitTagList splits a string into a list of tags. The tags in the string
// need to be separated by commas. Whitespace is stripped around the individual
// tags.
func SplitTagList(s string) (l TagList) {
for _, t := range strings.Split(s, ",") {
l = append(l, strings.TrimSpace(t))
}
return l
}
func (l TagList) String() string {
return "[" + strings.Join(l, ", ") + "]"
}
// Set updates the TagList's value.
func (l *TagList) Set(s string) error {
*l = SplitTagList(s)
return nil
}
// Type returns a description of the type.
func (TagList) Type() string {
return "TagList"
}
// TagLists consists of several TagList.
type TagLists []TagList
// SplitTagLists splits a slice of strings into a slice of TagLists using
// SplitTagList.
func SplitTagLists(s []string) (l TagLists) {
l = make([]TagList, 0, len(s))
for _, t := range s {
l = append(l, SplitTagList(t))
}
return l
}
func (l TagLists) String() string {
return fmt.Sprintf("%v", []TagList(l))
}
// Set updates the TagList's value.
func (l *TagLists) Set(s string) error {
*l = append(*l, SplitTagList(s))
return nil
}
// Type returns a description of the type.
func (TagLists) Type() string {
return "TagLists"
}