github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/action/list.go (about) 1 // Copyright 2014, 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package action 5 6 import ( 7 "fmt" 8 "io" 9 "strings" 10 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "github.com/juju/gnuflag" 14 "github.com/juju/naturalsort" 15 "gopkg.in/juju/names.v2" 16 17 "github.com/juju/juju/apiserver/params" 18 jujucmd "github.com/juju/juju/cmd" 19 "github.com/juju/juju/cmd/modelcmd" 20 "github.com/juju/juju/cmd/output" 21 ) 22 23 func NewListCommand() cmd.Command { 24 return modelcmd.Wrap(&listCommand{}) 25 } 26 27 // listCommand lists actions defined by the charm of a given application. 28 type listCommand struct { 29 ActionCommandBase 30 applicationTag names.ApplicationTag 31 fullSchema bool 32 out cmd.Output 33 } 34 35 const listDoc = ` 36 List the actions available to run on the target application, with a short 37 description. To show the full schema for the actions, use --schema. 38 39 For more information, see also the 'run-action' command, which executes actions. 40 ` 41 42 // Set up the output. 43 func (c *listCommand) SetFlags(f *gnuflag.FlagSet) { 44 c.ActionCommandBase.SetFlags(f) 45 46 // `listCommand's` default format depends on the value of another flag. 47 // That is, if no default is selected using the `--format` flag, then 48 // the output format depends on whether or not the user has specified 49 // the `--schema` flag (schema output does not support tabular which is 50 // the default for all other output from this command). Currently the 51 // `cmd` package's `Output` structure has no methods that support 52 // selecting a default format dynamically. Here we introduce a "default" 53 // default which serves to indicate that the user wants default 54 // formatting behavior. This allows us to select the appropriate default 55 // behavior in the presence of the "default" format value. 56 c.out.AddFlags(f, "default", map[string]cmd.Formatter{ 57 "yaml": cmd.FormatYaml, 58 "json": cmd.FormatJson, 59 "tabular": c.printTabular, 60 "default": c.dummyDefault, 61 }) 62 f.BoolVar(&c.fullSchema, "schema", false, "Display the full action schema") 63 } 64 65 func (c *listCommand) Info() *cmd.Info { 66 return jujucmd.Info(&cmd.Info{ 67 Name: "actions", 68 Args: "<application name>", 69 Purpose: "List actions defined for an application.", 70 Doc: listDoc, 71 Aliases: []string{"list-actions"}, 72 }) 73 } 74 75 // Init validates the application name and any other options. 76 func (c *listCommand) Init(args []string) error { 77 if c.out.Name() == "tabular" && c.fullSchema { 78 return errors.New("full schema not compatible with tabular output") 79 } 80 switch len(args) { 81 case 0: 82 return errors.New("no application name specified") 83 case 1: 84 svcName := args[0] 85 if !names.IsValidApplication(svcName) { 86 return errors.Errorf("invalid application name %q", svcName) 87 } 88 c.applicationTag = names.NewApplicationTag(svcName) 89 return nil 90 default: 91 return cmd.CheckEmpty(args[1:]) 92 } 93 } 94 95 // Run grabs the Actions spec from the api. It then sets up a sensible 96 // output format for the map. 97 func (c *listCommand) Run(ctx *cmd.Context) error { 98 api, err := c.NewActionAPIClient() 99 if err != nil { 100 return err 101 } 102 defer api.Close() 103 104 actions, err := api.ApplicationCharmActions(params.Entity{Tag: c.applicationTag.String()}) 105 if err != nil { 106 return err 107 } 108 109 if c.fullSchema { 110 verboseSpecs := make(map[string]interface{}) 111 for k, v := range actions { 112 verboseSpecs[k] = v.Params 113 } 114 115 if c.out.Name() == "default" { 116 return c.out.WriteFormatter(ctx, cmd.FormatYaml, verboseSpecs) 117 } else { 118 return c.out.Write(ctx, verboseSpecs) 119 } 120 } 121 122 shortOutput := make(map[string]string) 123 var sortedNames []string 124 for name, action := range actions { 125 shortOutput[name] = action.Description 126 if shortOutput[name] == "" { 127 shortOutput[name] = "No description" 128 } 129 sortedNames = append(sortedNames, name) 130 } 131 naturalsort.Sort(sortedNames) 132 133 var output interface{} 134 switch c.out.Name() { 135 case "yaml", "json": 136 output = shortOutput 137 default: 138 if len(sortedNames) == 0 { 139 ctx.Infof("No actions defined for %s.", c.applicationTag.Id()) 140 return nil 141 } 142 var list []listOutput 143 for _, name := range sortedNames { 144 list = append(list, listOutput{name, shortOutput[name]}) 145 } 146 output = list 147 } 148 149 if c.out.Name() == "default" { 150 return c.out.WriteFormatter(ctx, c.printTabular, output) 151 } else { 152 return c.out.Write(ctx, output) 153 } 154 155 } 156 157 type listOutput struct { 158 action string 159 description string 160 } 161 162 // printTabular prints the list of actions in tabular format 163 func (c *listCommand) printTabular(writer io.Writer, value interface{}) error { 164 list, ok := value.([]listOutput) 165 if !ok { 166 return errors.New("unexpected value") 167 } 168 169 tw := output.TabWriter(writer) 170 fmt.Fprintf(tw, "%s\t%s\n", "Action", "Description") 171 for _, value := range list { 172 fmt.Fprintf(tw, "%s\t%s\n", value.action, strings.TrimSpace(value.description)) 173 } 174 tw.Flush() 175 return nil 176 } 177 178 // This method represents a default format that is used to express the need for 179 // a dynamically selected default. That is, when the `actions` command 180 // determines its default output format based on the presence of a flag other 181 // than "format", then this method is used to indicate that a "dynamic" default 182 // is desired. NOTE: It is very possible that this functionality should live in 183 // the cmd package where this kind of thing can be handled in a more elegant 184 // and DRY fashion. 185 func (c *listCommand) dummyDefault(writer io.Writer, value interface{}) error { 186 return nil 187 }