package cli import ( "context" "sort" "strconv" "github.com/pkg/errors" "github.com/kopia/kopia/internal/units" "github.com/kopia/kopia/repo" "github.com/kopia/kopia/repo/compression" "github.com/kopia/kopia/snapshot/policy" ) type commandPolicySet struct { policyTargetFlags inherit []bool // not really a list, just an optional boolean policyActionFlags policyCompressionFlags policyMetadataCompressionFlags policySplitterFlags policyErrorFlags policyFilesFlags policyLoggingFlags policyRetentionFlags policySchedulingFlags policyOSSnapshotFlags policyUploadFlags } func (c *commandPolicySet) setup(svc appServices, parent commandParent) { cmd := parent.Command("set", "Set policy.") c.policyTargetFlags.setup(cmd) cmd.Flag(inheritPolicyString, "Enable or disable inheriting policies from the parent").BoolListVar(&c.inherit) c.policyActionFlags.setup(cmd) c.policyCompressionFlags.setup(cmd) c.policyMetadataCompressionFlags.setup(cmd) c.policySplitterFlags.setup(cmd) c.policyErrorFlags.setup(cmd) c.policyFilesFlags.setup(cmd) c.policyLoggingFlags.setup(cmd) c.policyRetentionFlags.setup(cmd) c.policySchedulingFlags.setup(cmd) c.policyOSSnapshotFlags.setup(cmd) c.policyUploadFlags.setup(cmd) cmd.Action(svc.repositoryWriterAction(c.run)) } //nolint:gochecknoglobals var booleanEnumValues = []string{"true", "false", "inherit"} const ( inheritPolicyString = "inherit" defaultPolicyString = "default" ) func (c *commandPolicySet) run(ctx context.Context, rep repo.RepositoryWriter) error { targets, err := c.policyTargets(ctx, rep) if err != nil { return err } for _, target := range targets { p, err := policy.GetDefinedPolicy(ctx, rep, target) switch { case errors.Is(err, policy.ErrPolicyNotFound): p = &policy.Policy{} case err != nil: return errors.Wrap(err, "could not get defined policy") } log(ctx).Infof("Setting policy for %v", target) changeCount := 0 if err := c.setPolicyFromFlags(ctx, p, &changeCount); err != nil { return err } if changeCount == 0 { return errors.New("no changes specified") } if err := policy.SetPolicy(ctx, rep, target, p); err != nil { return errors.Wrapf(err, "can't save policy for %v", target) } } return nil } func (c *commandPolicySet) setPolicyFromFlags(ctx context.Context, p *policy.Policy, changeCount *int) error { if err := c.setRetentionPolicyFromFlags(ctx, &p.RetentionPolicy, changeCount); err != nil { return errors.Wrap(err, "retention policy") } if err := c.setFilesPolicyFromFlags(ctx, &p.FilesPolicy, changeCount); err != nil { return errors.Wrap(err, "files policy") } if err := c.setErrorHandlingPolicyFromFlags(ctx, &p.ErrorHandlingPolicy, changeCount); err != nil { return errors.Wrap(err, "error handling policy") } if err := c.setCompressionPolicyFromFlags(ctx, &p.CompressionPolicy, changeCount); err != nil { return errors.Wrap(err, "compression policy") } if err := c.setMetadataCompressionPolicyFromFlags(ctx, &p.MetadataCompressionPolicy, changeCount); err != nil { return errors.Wrap(err, "metadata compression policy") } if err := c.setSplitterPolicyFromFlags(ctx, &p.SplitterPolicy, changeCount); err != nil { return errors.Wrap(err, "splitter policy") } if err := c.setSchedulingPolicyFromFlags(ctx, &p.SchedulingPolicy, changeCount); err != nil { return errors.Wrap(err, "scheduling policy") } if err := c.setActionsFromFlags(ctx, &p.Actions, changeCount); err != nil { return errors.Wrap(err, "actions policy") } if err := c.setOSSnapshotPolicyFromFlags(ctx, &p.OSSnapshotPolicy, changeCount); err != nil { return errors.Wrap(err, "OS snapshot policy") } if err := c.setLoggingPolicyFromFlags(ctx, &p.LoggingPolicy, changeCount); err != nil { return errors.Wrap(err, "actions policy") } if err := c.setUploadPolicyFromFlags(ctx, &p.UploadPolicy, changeCount); err != nil { return errors.Wrap(err, "upload policy") } // It's not really a list, just optional boolean, last one wins. for _, inherit := range c.inherit { *changeCount++ p.NoParent = !inherit } return nil } func applyPolicyStringList(ctx context.Context, desc string, val *[]string, add, remove []string, clearList bool, changeCount *int) { if clearList { log(ctx).Infof(" - removing all from %q", desc) *changeCount++ *val = nil return } entries := map[string]bool{} for _, b := range *val { entries[b] = true } for _, b := range add { *changeCount++ log(ctx).Infof(" - adding %q to %q", b, desc) entries[b] = true } for _, b := range remove { *changeCount++ log(ctx).Infof(" - removing %q from %q", b, desc) delete(entries, b) } var s []string for k := range entries { s = append(s, k) } sort.Strings(s) *val = s } func applyOptionalInt(ctx context.Context, desc string, val **policy.OptionalInt, str string, changeCount *int) error { if str == "" { // not changed return nil } if str == inheritPolicyString || str == defaultPolicyString { *changeCount++ log(ctx).Infof(" - resetting %q to a default value inherited from parent.", desc) *val = nil return nil } v, err := strconv.ParseInt(str, 10, 32) if err != nil { return errors.Wrapf(err, "can't parse the %v %q", desc, str) } i := policy.OptionalInt(v) *changeCount++ log(ctx).Infof(" - setting %q to %v.", desc, i) *val = &i return nil } func applyOptionalInt64MiB(ctx context.Context, desc string, val **policy.OptionalInt64, str string, changeCount *int) error { if str == "" { // not changed return nil } if str == inheritPolicyString || str == defaultPolicyString { *changeCount++ log(ctx).Infof(" - resetting %q to a default value inherited from parent.", desc) *val = nil return nil } v, err := strconv.ParseInt(str, 10, 32) if err != nil { return errors.Wrapf(err, "can't parse the %v %q", desc, str) } // convert MiB to bytes v *= 1 << 20 //nolint:mnd i := policy.OptionalInt64(v) *changeCount++ log(ctx).Infof(" - setting %q to %v.", desc, units.BytesString(v)) *val = &i return nil } func applyPolicyNumber64(ctx context.Context, desc string, val *int64, str string, changeCount *int) error { if str == "" { // not changed return nil } if str == inheritPolicyString || str == defaultPolicyString { *changeCount++ log(ctx).Infof(" - resetting %q to a default value inherited from parent.", desc) *val = 0 return nil } v, err := strconv.ParseInt(str, 10, 64) if err != nil { return errors.Wrapf(err, "can't parse the %q %q", desc, str) } *changeCount++ log(ctx).Infof(" - setting %q to %v.", desc, v) *val = v return nil } func applyPolicyBoolPtr(ctx context.Context, desc string, val **policy.OptionalBool, str string, changeCount *int) error { if str == "" { // not changed return nil } if str == inheritPolicyString || str == defaultPolicyString { *changeCount++ log(ctx).Infof(" - resetting %q to a default value inherited from parent.", desc) *val = nil return nil } v, err := strconv.ParseBool(str) if err != nil { return errors.Wrapf(err, "can't parse the %q %q", desc, str) } *changeCount++ log(ctx).Infof(" - setting %q to %v.", desc, v) ov := policy.OptionalBool(v) *val = &ov return nil } func supportedCompressionAlgorithms() []string { var res []string for name := range compression.ByName { res = append(res, string(name)) } sort.Strings(res) return append([]string{inheritPolicyString, "none"}, res...) }