mirror of
https://github.com/kopia/kopia.git
synced 2026-03-12 03:06:31 -04:00
* linter: upgraded to 1.33, disabled some linters * lint: fixed 'errorlint' errors This ensures that all error comparisons use errors.Is() or errors.As(). We will be wrapping more errors going forward so it's important that error checks are not strict everywhere. Verified that there are no exceptions for errorlint linter which guarantees that. * lint: fixed or suppressed wrapcheck errors * lint: nolintlint and misc cleanups Co-authored-by: Julio López <julio+gh@kasten.io>
136 lines
3.5 KiB
Go
136 lines
3.5 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/internal/editor"
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/snapshot/policy"
|
|
)
|
|
|
|
const policyEditHelpText = `
|
|
# Editing policy for '%v'
|
|
|
|
# Make changes to the policy, save your file and exit the editor.
|
|
# The output must be valid JSON.
|
|
|
|
# Lines starting with # are comments and automatically removed.
|
|
|
|
`
|
|
|
|
const policyEditRetentionHelpText = ` # Retention for snapshots of this directory. Options include:
|
|
# "keepLatest": number
|
|
# "keepDaily": number
|
|
# "keepHourly": number
|
|
# "keepWeekly": number
|
|
# "keepMonthly": number
|
|
# "keepAnnual": number
|
|
`
|
|
|
|
const policyEditFilesHelpText = `
|
|
# Which files to include in snapshots. Options include:
|
|
# "ignore": ["*.ext", "*.ext2"]
|
|
# "dotIgnoreFiles": [".gitignore", ".kopiaignore"]
|
|
# "maxFileSize": number
|
|
# "noParentDotFiles": true
|
|
# "noParentIgnore": true
|
|
# "oneFileSystem": false
|
|
`
|
|
|
|
const policyEditSchedulingHelpText = `
|
|
# Snapshot scheduling options. Options include:
|
|
# "intervalSeconds": number /* 86400-day, 3600-hour, 60-minute */
|
|
# "timesOfDay": [{"hour":H,"min":M},{"hour":H,"min":M}]
|
|
`
|
|
|
|
var (
|
|
policyEditCommand = policyCommands.Command("edit", "Set snapshot policy for a single directory, user@host or a global policy.")
|
|
policyEditTargets = policyEditCommand.Arg("target", "Target of a policy ('global','user@host','@host') or a path").Strings()
|
|
policyEditGlobal = policyEditCommand.Flag("global", "Set global policy").Bool()
|
|
)
|
|
|
|
func init() {
|
|
policyEditCommand.Action(repositoryAction(editPolicy))
|
|
}
|
|
|
|
func editPolicy(ctx context.Context, rep repo.Repository) error {
|
|
targets, err := policyTargets(ctx, rep, policyEditGlobal, policyEditTargets)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, target := range targets {
|
|
original, err := policy.GetDefinedPolicy(ctx, rep, target)
|
|
if errors.Is(err, policy.ErrPolicyNotFound) {
|
|
original = &policy.Policy{}
|
|
}
|
|
|
|
log(ctx).Infof("Editing policy for %v using external editor...", target)
|
|
|
|
s := policyEditHelpText + prettyJSON(original)
|
|
s = insertHelpText(s, ` "retention": {`, policyEditRetentionHelpText)
|
|
s = insertHelpText(s, ` "files": {`, policyEditFilesHelpText)
|
|
s = insertHelpText(s, ` "scheduling": {`, policyEditSchedulingHelpText)
|
|
|
|
var updated *policy.Policy
|
|
|
|
if err := editor.EditLoop(ctx, "policy.conf", s, func(edited string) error {
|
|
updated = &policy.Policy{}
|
|
d := json.NewDecoder(bytes.NewBufferString(edited))
|
|
d.DisallowUnknownFields()
|
|
return d.Decode(updated)
|
|
}); err != nil {
|
|
return errors.Wrap(err, "unable to launch editor")
|
|
}
|
|
|
|
if jsonEqual(updated, original) {
|
|
log(ctx).Infof("Policy for %v unchanged", target)
|
|
continue
|
|
}
|
|
|
|
log(ctx).Infof("Updated policy for %v\n%v", target, prettyJSON(updated))
|
|
|
|
fmt.Print("Save updated policy? (y/N) ")
|
|
|
|
var shouldSave string
|
|
|
|
fmt.Scanf("%v", &shouldSave)
|
|
|
|
if strings.HasPrefix(strings.ToLower(shouldSave), "y") {
|
|
if err := policy.SetPolicy(ctx, rep, target, updated); err != nil {
|
|
return errors.Wrapf(err, "can't save policy for %v", target)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func prettyJSON(v interface{}) string {
|
|
var b bytes.Buffer
|
|
e := json.NewEncoder(&b)
|
|
e.SetIndent("", " ")
|
|
e.Encode(v) //nolint:errcheck
|
|
|
|
return b.String()
|
|
}
|
|
|
|
func jsonEqual(v1, v2 interface{}) bool {
|
|
return prettyJSON(v1) == prettyJSON(v2)
|
|
}
|
|
|
|
func insertHelpText(s, lookFor, help string) string {
|
|
p := strings.Index(s, lookFor)
|
|
if p < 0 {
|
|
return s
|
|
}
|
|
|
|
return s[0:p] + help + s[p:]
|
|
}
|