github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/runner/jujuc/action-get.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  	"strings"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/gnuflag"
    11  
    12  	jujucmd "github.com/juju/juju/cmd"
    13  )
    14  
    15  // ActionGetCommand implements the action-get command.
    16  type ActionGetCommand struct {
    17  	cmd.CommandBase
    18  	ctx      Context
    19  	keys     []string
    20  	response interface{}
    21  	out      cmd.Output
    22  }
    23  
    24  // NewActionGetCommand returns an ActionGetCommand for use with the given
    25  // context.
    26  func NewActionGetCommand(ctx Context) (cmd.Command, error) {
    27  	return &ActionGetCommand{ctx: ctx}, nil
    28  }
    29  
    30  // Info returns the content for --help.
    31  func (c *ActionGetCommand) Info() *cmd.Info {
    32  	doc := `
    33  action-get will print the value of the parameter at the given key, serialized
    34  as YAML.  If multiple keys are passed, action-get will recurse into the param
    35  map as needed.
    36  `
    37  	return jujucmd.Info(&cmd.Info{
    38  		Name:    "action-get",
    39  		Args:    "[<key>[.<key>.<key>...]]",
    40  		Purpose: "get action parameters",
    41  		Doc:     doc,
    42  	})
    43  }
    44  
    45  // SetFlags handles known option flags; in this case, [--output={json|yaml}]
    46  // and --help.
    47  func (c *ActionGetCommand) SetFlags(f *gnuflag.FlagSet) {
    48  	c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
    49  }
    50  
    51  // Init makes sure there are no additional unknown arguments to action-get.
    52  func (c *ActionGetCommand) Init(args []string) error {
    53  	if len(args) > 0 {
    54  		err := cmd.CheckEmpty(args[1:])
    55  		if err != nil {
    56  			return err
    57  		}
    58  		c.keys = strings.Split(args[0], ".")
    59  	}
    60  	return nil
    61  }
    62  
    63  // recurseMapOnKeys returns the value of a map keyed recursively by the
    64  // strings given in "keys".  Thus, recurseMapOnKeys({a,b}, {a:{b:{c:d}}})
    65  // would return {c:d}.
    66  func recurseMapOnKeys(keys []string, params map[string]interface{}) (interface{}, bool) {
    67  	key, rest := keys[0], keys[1:]
    68  	answer, ok := params[key]
    69  
    70  	// If we're out of keys, we have our answer.
    71  	if len(rest) == 0 {
    72  		return answer, ok
    73  	}
    74  
    75  	// If we're not out of keys, but we tried a key that wasn't in the
    76  	// map, there's no answer.
    77  	if !ok {
    78  		return nil, false
    79  	}
    80  
    81  	switch typed := answer.(type) {
    82  	// If our value is a map[s]i{}, we can keep recursing.
    83  	case map[string]interface{}:
    84  		return recurseMapOnKeys(keys[1:], typed)
    85  		// If it's a map[i{}]i{}, we need to check whether it's a map[s]i{}.
    86  	case map[interface{}]interface{}:
    87  		m := make(map[string]interface{})
    88  		for k, v := range typed {
    89  			if tK, ok := k.(string); ok {
    90  				m[tK] = v
    91  			} else {
    92  				// If it's not, we don't have something we
    93  				// can work with.
    94  				return nil, false
    95  			}
    96  		}
    97  		// If it is, recurse into it.
    98  		return recurseMapOnKeys(keys[1:], m)
    99  
   100  		// Otherwise, we're trying to recurse into something we don't know
   101  		// how to deal with, so our answer is that we don't have an answer.
   102  	default:
   103  		return nil, false
   104  	}
   105  }
   106  
   107  // Run recurses into the params map for the Action, given the list of keys
   108  // into the map, and returns either the keyed value, or nothing.
   109  // In the case of an empty keys list, the entire params map will be returned.
   110  func (c *ActionGetCommand) Run(ctx *cmd.Context) error {
   111  	params, err := c.ctx.ActionParams()
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	var answer interface{}
   117  
   118  	if len(c.keys) == 0 {
   119  		answer = params
   120  	} else {
   121  		answer, _ = recurseMapOnKeys(c.keys, params)
   122  	}
   123  
   124  	return c.out.Write(ctx, answer)
   125  }