2017-03-05 06:20:32 +01:00
package main
import (
2017-03-08 20:28:44 +01:00
"context"
2017-03-05 06:20:32 +01:00
"github.com/spf13/cobra"
2023-10-01 11:40:12 +02:00
"github.com/restic/restic/internal/backend"
2017-07-23 14:21:03 +02:00
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository"
2017-07-24 17:42:25 +02:00
"github.com/restic/restic/internal/restic"
2017-03-05 06:20:32 +01:00
)
var cmdTag = & cobra . Command {
2023-08-05 12:35:45 +02:00
Use : "tag [flags] [snapshotID ...]" ,
2017-09-11 18:32:44 +02:00
Short : "Modify tags on snapshots" ,
2017-03-05 06:20:32 +01:00
Long : `
The "tag" command allows you to modify tags on exiting snapshots .
You can either set / replace the entire set of tags on a snapshot , or
add tags to / remove tags from the existing set .
2023-08-05 12:35:45 +02:00
When no snapshotID is given , all snapshots matching the host , tag and path filter criteria are modified .
2019-11-05 07:03:38 +01:00
EXIT STATUS
== == == == == =
Exit status is 0 if the command was successful , and non - zero if there was any error .
2017-03-05 06:20:32 +01:00
` ,
2017-08-06 21:02:16 +02:00
DisableAutoGenTag : true ,
2017-03-05 06:20:32 +01:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
2022-10-02 23:24:37 +02:00
return runTag ( cmd . Context ( ) , tagOptions , globalOptions , args )
2017-03-05 06:20:32 +01:00
} ,
}
// TagOptions bundles all options for the 'tag' command.
type TagOptions struct {
2023-02-17 16:13:46 +01:00
restic . SnapshotFilter
2020-12-29 10:59:09 +01:00
SetTags restic . TagLists
AddTags restic . TagLists
RemoveTags restic . TagLists
2017-03-05 06:20:32 +01:00
}
var tagOptions TagOptions
func init ( ) {
cmdRoot . AddCommand ( cmdTag )
tagFlags := cmdTag . Flags ( )
2020-11-14 22:55:30 +01:00
tagFlags . Var ( & tagOptions . SetTags , "set" , "`tags` which will replace the existing tags in the format `tag[,tag,...]` (can be given multiple times)" )
tagFlags . Var ( & tagOptions . AddTags , "add" , "`tags` which will be added to the existing tags in the format `tag[,tag,...]` (can be given multiple times)" )
tagFlags . Var ( & tagOptions . RemoveTags , "remove" , "`tags` which will be removed from the existing tags in the format `tag[,tag,...]` (can be given multiple times)" )
2023-02-17 16:13:46 +01:00
initMultiSnapshotFilter ( tagFlags , & tagOptions . SnapshotFilter , true )
2017-03-05 06:20:32 +01:00
}
use global context for check, debug, dump, find, forget, init, key,
list, mount, tag, unlock commands
gh-1434
2017-12-06 13:02:55 +01:00
func changeTags ( ctx context . Context , repo * repository . Repository , sn * restic . Snapshot , setTags , addTags , removeTags [ ] string ) ( bool , error ) {
2017-03-05 06:20:32 +01:00
var changed bool
if len ( setTags ) != 0 {
2017-03-05 17:43:18 +01:00
// Setting the tag to an empty string really means no tags.
2017-03-05 06:20:32 +01:00
if len ( setTags ) == 1 && setTags [ 0 ] == "" {
setTags = nil
}
sn . Tags = setTags
changed = true
} else {
2017-03-05 17:43:18 +01:00
changed = sn . AddTags ( addTags )
if sn . RemoveTags ( removeTags ) {
changed = true
2017-03-05 06:20:32 +01:00
}
}
if changed {
2017-03-05 17:51:57 +01:00
// Retain the original snapshot id over all tag changes.
if sn . Original == nil {
sn . Original = sn . ID ( )
}
2017-03-05 06:20:32 +01:00
// Save the new snapshot.
2022-06-12 14:38:19 +02:00
id , err := restic . SaveSnapshot ( ctx , repo , sn )
2017-03-05 06:20:32 +01:00
if err != nil {
return false , err
}
2018-01-25 20:49:41 +01:00
debug . Log ( "new snapshot saved as %v" , id )
2017-03-05 06:20:32 +01:00
// Remove the old snapshot.
2023-10-01 11:40:12 +02:00
h := backend . Handle { Type : restic . SnapshotFile , Name : sn . ID ( ) . String ( ) }
use global context for check, debug, dump, find, forget, init, key,
list, mount, tag, unlock commands
gh-1434
2017-12-06 13:02:55 +01:00
if err = repo . Backend ( ) . Remove ( ctx , h ) ; err != nil {
2017-03-05 06:20:32 +01:00
return false , err
}
debug . Log ( "old snapshot %v removed" , sn . ID ( ) )
}
return changed , nil
}
2021-10-31 23:08:13 +01:00
func runTag ( ctx context . Context , opts TagOptions , gopts GlobalOptions , args [ ] string ) error {
2017-03-05 06:20:32 +01:00
if len ( opts . SetTags ) == 0 && len ( opts . AddTags ) == 0 && len ( opts . RemoveTags ) == 0 {
return errors . Fatal ( "nothing to do!" )
}
if len ( opts . SetTags ) != 0 && ( len ( opts . AddTags ) != 0 || len ( opts . RemoveTags ) != 0 ) {
return errors . Fatal ( "--set and --add/--remove cannot be given at the same time" )
}
2024-02-24 15:19:02 +01:00
Verbosef ( "create exclusive lock for repository\n" )
ctx , repo , unlock , err := openWithExclusiveLock ( ctx , gopts , false )
2017-03-05 06:20:32 +01:00
if err != nil {
2024-02-24 15:19:02 +01:00
return nil
2017-03-05 06:20:32 +01:00
}
2024-02-24 15:19:02 +01:00
defer unlock ( )
2017-03-05 06:20:32 +01:00
changeCnt := 0
2023-10-01 13:05:56 +02:00
for sn := range FindFilteredSnapshots ( ctx , repo , repo , & opts . SnapshotFilter , args ) {
2020-12-29 10:59:09 +01:00
changed , err := changeTags ( ctx , repo , sn , opts . SetTags . Flatten ( ) , opts . AddTags . Flatten ( ) , opts . RemoveTags . Flatten ( ) )
2017-03-05 06:20:32 +01:00
if err != nil {
2017-03-08 20:28:44 +01:00
Warnf ( "unable to modify the tags for snapshot ID %q, ignoring: %v\n" , sn . ID ( ) , err )
2017-03-05 06:20:32 +01:00
continue
}
if changed {
changeCnt ++
}
}
2024-03-30 00:19:58 +01:00
if ctx . Err ( ) != nil {
return ctx . Err ( )
}
2017-03-05 06:20:32 +01:00
if changeCnt == 0 {
2017-10-27 21:06:34 +02:00
Verbosef ( "no snapshots were modified\n" )
2017-03-05 06:20:32 +01:00
} else {
2017-10-27 21:06:34 +02:00
Verbosef ( "modified tags on %v snapshots\n" , changeCnt )
2017-03-05 06:20:32 +01:00
}
return nil
}