/
detach.go
162 lines (140 loc) · 4.08 KB
/
detach.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
// Copyright 2017 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/rpc/params"
)
// NewDetachStorageCommandWithAPI returns a command
// used to detach storage from application units.
func NewDetachStorageCommandWithAPI() cmd.Command {
command := &detachStorageCommand{}
command.newEntityDetacherCloser = func() (EntityDetacherCloser, error) {
return command.NewStorageAPI()
}
return modelcmd.Wrap(command)
}
// NewDetachStorageCommand returns a command used to
// detach storage from application units.
func NewDetachStorageCommand(new NewEntityDetacherCloserFunc) cmd.Command {
command := &detachStorageCommand{}
command.newEntityDetacherCloser = new
return modelcmd.Wrap(command)
}
const (
detachStorageCommandDoc = `
Detaches storage from units. Specify one or more unit/application storage IDs,
as output by "juju storage". The storage will remain in the model until it is
removed by an operator.
Detaching storage may fail but under some circumstances, Juju user may need
to force storage detachment despite operational errors.
`
detachStorageCommandExamples = `
juju detach-storage pgdata/0
juju detach-storage --force pgdata/0
`
detachStorageCommandArgs = `<storage> [<storage> ...]`
)
// detachStorageCommand detaches storage instances.
type detachStorageCommand struct {
StorageCommandBase
modelcmd.IAASOnlyCommand
newEntityDetacherCloser NewEntityDetacherCloserFunc
storageIds []string
Force bool
NoWait bool
fs *gnuflag.FlagSet
}
// Init implements Command.Init.
func (c *detachStorageCommand) Init(args []string) error {
if len(args) < 1 {
return errors.New("detach-storage requires at least one storage ID")
}
c.storageIds = args
return nil
}
// SetFlags implements Command.SetFlags.
func (c *detachStorageCommand) SetFlags(f *gnuflag.FlagSet) {
c.StorageCommandBase.SetFlags(f)
f.BoolVar(&c.Force, "force", false, "Forcefully detach storage")
c.fs = f
}
// Info implements Command.Info.
func (c *detachStorageCommand) Info() *cmd.Info {
return jujucmd.Info(&cmd.Info{
Name: "detach-storage",
Purpose: "Detaches storage from units.",
Doc: detachStorageCommandDoc,
Examples: detachStorageCommandExamples,
Args: detachStorageCommandArgs,
})
}
// Run implements Command.Run.
func (c *detachStorageCommand) 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
}
}
detacher, err := c.newEntityDetacherCloser()
if err != nil {
return errors.Trace(err)
}
defer detacher.Close()
results, err := detacher.Detach(c.storageIds, &c.Force, maxWait)
if err != nil {
if params.IsCodeUnauthorized(err) {
common.PermissionsMessage(ctx.Stderr, "detach storage")
}
return err
}
for i, result := range results {
if result.Error == nil {
ctx.Infof("detaching %s", c.storageIds[i])
}
}
anyFailed := false
for i, result := range results {
if result.Error != nil {
ctx.Infof("failed to detach %s: %s", c.storageIds[i], result.Error)
anyFailed = true
}
}
if anyFailed {
return cmd.ErrSilent
}
return nil
}
// NewEntityDetacherCloser is the type of a function that returns an
// EntityDetacherCloser.
type NewEntityDetacherCloserFunc func() (EntityDetacherCloser, error)
// EntityDetacherCloser extends EntityDetacher with a Closer method.
type EntityDetacherCloser interface {
EntityDetacher
Close() error
}
// EntityDetacher defines an interface for detaching storage with the
// specified IDs.
type EntityDetacher interface {
Detach([]string, *bool, *time.Duration) ([]params.ErrorResult, error)
}