github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/resource/charmresources.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package resource 5 6 import ( 7 "github.com/juju/cmd" 8 "github.com/juju/errors" 9 "github.com/juju/gnuflag" 10 "gopkg.in/juju/charm.v6" 11 charmresource "gopkg.in/juju/charm.v6/resource" 12 csparams "gopkg.in/juju/charmrepo.v3/csclient/params" 13 14 "github.com/juju/juju/api" 15 "github.com/juju/juju/api/controller" 16 "github.com/juju/juju/charmstore" 17 jujucmd "github.com/juju/juju/cmd" 18 "github.com/juju/juju/cmd/modelcmd" 19 ) 20 21 // CharmResourcesCommand implements the "juju charm-resources" command. 22 type CharmResourcesCommand struct { 23 baseCharmResourcesCommand 24 } 25 26 // NewCharmResourcesCommand returns a new command that lists resources defined 27 // by a charm. 28 func NewCharmResourcesCommand(resourceLister ResourceLister) modelcmd.ModelCommand { 29 var c CharmResourcesCommand 30 c.setResourceLister(resourceLister) 31 return modelcmd.Wrap(&c) 32 } 33 34 // Info implements cmd.Command. 35 func (c *CharmResourcesCommand) Info() *cmd.Info { 36 i := c.baseInfo() 37 i.Name = "charm-resources" 38 i.Aliases = []string{"list-charm-resources"} 39 return jujucmd.Info(i) 40 } 41 42 // SetFlags implements cmd.Command. 43 func (c *CharmResourcesCommand) SetFlags(f *gnuflag.FlagSet) { 44 c.setBaseFlags(f) 45 } 46 47 // Init implements cmd.Command. 48 func (c *CharmResourcesCommand) Init(args []string) error { 49 return c.baseInit(args) 50 } 51 52 // Run implements cmd.Command. 53 func (c *CharmResourcesCommand) Run(ctx *cmd.Context) error { 54 return c.baseRun(ctx) 55 } 56 57 // CharmResourceLister lists resources for the given charm ids. 58 type ResourceLister interface { 59 ListResources(ids []charmstore.CharmID) ([][]charmresource.Resource, error) 60 } 61 62 type baseCharmResourcesCommand struct { 63 modelcmd.ModelCommandBase 64 65 // resourceLister is called by Run to list charm resources and 66 // uses juju/juju/charmstore.Client. 67 resourceLister ResourceLister 68 69 out cmd.Output 70 channel string 71 charm string 72 } 73 74 func (b *baseCharmResourcesCommand) setResourceLister(resourceLister ResourceLister) { 75 if resourceLister == nil { 76 resourceLister = b 77 } 78 b.resourceLister = resourceLister 79 } 80 81 func (c *baseCharmResourcesCommand) baseInfo() *cmd.Info { 82 return jujucmd.Info(&cmd.Info{ 83 Args: "<charm>", 84 Purpose: "Display the resources for a charm in the charm store.", 85 Doc: charmResourcesDoc, 86 }) 87 } 88 89 func (c *baseCharmResourcesCommand) setBaseFlags(f *gnuflag.FlagSet) { 90 c.ModelCommandBase.SetFlags(f) 91 defaultFormat := "tabular" 92 c.out.AddFlags(f, defaultFormat, map[string]cmd.Formatter{ 93 "tabular": FormatCharmTabular, 94 "yaml": cmd.FormatYaml, 95 "json": cmd.FormatJson, 96 }) 97 f.StringVar(&c.channel, "channel", "stable", "the charmstore channel of the charm") 98 } 99 100 func (c *baseCharmResourcesCommand) baseInit(args []string) error { 101 if len(args) == 0 { 102 return errors.New("missing charm") 103 } 104 c.charm = args[0] 105 106 if err := cmd.CheckEmpty(args[1:]); err != nil { 107 return errors.Trace(err) 108 } 109 return nil 110 } 111 112 func (c *baseCharmResourcesCommand) baseRun(ctx *cmd.Context) error { 113 // TODO(ericsnow) Adjust this to the charm store. 114 115 charmURL, err := resolveCharm(c.charm) 116 if err != nil { 117 return errors.Trace(err) 118 } 119 charm := charmstore.CharmID{URL: charmURL, Channel: csparams.Channel(c.channel)} 120 121 resources, err := c.resourceLister.ListResources([]charmstore.CharmID{charm}) 122 if err != nil { 123 return errors.Trace(err) 124 } 125 if len(resources) != 1 { 126 return errors.New("got bad data from charm store") 127 } 128 res := resources[0] 129 130 if len(res) == 0 && c.out.Name() == "tabular" { 131 ctx.Infof("No resources to display.") 132 return nil 133 } 134 135 // Note that we do not worry about c.CompatVersion 136 // for show-charm-resources... 137 formatter := newCharmResourcesFormatter(resources[0]) 138 formatted := formatter.format() 139 return c.out.Write(ctx, formatted) 140 } 141 142 var charmResourcesDoc = ` 143 This command will report the resources for a charm in the charm store. 144 145 <charm> can be a charm URL, or an unambiguously condensed form of it, 146 just like the deploy command. So the following forms will be accepted: 147 148 For cs:trusty/mysql 149 mysql 150 trusty/mysql 151 152 For cs:~user/trusty/mysql 153 cs:~user/mysql 154 155 Where the series is not supplied, the series from your local host is used. 156 Thus the above examples imply that the local series is trusty. 157 ` 158 159 // ListCharmResources implements CharmResourceLister by getting the charmstore client 160 // from the command's ModelCommandBase. 161 func (c *baseCharmResourcesCommand) ListResources(ids []charmstore.CharmID) ([][]charmresource.Resource, error) { 162 bakeryClient, err := c.BakeryClient() 163 if err != nil { 164 return nil, errors.Trace(err) 165 } 166 conAPIRoot, err := c.NewControllerAPIRoot() 167 if err != nil { 168 return nil, errors.Trace(err) 169 } 170 csURL, err := getCharmStoreAPIURL(conAPIRoot) 171 if err != nil { 172 return nil, errors.Trace(err) 173 } 174 client, err := charmstore.NewCustomClient(bakeryClient, csURL) 175 if err != nil { 176 return nil, errors.Trace(err) 177 } 178 return client.ListResources(ids) 179 } 180 181 func resolveCharm(raw string) (*charm.URL, error) { 182 charmURL, err := charm.ParseURL(raw) 183 if err != nil { 184 return charmURL, errors.Trace(err) 185 } 186 187 if charmURL.Series == "bundle" { 188 return charmURL, errors.Errorf("charm bundles are not supported") 189 } 190 191 return charmURL, nil 192 } 193 194 // getCharmStoreAPIURL consults the controller config for the charmstore api url to use. 195 var getCharmStoreAPIURL = func(conAPIRoot api.Connection) (string, error) { 196 controllerAPI := controller.NewClient(conAPIRoot) 197 controllerCfg, err := controllerAPI.ControllerConfig() 198 if err != nil { 199 return "", errors.Trace(err) 200 } 201 return controllerCfg.CharmStoreURL(), nil 202 }