github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/names"
    10  	"launchpad.net/gnuflag"
    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 services 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 service
    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:    "list-resources",
    52  		Aliases: []string{"resources"},
    53  		Args:    "service-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  service or unit in your model.  When run for a service, 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  	const defaultFlag = "tabular"
    66  	c.out.AddFlags(f, defaultFlag, map[string]cmd.Formatter{
    67  		defaultFlag: FormatSvcTabular,
    68  		"yaml":      cmd.FormatYaml,
    69  		"json":      cmd.FormatJson,
    70  	})
    71  
    72  	f.BoolVar(&c.details, "details", false, "show detailed information about resources used by each unit.")
    73  }
    74  
    75  // Init implements cmd.Command.Init. It will return an error satisfying
    76  // errors.BadRequest if you give it an incorrect number of arguments.
    77  func (c *ShowServiceCommand) Init(args []string) error {
    78  	if len(args) == 0 {
    79  		return errors.NewBadRequest(nil, "missing service name")
    80  	}
    81  	c.target = args[0]
    82  	if err := cmd.CheckEmpty(args[1:]); err != nil {
    83  		return errors.NewBadRequest(err, "")
    84  	}
    85  	return nil
    86  }
    87  
    88  // Run implements cmd.Command.Run.
    89  func (c *ShowServiceCommand) Run(ctx *cmd.Context) error {
    90  	apiclient, err := c.deps.NewClient(c)
    91  	if err != nil {
    92  		return errors.Annotatef(err, "can't connect to %s", c.ConnectionName())
    93  	}
    94  	defer apiclient.Close()
    95  
    96  	var unit string
    97  	var service string
    98  	if names.IsValidService(c.target) {
    99  		service = c.target
   100  	} else {
   101  		service, err = names.UnitService(c.target)
   102  		if err != nil {
   103  			return errors.Errorf("%q is neither a service nor a unit", c.target)
   104  		}
   105  		unit = c.target
   106  	}
   107  
   108  	vals, err := apiclient.ListResources([]string{service})
   109  	if err != nil {
   110  		return errors.Trace(err)
   111  	}
   112  	if len(vals) != 1 {
   113  		return errors.Errorf("bad data returned from server")
   114  	}
   115  	v := vals[0]
   116  	if unit == "" {
   117  		return c.formatServiceResources(ctx, v)
   118  	}
   119  	return c.formatUnitResources(ctx, unit, service, v)
   120  }
   121  
   122  func (c *ShowServiceCommand) formatServiceResources(ctx *cmd.Context, sr resource.ServiceResources) error {
   123  	if c.details {
   124  		formatted, err := FormatServiceDetails(sr)
   125  		if err != nil {
   126  			return errors.Trace(err)
   127  		}
   128  
   129  		return c.out.Write(ctx, formatted)
   130  	}
   131  
   132  	formatted, err := formatServiceResources(sr)
   133  	if err != nil {
   134  		return errors.Trace(err)
   135  	}
   136  	return c.out.Write(ctx, formatted)
   137  }
   138  
   139  func (c *ShowServiceCommand) formatUnitResources(ctx *cmd.Context, unit, service string, sr resource.ServiceResources) error {
   140  	if c.details {
   141  		formatted, err := detailedResources(unit, sr)
   142  		if err != nil {
   143  			return errors.Trace(err)
   144  		}
   145  		return c.out.Write(ctx, FormattedUnitDetails(formatted))
   146  	}
   147  
   148  	resources, err := unitResources(unit, service, sr)
   149  	if err != nil {
   150  		return errors.Trace(err)
   151  	}
   152  	res := make([]FormattedUnitResource, len(resources))
   153  
   154  	for i, r := range resources {
   155  		res[i] = FormattedUnitResource(FormatSvcResource(r))
   156  	}
   157  
   158  	return c.out.Write(ctx, res)
   159  
   160  }
   161  
   162  func unitResources(unit, service string, v resource.ServiceResources) ([]resource.Resource, error) {
   163  	for _, res := range v.UnitResources {
   164  		if res.Tag.Id() == unit {
   165  			return res.Resources, nil
   166  		}
   167  	}
   168  	// TODO(natefinch): we need to differentiate between a unit with no
   169  	// resources and a unit that doesn't exist. This requires a serverside
   170  	// change.
   171  	return nil, nil
   172  }