mirror of
https://github.com/kopia/kopia.git
synced 2025-12-23 22:57:50 -05:00
142 lines
3.5 KiB
Go
142 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"]
|
|
# "ignoreDotFiles": [".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 */
|
|
# "timeOfDay": [{"hour":H,"min":M},{"hour":H,"min":M}]
|
|
# "manual": false /* Only create snapshots manually if set to true. NOTE: cannot be used with the above two fields */
|
|
`
|
|
|
|
type commandPolicyEdit struct {
|
|
policyTargetFlags
|
|
|
|
out textOutput
|
|
}
|
|
|
|
func (c *commandPolicyEdit) setup(svc appServices, parent commandParent) {
|
|
cmd := parent.Command("edit", "Edit policy.")
|
|
c.policyTargetFlags.setup(cmd)
|
|
cmd.Action(svc.repositoryWriterAction(c.run))
|
|
c.out.setup(svc)
|
|
}
|
|
|
|
func (c *commandPolicyEdit) run(ctx context.Context, rep repo.RepositoryWriter) error {
|
|
targets, err := c.policyTargets(ctx, rep)
|
|
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 := fmt.Sprintf(policyEditHelpText, target) + 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, true, 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))
|
|
|
|
c.out.printStdout("Save updated policy? (y/N) ")
|
|
|
|
var shouldSave string
|
|
|
|
fmt.Scanf("%v", &shouldSave) //nolint:errcheck
|
|
|
|
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 *policy.Policy) string {
|
|
var b bytes.Buffer
|
|
|
|
e := json.NewEncoder(&b)
|
|
e.SetIndent("", " ")
|
|
e.Encode(v) //nolint:errcheck,errchkjson
|
|
|
|
return b.String()
|
|
}
|
|
|
|
func jsonEqual(v1, v2 *policy.Policy) 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:]
|
|
}
|