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  }