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 }