mirror of
https://github.com/kopia/kopia.git
synced 2026-05-14 17:56:59 -04:00
* refactor: move from io/ioutil to io and os package The io/ioutil package has been deprecated as of Go 1.16, see https://golang.org/doc/go1.16#ioutil. This commit replaces the existing io/ioutil functions with their new definitions in io and os packages. Signed-off-by: Eng Zer Jun <engzerjun@gmail.com> * chore: remove //nolint:gosec for os.ReadFile At the time of this commit, the G304 rule of gosec does not include the `os.ReadFile` function. We remove `//nolint:gosec` temporarily until https://github.com/securego/gosec/pull/706 is merged. Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
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"
|
|
"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)
|
|
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, " ")
|
|
}
|