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 }