forked from TrueCloudLab/restic
Use TagLists for all commands
This commit is contained in:
parent
f5b1c7e5f1
commit
7362569cf5
13 changed files with 61 additions and 18 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue