2017-03-05 05:20:32 +00:00
package main
import (
2017-03-08 19:28:44 +00:00
"context"
2017-03-05 05:20:32 +00:00
"github.com/spf13/cobra"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository"
2017-07-24 15:42:25 +00:00
"github.com/restic/restic/internal/restic"
2017-03-05 05:20:32 +00:00
)
var cmdTag = & cobra . Command {
Use : "tag [flags] [snapshot-ID ...]" ,
2017-09-11 16:32:44 +00:00
Short : "Modify tags on snapshots" ,
2017-03-05 05:20:32 +00: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 .
When no snapshot - ID is given , all snapshots matching the host , tag and path filter criteria are modified .
2019-11-05 06:03:38 +00:00
EXIT STATUS
== == == == == =
Exit status is 0 if the command was successful , and non - zero if there was any error .
2017-03-05 05:20:32 +00:00
` ,
2017-08-06 19:02:16 +00:00
DisableAutoGenTag : true ,
2017-03-05 05:20:32 +00:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
2022-10-02 21:24:37 +00:00
return runTag ( cmd . Context ( ) , tagOptions , globalOptions , args )
2017-03-05 05:20:32 +00:00
} ,
}
// TagOptions bundles all options for the 'tag' command.
type TagOptions struct {
2022-09-02 22:19:19 +00:00
snapshotFilterOptions
2020-12-29 09:59:09 +00:00
SetTags restic . TagLists
AddTags restic . TagLists
RemoveTags restic . TagLists
2017-03-05 05:20:32 +00:00
}
var tagOptions TagOptions
func init ( ) {
cmdRoot . AddCommand ( cmdTag )
tagFlags := cmdTag . Flags ( )
2020-11-14 21:55:30 +00: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)" )
2022-09-02 22:19:19 +00:00
initMultiSnapshotFilterOptions ( tagFlags , & tagOptions . snapshotFilterOptions , true )
2017-03-05 05:20:32 +00:00
}
use global context for check, debug, dump, find, forget, init, key,
list, mount, tag, unlock commands
gh-1434
2017-12-06 12:02:55 +00:00
func changeTags ( ctx context . Context , repo * repository . Repository , sn * restic . Snapshot , setTags , addTags , removeTags [ ] string ) ( bool , error ) {
2017-03-05 05:20:32 +00:00
var changed bool
if len ( setTags ) != 0 {
2017-03-05 16:43:18 +00:00
// Setting the tag to an empty string really means no tags.
2017-03-05 05:20:32 +00:00
if len ( setTags ) == 1 && setTags [ 0 ] == "" {
setTags = nil
}
sn . Tags = setTags
changed = true
} else {
2017-03-05 16:43:18 +00:00
changed = sn . AddTags ( addTags )
if sn . RemoveTags ( removeTags ) {
changed = true
2017-03-05 05:20:32 +00:00
}
}
if changed {
2017-03-05 16:51:57 +00:00
// Retain the original snapshot id over all tag changes.
if sn . Original == nil {
sn . Original = sn . ID ( )
}
2017-03-05 05:20:32 +00:00
// Save the new snapshot.
2022-06-12 12:38:19 +00:00
id , err := restic . SaveSnapshot ( ctx , repo , sn )
2017-03-05 05:20:32 +00:00
if err != nil {
return false , err
}
2018-01-25 19:49:41 +00:00
debug . Log ( "new snapshot saved as %v" , id )
2017-03-05 05:20:32 +00:00
// Remove the old snapshot.
h := restic . 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 12:02:55 +00:00
if err = repo . Backend ( ) . Remove ( ctx , h ) ; err != nil {
2017-03-05 05:20:32 +00:00
return false , err
}
debug . Log ( "old snapshot %v removed" , sn . ID ( ) )
}
return changed , nil
}
2021-10-31 22:08:13 +00:00
func runTag ( ctx context . Context , opts TagOptions , gopts GlobalOptions , args [ ] string ) error {
2017-03-05 05:20:32 +00: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" )
}
2021-10-31 22:08:13 +00:00
repo , err := OpenRepository ( ctx , gopts )
2017-03-05 05:20:32 +00:00
if err != nil {
return err
}
if ! gopts . NoLock {
2017-10-27 19:06:34 +00:00
Verbosef ( "create exclusive lock for repository\n" )
2021-10-31 22:19:27 +00:00
var lock * restic . Lock
lock , ctx , err = lockRepoExclusive ( ctx , repo )
2017-03-05 05:20:32 +00:00
defer unlockRepo ( lock )
if err != nil {
return err
}
}
changeCnt := 0
2021-11-06 00:14:24 +00:00
for sn := range FindFilteredSnapshots ( ctx , repo . Backend ( ) , repo , opts . Hosts , opts . Tags , opts . Paths , args ) {
2020-12-29 09:59:09 +00:00
changed , err := changeTags ( ctx , repo , sn , opts . SetTags . Flatten ( ) , opts . AddTags . Flatten ( ) , opts . RemoveTags . Flatten ( ) )
2017-03-05 05:20:32 +00:00
if err != nil {
2017-03-08 19:28:44 +00:00
Warnf ( "unable to modify the tags for snapshot ID %q, ignoring: %v\n" , sn . ID ( ) , err )
2017-03-05 05:20:32 +00:00
continue
}
if changed {
changeCnt ++
}
}
if changeCnt == 0 {
2017-10-27 19:06:34 +00:00
Verbosef ( "no snapshots were modified\n" )
2017-03-05 05:20:32 +00:00
} else {
2017-10-27 19:06:34 +00:00
Verbosef ( "modified tags on %v snapshots\n" , changeCnt )
2017-03-05 05:20:32 +00:00
}
return nil
}