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 }