github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/resource/cmd/show_service.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  	"github.com/juju/gnuflag"
    10  	"gopkg.in/juju/names.v2"
    11  
    12  	"github.com/juju/juju/cmd/modelcmd"
    13  	"github.com/juju/juju/resource"
    14  )
    15  
    16  // ShowServiceClient has the API client methods needed by ShowServiceCommand.
    17  type ShowServiceClient interface {
    18  	// ListResources returns info about resources for applications in the model.
    19  	ListResources(services []string) ([]resource.ServiceResources, error)
    20  	// Close closes the connection.
    21  	Close() error
    22  }
    23  
    24  // ShowServiceDeps is a type that contains external functions that ShowService
    25  // depends on to function.
    26  type ShowServiceDeps struct {
    27  	// NewClient returns the value that wraps the API for showing application
    28  	// resources from the server.
    29  	NewClient func(*ShowServiceCommand) (ShowServiceClient, error)
    30  }
    31  
    32  // ShowServiceCommand implements the upload command.
    33  type ShowServiceCommand struct {
    34  	modelcmd.ModelCommandBase
    35  
    36  	details bool
    37  	deps    ShowServiceDeps
    38  	out     cmd.Output
    39  	target  string
    40  }
    41  
    42  // NewShowServiceCommand returns a new command that lists resources defined
    43  // by a charm.
    44  func NewShowServiceCommand(deps ShowServiceDeps) *ShowServiceCommand {
    45  	return &ShowServiceCommand{deps: deps}
    46  }
    47  
    48  // Info implements cmd.Command.Info.
    49  func (c *ShowServiceCommand) Info() *cmd.Info {
    50  	return &cmd.Info{
    51  		Name:    "resources",
    52  		Aliases: []string{"list-resources"},
    53  		Args:    "application-or-unit",
    54  		Purpose: "show the resources for a service or unit",
    55  		Doc: `
    56  This command shows the resources required by and those in use by an existing
    57  application or unit in your model.  When run for an application, it will also show any
    58  updates available for resources from the charmstore.
    59  `,
    60  	}
    61  }
    62  
    63  // SetFlags implements cmd.Command.SetFlags.
    64  func (c *ShowServiceCommand) SetFlags(f *gnuflag.FlagSet) {
    65  	c.ModelCommandBase.SetFlags(f)
    66  	const defaultFormat = "tabular"
    67  	c.out.AddFlags(f, defaultFormat, map[string]cmd.Formatter{
    68  		defaultFormat: FormatSvcTabular,
    69  		"yaml":        cmd.FormatYaml,
    70  		"json":        cmd.FormatJson,
    71  	})
    72  
    73  	f.BoolVar(&c.details, "details", false, "show detailed information about resources used by each unit.")
    74  }
    75  
    76  // Init implements cmd.Command.Init. It will return an error satisfying
    77  // errors.BadRequest if you give it an incorrect number of arguments.
    78  func (c *ShowServiceCommand) Init(args []string) error {
    79  	if len(args) == 0 {
    80  		return errors.NewBadRequest(nil, "missing application name")
    81  	}
    82  	c.target = args[0]
    83  	if err := cmd.CheckEmpty(args[1:]); err != nil {
    84  		return errors.NewBadRequest(err, "")
    85  	}
    86  	return nil
    87  }
    88  
    89  // Run implements cmd.Command.Run.
    90  func (c *ShowServiceCommand) Run(ctx *cmd.Context) error {
    91  	apiclient, err := c.deps.NewClient(c)
    92  	if err != nil {
    93  		return errors.Annotatef(err, "can't connect to %s", c.ConnectionName())
    94  	}
    95  	defer apiclient.Close()
    96  
    97  	var unit string
    98  	var service string
    99  	if names.IsValidApplication(c.target) {
   100  		service = c.target
   101  	} else {
   102  		service, err = names.UnitApplication(c.target)
   103  		if err != nil {
   104  			return errors.Errorf("%q is neither an application nor a unit", c.target)
   105  		}
   106  		unit = c.target
   107  	}
   108  
   109  	vals, err := apiclient.ListResources([]string{service})
   110  	if err != nil {
   111  		return errors.Trace(err)
   112  	}
   113  	if len(vals) != 1 {
   114  		return errors.Errorf("bad data returned from server")
   115  	}
   116  	v := vals[0]
   117  
   118  	if unit == "" {
   119  		return c.formatServiceResources(ctx, v)
   120  	}
   121  	return c.formatUnitResources(ctx, unit, service, v)
   122  }
   123  
   124  const noResources = "No resources to display."
   125  
   126  func (c *ShowServiceCommand) formatServiceResources(ctx *cmd.Context, sr resource.ServiceResources) error {
   127  	if c.details {
   128  		formatted, err := FormatServiceDetails(sr)
   129  		if err != nil {
   130  			return errors.Trace(err)
   131  		}
   132  		if len(formatted.Resources) == 0 && len(formatted.Updates) == 0 {
   133  			ctx.Infof(noResources)
   134  			return nil
   135  		}
   136  
   137  		return c.out.Write(ctx, formatted)
   138  	}
   139  
   140  	formatted, err := formatServiceResources(sr)
   141  	if err != nil {
   142  		return errors.Trace(err)
   143  	}
   144  	if len(formatted.Resources) == 0 && len(formatted.Updates) == 0 {
   145  		ctx.Infof(noResources)
   146  		return nil
   147  	}
   148  	return c.out.Write(ctx, formatted)
   149  }
   150  
   151  func (c *ShowServiceCommand) formatUnitResources(ctx *cmd.Context, unit, service string, sr resource.ServiceResources) error {
   152  	if len(sr.UnitResources) == 0 {
   153  		ctx.Infof(noResources)
   154  		return nil
   155  	}
   156  
   157  	if c.details {
   158  		formatted, err := detailedResources(unit, sr)
   159  		if err != nil {
   160  			return errors.Trace(err)
   161  		}
   162  		return c.out.Write(ctx, FormattedUnitDetails(formatted))
   163  	}
   164  
   165  	resources, err := unitResources(unit, service, sr)
   166  	if err != nil {
   167  		return errors.Trace(err)
   168  	}
   169  
   170  	res := make([]FormattedUnitResource, len(resources))
   171  
   172  	for i, r := range resources {
   173  		res[i] = FormattedUnitResource(FormatSvcResource(r))
   174  	}
   175  
   176  	return c.out.Write(ctx, res)
   177  
   178  }
   179  
   180  func unitResources(unit, service string, v resource.ServiceResources) ([]resource.Resource, error) {
   181  	for _, res := range v.UnitResources {
   182  		if res.Tag.Id() == unit {
   183  			return res.Resources, nil
   184  		}
   185  	}
   186  	// TODO(natefinch): we need to differentiate between a unit with no
   187  	// resources and a unit that doesn't exist. This requires a serverside
   188  	// change.
   189  	return nil, nil
   190  }