mirror of
https://github.com/kopia/kopia.git
synced 2026-01-27 15:58:03 -05:00
Added policy.Definition which allows us to precisely report where each piece of policy came from. Fixed a one-off bug with "noParent", which prevented merging of parent policies one level too soon. Added a whole bunch of merging helpers and generic reflection-based test that ensures every single merge is tested.
319 lines
10 KiB
Go
319 lines
10 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/internal/units"
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/snapshot"
|
|
"github.com/kopia/kopia/snapshot/policy"
|
|
)
|
|
|
|
type commandPolicyShow struct {
|
|
global bool
|
|
targets []string
|
|
jo jsonOutput
|
|
out textOutput
|
|
}
|
|
|
|
func (c *commandPolicyShow) setup(svc appServices, parent commandParent) {
|
|
cmd := parent.Command("show", "Show snapshot policy.").Alias("get")
|
|
cmd.Flag("global", "Get global policy").BoolVar(&c.global)
|
|
cmd.Arg("target", "Target to show the policy for").StringsVar(&c.targets)
|
|
c.jo.setup(svc, cmd)
|
|
c.out.setup(svc)
|
|
cmd.Action(svc.repositoryReaderAction(c.run))
|
|
}
|
|
|
|
func (c *commandPolicyShow) run(ctx context.Context, rep repo.Repository) error {
|
|
targets, err := policyTargets(ctx, rep, c.global, c.targets)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, target := range targets {
|
|
effective, definition, _, err := policy.GetEffectivePolicy(ctx, rep, target)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "can't get effective policy for %q", target)
|
|
}
|
|
|
|
if c.jo.jsonOutput {
|
|
c.out.printStdout("%s\n", c.jo.jsonBytes(effective))
|
|
} else {
|
|
printPolicy(&c.out, effective, definition)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func definitionPointToString(target, src snapshot.SourceInfo) string {
|
|
if src == target {
|
|
return "(defined for this target)"
|
|
}
|
|
|
|
return "inherited from " + src.String()
|
|
}
|
|
|
|
func printPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) {
|
|
out.printStdout("Policy for %v:\n\n", p.Target())
|
|
|
|
printRetentionPolicy(out, p, def)
|
|
out.printStdout("\n")
|
|
printFilesPolicy(out, p, def)
|
|
out.printStdout("\n")
|
|
printErrorHandlingPolicy(out, p, def)
|
|
out.printStdout("\n")
|
|
printSchedulingPolicy(out, p, def)
|
|
out.printStdout("\n")
|
|
printCompressionPolicy(out, p, def)
|
|
out.printStdout("\n")
|
|
printActions(out, p, def)
|
|
out.printStdout("\n")
|
|
printLoggingPolicy(out, p, def)
|
|
}
|
|
|
|
func printRetentionPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) {
|
|
out.printStdout("Retention:\n")
|
|
out.printStdout(" Annual snapshots: %3v %v\n",
|
|
valueOrNotSet(p.RetentionPolicy.KeepAnnual),
|
|
definitionPointToString(p.Target(), def.RetentionPolicy.KeepAnnual))
|
|
out.printStdout(" Monthly snapshots: %3v %v\n",
|
|
valueOrNotSet(p.RetentionPolicy.KeepMonthly),
|
|
definitionPointToString(p.Target(), def.RetentionPolicy.KeepMonthly))
|
|
out.printStdout(" Weekly snapshots: %3v %v\n",
|
|
valueOrNotSet(p.RetentionPolicy.KeepWeekly),
|
|
definitionPointToString(p.Target(), def.RetentionPolicy.KeepWeekly))
|
|
out.printStdout(" Daily snapshots: %3v %v\n",
|
|
valueOrNotSet(p.RetentionPolicy.KeepDaily),
|
|
definitionPointToString(p.Target(), def.RetentionPolicy.KeepDaily))
|
|
out.printStdout(" Hourly snapshots: %3v %v\n",
|
|
valueOrNotSet(p.RetentionPolicy.KeepHourly),
|
|
definitionPointToString(p.Target(), def.RetentionPolicy.KeepHourly))
|
|
out.printStdout(" Latest snapshots: %3v %v\n",
|
|
valueOrNotSet(p.RetentionPolicy.KeepLatest),
|
|
definitionPointToString(p.Target(), def.RetentionPolicy.KeepLatest))
|
|
}
|
|
|
|
func printFilesPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) {
|
|
out.printStdout("Files policy:\n")
|
|
|
|
out.printStdout(" Ignore cache directories: %5v %v\n",
|
|
p.FilesPolicy.IgnoreCacheDirectories.OrDefault(true),
|
|
definitionPointToString(p.Target(), def.FilesPolicy.IgnoreCacheDirectories))
|
|
|
|
if len(p.FilesPolicy.IgnoreRules) > 0 {
|
|
out.printStdout(" Ignore rules: %v\n",
|
|
definitionPointToString(p.Target(), def.FilesPolicy.IgnoreRules))
|
|
} else {
|
|
out.printStdout(" No ignore rules.\n")
|
|
}
|
|
|
|
for _, rule := range p.FilesPolicy.IgnoreRules {
|
|
out.printStdout(" %-30v\n", rule)
|
|
}
|
|
|
|
if len(p.FilesPolicy.DotIgnoreFiles) > 0 {
|
|
out.printStdout(" Read ignore rules from files: %v\n",
|
|
definitionPointToString(p.Target(), def.FilesPolicy.DotIgnoreFiles))
|
|
}
|
|
|
|
for _, dotFile := range p.FilesPolicy.DotIgnoreFiles {
|
|
out.printStdout(" %-30v\n", dotFile)
|
|
}
|
|
|
|
if maxSize := p.FilesPolicy.MaxFileSize; maxSize > 0 {
|
|
out.printStdout(" Ignore files above: %10v %v\n",
|
|
units.BytesStringBase2(maxSize),
|
|
definitionPointToString(p.Target(), def.FilesPolicy.MaxFileSize))
|
|
}
|
|
|
|
out.printStdout(" Scan one filesystem only: %5v %v\n",
|
|
p.FilesPolicy.OneFileSystem.OrDefault(false),
|
|
definitionPointToString(p.Target(), def.FilesPolicy.OneFileSystem))
|
|
}
|
|
|
|
func printErrorHandlingPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) {
|
|
out.printStdout("Error handling policy:\n")
|
|
|
|
out.printStdout(" Ignore file read errors: %5v %v\n",
|
|
p.ErrorHandlingPolicy.IgnoreFileErrors.OrDefault(false),
|
|
definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreFileErrors))
|
|
|
|
out.printStdout(" Ignore directory read errors: %5v %v\n",
|
|
p.ErrorHandlingPolicy.IgnoreDirectoryErrors.OrDefault(false),
|
|
definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreDirectoryErrors))
|
|
|
|
out.printStdout(" Ignore unknown types: %5v %v\n",
|
|
p.ErrorHandlingPolicy.IgnoreUnknownTypes.OrDefault(true),
|
|
definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreUnknownTypes))
|
|
}
|
|
|
|
func printLoggingPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) {
|
|
out.printStdout("Logging details (%v-none, %v-maximum):\n", policy.LogDetailNone, policy.LogDetailMax)
|
|
|
|
out.printStdout(" Directory snapshotted: %5v %v\n",
|
|
p.LoggingPolicy.Directories.Snapshotted.OrDefault(policy.LogDetailNone),
|
|
definitionPointToString(p.Target(), def.LoggingPolicy.Directories.Snapshotted))
|
|
|
|
out.printStdout(" Directory ignored: %5v %v\n",
|
|
p.LoggingPolicy.Directories.Ignored.OrDefault(policy.LogDetailNone),
|
|
definitionPointToString(p.Target(), def.LoggingPolicy.Directories.Ignored))
|
|
|
|
out.printStdout(" Entry snapshotted: %5v %v\n",
|
|
p.LoggingPolicy.Entries.Snapshotted.OrDefault(policy.LogDetailNone),
|
|
definitionPointToString(p.Target(), def.LoggingPolicy.Entries.Snapshotted))
|
|
|
|
out.printStdout(" Entry ignored: %5v %v\n",
|
|
p.LoggingPolicy.Entries.Ignored.OrDefault(policy.LogDetailNone),
|
|
definitionPointToString(p.Target(), def.LoggingPolicy.Entries.Ignored))
|
|
|
|
out.printStdout(" Entry cache hit %5v %v\n",
|
|
p.LoggingPolicy.Entries.CacheHit.OrDefault(policy.LogDetailNone),
|
|
definitionPointToString(p.Target(), def.LoggingPolicy.Entries.CacheHit))
|
|
|
|
out.printStdout(" Entry cache miss %5v %v\n",
|
|
p.LoggingPolicy.Entries.CacheMiss.OrDefault(policy.LogDetailNone),
|
|
definitionPointToString(p.Target(), def.LoggingPolicy.Entries.CacheMiss))
|
|
}
|
|
|
|
func printSchedulingPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) {
|
|
out.printStdout("Scheduling policy:\n")
|
|
|
|
any := false
|
|
|
|
out.printStdout(" Scheduled snapshots:\n")
|
|
|
|
if p.SchedulingPolicy.Interval() != 0 {
|
|
out.printStdout(" Snapshot interval: %10v %v\n", p.SchedulingPolicy.Interval(),
|
|
definitionPointToString(p.Target(), def.SchedulingPolicy.IntervalSeconds))
|
|
|
|
any = true
|
|
}
|
|
|
|
if len(p.SchedulingPolicy.TimesOfDay) > 0 {
|
|
out.printStdout(" Snapshot times: %v\n", definitionPointToString(p.Target(), def.SchedulingPolicy.TimesOfDay))
|
|
|
|
for _, tod := range p.SchedulingPolicy.TimesOfDay {
|
|
out.printStdout(" %9v\n", tod)
|
|
}
|
|
|
|
any = true
|
|
}
|
|
|
|
if !any {
|
|
out.printStdout(" None\n")
|
|
}
|
|
|
|
out.printStdout(" Manual snapshot: %5v %v\n",
|
|
p.SchedulingPolicy.Manual,
|
|
definitionPointToString(p.Target(), def.SchedulingPolicy.Manual))
|
|
}
|
|
|
|
func printCompressionPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) {
|
|
if p.CompressionPolicy.CompressorName != "" && p.CompressionPolicy.CompressorName != "none" {
|
|
out.printStdout("Compression:\n")
|
|
out.printStdout(" Compressor: %q %v\n", p.CompressionPolicy.CompressorName,
|
|
definitionPointToString(p.Target(), def.CompressionPolicy.CompressorName))
|
|
} else {
|
|
out.printStdout("Compression disabled.\n")
|
|
return
|
|
}
|
|
|
|
switch {
|
|
case len(p.CompressionPolicy.OnlyCompress) > 0:
|
|
out.printStdout(" Only compress files with the following extensions: %v\n",
|
|
definitionPointToString(p.Target(), def.CompressionPolicy.OnlyCompress))
|
|
|
|
for _, rule := range p.CompressionPolicy.OnlyCompress {
|
|
out.printStdout(" %-30v\n", rule)
|
|
}
|
|
|
|
case len(p.CompressionPolicy.NeverCompress) > 0:
|
|
out.printStdout(" Compress all files except the following extensions: %v\n",
|
|
definitionPointToString(p.Target(), def.CompressionPolicy.NeverCompress))
|
|
|
|
for _, rule := range p.CompressionPolicy.NeverCompress {
|
|
out.printStdout(" %-30v\n", rule)
|
|
}
|
|
|
|
default:
|
|
out.printStdout(" Compress files regardless of extensions.\n")
|
|
}
|
|
|
|
switch {
|
|
case p.CompressionPolicy.MaxSize > 0:
|
|
out.printStdout(" Only compress files between %v and %v.\n", units.BytesStringBase10(p.CompressionPolicy.MinSize), units.BytesStringBase10(p.CompressionPolicy.MaxSize))
|
|
|
|
case p.CompressionPolicy.MinSize > 0:
|
|
out.printStdout(" Only compress files bigger than %v.\n", units.BytesStringBase10(p.CompressionPolicy.MinSize))
|
|
|
|
default:
|
|
out.printStdout(" Compress files of all sizes.\n")
|
|
}
|
|
}
|
|
|
|
func printActions(out *textOutput, p *policy.Policy, def *policy.Definition) {
|
|
var anyActions bool
|
|
|
|
if h := p.Actions.BeforeSnapshotRoot; h != nil {
|
|
out.printStdout("Run command before snapshot root: %v\n",
|
|
definitionPointToString(p.Target(), def.Actions.BeforeSnapshotRoot))
|
|
|
|
printActionCommand(out, h)
|
|
|
|
anyActions = true
|
|
}
|
|
|
|
if h := p.Actions.AfterSnapshotRoot; h != nil {
|
|
out.printStdout("Run command after snapshot root: %v\n",
|
|
definitionPointToString(p.Target(), def.Actions.AfterSnapshotRoot))
|
|
printActionCommand(out, h)
|
|
|
|
anyActions = true
|
|
}
|
|
|
|
if h := p.Actions.BeforeFolder; h != nil {
|
|
out.printStdout("Run command before this folder: (non-inheritable)\n")
|
|
|
|
printActionCommand(out, h)
|
|
|
|
anyActions = true
|
|
}
|
|
|
|
if h := p.Actions.AfterFolder; h != nil {
|
|
out.printStdout("Run command after this folder: (non-inheritable)\n")
|
|
printActionCommand(out, h)
|
|
|
|
anyActions = true
|
|
}
|
|
|
|
if !anyActions {
|
|
out.printStdout("No actions defined.\n")
|
|
}
|
|
}
|
|
|
|
func printActionCommand(out *textOutput, h *policy.ActionCommand) {
|
|
if h.Script != "" {
|
|
out.printStdout(" Embedded Script: %q\n", h.Script)
|
|
} else {
|
|
out.printStdout(" Command: %v %v\n", h.Command, strings.Join(h.Arguments, " "))
|
|
}
|
|
|
|
out.printStdout(" Mode: %v\n", h.Mode)
|
|
out.printStdout(" Timeout: %v\n", h.TimeoutSeconds)
|
|
out.printStdout("\n")
|
|
}
|
|
|
|
func valueOrNotSet(p *int) string {
|
|
if p == nil {
|
|
return "-"
|
|
}
|
|
|
|
return fmt.Sprintf("%v", *p)
|
|
}
|