-
Notifications
You must be signed in to change notification settings - Fork 1
/
destroy.go
executable file
·153 lines (142 loc) · 4.17 KB
/
destroy.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
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package client
import (
"fmt"
"strings"
"launchpad.net/juju-core/environs"
"launchpad.net/juju-core/instance"
"launchpad.net/juju-core/state"
)
// DestroyEnvironment destroys all services and non-manager machine
// instances in the environment.
func (c *Client) DestroyEnvironment() error {
// TODO(axw) 2013-08-30 bug 1218688
//
// There's a race here: a client might add a manual machine
// after another client checks. Destroy-environment will
// still fail, but the environment will be in a state where
// entities can only be destroyed. In the future we should
// introduce a method of preventing Environment.Destroy()
// from succeeding if machines have been added.
// First, check for manual machines. We bail out if there are any,
// to stop the user from prematurely hobbling the environment.
machines, err := c.api.state.AllMachines()
if err != nil {
return err
}
if err := checkManualMachines(machines); err != nil {
return err
}
// Set the environment to Dying, to lock out new machines and services.
// Environment.Destroy() also schedules a cleanup for existing services.
// Afterwards, refresh the machines in case any were added between the
// first check and the Environment.Destroy().
env, err := c.api.state.Environment()
if err != nil {
return err
}
if err = env.Destroy(); err != nil {
return err
}
machines, err = c.api.state.AllMachines()
if err != nil {
return err
}
// We must destroy instances server-side to support hosted Juju,
// as there's no CLI to fall back on. In that case, we only ever
// destroy non-state machines; we leave destroying state servers
// in non-hosted environments to the CLI, as otherwise the API
// server may get cut off.
if err := destroyInstances(c.api.state, machines); err != nil {
return err
}
// Make sure once again that there are no manually provisioned
// non-manager machines. This caters for the race between the
// first check and the Environment.Destroy().
if err := checkManualMachines(machines); err != nil {
return 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 state servers, any straggler instances, and
// other provider-specific resources.
return nil
}
// destroyInstances directly destroys all non-manager,
// non-manual machine instances.
func destroyInstances(st *state.State, machines []*state.Machine) error {
var ids []instance.Id
for _, m := range machines {
if m.IsManager() {
continue
}
manual, err := m.IsManual()
if manual {
continue
} else if err != nil {
return err
}
id, err := m.InstanceId()
if err != nil {
continue
}
ids = append(ids, id)
}
if len(ids) == 0 {
return nil
}
envcfg, err := st.EnvironConfig()
if err != nil {
return err
}
env, err := environs.New(envcfg)
if err != nil {
return err
}
// TODO(axw) 2013-12-12 #1260171
// Modify InstanceBroker.StopInstances to take
// a slice of IDs rather than Instances.
instances, err := env.Instances(ids)
switch err {
case nil:
default:
return err
case environs.ErrNoInstances:
return nil
case environs.ErrPartialInstances:
var nonNilInstances []instance.Instance
for i, inst := range instances {
if inst == nil {
logger.Warningf("unknown instance ID: %v", ids[i])
continue
}
nonNilInstances = append(nonNilInstances, inst)
}
instances = nonNilInstances
}
return env.StopInstances(instances)
}
// checkManualMachines checks if any of the machines in the slice were
// manually provisioned, and are non-manager machines. These machines
// must (currently) be manually destroyed via destroy-machine before
// destroy-environment can successfully complete.
func checkManualMachines(machines []*state.Machine) error {
var ids []string
for _, m := range machines {
if m.IsManager() {
continue
}
manual, err := m.IsManual()
if err != nil {
return err
}
if manual {
ids = append(ids, m.Id())
}
}
if len(ids) > 0 {
return fmt.Errorf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", strings.Join(ids, " "))
}
return nil
}