mirror of
https://github.com/octoleo/restic.git
synced 2025-01-09 01:21:15 +00:00
24f4e780f1
The go std library should be good enough to manage the files in the backend and cache folders.
180 lines
3.7 KiB
Go
180 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/restic/restic/internal/backend/cache"
|
|
"github.com/restic/restic/internal/errors"
|
|
"github.com/restic/restic/internal/ui"
|
|
"github.com/restic/restic/internal/ui/table"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var cmdCache = &cobra.Command{
|
|
Use: "cache",
|
|
Short: "Operate on local cache directories",
|
|
Long: `
|
|
The "cache" command allows listing and cleaning local cache directories.
|
|
|
|
EXIT STATUS
|
|
===========
|
|
|
|
Exit status is 0 if the command was successful.
|
|
Exit status is 1 if there was any error.
|
|
`,
|
|
GroupID: cmdGroupDefault,
|
|
DisableAutoGenTag: true,
|
|
RunE: func(_ *cobra.Command, args []string) error {
|
|
return runCache(cacheOptions, globalOptions, args)
|
|
},
|
|
}
|
|
|
|
// CacheOptions bundles all options for the snapshots command.
|
|
type CacheOptions struct {
|
|
Cleanup bool
|
|
MaxAge uint
|
|
NoSize bool
|
|
}
|
|
|
|
var cacheOptions CacheOptions
|
|
|
|
func init() {
|
|
cmdRoot.AddCommand(cmdCache)
|
|
|
|
f := cmdCache.Flags()
|
|
f.BoolVar(&cacheOptions.Cleanup, "cleanup", false, "remove old cache directories")
|
|
f.UintVar(&cacheOptions.MaxAge, "max-age", 30, "max age in `days` for cache directories to be considered old")
|
|
f.BoolVar(&cacheOptions.NoSize, "no-size", false, "do not output the size of the cache directories")
|
|
}
|
|
|
|
func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
|
|
if len(args) > 0 {
|
|
return errors.Fatal("the cache command expects no arguments, only options - please see `restic help cache` for usage and flags")
|
|
}
|
|
|
|
if gopts.NoCache {
|
|
return errors.Fatal("Refusing to do anything, the cache is disabled")
|
|
}
|
|
|
|
var (
|
|
cachedir = gopts.CacheDir
|
|
err error
|
|
)
|
|
|
|
if cachedir == "" {
|
|
cachedir, err = cache.DefaultDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if opts.Cleanup || gopts.CleanupCache {
|
|
oldDirs, err := cache.OlderThan(cachedir, time.Duration(opts.MaxAge)*24*time.Hour)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(oldDirs) == 0 {
|
|
Verbosef("no old cache dirs found\n")
|
|
return nil
|
|
}
|
|
|
|
Verbosef("remove %d old cache directories\n", len(oldDirs))
|
|
|
|
for _, item := range oldDirs {
|
|
dir := filepath.Join(cachedir, item.Name())
|
|
err = os.RemoveAll(dir)
|
|
if err != nil {
|
|
Warnf("unable to remove %v: %v\n", dir, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
tab := table.New()
|
|
|
|
type data struct {
|
|
ID string
|
|
Last string
|
|
Old string
|
|
Size string
|
|
}
|
|
|
|
tab.AddColumn("Repo ID", "{{ .ID }}")
|
|
tab.AddColumn("Last Used", "{{ .Last }}")
|
|
tab.AddColumn("Old", "{{ .Old }}")
|
|
|
|
if !opts.NoSize {
|
|
tab.AddColumn("Size", "{{ .Size }}")
|
|
}
|
|
|
|
dirs, err := cache.All(cachedir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(dirs) == 0 {
|
|
Printf("no cache dirs found, basedir is %v\n", cachedir)
|
|
return nil
|
|
}
|
|
|
|
sort.Slice(dirs, func(i, j int) bool {
|
|
return dirs[i].ModTime().Before(dirs[j].ModTime())
|
|
})
|
|
|
|
for _, entry := range dirs {
|
|
var old string
|
|
if cache.IsOld(entry.ModTime(), time.Duration(opts.MaxAge)*24*time.Hour) {
|
|
old = "yes"
|
|
}
|
|
|
|
var size string
|
|
if !opts.NoSize {
|
|
bytes, err := dirSize(filepath.Join(cachedir, entry.Name()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
size = fmt.Sprintf("%11s", ui.FormatBytes(uint64(bytes)))
|
|
}
|
|
|
|
name := entry.Name()
|
|
if !strings.HasPrefix(name, "restic-check-cache-") {
|
|
name = name[:10]
|
|
}
|
|
|
|
tab.AddRow(data{
|
|
name,
|
|
fmt.Sprintf("%d days ago", uint(time.Since(entry.ModTime()).Hours()/24)),
|
|
old,
|
|
size,
|
|
})
|
|
}
|
|
|
|
_ = tab.Write(globalOptions.stdout)
|
|
Printf("%d cache dirs in %s\n", len(dirs), cachedir)
|
|
|
|
return nil
|
|
}
|
|
|
|
func dirSize(path string) (int64, error) {
|
|
var size int64
|
|
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
|
|
if err != nil || info == nil {
|
|
return err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
size += info.Size()
|
|
}
|
|
|
|
return nil
|
|
})
|
|
return size, err
|
|
}
|