forked from juju/juju
-
Notifications
You must be signed in to change notification settings - Fork 0
/
remove.go
231 lines (195 loc) · 7.04 KB
/
remove.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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package space
import (
"fmt"
"strings"
"github.com/juju/cmd/v3"
"github.com/juju/errors"
"github.com/juju/gnuflag"
"github.com/juju/names/v4"
"github.com/DavinZhang/juju/apiserver/params"
jujucmd "github.com/DavinZhang/juju/cmd"
"github.com/DavinZhang/juju/cmd/modelcmd"
)
// NewRemoveCommand returns a command used to remove a space.
func NewRemoveCommand() modelcmd.ModelCommand {
return modelcmd.Wrap(&RemoveCommand{})
}
// RemoveCommand calls the API to remove an existing network space.
type RemoveCommand struct {
SpaceCommandBase
name string
assumeYes bool
force bool
}
const removeCommandDoc = `
Removes an existing Juju network space with the given name. Any subnets
associated with the space will be transferred to the default space.
The command will fail if existing constraints, bindings or controller settings are bound to the given space.
If the --force option is specified, the space will be deleted even if there are existing bindings, constraints or settings.
Examples:
Remove a space by name:
juju remove-space db-space
Remove a space by name with force, without need for confirmation:
juju remove-space db-space --force -y
See also:
add-space
list-spaces
reload-spaces
rename-space
show-space
`
var removeSpaceMsgNoBounds = `
WARNING! This command will remove the space.
Safe removal possible. No constraints, bindings or controller config found with dependency on the given space.
Continue [y/N]? `[1:]
// Info is defined on the cmd.Command interface.
func (c *RemoveCommand) Info() *cmd.Info {
return jujucmd.Info(&cmd.Info{
Name: "remove-space",
Args: "<name>",
Purpose: "Remove a network space.",
Doc: strings.TrimSpace(removeCommandDoc),
})
}
// SetFlags implements Command.SetFlags.
func (c *RemoveCommand) SetFlags(f *gnuflag.FlagSet) {
c.SpaceCommandBase.SetFlags(f)
f.BoolVar(&c.force, "force", false, "remove the offer as well as any relations to the offer")
f.BoolVar(&c.assumeYes, "y", false, "Do not prompt for confirmation")
f.BoolVar(&c.assumeYes, "yes", false, "")
}
// Init is defined on the cmd.Command interface. It checks the
// arguments for sanity and sets up the command to run.
func (c *RemoveCommand) Init(args []string) (err error) {
defer errors.DeferredAnnotatef(&err, "invalid arguments specified")
// Validate given name.
if len(args) == 0 {
return errors.New("space name is required")
}
givenName := args[0]
if !names.IsValidSpace(givenName) {
return errors.Errorf("%q is not a valid space name", givenName)
}
c.name = givenName
return cmd.CheckEmpty(args[1:])
}
// Run implements Command.Run.
func (c *RemoveCommand) Run(ctx *cmd.Context) error {
controllerName, err := c.ControllerName()
if err != nil {
return errors.Trace(err)
}
store := c.ClientStore()
currentModel, err := store.CurrentModel(controllerName)
if err != nil {
return errors.Trace(err)
}
return c.RunWithSpaceAPI(ctx, func(api SpaceAPI, ctx *cmd.Context) error {
if !c.assumeYes && c.force {
if err := c.handleForceOption(api, currentModel, ctx); err != nil {
return errors.Annotatef(err, "cannot remove space %q", c.name)
}
}
space, err := api.RemoveSpace(c.name, c.force, false)
if err != nil {
return errors.Annotatef(err, "cannot remove space %q", c.name)
}
result, err := removeSpaceFromResult(c.name, space)
if err != nil {
return errors.Annotatef(err, "failed to parse space %q", c.name)
}
errorList := buildRemoveErrorList(result, currentModel)
if len(errorList) > 0 {
return errors.Errorf("Cannot remove space %q\n\n%s\n\nUse --force to remove space\n", c.name, strings.Join(errorList, "\n"))
}
ctx.Infof("removed space %q", c.name)
return nil
})
}
func (c *RemoveCommand) handleForceOption(api SpaceAPI, currentModel string, ctx *cmd.Context) error {
space, err := api.RemoveSpace(c.name, false, true)
if err != nil {
return errors.Trace(err)
}
result, err := removeSpaceFromResult(c.name, space)
if err != nil {
return errors.Annotatef(err, "failed to parse space %q", c.name)
}
errorList := buildRemoveErrorList(result, currentModel)
if len(errorList) == 0 {
fmt.Fprintf(ctx.Stdout, removeSpaceMsgNoBounds)
} else {
removeSpaceMsg := fmt.Sprintf(""+
"WARNING! This command will remove the space"+
" with the following existing boundaries:"+
"\n\n%v\n\n\n"+
"Continue [y/N]?", strings.Join(errorList, "\n"))
fmt.Fprintf(ctx.Stdout, removeSpaceMsg)
}
if err := jujucmd.UserConfirmYes(ctx); err != nil {
return errors.Annotate(err, "space removal")
}
return nil
}
func removeSpaceFromResult(name string, result params.RemoveSpaceResult) (RemoveSpace, error) {
constraints, err := convertEntitiesToStringAndSkipModel(result.Constraints)
if err != nil {
return RemoveSpace{}, err
}
hasModel, err := hasModelConstraint(result.Constraints)
if err != nil {
return RemoveSpace{}, err
}
bindings, err := convertEntitiesToStringAndSkipModel(result.Bindings)
if err != nil {
return RemoveSpace{}, err
}
return RemoveSpace{
HasModelConstraint: hasModel,
Space: name,
Constraints: constraints,
Bindings: bindings,
ControllerConfig: result.ControllerSettings,
}, nil
}
func buildRemoveErrorList(removeSpace RemoveSpace, currentModel string) []string {
constraints := removeSpace.Constraints
bindings := removeSpace.Bindings
config := removeSpace.ControllerConfig
spaceName := removeSpace.Space
var list []string
var msg string
if len(removeSpace.Constraints) > 0 {
msg = "- %q is used as a constraint on: %v"
list = append(list, fmt.Sprintf(msg, spaceName, strings.Join(constraints, ", ")))
if removeSpace.HasModelConstraint {
msg = "- %q is used as a model constraint: %v"
list = append(list, fmt.Sprintf(msg, spaceName, currentModel))
}
}
if len(removeSpace.Bindings) > 0 {
msg = "- %q is used as a binding on: %v"
list = append(list, fmt.Sprintf(msg, spaceName, strings.Join(bindings, ", ")))
}
if len(removeSpace.ControllerConfig) > 0 {
msg = "- %q is used for controller config(s): %v"
list = append(list, fmt.Sprintf(msg, spaceName, strings.Join(config, ", ")))
}
return list
}
// RemoveSpace represents space information why a space could not be removed.
type RemoveSpace struct {
// The space which cannot be removed. Only with --force.
Space string `json:"space" yaml:"space"`
// HasModelConstraint is the model constraint.
HasModelConstraint bool `json:"has-model-constraint" yaml:"has-model-constraint"`
// Constraints are the constraints which blocks the remove. Blocking Constraints are: Application.
Constraints []string `json:"constraints,omitempty" yaml:"constraints,omitempty"`
// Bindings are the application bindings which blocks the remove.
Bindings []string `json:"bindings,omitempty" yaml:"bindings,omitempty"`
// ControllerConfig are the config settings of the controller model which are using the space.
// This is only valid if the current model is a controller model.
ControllerConfig []string `json:"controller-settings,omitempty" yaml:"controller-settings,omitempty"`
}