mirror of https://github.com/restic/restic.git
tag: output the original ID and new snapshotID (#5144)
* tag: output the original ID and new snapshotID tag: print changed snapshot information immediately * print changed snapshot immediately after it has been saved * add message type to the changedSnapshot * add a summary type which will share the JSON output of the numer of changed snapshots * updated verbosity of the changed snapshot in text mode to only work when verbosity > 2 * also use the terminal status printer for a standard handling for stdout messages
This commit is contained in:
parent
e6f9cfb8c8
commit
115ecb3c92
|
@ -0,0 +1,8 @@
|
||||||
|
Enhancement: Restic tag command returns the modified snapshot information
|
||||||
|
|
||||||
|
Restic `tag` command now returns the modified snapshot information in the
|
||||||
|
output. Added `--json` option to the command to get the output in JSON format
|
||||||
|
for scripting access.
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/5137
|
||||||
|
https://github.com/restic/restic/pull/5144
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
"github.com/restic/restic/internal/repository"
|
"github.com/restic/restic/internal/repository"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
|
"github.com/restic/restic/internal/ui"
|
||||||
|
"github.com/restic/restic/internal/ui/termstatus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdTag = &cobra.Command{
|
var cmdTag = &cobra.Command{
|
||||||
|
@ -34,7 +36,9 @@ Exit status is 12 if the password is incorrect.
|
||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return runTag(cmd.Context(), tagOptions, globalOptions, args)
|
term, cancel := setupTermstatus()
|
||||||
|
defer cancel()
|
||||||
|
return runTag(cmd.Context(), tagOptions, globalOptions, term, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +62,18 @@ func init() {
|
||||||
initMultiSnapshotFilter(tagFlags, &tagOptions.SnapshotFilter, true)
|
initMultiSnapshotFilter(tagFlags, &tagOptions.SnapshotFilter, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot, setTags, addTags, removeTags []string) (bool, error) {
|
type changedSnapshot struct {
|
||||||
|
MessageType string `json:"message_type"` // changed
|
||||||
|
OldSnapshotID restic.ID `json:"old_snapshot_id"`
|
||||||
|
NewSnapshotID restic.ID `json:"new_snapshot_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type changedSnapshotsSummary struct {
|
||||||
|
MessageType string `json:"message_type"` // summary
|
||||||
|
ChangedSnapshots int `json:"changed_snapshots"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot, setTags, addTags, removeTags []string, printFunc func(changedSnapshot)) (bool, error) {
|
||||||
var changed bool
|
var changed bool
|
||||||
|
|
||||||
if len(setTags) != 0 {
|
if len(setTags) != 0 {
|
||||||
|
@ -87,7 +102,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("new snapshot saved as %v", id)
|
debug.Log("old snapshot %v saved as a new snapshot %v", sn.ID(), id)
|
||||||
|
|
||||||
// Remove the old snapshot.
|
// Remove the old snapshot.
|
||||||
if err = repo.RemoveUnpacked(ctx, restic.WriteableSnapshotFile, *sn.ID()); err != nil {
|
if err = repo.RemoveUnpacked(ctx, restic.WriteableSnapshotFile, *sn.ID()); err != nil {
|
||||||
|
@ -95,11 +110,13 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("old snapshot %v removed", sn.ID())
|
debug.Log("old snapshot %v removed", sn.ID())
|
||||||
|
|
||||||
|
printFunc(changedSnapshot{MessageType: "changed", OldSnapshotID: *sn.ID(), NewSnapshotID: id})
|
||||||
}
|
}
|
||||||
return changed, nil
|
return changed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, args []string) error {
|
func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, term *termstatus.Terminal, args []string) error {
|
||||||
if len(opts.SetTags) == 0 && len(opts.AddTags) == 0 && len(opts.RemoveTags) == 0 {
|
if len(opts.SetTags) == 0 && len(opts.AddTags) == 0 && len(opts.RemoveTags) == 0 {
|
||||||
return errors.Fatal("nothing to do!")
|
return errors.Fatal("nothing to do!")
|
||||||
}
|
}
|
||||||
|
@ -114,24 +131,44 @@ func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, args []st
|
||||||
}
|
}
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
changeCnt := 0
|
printFunc := func(c changedSnapshot) {
|
||||||
|
Verboseff("old snapshot ID: %v -> new snapshot ID: %v\n", c.OldSnapshotID, c.NewSnapshotID)
|
||||||
|
}
|
||||||
|
|
||||||
|
summary := changedSnapshotsSummary{MessageType: "summary", ChangedSnapshots: 0}
|
||||||
|
printSummary := func(c changedSnapshotsSummary) {
|
||||||
|
if c.ChangedSnapshots == 0 {
|
||||||
|
Verbosef("no snapshots were modified\n")
|
||||||
|
} else {
|
||||||
|
Verbosef("modified %v snapshots\n", c.ChangedSnapshots)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gopts.JSON {
|
||||||
|
printFunc = func(c changedSnapshot) {
|
||||||
|
term.Print(ui.ToJSONString(c))
|
||||||
|
}
|
||||||
|
printSummary = func(c changedSnapshotsSummary) {
|
||||||
|
term.Print(ui.ToJSONString(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for sn := range FindFilteredSnapshots(ctx, repo, repo, &opts.SnapshotFilter, args) {
|
for sn := range FindFilteredSnapshots(ctx, repo, repo, &opts.SnapshotFilter, args) {
|
||||||
changed, err := changeTags(ctx, repo, sn, opts.SetTags.Flatten(), opts.AddTags.Flatten(), opts.RemoveTags.Flatten())
|
changed, err := changeTags(ctx, repo, sn, opts.SetTags.Flatten(), opts.AddTags.Flatten(), opts.RemoveTags.Flatten(), printFunc)
|
||||||
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)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
changeCnt++
|
summary.ChangedSnapshots++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
if changeCnt == 0 {
|
|
||||||
Verbosef("no snapshots were modified\n")
|
printSummary(summary)
|
||||||
} else {
|
|
||||||
Verbosef("modified tags on %v snapshots\n", changeCnt)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) {
|
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) {
|
||||||
rtest.OK(t, runTag(context.TODO(), opts, gopts, []string{}))
|
rtest.OK(t, runTag(context.TODO(), opts, gopts, nil, []string{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: staticcheck // false positive nil pointer dereference check
|
// nolint: staticcheck // false positive nil pointer dereference check
|
||||||
|
|
|
@ -722,6 +722,30 @@ The stats command returns a single JSON object.
|
||||||
| ``compression_space_saving`` | Overall space saving due to compression |
|
| ``compression_space_saving`` | Overall space saving due to compression |
|
||||||
+------------------------------+-----------------------------------------------------+
|
+------------------------------+-----------------------------------------------------+
|
||||||
|
|
||||||
|
tag
|
||||||
|
---
|
||||||
|
|
||||||
|
The ``tag`` command uses the JSON lines format with the following message types.
|
||||||
|
|
||||||
|
Changed
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
+--------------------------+-------------------------------------------+
|
||||||
|
| ``message_type`` | Always "changed" |
|
||||||
|
+--------------------------+-------------------------------------------+
|
||||||
|
| ``old_snapshot_id`` | ID of the snapshot before the change |
|
||||||
|
+--------------------------+-------------------------------------------+
|
||||||
|
| ``new_snapshot_id`` | ID of the snapshot after the change |
|
||||||
|
+--------------------------+-------------------------------------------+
|
||||||
|
|
||||||
|
Summary
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
+-----------------------------+-------------------------------------------+
|
||||||
|
| ``message_type`` | Always "summary" |
|
||||||
|
+-----------------------------+-------------------------------------------+
|
||||||
|
| ``changed_snapshot_count`` | Total number of changed snapshots |
|
||||||
|
+-----------------------------+-------------------------------------------+
|
||||||
|
|
||||||
version
|
version
|
||||||
-------
|
-------
|
||||||
|
|
Loading…
Reference in New Issue