github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/application/show.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package application
     5  
     6  import (
     7  	"strings"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/gnuflag"
    12  	"gopkg.in/juju/names.v2"
    13  
    14  	"github.com/juju/juju/api/application"
    15  	"github.com/juju/juju/apiserver/params"
    16  	jujucmd "github.com/juju/juju/cmd"
    17  	"github.com/juju/juju/cmd/modelcmd"
    18  	"github.com/juju/juju/core/constraints"
    19  )
    20  
    21  const showApplicationDoc = `
    22  The command takes deployed application names or aliases as an argument.
    23  
    24  The command does an exact search. It does not support wildcards.
    25  
    26  Examples:
    27      $ juju show-application mysql
    28      $ juju show-application mysql wordpress
    29  
    30      $ juju show-application myapplication
    31          where "myapplication" is the application name alias, see "juju help deploy" for more information
    32  
    33  `
    34  
    35  // NewShowApplicationCommand returns a command that displays applications info.
    36  func NewShowApplicationCommand() cmd.Command {
    37  	s := &showApplicationCommand{}
    38  	s.newAPIFunc = func() (ApplicationsInfoAPI, error) {
    39  		return s.newApplicationAPI()
    40  	}
    41  	return modelcmd.Wrap(s)
    42  }
    43  
    44  // showApplicationCommand displays application information.
    45  type showApplicationCommand struct {
    46  	modelcmd.ModelCommandBase
    47  
    48  	out        cmd.Output
    49  	apps       []string
    50  	newAPIFunc func() (ApplicationsInfoAPI, error)
    51  }
    52  
    53  // Info implements Command.Info.
    54  func (c *showApplicationCommand) Info() *cmd.Info {
    55  	showCmd := &cmd.Info{
    56  		Name:    "show-application",
    57  		Args:    "<application name or alias>",
    58  		Purpose: "Displays information about an application.",
    59  		Doc:     showApplicationDoc,
    60  	}
    61  	return jujucmd.Info(showCmd)
    62  }
    63  
    64  // Init implements Command.Init.
    65  func (c *showApplicationCommand) Init(args []string) error {
    66  	if len(args) < 1 {
    67  		return errors.Errorf("an application name must be supplied")
    68  	}
    69  	c.apps = args
    70  	var invalid []string
    71  	for _, one := range c.apps {
    72  		if !names.IsValidApplication(one) {
    73  			invalid = append(invalid, one)
    74  		}
    75  	}
    76  	if len(invalid) == 0 {
    77  		return nil
    78  	}
    79  	plural := "s"
    80  	if len(invalid) == 1 {
    81  		plural = ""
    82  	}
    83  	return errors.NotValidf(`application name%v %v`, plural, strings.Join(invalid, `, `))
    84  }
    85  
    86  // SetFlags implements Command.SetFlags.
    87  func (c *showApplicationCommand) SetFlags(f *gnuflag.FlagSet) {
    88  	c.ModelCommandBase.SetFlags(f)
    89  	c.out.AddFlags(f, "yaml", cmd.DefaultFormatters)
    90  }
    91  
    92  // ApplicationsInfoAPI defines the API methods that show-application command uses.
    93  type ApplicationsInfoAPI interface {
    94  	Close() error
    95  	BestAPIVersion() int
    96  	ApplicationsInfo([]names.ApplicationTag) ([]params.ApplicationInfoResult, error)
    97  }
    98  
    99  func (c *showApplicationCommand) newApplicationAPI() (ApplicationsInfoAPI, error) {
   100  	root, err := c.NewAPIRoot()
   101  	if err != nil {
   102  		return nil, errors.Trace(err)
   103  	}
   104  	return application.NewClient(root), nil
   105  }
   106  
   107  func (c *showApplicationCommand) Run(ctx *cmd.Context) error {
   108  	client, err := c.newAPIFunc()
   109  	if err != nil {
   110  		return err
   111  	}
   112  	defer client.Close()
   113  
   114  	if v := client.BestAPIVersion(); v < 9 {
   115  		// old client does not support showing applications.
   116  		return errors.NotSupportedf("show applications on API server version %v", v)
   117  	}
   118  
   119  	tags, err := c.getApplicationTags()
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	results, err := client.ApplicationsInfo(tags)
   125  	if err != nil {
   126  		return errors.Trace(err)
   127  	}
   128  
   129  	var errs params.ErrorResults
   130  	var valid []params.ApplicationInfo
   131  	for _, result := range results {
   132  		if result.Error != nil {
   133  			errs.Results = append(errs.Results, params.ErrorResult{result.Error})
   134  			continue
   135  		}
   136  		valid = append(valid, *result.Result)
   137  	}
   138  	if len(errs.Results) > 0 {
   139  		return errs.Combine()
   140  	}
   141  
   142  	output, err := formatApplicationInfos(valid)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	return c.out.Write(ctx, output)
   147  }
   148  
   149  func (c *showApplicationCommand) getApplicationTags() ([]names.ApplicationTag, error) {
   150  	tags := make([]names.ApplicationTag, len(c.apps))
   151  	for i, one := range c.apps {
   152  		if !names.IsValidApplication(one) {
   153  			return nil, errors.Errorf("invalid application name %v", one)
   154  		}
   155  		tags[i] = names.NewApplicationTag(one)
   156  	}
   157  	return tags, nil
   158  }
   159  
   160  // formatApplicationInfos takes a set of params.ApplicationInfo and
   161  // creates a mapping from storage ID application name to application info.
   162  func formatApplicationInfos(all []params.ApplicationInfo) (map[string]ApplicationInfo, error) {
   163  	if len(all) == 0 {
   164  		return nil, nil
   165  	}
   166  	output := make(map[string]ApplicationInfo)
   167  	for _, one := range all {
   168  		tag, info, err := createApplicationInfo(one)
   169  		if err != nil {
   170  			return nil, errors.Trace(err)
   171  		}
   172  		output[tag.Name] = info
   173  	}
   174  	return output, nil
   175  }
   176  
   177  // ApplicationInfo defines the serialization behaviour of the application information.
   178  type ApplicationInfo struct {
   179  	Charm            string            `yaml:"charm,omitempty" json:"charm,omitempty"`
   180  	Series           string            `yaml:"series,omitempty" json:"series,omitempty"`
   181  	Channel          string            `yaml:"channel,omitempty" json:"channel,omitempty"`
   182  	Constraints      constraints.Value `yaml:"constraints,omitempty" json:"constraints,omitempty"`
   183  	Principal        bool              `yaml:"principal" json:"principal"`
   184  	Exposed          bool              `yaml:"exposed" json:"exposed"`
   185  	Remote           bool              `yaml:"remote" json:"remote"`
   186  	EndpointBindings map[string]string `yaml:"endpoint-bindings,omitempty" json:"endpoint-bindings,omitempty"`
   187  }
   188  
   189  func createApplicationInfo(details params.ApplicationInfo) (names.ApplicationTag, ApplicationInfo, error) {
   190  	tag, err := names.ParseApplicationTag(details.Tag)
   191  	if err != nil {
   192  		return names.ApplicationTag{}, ApplicationInfo{}, errors.Trace(err)
   193  	}
   194  
   195  	info := ApplicationInfo{
   196  		Charm:            details.Charm,
   197  		Series:           details.Series,
   198  		Channel:          details.Channel,
   199  		Constraints:      details.Constraints,
   200  		Principal:        details.Principal,
   201  		Exposed:          details.Exposed,
   202  		Remote:           details.Remote,
   203  		EndpointBindings: details.EndpointBindings,
   204  	}
   205  	return tag, info, nil
   206  }