2017-06-18 11:06:52 +00:00
|
|
|
package restic
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-10-03 12:27:11 +00:00
|
|
|
"fmt"
|
2018-03-03 17:02:47 +00:00
|
|
|
"path/filepath"
|
2017-06-18 11:06:52 +00:00
|
|
|
"time"
|
2017-07-23 12:21:03 +00:00
|
|
|
|
|
|
|
"github.com/restic/restic/internal/errors"
|
2017-06-18 11:06:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// ErrNoSnapshotFound is returned when no snapshot for the given criteria could be found.
|
|
|
|
var ErrNoSnapshotFound = errors.New("no snapshot found")
|
|
|
|
|
2022-10-03 12:30:48 +00:00
|
|
|
// findLatestSnapshot finds latest snapshot with optional target/directory, tags, hostname, and timestamp filters.
|
2022-10-03 12:48:14 +00:00
|
|
|
func findLatestSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, hosts []string,
|
|
|
|
tags []TagList, paths []string, timeStampLimit *time.Time) (*Snapshot, error) {
|
2021-11-06 00:14:24 +00:00
|
|
|
|
2018-03-03 17:02:47 +00:00
|
|
|
var err error
|
2022-10-03 12:41:03 +00:00
|
|
|
absTargets := make([]string, 0, len(paths))
|
|
|
|
for _, target := range paths {
|
2018-03-03 17:02:47 +00:00
|
|
|
if !filepath.IsAbs(target) {
|
|
|
|
target, err = filepath.Abs(target)
|
|
|
|
if err != nil {
|
2022-10-03 12:48:14 +00:00
|
|
|
return nil, errors.Wrap(err, "Abs")
|
2018-03-03 17:02:47 +00:00
|
|
|
}
|
|
|
|
}
|
2018-04-30 20:03:11 +00:00
|
|
|
absTargets = append(absTargets, filepath.Clean(target))
|
2018-03-03 17:02:47 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 12:48:14 +00:00
|
|
|
var latest *Snapshot
|
2017-06-18 11:06:52 +00:00
|
|
|
|
2021-11-06 00:14:24 +00:00
|
|
|
err = ForAllSnapshots(ctx, be, loader, nil, func(id ID, snapshot *Snapshot, err error) error {
|
2017-06-18 11:06:52 +00:00
|
|
|
if err != nil {
|
2020-11-28 07:59:12 +00:00
|
|
|
return errors.Errorf("Error loading snapshot %v: %v", id.Str(), err)
|
2017-06-18 11:06:52 +00:00
|
|
|
}
|
2020-02-26 21:17:59 +00:00
|
|
|
|
2022-02-05 21:42:38 +00:00
|
|
|
if timeStampLimit != nil && snapshot.Time.After(*timeStampLimit) {
|
2022-01-04 06:03:53 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-03 12:48:14 +00:00
|
|
|
if latest != nil && snapshot.Time.Before(latest.Time) {
|
2020-02-26 21:17:59 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-03 12:41:03 +00:00
|
|
|
if !snapshot.HasHostname(hosts) {
|
2018-01-21 16:25:36 +00:00
|
|
|
return nil
|
2017-07-09 07:47:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 12:41:03 +00:00
|
|
|
if !snapshot.HasTagList(tags) {
|
2018-01-21 16:25:36 +00:00
|
|
|
return nil
|
2017-06-18 11:06:52 +00:00
|
|
|
}
|
2017-07-09 07:47:41 +00:00
|
|
|
|
2018-03-03 17:02:47 +00:00
|
|
|
if !snapshot.HasPaths(absTargets) {
|
2018-01-21 16:25:36 +00:00
|
|
|
return nil
|
2017-07-09 07:47:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 12:48:14 +00:00
|
|
|
latest = snapshot
|
2018-01-21 16:25:36 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
2022-10-03 12:48:14 +00:00
|
|
|
return nil, err
|
2017-06-18 11:06:52 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 12:48:14 +00:00
|
|
|
if latest == nil {
|
|
|
|
return nil, ErrNoSnapshotFound
|
2017-06-18 11:06:52 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 12:48:14 +00:00
|
|
|
return latest, nil
|
2017-06-18 11:06:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindSnapshot takes a string and tries to find a snapshot whose ID matches
|
|
|
|
// the string as closely as possible.
|
2022-10-03 12:48:14 +00:00
|
|
|
func FindSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, s string) (*Snapshot, error) {
|
2022-10-03 12:51:00 +00:00
|
|
|
// no need to list snapshots if `s` is already a full id
|
|
|
|
id, err := ParseID(s)
|
2017-06-18 11:06:52 +00:00
|
|
|
if err != nil {
|
2022-10-03 12:51:00 +00:00
|
|
|
// find snapshot id with prefix
|
2022-10-15 14:00:05 +00:00
|
|
|
id, err = Find(ctx, be, SnapshotFile, s)
|
2022-10-03 12:51:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-10-03 12:48:14 +00:00
|
|
|
}
|
|
|
|
return LoadSnapshot(ctx, loader, id)
|
2017-06-18 11:06:52 +00:00
|
|
|
}
|
2017-06-18 11:18:12 +00:00
|
|
|
|
2022-10-03 12:48:14 +00:00
|
|
|
// FindFilteredSnapshot returns either the latests from a filtered list of all snapshots or a snapshot specified by `snapshotID`.
|
|
|
|
func FindFilteredSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, hosts []string, tags []TagList, paths []string, timeStampLimit *time.Time, snapshotID string) (*Snapshot, error) {
|
2022-10-03 12:16:33 +00:00
|
|
|
if snapshotID == "latest" {
|
2022-10-03 12:48:14 +00:00
|
|
|
sn, err := findLatestSnapshot(ctx, be, loader, hosts, tags, paths, timeStampLimit)
|
2022-10-03 12:16:33 +00:00
|
|
|
if err == ErrNoSnapshotFound {
|
2022-10-03 12:27:11 +00:00
|
|
|
err = fmt.Errorf("snapshot filter (Paths:%v Tags:%v Hosts:%v): %w", paths, tags, hosts, err)
|
2022-10-03 12:16:33 +00:00
|
|
|
}
|
2022-10-03 12:48:14 +00:00
|
|
|
return sn, err
|
2022-10-03 12:16:33 +00:00
|
|
|
}
|
2022-10-03 12:48:14 +00:00
|
|
|
return FindSnapshot(ctx, be, loader, snapshotID)
|
2022-10-03 12:16:33 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 11:51:41 +00:00
|
|
|
type SnapshotFindCb func(string, *Snapshot, error) error
|
|
|
|
|
|
|
|
// FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots.
|
|
|
|
func FindFilteredSnapshots(ctx context.Context, be Lister, loader LoaderUnpacked, hosts []string, tags []TagList, paths []string, snapshotIDs []string, fn SnapshotFindCb) error {
|
|
|
|
if len(snapshotIDs) != 0 {
|
|
|
|
var err error
|
|
|
|
usedFilter := false
|
|
|
|
|
|
|
|
ids := NewIDSet()
|
|
|
|
// Process all snapshot IDs given as arguments.
|
|
|
|
for _, s := range snapshotIDs {
|
2022-10-03 12:48:14 +00:00
|
|
|
var sn *Snapshot
|
2022-10-03 11:51:41 +00:00
|
|
|
if s == "latest" {
|
|
|
|
if usedFilter {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
usedFilter = true
|
|
|
|
|
2022-10-03 12:48:14 +00:00
|
|
|
sn, err = findLatestSnapshot(ctx, be, loader, hosts, tags, paths, nil)
|
2022-10-03 11:51:41 +00:00
|
|
|
if err == ErrNoSnapshotFound {
|
|
|
|
err = errors.Errorf("no snapshot matched given filter (Paths:%v Tags:%v Hosts:%v)", paths, tags, hosts)
|
|
|
|
}
|
2022-10-03 12:48:14 +00:00
|
|
|
if sn != nil {
|
|
|
|
ids.Insert(*sn.ID())
|
|
|
|
}
|
2022-10-03 11:51:41 +00:00
|
|
|
} else {
|
2022-10-03 12:48:14 +00:00
|
|
|
sn, err = FindSnapshot(ctx, be, loader, s)
|
|
|
|
if err == nil {
|
|
|
|
if ids.Has(*sn.ID()) {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
ids.Insert(*sn.ID())
|
|
|
|
s = sn.ID().String()
|
|
|
|
}
|
|
|
|
}
|
2022-10-03 11:51:41 +00:00
|
|
|
}
|
|
|
|
err = fn(s, sn, err)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Give the user some indication their filters are not used.
|
|
|
|
if !usedFilter && (len(hosts) != 0 || len(tags) != 0 || len(paths) != 0) {
|
|
|
|
return fn("filters", nil, errors.Errorf("explicit snapshot ids are given"))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return ForAllSnapshots(ctx, be, loader, nil, func(id ID, sn *Snapshot, err error) error {
|
2017-06-18 11:18:12 +00:00
|
|
|
if err != nil {
|
2022-10-03 11:51:41 +00:00
|
|
|
return fn(id.String(), sn, err)
|
2017-06-18 11:18:12 +00:00
|
|
|
}
|
2018-01-21 16:25:36 +00:00
|
|
|
|
2020-02-26 21:17:59 +00:00
|
|
|
if !sn.HasHostname(hosts) || !sn.HasTagList(tags) || !sn.HasPaths(paths) {
|
2018-01-21 16:25:36 +00:00
|
|
|
return nil
|
2017-06-18 11:18:12 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 11:51:41 +00:00
|
|
|
return fn(id.String(), sn, err)
|
2018-01-21 16:25:36 +00:00
|
|
|
})
|
2017-06-18 11:18:12 +00:00
|
|
|
}
|