github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/crossmodel/show.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package crossmodel 5 6 import ( 7 "github.com/juju/cmd" 8 "github.com/juju/errors" 9 "github.com/juju/gnuflag" 10 "gopkg.in/juju/names.v2" 11 12 jujucmd "github.com/juju/juju/cmd" 13 "github.com/juju/juju/cmd/modelcmd" 14 "github.com/juju/juju/core/crossmodel" 15 ) 16 17 const showCommandDoc = ` 18 This command is intended to enable users to learn more about the 19 application offered from a particular URL. In addition to the URL of 20 the offer, extra information is provided from the readme file of the 21 charm being offered. 22 23 Examples: 24 To show the extended information for the application 'prod' offered 25 from the model 'default' on the same Juju controller: 26 27 juju show-offer default.prod 28 29 The supplied URL can also include a username where offers require them. 30 This will be given as part of the URL retrieved from the 31 'juju find-offers' command. To show information for the application 32 'prod' from the model 'default' from the user 'admin': 33 34 juju show-offer admin/default.prod 35 36 To show the information regarding the application 'prod' offered from 37 the model 'default' on an accessible controller named 'controller': 38 39 juju show-offer controller:default.prod 40 41 See also: 42 find-offers 43 ` 44 45 type showCommand struct { 46 RemoteEndpointsCommandBase 47 48 url string 49 out cmd.Output 50 newAPIFunc func(string) (ShowAPI, error) 51 } 52 53 // NewShowOfferedEndpointCommand constructs command that 54 // allows to show details of offered application's endpoint. 55 func NewShowOfferedEndpointCommand() cmd.Command { 56 showCmd := &showCommand{} 57 showCmd.newAPIFunc = func(controllerName string) (ShowAPI, error) { 58 return showCmd.NewRemoteEndpointsAPI(controllerName) 59 } 60 return modelcmd.WrapController(showCmd) 61 } 62 63 // Init implements Command.Init. 64 func (c *showCommand) Init(args []string) (err error) { 65 if len(args) != 1 { 66 return errors.New("must specify endpoint URL") 67 } 68 c.url = args[0] 69 return nil 70 } 71 72 // Info implements Command.Info. 73 func (c *showCommand) Info() *cmd.Info { 74 return jujucmd.Info(&cmd.Info{ 75 Name: "show-offer", 76 Args: "[<controller>:]<offer url>", 77 Purpose: "Shows extended information about the offered application.", 78 Doc: showCommandDoc, 79 }) 80 } 81 82 // SetFlags implements Command.SetFlags. 83 func (c *showCommand) SetFlags(f *gnuflag.FlagSet) { 84 c.RemoteEndpointsCommandBase.SetFlags(f) 85 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 86 "yaml": cmd.FormatYaml, 87 "json": cmd.FormatJson, 88 "tabular": formatShowTabular, 89 }) 90 } 91 92 // Run implements Command.Run. 93 func (c *showCommand) Run(ctx *cmd.Context) (err error) { 94 controllerName, err := c.ControllerName() 95 if err != nil { 96 return err 97 } 98 url, err := crossmodel.ParseOfferURL(c.url) 99 if err != nil { 100 store := c.ClientStore() 101 currentModel, err := store.CurrentModel(controllerName) 102 if err != nil { 103 return errors.Trace(err) 104 } 105 url, err = makeURLFromCurrentModel(c.url, controllerName, currentModel) 106 if err != nil { 107 return errors.Trace(err) 108 } 109 } 110 if url.Source != "" { 111 controllerName = url.Source 112 } 113 accountDetails, err := c.CurrentAccountDetails() 114 if err != nil { 115 return err 116 } 117 loggedInUser := accountDetails.User 118 119 api, err := c.newAPIFunc(controllerName) 120 if err != nil { 121 return err 122 } 123 defer api.Close() 124 125 url.Source = "" 126 found, err := api.ApplicationOffer(url.String()) 127 if err != nil { 128 return err 129 } 130 131 output, err := convertOffers(controllerName, names.NewUserTag(loggedInUser), found) 132 if err != nil { 133 return err 134 } 135 return c.out.Write(ctx, output) 136 } 137 138 // ShowAPI defines the API methods that cross model show command uses. 139 type ShowAPI interface { 140 Close() error 141 ApplicationOffer(url string) (*crossmodel.ApplicationOfferDetails, error) 142 } 143 144 type OfferUser struct { 145 UserName string `yaml:"-" json:"-"` 146 DisplayName string `yaml:"display-name,omitempty" json:"display-name,omitempty"` 147 Access string `yaml:"access" json:"access"` 148 } 149 150 // ShowOfferedApplication defines the serialization behaviour of an application offer. 151 // This is used in map-style yaml output where remote application name is the key. 152 type ShowOfferedApplication struct { 153 // Description is the user entered description. 154 Description string `yaml:"description,omitempty" json:"description,omitempty"` 155 156 // Access is the level of access the user has on the offer. 157 Access string `yaml:"access" json:"access"` 158 159 // Endpoints list of offered application endpoints. 160 Endpoints map[string]RemoteEndpoint `yaml:"endpoints" json:"endpoints"` 161 162 // Users are the users who can access the offer. 163 Users map[string]OfferUser `yaml:"users,omitempty" json:"users,omitempty"` 164 } 165 166 // convertOffers takes any number of api-formatted remote applications and 167 // creates a collection of ui-formatted offers. 168 func convertOffers( 169 store string, loggedInUser names.UserTag, offers ...*crossmodel.ApplicationOfferDetails, 170 ) (map[string]ShowOfferedApplication, error) { 171 if len(offers) == 0 { 172 return nil, nil 173 } 174 output := make(map[string]ShowOfferedApplication, len(offers)) 175 for _, one := range offers { 176 access := accessForUser(loggedInUser, one.Users) 177 app := ShowOfferedApplication{ 178 Access: access, 179 Endpoints: convertRemoteEndpoints(one.Endpoints...), 180 Users: convertUsers(one.Users...), 181 } 182 if one.ApplicationDescription != "" { 183 app.Description = one.ApplicationDescription 184 } 185 url, err := crossmodel.ParseOfferURL(one.OfferURL) 186 if err != nil { 187 return nil, err 188 } 189 if url.Source == "" { 190 url.Source = store 191 } 192 output[url.String()] = app 193 } 194 return output, nil 195 } 196 197 func convertUsers(users ...crossmodel.OfferUserDetails) map[string]OfferUser { 198 if len(users) == 0 { 199 return nil 200 } 201 output := make(map[string]OfferUser, len(users)) 202 for _, one := range users { 203 output[one.UserName] = OfferUser{one.UserName, one.DisplayName, string(one.Access)} 204 } 205 return output 206 }