/
remove.go
189 lines (164 loc) · 4.88 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
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package storage
import (
"time"
"github.com/juju/cmd/v3"
"github.com/juju/errors"
"github.com/juju/gnuflag"
jujucmd "github.com/juju/juju/cmd"
"github.com/juju/juju/cmd/juju/common"
"github.com/juju/juju/cmd/modelcmd"
"github.com/juju/juju/core/model"
"github.com/juju/juju/rpc/params"
)
// NewRemoveStorageCommandWithAPI returns a command
// used to remove storage from the model.
func NewRemoveStorageCommandWithAPI() cmd.Command {
command := &removeStorageCommand{}
command.newStorageRemoverCloser = func() (StorageRemoverCloser, error) {
return command.NewStorageAPI()
}
return modelcmd.Wrap(command)
}
const (
removeStorageCommandDoc = `
Removes storage from the model. Specify one or more
storage IDs, as output by "juju storage".
By default, remove-storage will fail if the storage
is attached to any units. To override this behaviour,
you can use "juju remove-storage --force".
Note: forced detach is not available on container models.
`
removeStorageCommandExamples = `
Remove the detached storage pgdata/0:
juju remove-storage pgdata/0
Remove the possibly attached storage pgdata/0:
juju remove-storage --force pgdata/0
Remove the storage pgdata/0, without destroying
the corresponding cloud storage:
juju remove-storage --no-destroy pgdata/0
`
removeStorageCommandArgs = `<storage> [<storage> ...]`
)
type removeStorageCommand struct {
StorageCommandBase
newStorageRemoverCloser NewStorageRemoverCloserFunc
storageIds []string
force bool
noDestroy bool
NoWait bool
modelType model.ModelType
fs *gnuflag.FlagSet
}
// Info implements Command.Info.
func (c *removeStorageCommand) Info() *cmd.Info {
return jujucmd.Info(&cmd.Info{
Name: "remove-storage",
Purpose: "Removes storage from the model.",
Doc: removeStorageCommandDoc,
Args: removeStorageCommandArgs,
Examples: removeStorageCommandExamples,
})
}
// SetFlags implements Command.SetFlags.
func (c *removeStorageCommand) SetFlags(f *gnuflag.FlagSet) {
c.StorageCommandBase.SetFlags(f)
f.BoolVar(&c.force, "force", false, "Remove storage even if it is currently attached")
f.BoolVar(&c.noDestroy, "no-destroy", false, "Remove the storage without destroying it")
c.fs = f
}
// Init implements Command.Init.
func (c *removeStorageCommand) Init(args []string) error {
if len(args) < 1 {
return errors.New("remove-storage requires at least one storage ID")
}
var err error
if c.modelType, err = c.ModelType(); err != nil {
return errors.Trace(err)
}
if c.modelType == model.CAAS && c.force {
return errors.NotSupportedf("forced detachment of storage on container models")
}
c.storageIds = args
return nil
}
// Run implements Command.Run.
func (c *removeStorageCommand) Run(ctx *cmd.Context) error {
noWaitSet := false
forceSet := false
c.fs.Visit(func(flag *gnuflag.Flag) {
if flag.Name == "no-wait" {
noWaitSet = true
} else if flag.Name == "force" {
forceSet = true
}
})
if !forceSet && noWaitSet {
return errors.NotValidf("--no-wait without --force")
}
var maxWait *time.Duration
if c.force {
if c.NoWait {
zeroSec := 0 * time.Second
maxWait = &zeroSec
}
}
remover, err := c.newStorageRemoverCloser()
if err != nil {
return errors.Trace(err)
}
defer remover.Close()
destroyAttachments := c.force
destroyStorage := !c.noDestroy
results, err := remover.Remove(c.storageIds, destroyAttachments, destroyStorage, &c.force, maxWait)
if err != nil {
if params.IsCodeUnauthorized(err) {
common.PermissionsMessage(ctx.Stderr, "remove storage")
}
return err
}
for i, result := range results {
if result.Error == nil {
ctx.Infof("removing %s", c.storageIds[i])
}
}
anyFailed := false
anyAttached := false
for i, result := range results {
if result.Error != nil {
ctx.Infof("failed to remove %s: %s", c.storageIds[i], result.Error)
if params.IsCodeStorageAttached(result.Error) {
anyAttached = true
}
anyFailed = true
}
}
if anyAttached && c.modelType != model.CAAS {
ctx.Infof(`
Use the --force option to remove attached storage, or use
"juju detach-storage" to explicitly detach the storage
before removing.`)
}
if anyFailed {
return cmd.ErrSilent
}
return nil
}
// NewStorageRemoverCloserFunc is the type of a function that returns an
// StorageRemoverCloser.
type NewStorageRemoverCloserFunc func() (StorageRemoverCloser, error)
// StorageRemoverCloser extends StorageRemover with a Closer method.
type StorageRemoverCloser interface {
StorageRemover
Close() error
}
// StorageRemover defines an interface for destroying storage instances
// with the specified IDs.
type StorageRemover interface {
Remove(
storageIds []string,
destroyAttachments, destroyStorage bool,
force *bool, maxWait *time.Duration,
) ([]params.ErrorResult, error)
}