changed how paths are interpreted for 'kopia backups' and 'kopia expire'

This commit is contained in:
Jarek Kowalski
2016-11-23 23:49:29 -08:00
parent a0af1412f1
commit 65bc50de5d
4 changed files with 69 additions and 47 deletions

View File

@@ -79,8 +79,8 @@ func runBackupCommand(c *kingpin.ParseContext) error {
sourceInfo := repofs.SnapshotSourceInfo{
Path: filepath.Clean(dir),
Host: getBackupHostName(),
UserName: getBackupUser(),
Host: getHostNameOrDefault(*backupHostName),
UserName: getUserOrDefault(*backupUser),
}
if len(*backupDescription) > backupMaxDescriptionLength {
@@ -142,8 +142,8 @@ func runBackupCommand(c *kingpin.ParseContext) error {
}
func getLocalBackupPaths(vlt *vault.Vault) ([]string, error) {
u := getBackupUser()
h := getBackupHostName()
u := getHostNameOrDefault(*backupHostName)
h := getUserOrDefault(*backupUser)
log.Printf("Looking for previous backups of '%v@%v'...", u, h)
backupItems, err := vlt.List("B")
if err != nil {
@@ -179,19 +179,15 @@ func hashObjectID(oid string) string {
return hex.EncodeToString(sum[0:foldLen])
}
func getBackupUser() string {
return getUserOrDefault(*backupUser)
}
func getBackupHostName() string {
return getHostNameOrDefault(*backupHostName)
}
func getUserOrDefault(userName string) string {
if userName != "" {
return userName
}
return getUserName()
}
func getUserName() string {
currentUser, err := user.Current()
if err != nil {
log.Fatalf("Cannot determine current user: %s", err)
@@ -213,6 +209,10 @@ func getHostNameOrDefault(hostName string) string {
return hostName
}
return getHostName()
}
func getHostName() string {
hostname, err := os.Hostname()
if err != nil {
log.Fatalf("Unable to determine hostname: %s", err)
@@ -223,6 +223,7 @@ func getHostNameOrDefault(hostName string) string {
return hostname
}
func init() {
backupCommand.Action(runBackupCommand)
}

View File

@@ -19,16 +19,10 @@
maxResultsPerPath = backupsCommand.Flag("maxresults", "Maximum number of results.").Default("100").Int()
)
func findBackups(vlt *vault.Vault, path string) ([]string, string, error) {
func findBackups(vlt *vault.Vault, sourceInfo repofs.SnapshotSourceInfo) ([]string, string, error) {
var relPath string
for len(path) > 0 {
sourceInfo := repofs.SnapshotSourceInfo{
Path: path,
Host: getBackupHostName(),
UserName: getBackupUser(),
}
for len(sourceInfo.Path) > 0 {
prefix := sourceInfo.HashString() + "."
list, err := vlt.List("B" + prefix)
@@ -41,18 +35,18 @@ func findBackups(vlt *vault.Vault, path string) ([]string, string, error) {
}
if len(relPath) > 0 {
relPath = filepath.Base(path) + "/" + relPath
relPath = filepath.Base(sourceInfo.Path) + "/" + relPath
} else {
relPath = filepath.Base(path)
relPath = filepath.Base(sourceInfo.Path)
}
log.Printf("No backups of %v@%v:%v", sourceInfo.UserName, sourceInfo.Host, sourceInfo.Path)
parent := filepath.Dir(path)
if parent == path {
parentPath := filepath.Dir(sourceInfo.Path)
if parentPath == sourceInfo.Path {
break
}
path = parent
sourceInfo.Path = parentPath
}
return nil, "", nil
@@ -67,12 +61,15 @@ func runBackupsCommand(context *kingpin.ParseContext) error {
var err error
if *backupsPath != "" {
path, err := filepath.Abs(*backupsPath)
si, err := repofs.ParseSourceSnashotInfo(
*backupsPath,
getHostName(),
getUserName())
if err != nil {
return fmt.Errorf("invalid directory: '%s': %s", *backupsPath, err)
}
previous, relPath, err = findBackups(conn.Vault, filepath.Clean(path))
previous, relPath, err = findBackups(conn.Vault, si)
if relPath != "" {
relPath = "/" + relPath
}
@@ -165,6 +162,4 @@ func loadBackupManifests(vlt *vault.Vault, names []string) []*repofs.Snapshot {
func init() {
backupsCommand.Action(runBackupsCommand)
backupsCommand.Flag("host", "Override backup hostname.").StringVar(backupHostName)
backupsCommand.Flag("user", "Override backup user.").StringVar(backupUser)
}

View File

@@ -4,7 +4,6 @@
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
@@ -18,9 +17,9 @@
expireCommand = app.Command("expire", "Remove old backups.")
expirationPolicies = map[string]func(){
"KEEP_ALL": expirationPolicyKeepAll,
"MANUAL": expirationPolicyManual,
"DEFAULT": expirationPolicyDefault,
"keep-all": expirationPolicyKeepAll,
"manual": expirationPolicyManual,
"default": expirationPolicyDefault,
}
expireKeepLatest = expireCommand.Flag("keep-latest", "Number of most recent backups to keep per source").Int()
@@ -29,9 +28,10 @@
expireKeepWeekly = expireCommand.Flag("keep-weekly", "Number of most-recent weekly backups to keep per source").Int()
expireKeepMonthly = expireCommand.Flag("keep-monthly", "Number of most-recent monthly backups to keep per source").Int()
expireKeepAnnual = expireCommand.Flag("keep-annual", "Number of most-recent annual backups to keep per source").Int()
expirePolicy = expireCommand.Flag("policy", "Expiration policy to use: "+strings.Join(expirationPolicyNames(), ",")).Default("DEFAULT").Enum(expirationPolicyNames()...)
expirePolicy = expireCommand.Flag("policy", "Expiration policy to use: "+strings.Join(expirationPolicyNames(), ",")).Required().Enum(expirationPolicyNames()...)
expireHost = expireCommand.Flag("host", "Expire backups from a given host").Default("").String()
expireUser = expireCommand.Flag("user", "Expire backups from a given user").Default("").String()
expireAll = expireCommand.Flag("all", "Expire all backups").Bool()
expirePaths = expireCommand.Arg("path", "Expire backups for a given paths only").Strings()
expireDelete = expireCommand.Flag("delete", "Whether to actually delete backups").Default("no").String()
@@ -149,28 +149,23 @@ func expire(snapshots []*repofs.Snapshot, snapshotNames []string) []string {
}
func getSnapshotNamesToExpire(v *vault.Vault) ([]string, error) {
if len(*expirePaths) == 0 {
if !*expireAll && len(*expirePaths) == 0 {
return nil, fmt.Errorf("Must specify paths to expire or --all")
}
if *expireAll {
fmt.Fprintf(os.Stderr, "Scanning all active snapshots...\n")
return v.List("B")
}
var result []string
hostName := getHostNameOrDefault(*expireHost)
user := getUserOrDefault(*expireUser)
for _, p := range *expirePaths {
path, err := filepath.Abs(p)
si, err := repofs.ParseSourceSnashotInfo(p, *expireHost, *expireUser)
if err != nil {
return nil, fmt.Errorf("invalid directory: '%s': %s", p, err)
return nil, fmt.Errorf("unable to parse %v: %v", p, err)
}
var si repofs.SnapshotSourceInfo
si.Host = hostName
si.UserName = user
si.Path = filepath.Clean(path)
log.Printf("Looking for backups of %v", si)
matches, err := v.List("B" + si.HashString())
@@ -178,6 +173,8 @@ func getSnapshotNamesToExpire(v *vault.Vault) ([]string, error) {
return nil, fmt.Errorf("error listing backups for %v: %v", si, err)
}
log.Printf("Found %v backups of %v", len(matches), si)
result = append(result, matches...)
}

View File

@@ -5,6 +5,8 @@
"encoding/hex"
"fmt"
"io"
"path/filepath"
"strings"
"time"
"github.com/kopia/kopia/repo"
@@ -20,7 +22,34 @@ type SnapshotSourceInfo struct {
}
func (ssi SnapshotSourceInfo) String() string {
return fmt.Sprintf("%v@%v : %v", ssi.UserName, ssi.Host, ssi.Path)
return fmt.Sprintf("%v@%v:%v", ssi.UserName, ssi.Host, ssi.Path)
}
// ParseSourceSnashotInfo parses a given path in the context of given hostname and username and returns
// SnapshotSourceInfo. The path may be bare (in which case it's interpreted as local path and canonicalized)
// or may be 'username@host:path' where path, username and host are not processed.
func ParseSourceSnashotInfo(path string, hostname string, username string) (SnapshotSourceInfo, error) {
p1 := strings.Index(path, "@")
p2 := strings.Index(path, ":")
if p1 > 0 && p2 > 0 && p1 < p2 && p2 < len(path) {
return SnapshotSourceInfo{
UserName: path[0:p1],
Host: path[p1+1 : p2],
Path: path[p2+1:],
}, nil
}
absPath, err := filepath.Abs(path)
if err != nil {
return SnapshotSourceInfo{}, fmt.Errorf("invalid directory: '%s': %s", path, err)
}
return SnapshotSourceInfo{
Host: hostname,
UserName: username,
Path: filepath.Clean(absPath),
}, nil
}
// HashString generates hash of SnapshotSourceInfo.