mirror of
https://github.com/kopia/kopia.git
synced 2025-12-23 22:57:50 -05:00
also upgraded github.com/klauspost/reedsolomon to latest non-retracted version go mod tidy
129 lines
4.8 KiB
Go
129 lines
4.8 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"encoding/csv"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/alecthomas/kingpin/v2"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/snapshot/policy"
|
|
)
|
|
|
|
const maxScriptLength = 32000
|
|
|
|
type policyActionFlags struct {
|
|
policySetBeforeFolderActionCommand string
|
|
policySetAfterFolderActionCommand string
|
|
policySetBeforeSnapshotRootActionCommand string
|
|
policySetAfterSnapshotRootActionCommand string
|
|
policySetActionCommandTimeout time.Duration
|
|
policySetActionCommandMode string
|
|
policySetPersistActionScript bool
|
|
}
|
|
|
|
func (c *policyActionFlags) setup(cmd *kingpin.CmdClause) {
|
|
cmd.Flag("before-folder-action", "Path to before-folder action command ('none' to remove)").Default("-").PlaceHolder("COMMAND").StringVar(&c.policySetBeforeFolderActionCommand)
|
|
cmd.Flag("after-folder-action", "Path to after-folder action command ('none' to remove)").Default("-").PlaceHolder("COMMAND").StringVar(&c.policySetAfterFolderActionCommand)
|
|
cmd.Flag("before-snapshot-root-action", "Path to before-snapshot-root action command ('none' to remove or 'inherit')").Default("-").PlaceHolder("COMMAND").StringVar(&c.policySetBeforeSnapshotRootActionCommand)
|
|
cmd.Flag("after-snapshot-root-action", "Path to after-snapshot-root action command ('none' to remove or 'inherit')").Default("-").PlaceHolder("COMMAND").StringVar(&c.policySetAfterSnapshotRootActionCommand)
|
|
cmd.Flag("action-command-timeout", "Max time allowed for an action to run in seconds").Default("5m").DurationVar(&c.policySetActionCommandTimeout)
|
|
cmd.Flag("action-command-mode", "Action command mode").Default("essential").EnumVar(&c.policySetActionCommandMode, "essential", "optional", "async")
|
|
cmd.Flag("persist-action-script", "Persist action script").BoolVar(&c.policySetPersistActionScript)
|
|
}
|
|
|
|
func (c *policyActionFlags) setActionsFromFlags(ctx context.Context, p *policy.ActionsPolicy, changeCount *int) error {
|
|
if err := c.setActionCommandFromFlags(ctx, "before-folder", &p.BeforeFolder, c.policySetBeforeFolderActionCommand, changeCount); err != nil {
|
|
return errors.Wrap(err, "invalid before-folder-action")
|
|
}
|
|
|
|
if err := c.setActionCommandFromFlags(ctx, "after-folder", &p.AfterFolder, c.policySetAfterFolderActionCommand, changeCount); err != nil {
|
|
return errors.Wrap(err, "invalid after-folder-action")
|
|
}
|
|
|
|
if err := c.setActionCommandFromFlags(ctx, "before-snapshot-root", &p.BeforeSnapshotRoot, c.policySetBeforeSnapshotRootActionCommand, changeCount); err != nil {
|
|
return errors.Wrap(err, "invalid before-snapshot-root-action")
|
|
}
|
|
|
|
if err := c.setActionCommandFromFlags(ctx, "after-snapshot-root", &p.AfterSnapshotRoot, c.policySetAfterSnapshotRootActionCommand, changeCount); err != nil {
|
|
return errors.Wrap(err, "invalid after-snapshot-root-action")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *policyActionFlags) setActionCommandFromFlags(ctx context.Context, actionName string, cmd **policy.ActionCommand, value string, changeCount *int) error {
|
|
if value == "-" {
|
|
// not set
|
|
return nil
|
|
}
|
|
|
|
if value == "" {
|
|
log(ctx).Infof(" - removing %v action", actionName)
|
|
|
|
*changeCount++
|
|
|
|
*cmd = nil
|
|
|
|
return nil
|
|
}
|
|
|
|
*cmd = &policy.ActionCommand{
|
|
TimeoutSeconds: int(c.policySetActionCommandTimeout.Seconds()),
|
|
Mode: c.policySetActionCommandMode,
|
|
}
|
|
|
|
*changeCount++
|
|
|
|
if c.policySetPersistActionScript {
|
|
script, err := os.ReadFile(value) //nolint:gosec
|
|
if err != nil {
|
|
return errors.Wrap(err, "unable to read script file")
|
|
}
|
|
|
|
if len(script) > maxScriptLength {
|
|
return errors.Errorf("action script file (%v) too long: %v, max allowed %d", value, len(script), maxScriptLength)
|
|
}
|
|
|
|
log(ctx).Infof(" - setting %v (%v) action script from file %v (%v bytes) with timeout %v", actionName, c.policySetActionCommandMode, value, len(script), c.policySetActionCommandTimeout)
|
|
|
|
(*cmd).Script = string(script)
|
|
|
|
return nil
|
|
}
|
|
|
|
// parse path as CSV as if space was the separator, this automatically takes care of quotations
|
|
r := csv.NewReader(strings.NewReader(value))
|
|
r.Comma = ' ' // space
|
|
|
|
fields, err := r.Read()
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error parsing %v command", actionName)
|
|
}
|
|
|
|
(*cmd).Command = fields[0]
|
|
(*cmd).Arguments = fields[1:]
|
|
|
|
if len((*cmd).Arguments) == 0 {
|
|
log(ctx).Infof(" - setting %v (%v) action command to %v and timeout %v", actionName, c.policySetActionCommandMode, quoteArguments((*cmd).Command), c.policySetActionCommandTimeout)
|
|
} else {
|
|
log(ctx).Infof(" - setting %v (%v) action command to %v with arguments %v and timeout %v", actionName, c.policySetActionCommandMode, quoteArguments((*cmd).Command), quoteArguments((*cmd).Arguments...), c.policySetActionCommandTimeout)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func quoteArguments(s ...string) string {
|
|
var result []string
|
|
|
|
for _, v := range s {
|
|
result = append(result, fmt.Sprintf("\"%v\"", v))
|
|
}
|
|
|
|
return strings.Join(result, " ")
|
|
}
|