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/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
"github.com/restic/restic/internal/ui/termstatus"
|
||||
)
|
||||
|
||||
var cmdTag = &cobra.Command{
|
||||
|
@ -34,7 +36,9 @@ Exit status is 12 if the password is incorrect.
|
|||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if len(setTags) != 0 {
|
||||
|
@ -87,7 +102,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna
|
|||
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.
|
||||
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())
|
||||
|
||||
printFunc(changedSnapshot{MessageType: "changed", OldSnapshotID: *sn.ID(), NewSnapshotID: id})
|
||||
}
|
||||
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 {
|
||||
return errors.Fatal("nothing to do!")
|
||||
}
|
||||
|
@ -114,24 +131,44 @@ func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, args []st
|
|||
}
|
||||
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) {
|
||||
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 {
|
||||
Warnf("unable to modify the tags for snapshot ID %q, ignoring: %v\n", sn.ID(), err)
|
||||
continue
|
||||
}
|
||||
if changed {
|
||||
changeCnt++
|
||||
summary.ChangedSnapshots++
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
if changeCnt == 0 {
|
||||
Verbosef("no snapshots were modified\n")
|
||||
} else {
|
||||
Verbosef("modified tags on %v snapshots\n", changeCnt)
|
||||
}
|
||||
|
||||
printSummary(summary)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
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
|
||||
|
|
|
@ -722,6 +722,30 @@ The stats command returns a single JSON object.
|
|||
| ``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
|
||||
-------
|
||||
|
|
Loading…
Reference in New Issue