github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/uniter/runner/jujuc/action-set.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package jujuc
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/charm/v12"
    11  	"github.com/juju/cmd/v3"
    12  	"github.com/juju/gnuflag"
    13  
    14  	jujucmd "github.com/juju/juju/cmd"
    15  )
    16  
    17  var keyRule = charm.GetActionNameRule()
    18  
    19  // list of action result keys that are not allowed to be set by users
    20  var reservedKeys = []string{"stdout", "stdout-encoding", "stderr", "stderr-encoding"}
    21  
    22  // ActionSetCommand implements the action-set command.
    23  type ActionSetCommand struct {
    24  	cmd.CommandBase
    25  	ctx  Context
    26  	args [][]string
    27  }
    28  
    29  // NewActionSetCommand returns a new ActionSetCommand with the given context.
    30  func NewActionSetCommand(ctx Context) (cmd.Command, error) {
    31  	return &ActionSetCommand{ctx: ctx}, nil
    32  }
    33  
    34  // Info returns the content for --help.
    35  func (c *ActionSetCommand) Info() *cmd.Info {
    36  	reservedText := `"` + strings.Join(reservedKeys, `", "`) + `"`
    37  	doc := fmt.Sprintf(`
    38  action-set adds the given values to the results map of the Action. This map
    39  is returned to the user after the completion of the Action. Keys must start
    40  and end with lowercase alphanumeric, and contain only lowercase alphanumeric,
    41  hyphens and periods.  The following special keys are reserved for internal use: 
    42  %s.
    43  
    44  Example usage:
    45   action-set outfile.size=10G
    46   action-set foo.bar=2
    47   action-set foo.baz.val=3
    48   action-set foo.bar.zab=4
    49   action-set foo.baz=1
    50  
    51   will yield:
    52  
    53   outfile:
    54     size: "10G"
    55   foo:
    56     bar:
    57       zab: "4"
    58     baz: "1"
    59  `, reservedText)
    60  	return jujucmd.Info(&cmd.Info{
    61  		Name:    "action-set",
    62  		Args:    "<key>=<value> [<key>=<value> ...]",
    63  		Purpose: "set action results",
    64  		Doc:     doc,
    65  	})
    66  }
    67  
    68  // SetFlags handles known option flags.
    69  func (c *ActionSetCommand) SetFlags(f *gnuflag.FlagSet) {
    70  	// TODO(binary132): add cmd.Input type as in cmd.Output for YAML piping.
    71  }
    72  
    73  // Init accepts maps in the form of key=value, key.key2.keyN....=value
    74  func (c *ActionSetCommand) Init(args []string) error {
    75  	c.args = make([][]string, 0)
    76  	for _, arg := range args {
    77  		thisArg := strings.SplitN(arg, "=", 2)
    78  		if len(thisArg) != 2 {
    79  			return fmt.Errorf("argument %q must be of the form key...=value", arg)
    80  		}
    81  		keySlice := strings.Split(thisArg[0], ".")
    82  		// check each key for validity
    83  		for _, key := range keySlice {
    84  			if valid := keyRule.MatchString(key); !valid {
    85  				return fmt.Errorf("key %q must start and end with lowercase alphanumeric, and contain only lowercase alphanumeric, hyphens and periods", key)
    86  			}
    87  
    88  			for _, reserved := range reservedKeys {
    89  				if reserved == key {
    90  					return fmt.Errorf(`cannot set reserved action key "%s"`, key)
    91  				}
    92  			}
    93  		}
    94  		// [key, key, key, key, value]
    95  		c.args = append(c.args, append(keySlice, thisArg[1]))
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  // Run adds the given <key list>/<value> pairs, such as foo.bar=baz to the
   102  // existing map of results for the Action.
   103  func (c *ActionSetCommand) Run(ctx *cmd.Context) error {
   104  	for _, argSlice := range c.args {
   105  		valueIndex := len(argSlice) - 1
   106  		keys := argSlice[:valueIndex]
   107  		value := argSlice[valueIndex]
   108  		err := c.ctx.UpdateActionResults(keys, value)
   109  		if err != nil {
   110  			return err
   111  		}
   112  	}
   113  
   114  	return nil
   115  }