Use TagLists for all commands

This commit is contained in:
Alexander Neumann 2017-07-09 09:47:41 +02:00
parent f5b1c7e5f1
commit 7362569cf5
13 changed files with 61 additions and 18 deletions

View file

@ -392,7 +392,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
// Find last snapshot to set it as parent, if not already set // Find last snapshot to set it as parent, if not already set
if !opts.Force && parentSnapshotID == nil { if !opts.Force && parentSnapshotID == nil {
id, err := restic.FindLatestSnapshot(context.TODO(), repo, target, opts.Tags, opts.Hostname) id, err := restic.FindLatestSnapshot(context.TODO(), repo, target, []restic.TagList{opts.Tags}, opts.Hostname)
if err == nil { if err == nil {
parentSnapshotID = &id parentSnapshotID = &id
} else if err != restic.ErrNoSnapshotFound { } else if err != restic.ErrNoSnapshotFound {

View file

@ -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 this `tag`, 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.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,13 +290,15 @@ 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, opts.Tags, opts.Paths, opts.Snapshots) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, tagLists, opts.Paths, opts.Snapshots) {
if err = f.findInSnapshot(sn); err != nil { if err = f.findInSnapshot(sn); err != nil {
return err return err
} }

View file

@ -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, opts.Tags, opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(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 {

View file

@ -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, opts.Tags, opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(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

@ -6,6 +6,7 @@ package main
import ( import (
"context" "context"
"os" "os"
"restic"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -103,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: opts.Tags, Tags: restic.SplitTagLists(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

@ -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, opts.Tags, opts.Host) id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, restic.SplitTagLists(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

@ -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, opts.Tags, opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(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

@ -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, opts.Tags, opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(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

@ -8,7 +8,7 @@ import (
) )
// FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots. // FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots.
func FindFilteredSnapshots(ctx context.Context, repo *repository.Repository, host string, tags []string, paths []string, snapshotIDs []string) <-chan *restic.Snapshot { func FindFilteredSnapshots(ctx context.Context, repo *repository.Repository, host string, tags []restic.TagList, paths []string, snapshotIDs []string) <-chan *restic.Snapshot {
out := make(chan *restic.Snapshot) out := make(chan *restic.Snapshot)
go func() { go func() {
defer close(out) defer close(out)

View file

@ -16,7 +16,7 @@ import (
type Config struct { type Config struct {
OwnerIsRoot bool OwnerIsRoot bool
Host string Host string
Tags []string Tags []restic.TagList
Paths []string Paths []string
} }

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os/user" "os/user"
"path/filepath" "path/filepath"
"restic/debug"
"time" "time"
) )
@ -150,6 +151,25 @@ func (sn *Snapshot) HasTags(l []string) bool {
return true return true
} }
// HasTagList returns true if the snapshot satisfies at least one TagList,
// so there is a TagList in l for which all tags are included in sn.
func (sn *Snapshot) HasTagList(l []TagList) bool {
debug.Log("testing snapshot with tags %v against list: %v", sn.Tags, l)
if len(l) == 0 {
return true
}
for _, tags := range l {
if sn.HasTags(tags) {
debug.Log(" snapshot satisfies %v", tags, l)
return true
}
}
return false
}
func (sn *Snapshot) hasPath(path string) bool { func (sn *Snapshot) hasPath(path string) bool {
for _, snPath := range sn.Paths { for _, snPath := range sn.Paths {
if path == snPath { if path == snPath {

View file

@ -12,7 +12,7 @@ import (
var ErrNoSnapshotFound = errors.New("no snapshot found") var ErrNoSnapshotFound = errors.New("no snapshot found")
// FindLatestSnapshot finds latest snapshot with optional target/directory, tags and hostname filters. // FindLatestSnapshot finds latest snapshot with optional target/directory, tags and hostname filters.
func FindLatestSnapshot(ctx context.Context, repo Repository, targets []string, tags []string, hostname string) (ID, error) { func FindLatestSnapshot(ctx context.Context, repo Repository, targets []string, tagLists []TagList, hostname string) (ID, error) {
var ( var (
latest time.Time latest time.Time
latestID ID latestID ID
@ -24,11 +24,21 @@ func FindLatestSnapshot(ctx context.Context, repo Repository, targets []string,
if err != nil { if err != nil {
return ID{}, errors.Errorf("Error listing snapshot: %v", err) return ID{}, errors.Errorf("Error listing snapshot: %v", err)
} }
if snapshot.Time.After(latest) && (hostname == "" || hostname == snapshot.Hostname) && snapshot.HasTags(tags) && snapshot.HasPaths(targets) { if snapshot.Time.Before(latest) || (hostname != "" && hostname != snapshot.Hostname) {
latest = snapshot.Time continue
latestID = snapshotID
found = true
} }
if !snapshot.HasTagList(tagLists) {
continue
}
if !snapshot.HasPaths(targets) {
continue
}
latest = snapshot.Time
latestID = snapshotID
found = true
} }
if !found { if !found {
@ -53,7 +63,7 @@ func FindSnapshot(repo Repository, s string) (ID, error) {
// FindFilteredSnapshots yields Snapshots filtered from the list of all // FindFilteredSnapshots yields Snapshots filtered from the list of all
// snapshots. // snapshots.
func FindFilteredSnapshots(ctx context.Context, repo Repository, host string, tags []string, paths []string) Snapshots { func FindFilteredSnapshots(ctx context.Context, repo Repository, host string, tags []TagList, paths []string) Snapshots {
results := make(Snapshots, 0, 20) results := make(Snapshots, 0, 20)
for id := range repo.List(ctx, SnapshotFile) { for id := range repo.List(ctx, SnapshotFile) {
@ -62,7 +72,7 @@ func FindFilteredSnapshots(ctx context.Context, repo Repository, host string, ta
fmt.Fprintf(os.Stderr, "could not load snapshot %v: %v\n", id.Str(), err) fmt.Fprintf(os.Stderr, "could not load snapshot %v: %v\n", id.Str(), err)
continue continue
} }
if (host != "" && host != sn.Hostname) || !sn.HasTags(tags) || !sn.HasPaths(paths) { if (host != "" && host != sn.Hostname) || !sn.HasTagList(tags) || !sn.HasPaths(paths) {
continue continue
} }

View file

@ -20,6 +20,16 @@ func SplitTagList(s string) (l TagList) {
return l 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