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 }