1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
8
"github.com/juju/errors"
9
"gopkg.in/juju/names.v2"
10
"launchpad.net/gnuflag"
12
"github.com/juju/juju/cmd/modelcmd"
13
"github.com/juju/juju/resource"
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.
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)
32
// ShowServiceCommand implements the upload command.
33
type ShowServiceCommand struct {
34
modelcmd.ModelCommandBase
42
// NewShowServiceCommand returns a new command that lists resources defined
44
func NewShowServiceCommand(deps ShowServiceDeps) *ShowServiceCommand {
45
return &ShowServiceCommand{deps: deps}
48
// Info implements cmd.Command.Info.
49
func (c *ShowServiceCommand) Info() *cmd.Info {
52
Aliases: []string{"list-resources"},
53
Args: "application-or-unit",
54
Purpose: "show the resources for a service or unit",
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.
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,
72
f.BoolVar(&c.details, "details", false, "show detailed information about resources used by each unit.")
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 {
79
return errors.NewBadRequest(nil, "missing application name")
82
if err := cmd.CheckEmpty(args[1:]); err != nil {
83
return errors.NewBadRequest(err, "")
88
// Run implements cmd.Command.Run.
89
func (c *ShowServiceCommand) Run(ctx *cmd.Context) error {
90
apiclient, err := c.deps.NewClient(c)
92
return errors.Annotatef(err, "can't connect to %s", c.ConnectionName())
94
defer apiclient.Close()
98
if names.IsValidApplication(c.target) {
101
service, err = names.UnitApplication(c.target)
103
return errors.Errorf("%q is neither an application nor a unit", c.target)
108
vals, err := apiclient.ListResources([]string{service})
110
return errors.Trace(err)
113
return errors.Errorf("bad data returned from server")
117
return c.formatServiceResources(ctx, v)
119
return c.formatUnitResources(ctx, unit, service, v)
122
func (c *ShowServiceCommand) formatServiceResources(ctx *cmd.Context, sr resource.ServiceResources) error {
124
formatted, err := FormatServiceDetails(sr)
126
return errors.Trace(err)
129
return c.out.Write(ctx, formatted)
132
formatted, err := formatServiceResources(sr)
134
return errors.Trace(err)
136
return c.out.Write(ctx, formatted)
139
func (c *ShowServiceCommand) formatUnitResources(ctx *cmd.Context, unit, service string, sr resource.ServiceResources) error {
141
formatted, err := detailedResources(unit, sr)
143
return errors.Trace(err)
145
return c.out.Write(ctx, FormattedUnitDetails(formatted))
148
resources, err := unitResources(unit, service, sr)
150
return errors.Trace(err)
152
res := make([]FormattedUnitResource, len(resources))
154
for i, r := range resources {
155
res[i] = FormattedUnitResource(FormatSvcResource(r))
158
return c.out.Write(ctx, res)
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
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