-
Notifications
You must be signed in to change notification settings - Fork 494
/
show.go
206 lines (178 loc) · 5.85 KB
/
show.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright 2019 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package application
import (
"strings"
"github.com/juju/cmd"
"github.com/juju/errors"
"github.com/juju/gnuflag"
"gopkg.in/juju/names.v3"
"github.com/juju/juju/api/application"
"github.com/juju/juju/apiserver/params"
jujucmd "github.com/juju/juju/cmd"
"github.com/juju/juju/cmd/modelcmd"
"github.com/juju/juju/core/constraints"
)
const showApplicationDoc = `
The command takes deployed application names or aliases as an argument.
The command does an exact search. It does not support wildcards.
Examples:
$ juju show-application mysql
$ juju show-application mysql wordpress
$ juju show-application myapplication
where "myapplication" is the application name alias, see "juju help deploy" for more information
`
// NewShowApplicationCommand returns a command that displays applications info.
func NewShowApplicationCommand() cmd.Command {
s := &showApplicationCommand{}
s.newAPIFunc = func() (ApplicationsInfoAPI, error) {
return s.newApplicationAPI()
}
return modelcmd.Wrap(s)
}
// showApplicationCommand displays application information.
type showApplicationCommand struct {
modelcmd.ModelCommandBase
out cmd.Output
apps []string
newAPIFunc func() (ApplicationsInfoAPI, error)
}
// Info implements Command.Info.
func (c *showApplicationCommand) Info() *cmd.Info {
showCmd := &cmd.Info{
Name: "show-application",
Args: "<application name or alias>",
Purpose: "Displays information about an application.",
Doc: showApplicationDoc,
}
return jujucmd.Info(showCmd)
}
// Init implements Command.Init.
func (c *showApplicationCommand) Init(args []string) error {
if len(args) < 1 {
return errors.Errorf("an application name must be supplied")
}
c.apps = args
var invalid []string
for _, one := range c.apps {
if !names.IsValidApplication(one) {
invalid = append(invalid, one)
}
}
if len(invalid) == 0 {
return nil
}
plural := "s"
if len(invalid) == 1 {
plural = ""
}
return errors.NotValidf(`application name%v %v`, plural, strings.Join(invalid, `, `))
}
// SetFlags implements Command.SetFlags.
func (c *showApplicationCommand) SetFlags(f *gnuflag.FlagSet) {
c.ModelCommandBase.SetFlags(f)
c.out.AddFlags(f, "yaml", cmd.DefaultFormatters.Formatters())
}
// ApplicationsInfoAPI defines the API methods that show-application command uses.
type ApplicationsInfoAPI interface {
Close() error
BestAPIVersion() int
ApplicationsInfo([]names.ApplicationTag) ([]params.ApplicationInfoResult, error)
}
func (c *showApplicationCommand) newApplicationAPI() (ApplicationsInfoAPI, error) {
root, err := c.NewAPIRoot()
if err != nil {
return nil, errors.Trace(err)
}
return application.NewClient(root), nil
}
func (c *showApplicationCommand) Run(ctx *cmd.Context) error {
client, err := c.newAPIFunc()
if err != nil {
return err
}
defer client.Close()
if v := client.BestAPIVersion(); v < 9 {
// old client does not support showing applications.
return errors.NotSupportedf("show applications on API server version %v", v)
}
tags, err := c.getApplicationTags()
if err != nil {
return err
}
results, err := client.ApplicationsInfo(tags)
if err != nil {
return errors.Trace(err)
}
var errs params.ErrorResults
var valid []params.ApplicationResult
for _, result := range results {
if result.Error != nil {
errs.Results = append(errs.Results, params.ErrorResult{result.Error})
continue
}
valid = append(valid, *result.Result)
}
if len(errs.Results) > 0 {
return errs.Combine()
}
output, err := formatApplicationInfos(valid)
if err != nil {
return err
}
return c.out.Write(ctx, output)
}
func (c *showApplicationCommand) getApplicationTags() ([]names.ApplicationTag, error) {
tags := make([]names.ApplicationTag, len(c.apps))
for i, one := range c.apps {
if !names.IsValidApplication(one) {
return nil, errors.Errorf("invalid application name %v", one)
}
tags[i] = names.NewApplicationTag(one)
}
return tags, nil
}
// formatApplicationInfos takes a set of params.ApplicationInfo and
// creates a mapping from storage ID application name to application info.
func formatApplicationInfos(all []params.ApplicationResult) (map[string]ApplicationInfo, error) {
if len(all) == 0 {
return nil, nil
}
output := make(map[string]ApplicationInfo)
for _, one := range all {
tag, info, err := createApplicationInfo(one)
if err != nil {
return nil, errors.Trace(err)
}
output[tag.Name] = info
}
return output, nil
}
// ApplicationInfo defines the serialization behaviour of the application information.
type ApplicationInfo struct {
Charm string `yaml:"charm,omitempty" json:"charm,omitempty"`
Series string `yaml:"series,omitempty" json:"series,omitempty"`
Channel string `yaml:"channel,omitempty" json:"channel,omitempty"`
Constraints constraints.Value `yaml:"constraints,omitempty" json:"constraints,omitempty"`
Principal bool `yaml:"principal" json:"principal"`
Exposed bool `yaml:"exposed" json:"exposed"`
Remote bool `yaml:"remote" json:"remote"`
EndpointBindings map[string]string `yaml:"endpoint-bindings,omitempty" json:"endpoint-bindings,omitempty"`
}
func createApplicationInfo(details params.ApplicationResult) (names.ApplicationTag, ApplicationInfo, error) {
tag, err := names.ParseApplicationTag(details.Tag)
if err != nil {
return names.ApplicationTag{}, ApplicationInfo{}, errors.Trace(err)
}
info := ApplicationInfo{
Charm: details.Charm,
Series: details.Series,
Channel: details.Channel,
Constraints: details.Constraints,
Principal: details.Principal,
Exposed: details.Exposed,
Remote: details.Remote,
EndpointBindings: details.EndpointBindings,
}
return tag, info, nil
}