1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
10
"github.com/juju/errors"
11
"github.com/juju/utils/clock"
12
"launchpad.net/gnuflag"
14
"github.com/juju/juju/apiserver/common"
15
"github.com/juju/juju/cmd/modelcmd"
16
"github.com/juju/juju/environs"
20
Forcibly destroy the specified controller. If the API server is accessible,
21
this command will attempt to destroy the controller model and all
22
hosted models and their resources.
24
If the API server is unreachable, the machines of the controller model
25
will be destroyed through the cloud provisioner. If there are additional
26
machines, including machines within hosted models, these machines will
27
not be destroyed and will never be reconnected to the Juju controller being
31
// NewKillCommand returns a command to kill a controller. Killing is a forceful
33
func NewKillCommand() cmd.Command {
34
// Even though this command is all about killing a controller we end up
35
// needing environment endpoints so we can fall back to the client destroy
36
// environment method. This shouldn't really matter in practice as the
37
// user trying to take down the controller will need to have access to the
38
// controller environment anyway.
39
return wrapKillCommand(&killCommand{}, nil, clock.WallClock)
42
// wrapKillCommand provides the common wrapping used by tests and
43
// the default NewKillCommand above.
44
func wrapKillCommand(kill *killCommand, apiOpen modelcmd.APIOpener, clock clock.Clock) cmd.Command {
46
apiOpen = modelcmd.OpenFunc(kill.JujuCommandBase.NewAPIRoot)
48
openStrategy := modelcmd.NewTimeoutOpener(apiOpen, clock, 10*time.Second)
49
return modelcmd.WrapController(
51
modelcmd.ControllerSkipFlags,
52
modelcmd.ControllerSkipDefault,
53
modelcmd.ControllerAPIOpener(openStrategy),
57
// killCommand kills the specified controller.
58
type killCommand struct {
62
// Info implements Command.Info.
63
func (c *killCommand) Info() *cmd.Info {
65
Name: "kill-controller",
66
Args: "<controller name>",
67
Purpose: "Forcibly terminate all machines and other associated resources for a Juju controller.",
72
// SetFlags implements Command.SetFlags.
73
func (c *killCommand) SetFlags(f *gnuflag.FlagSet) {
74
f.BoolVar(&c.assumeYes, "y", false, "Do not ask for confirmation")
75
f.BoolVar(&c.assumeYes, "yes", false, "")
78
// Init implements Command.Init.
79
func (c *killCommand) Init(args []string) error {
80
return c.destroyCommandBase.Init(args)
83
// Run implements Command.Run
84
func (c *killCommand) Run(ctx *cmd.Context) error {
85
controllerName := c.ControllerName()
86
store := c.ClientStore()
87
controllerDetails, err := store.ControllerByName(controllerName)
89
return errors.Annotate(err, "cannot read controller info")
93
if err = confirmDestruction(ctx, controllerName); err != nil {
98
// Attempt to connect to the API.
99
api, err := c.getControllerAPI()
103
case errors.Cause(err) == common.ErrPerm:
104
return errors.Annotate(err, "cannot destroy controller")
106
if errors.Cause(err) != modelcmd.ErrConnTimedOut {
107
logger.Debugf("unable to open api: %s", err)
109
ctx.Infof("Unable to open API: %s\n", err)
113
// Obtain controller environ so we can clean up afterwards.
114
controllerEnviron, err := c.getControllerEnviron(store, controllerName, api)
116
return errors.Annotate(err, "getting controller environ")
118
// If we were unable to connect to the API, just destroy the controller through
119
// the environs interface.
121
ctx.Infof("Unable to connect to the API server. Destroying through provider.")
122
return environs.Destroy(controllerName, controllerEnviron, store)
125
// Attempt to destroy the controller and all environments.
126
err = api.DestroyController(true)
128
ctx.Infof("Unable to destroy controller through the API: %s. Destroying through provider.", err)
129
return environs.Destroy(controllerName, controllerEnviron, store)
132
ctx.Infof("Destroying controller %q\nWaiting for resources to be reclaimed", controllerName)
134
updateStatus := newTimedStatusUpdater(ctx, api, controllerDetails.ControllerUUID)
135
for ctrStatus, envsStatus := updateStatus(0); hasUnDeadModels(envsStatus); ctrStatus, envsStatus = updateStatus(2 * time.Second) {
136
ctx.Infof(fmtCtrStatus(ctrStatus))
137
for _, envStatus := range envsStatus {
138
ctx.Verbosef(fmtModelStatus(envStatus))
142
ctx.Infof("All hosted models reclaimed, cleaning up controller machines")
144
return environs.Destroy(controllerName, controllerEnviron, store)