github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/resource/cmd/list_charm_resources.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cmd
     5  
     6  import (
     7  	"github.com/juju/cmd"
     8  	"github.com/juju/errors"
     9  	"gopkg.in/juju/charm.v6-unstable"
    10  	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
    11  	csparams "gopkg.in/juju/charmrepo.v2-unstable/csclient/params"
    12  	"launchpad.net/gnuflag"
    13  
    14  	"github.com/juju/juju/charmstore"
    15  	"github.com/juju/juju/cmd/modelcmd"
    16  )
    17  
    18  // CharmCommandBase exposes the functionality of charmcmd.CommandBase
    19  // needed here.
    20  type CharmCommandBase interface {
    21  	// Connect connects to the charm store and returns a client.
    22  	// cmd.Context needs to be passed in so that we can do authentication
    23  	// via the cli if available.
    24  	Connect(*cmd.Context) (CharmResourceLister, error)
    25  }
    26  
    27  // CharmResourceLister has the charm store API methods needed by ListCharmResourcesCommand.
    28  type CharmResourceLister interface {
    29  	// ListResources lists the resources for each of the identified charms.
    30  	ListResources([]charmstore.CharmID) ([][]charmresource.Resource, error)
    31  
    32  	// Close closes the client.
    33  	Close() error
    34  }
    35  
    36  // ListCharmResourcesCommand implements the "juju charm list-resources" command.
    37  type ListCharmResourcesCommand struct {
    38  	modelcmd.ModelCommandBase
    39  	CharmCommandBase
    40  	out     cmd.Output
    41  	channel string
    42  	charm   string
    43  }
    44  
    45  // NewListCharmResourcesCommand returns a new command that lists resources defined
    46  // by a charm.
    47  func NewListCharmResourcesCommand(base CharmCommandBase) *ListCharmResourcesCommand {
    48  	cmd := &ListCharmResourcesCommand{
    49  		CharmCommandBase: base,
    50  	}
    51  	return cmd
    52  }
    53  
    54  var listCharmResourcesDoc = `
    55  This command will report the resources for a charm in the charm store.
    56  
    57  <charm> can be a charm URL, or an unambiguously condensed form of it,
    58  just like the deploy command. So the following forms will be accepted:
    59  
    60  For cs:trusty/mysql
    61    mysql
    62    trusty/mysql
    63  
    64  For cs:~user/trusty/mysql
    65    cs:~user/mysql
    66  
    67  Where the series is not supplied, the series from your local host is used.
    68  Thus the above examples imply that the local series is trusty.
    69  `
    70  
    71  // Info implements cmd.Command.
    72  func (c *ListCharmResourcesCommand) Info() *cmd.Info {
    73  	return &cmd.Info{
    74  		Name:    "list-resources",
    75  		Args:    "<charm>",
    76  		Purpose: "display the resources for a charm in the charm store",
    77  		Doc:     listCharmResourcesDoc,
    78  		Aliases: []string{"resources"},
    79  	}
    80  }
    81  
    82  // SetFlags implements cmd.Command.
    83  func (c *ListCharmResourcesCommand) SetFlags(f *gnuflag.FlagSet) {
    84  	defaultFormat := "tabular"
    85  	c.out.AddFlags(f, defaultFormat, map[string]cmd.Formatter{
    86  		"tabular": FormatCharmTabular,
    87  		"yaml":    cmd.FormatYaml,
    88  		"json":    cmd.FormatJson,
    89  	})
    90  	f.StringVar(&c.channel, "channel", "stable", "the charmstore channel of the charm")
    91  }
    92  
    93  // Init implements cmd.Command.
    94  func (c *ListCharmResourcesCommand) Init(args []string) error {
    95  	if len(args) == 0 {
    96  		return errors.New("missing charm")
    97  	}
    98  	c.charm = args[0]
    99  
   100  	if err := cmd.CheckEmpty(args[1:]); err != nil {
   101  		return errors.Trace(err)
   102  	}
   103  
   104  	return nil
   105  }
   106  
   107  // Run implements cmd.Command.
   108  func (c *ListCharmResourcesCommand) Run(ctx *cmd.Context) error {
   109  	// TODO(ericsnow) Adjust this to the charm store.
   110  
   111  	apiclient, err := c.Connect(ctx)
   112  	if err != nil {
   113  		// TODO(ericsnow) Return a more user-friendly error?
   114  		return errors.Trace(err)
   115  	}
   116  	defer apiclient.Close()
   117  
   118  	charmURLs, err := resolveCharms([]string{c.charm})
   119  	if err != nil {
   120  		return errors.Trace(err)
   121  	}
   122  
   123  	charms := make([]charmstore.CharmID, len(charmURLs))
   124  	for i, id := range charmURLs {
   125  		charms[i] = charmstore.CharmID{URL: id, Channel: csparams.Channel(c.channel)}
   126  	}
   127  
   128  	resources, err := apiclient.ListResources(charms)
   129  	if err != nil {
   130  		return errors.Trace(err)
   131  	}
   132  	if len(resources) != 1 {
   133  		return errors.New("got bad data from charm store")
   134  	}
   135  
   136  	// Note that we do not worry about c.CompatVersion
   137  	// for show-charm-resources...
   138  	formatter := newCharmResourcesFormatter(resources[0])
   139  	formatted := formatter.format()
   140  	return c.out.Write(ctx, formatted)
   141  }
   142  
   143  func resolveCharms(charms []string) ([]*charm.URL, error) {
   144  	var charmURLs []*charm.URL
   145  	for _, raw := range charms {
   146  		charmURL, err := resolveCharm(raw)
   147  		if err != nil {
   148  			return nil, errors.Trace(err)
   149  		}
   150  		charmURLs = append(charmURLs, charmURL)
   151  	}
   152  	return charmURLs, nil
   153  }
   154  
   155  func resolveCharm(raw string) (*charm.URL, error) {
   156  	charmURL, err := charm.ParseURL(raw)
   157  	if err != nil {
   158  		return charmURL, errors.Trace(err)
   159  	}
   160  
   161  	if charmURL.Series == "bundle" {
   162  		return charmURL, errors.Errorf("charm bundles are not supported")
   163  	}
   164  
   165  	return charmURL, nil
   166  }