/
modeldestroy.go
161 lines (147 loc) · 4.25 KB
/
modeldestroy.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
// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package common
import (
"time"
"github.com/juju/clock"
"github.com/juju/errors"
"github.com/juju/juju/apiserver/facades/agent/metricsender"
"github.com/juju/juju/core/status"
"github.com/juju/juju/state"
stateerrors "github.com/juju/juju/state/errors"
)
var sendMetrics = func(st metricsender.ModelBackend) error {
ccfg, err := st.ControllerConfig()
if err != nil {
return errors.Annotate(err, "failed to get controller config")
}
meteringURL := ccfg.MeteringURL()
cfg, err := st.ModelConfig()
if err != nil {
return errors.Annotatef(err, "failed to get model config for %s", st.ModelTag())
}
err = metricsender.SendMetrics(
st,
metricsender.DefaultSenderFactory()(meteringURL),
clock.WallClock,
metricsender.DefaultMaxBatchesPerSend(),
cfg.TransmitVendorMetrics(),
)
return errors.Trace(err)
}
// DestroyController sets the controller model to Dying and, if requested,
// schedules cleanups so that all of the hosted models are destroyed, or
// otherwise returns an error indicating that there are hosted models
// remaining.
func DestroyController(
st ModelManagerBackend,
destroyHostedModels bool,
destroyStorage *bool,
force *bool,
maxWait *time.Duration,
modelTimeout *time.Duration,
) error {
modelTag := st.ModelTag()
controllerModelTag := st.ControllerModelTag()
if modelTag != controllerModelTag {
return errors.Errorf(
"expected state for controller model UUID %v, got %v",
controllerModelTag.Id(),
modelTag.Id(),
)
}
if destroyHostedModels {
uuids, err := st.AllModelUUIDs()
if err != nil {
return errors.Trace(err)
}
for _, uuid := range uuids {
modelSt, release, err := st.GetBackend(uuid)
if err != nil {
if errors.IsNotFound(err) {
// Model is already in the process of being destroyed.
continue
}
return errors.Trace(err)
}
defer release()
check := NewBlockChecker(modelSt)
if err = check.DestroyAllowed(); err != nil {
return errors.Trace(err)
}
err = sendMetrics(modelSt)
if err != nil {
logger.Errorf("failed to send leftover metrics: %v", err)
}
}
}
return destroyModel(st, state.DestroyModelParams{
DestroyHostedModels: destroyHostedModels,
DestroyStorage: destroyStorage,
Force: force,
MaxWait: MaxWait(maxWait),
Timeout: modelTimeout,
})
}
// DestroyModel sets the model to Dying, such that the model's resources will
// be destroyed and the model removed from the controller.
func DestroyModel(
st ModelManagerBackend,
destroyStorage *bool,
force *bool,
maxWait *time.Duration,
timeout *time.Duration,
) error {
return destroyModel(st, state.DestroyModelParams{
DestroyStorage: destroyStorage,
Force: force,
MaxWait: MaxWait(maxWait),
Timeout: timeout,
})
}
func destroyModel(st ModelManagerBackend, args state.DestroyModelParams) error {
check := NewBlockChecker(st)
if err := check.DestroyAllowed(); err != nil {
return errors.Trace(err)
}
model, err := st.Model()
if err != nil {
return errors.Trace(err)
}
notForcing := args.Force == nil || !*args.Force
if notForcing {
// If model status is suspended, then model's cloud credential is invalid.
modelStatus, err := model.Status()
if err != nil {
return errors.Trace(err)
}
if modelStatus.Status == status.Suspended {
return errors.Errorf("invalid cloud credential, use --force")
}
}
if err := model.Destroy(args); err != nil {
if notForcing {
return errors.Trace(err)
}
logger.Warningf("failed destroying model %v: %v", model.UUID(), err)
if err := filterNonCriticalErrorForForce(err); err != nil {
return errors.Trace(err)
}
}
err = sendMetrics(st)
if err != nil {
logger.Errorf("failed to send leftover metrics: %v", err)
}
// Return to the caller. If it's the CLI, it will finish up by calling the
// provider's Destroy method, which will destroy the controllers, any
// straggler instances, and other provider-specific resources. Once all
// resources are torn down, the Undertaker worker handles the removal of
// the model.
return nil
}
func filterNonCriticalErrorForForce(err error) error {
if errors.Is(err, stateerrors.PersistentStorageError) {
return err
}
return nil
}