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