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  }