2016-08-20 17:43:25 +02:00
package main
import (
2017-03-08 20:24:58 +01:00
"context"
2017-03-07 14:19:36 +01:00
"encoding/json"
"sort"
2016-08-20 17:59:10 +02:00
"strings"
2016-09-17 12:36:05 +02:00
2017-07-24 17:42:25 +02:00
"github.com/restic/restic/internal/restic"
2016-09-17 12:36:05 +02:00
"github.com/spf13/cobra"
2016-08-20 17:43:25 +02:00
)
2016-09-17 12:36:05 +02:00
var cmdForget = & cobra . Command {
Use : "forget [flags] [snapshot ID] [...]" ,
Short : "forget removes snapshots from the repository" ,
Long : `
The "forget" command removes snapshots according to a policy . Please note that
this command really only deletes the snapshot object in the repository , which
is a reference to data stored there . In order to remove this ( now unreferenced )
data after ' forget ' was run successfully , see the ' prune ' command . ` ,
2017-08-06 21:02:16 +02:00
DisableAutoGenTag : true ,
2016-09-17 12:36:05 +02:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
return runForget ( forgetOptions , globalOptions , args )
} ,
}
2016-08-20 17:43:25 +02:00
2016-09-17 12:36:05 +02:00
// ForgetOptions collects all options for the forget command.
type ForgetOptions struct {
2017-03-08 20:24:58 +01:00
Last int
Hourly int
Daily int
Weekly int
Monthly int
Yearly int
2017-07-09 12:45:49 +02:00
KeepTags restic . TagLists
2016-08-20 17:59:47 +02:00
2017-03-07 14:19:36 +01:00
Host string
2017-07-09 12:45:49 +02:00
Tags restic . TagLists
2017-03-07 14:19:36 +01:00
Paths [ ] string
2016-08-20 17:43:25 +02:00
2017-03-07 14:19:36 +01:00
GroupByTags bool
DryRun bool
Prune bool
2016-08-20 17:43:25 +02:00
}
2016-09-17 12:36:05 +02:00
var forgetOptions ForgetOptions
2016-08-20 17:43:25 +02:00
func init ( ) {
2016-09-17 12:36:05 +02:00
cmdRoot . AddCommand ( cmdForget )
f := cmdForget . Flags ( )
2016-09-29 20:39:55 +02:00
f . IntVarP ( & forgetOptions . Last , "keep-last" , "l" , 0 , "keep the last `n` snapshots" )
f . IntVarP ( & forgetOptions . Hourly , "keep-hourly" , "H" , 0 , "keep the last `n` hourly snapshots" )
f . IntVarP ( & forgetOptions . Daily , "keep-daily" , "d" , 0 , "keep the last `n` daily snapshots" )
f . IntVarP ( & forgetOptions . Weekly , "keep-weekly" , "w" , 0 , "keep the last `n` weekly snapshots" )
f . IntVarP ( & forgetOptions . Monthly , "keep-monthly" , "m" , 0 , "keep the last `n` monthly snapshots" )
f . IntVarP ( & forgetOptions . Yearly , "keep-yearly" , "y" , 0 , "keep the last `n` yearly snapshots" )
2017-07-09 12:45:49 +02:00
f . Var ( & forgetOptions . KeepTags , "keep-tag" , "keep snapshots with this `taglist` (can be specified multiple times)" )
2017-03-07 14:19:36 +01:00
f . BoolVarP ( & forgetOptions . GroupByTags , "group-by-tags" , "G" , false , "Group by host,paths,tags instead of just host,paths" )
// Sadly the commonly used shortcut `H` is already used.
f . StringVar ( & forgetOptions . Host , "host" , "" , "only consider snapshots with the given `host`" )
// Deprecated since 2017-03-07.
f . StringVar ( & forgetOptions . Host , "hostname" , "" , "only consider snapshots with the given `hostname` (deprecated)" )
2017-07-16 15:25:28 +02:00
f . Var ( & forgetOptions . Tags , "tag" , "only consider snapshots which include this `taglist` in the format `tag[,tag,...]` (can be specified multiple times)" )
2017-07-07 03:19:06 +02:00
f . StringArrayVar ( & forgetOptions . Paths , "path" , nil , "only consider snapshots which include this (absolute) `path` (can be specified multiple times)" )
2016-08-20 17:43:25 +02:00
2016-09-17 12:36:05 +02:00
f . BoolVarP ( & forgetOptions . DryRun , "dry-run" , "n" , false , "do not delete anything, just print what would be done" )
2017-02-21 10:58:30 +01:00
f . BoolVar ( & forgetOptions . Prune , "prune" , false , "automatically run the 'prune' command if snapshots have been removed" )
2017-04-21 19:25:21 +02:00
f . SortFlags = false
2016-08-20 17:43:25 +02:00
}
2016-09-17 12:36:05 +02:00
func runForget ( opts ForgetOptions , gopts GlobalOptions , args [ ] string ) error {
repo , err := OpenRepository ( gopts )
2016-08-20 17:43:25 +02:00
if err != nil {
return err
}
lock , err := lockRepoExclusive ( repo )
defer unlockRepo ( lock )
if err != nil {
return err
}
2017-03-08 20:24:58 +01:00
// group by hostname and dirs
type key struct {
Hostname string
Paths [ ] string
Tags [ ] string
}
snapshotGroups := make ( map [ string ] restic . Snapshots )
2016-08-20 17:43:25 +02:00
2017-05-04 16:35:35 +02:00
ctx , cancel := context . WithCancel ( gopts . ctx )
2017-03-08 20:24:58 +01:00
defer cancel ( )
2017-07-09 12:45:49 +02:00
for sn := range FindFilteredSnapshots ( ctx , repo , opts . Host , opts . Tags , opts . Paths , args ) {
2017-03-08 20:24:58 +01:00
if len ( args ) > 0 {
// When explicit snapshots args are given, remove them immediately.
2017-03-07 14:19:36 +01:00
if ! opts . DryRun {
2017-03-08 20:24:58 +01:00
h := restic . Handle { Type : restic . SnapshotFile , Name : sn . ID ( ) . String ( ) }
2017-06-04 11:16:55 +02:00
if err = repo . Backend ( ) . Remove ( context . TODO ( ) , h ) ; err != nil {
2017-03-07 14:19:36 +01:00
return err
}
2017-03-08 20:24:58 +01:00
Verbosef ( "removed snapshot %v\n" , sn . ID ( ) . Str ( ) )
2017-03-07 14:19:36 +01:00
} else {
2017-03-08 20:24:58 +01:00
Verbosef ( "would have removed snapshot %v\n" , sn . ID ( ) . Str ( ) )
}
} else {
var tags [ ] string
if opts . GroupByTags {
tags = sn . Tags
sort . StringSlice ( tags ) . Sort ( )
}
sort . StringSlice ( sn . Paths ) . Sort ( )
k , err := json . Marshal ( key { Hostname : sn . Hostname , Tags : tags , Paths : sn . Paths } )
if err != nil {
return err
2017-03-07 14:19:36 +01:00
}
2017-03-08 20:24:58 +01:00
snapshotGroups [ string ( k ) ] = append ( snapshotGroups [ string ( k ) ] , sn )
2016-08-20 17:43:25 +02:00
}
2017-03-08 20:24:58 +01:00
}
if len ( args ) > 0 {
2017-03-07 14:19:36 +01:00
return nil
2016-08-20 17:53:03 +02:00
}
policy := restic . ExpirePolicy {
2016-09-17 12:36:05 +02:00
Last : opts . Last ,
Hourly : opts . Hourly ,
Daily : opts . Daily ,
Weekly : opts . Weekly ,
Monthly : opts . Monthly ,
Yearly : opts . Yearly ,
2017-07-09 12:45:49 +02:00
Tags : opts . KeepTags ,
2016-08-20 17:53:03 +02:00
}
2016-08-20 17:43:25 +02:00
2017-03-07 14:19:36 +01:00
if policy . Empty ( ) {
Verbosef ( "no policy was specified, no snapshots will be removed\n" )
2017-03-08 20:24:58 +01:00
return nil
2016-08-20 17:43:25 +02:00
}
2017-02-21 10:58:30 +01:00
removeSnapshots := 0
2017-03-07 14:19:36 +01:00
for k , snapshotGroup := range snapshotGroups {
var key key
2017-03-08 20:24:58 +01:00
if json . Unmarshal ( [ ] byte ( k ) , & key ) != nil {
return err
}
2017-03-07 14:19:36 +01:00
if opts . GroupByTags {
2017-03-09 20:18:17 +01:00
Verbosef ( "snapshots for host %v, tags [%v], paths: [%v]:\n\n" , key . Hostname , strings . Join ( key . Tags , ", " ) , strings . Join ( key . Paths , ", " ) )
2017-03-07 14:19:36 +01:00
} else {
2017-03-09 20:18:17 +01:00
Verbosef ( "snapshots for host %v, paths: [%v]:\n\n" , key . Hostname , strings . Join ( key . Paths , ", " ) )
2017-03-07 14:19:36 +01:00
}
2016-08-20 17:43:25 +02:00
keep , remove := restic . ApplyPolicy ( snapshotGroup , policy )
2017-03-09 20:18:17 +01:00
if len ( keep ) != 0 && ! gopts . Quiet {
2017-03-07 14:19:36 +01:00
Printf ( "keep %d snapshots:\n" , len ( keep ) )
PrintSnapshots ( globalOptions . stdout , keep )
Printf ( "\n" )
}
2016-08-20 17:43:25 +02:00
2017-03-09 20:18:17 +01:00
if len ( remove ) != 0 && ! gopts . Quiet {
2017-03-07 14:19:36 +01:00
Printf ( "remove %d snapshots:\n" , len ( remove ) )
PrintSnapshots ( globalOptions . stdout , remove )
Printf ( "\n" )
}
2016-08-20 17:43:25 +02:00
2017-02-21 10:58:30 +01:00
removeSnapshots += len ( remove )
2016-09-17 12:36:05 +02:00
if ! opts . DryRun {
2016-08-20 17:43:25 +02:00
for _ , sn := range remove {
2017-01-25 17:48:35 +01:00
h := restic . Handle { Type : restic . SnapshotFile , Name : sn . ID ( ) . String ( ) }
2017-06-04 11:16:55 +02:00
err = repo . Backend ( ) . Remove ( context . TODO ( ) , h )
2016-08-20 17:43:25 +02:00
if err != nil {
return err
}
}
}
}
2017-02-21 10:58:30 +01:00
if removeSnapshots > 0 && opts . Prune {
2017-03-09 20:18:17 +01:00
Verbosef ( "%d snapshots have been removed, running prune\n" , removeSnapshots )
2017-02-21 10:58:30 +01:00
if ! opts . DryRun {
return pruneRepository ( gopts , repo )
}
}
2016-08-20 17:43:25 +02:00
return nil
}