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 }