~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/juju/controller/kill.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package controller
 
5
 
 
6
import (
 
7
        "time"
 
8
 
 
9
        "github.com/juju/cmd"
 
10
        "github.com/juju/errors"
 
11
        "github.com/juju/utils/clock"
 
12
        "launchpad.net/gnuflag"
 
13
 
 
14
        "github.com/juju/juju/apiserver/common"
 
15
        "github.com/juju/juju/cmd/modelcmd"
 
16
        "github.com/juju/juju/environs"
 
17
)
 
18
 
 
19
const killDoc = `
 
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.
 
23
 
 
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
 
28
destroyed. 
 
29
`
 
30
 
 
31
// NewKillCommand returns a command to kill a controller. Killing is a forceful
 
32
// destroy.
 
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)
 
40
}
 
41
 
 
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 {
 
45
        if apiOpen == nil {
 
46
                apiOpen = modelcmd.OpenFunc(kill.JujuCommandBase.NewAPIRoot)
 
47
        }
 
48
        openStrategy := modelcmd.NewTimeoutOpener(apiOpen, clock, 10*time.Second)
 
49
        return modelcmd.WrapController(
 
50
                kill,
 
51
                modelcmd.ControllerSkipFlags,
 
52
                modelcmd.ControllerSkipDefault,
 
53
                modelcmd.ControllerAPIOpener(openStrategy),
 
54
        )
 
55
}
 
56
 
 
57
// killCommand kills the specified controller.
 
58
type killCommand struct {
 
59
        destroyCommandBase
 
60
}
 
61
 
 
62
// Info implements Command.Info.
 
63
func (c *killCommand) Info() *cmd.Info {
 
64
        return &cmd.Info{
 
65
                Name:    "kill-controller",
 
66
                Args:    "<controller name>",
 
67
                Purpose: "Forcibly terminate all machines and other associated resources for a Juju controller.",
 
68
                Doc:     killDoc,
 
69
        }
 
70
}
 
71
 
 
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, "")
 
76
}
 
77
 
 
78
// Init implements Command.Init.
 
79
func (c *killCommand) Init(args []string) error {
 
80
        return c.destroyCommandBase.Init(args)
 
81
}
 
82
 
 
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)
 
88
        if err != nil {
 
89
                return errors.Annotate(err, "cannot read controller info")
 
90
        }
 
91
 
 
92
        if !c.assumeYes {
 
93
                if err = confirmDestruction(ctx, controllerName); err != nil {
 
94
                        return err
 
95
                }
 
96
        }
 
97
 
 
98
        // Attempt to connect to the API.
 
99
        api, err := c.getControllerAPI()
 
100
        switch {
 
101
        case err == nil:
 
102
                defer api.Close()
 
103
        case errors.Cause(err) == common.ErrPerm:
 
104
                return errors.Annotate(err, "cannot destroy controller")
 
105
        default:
 
106
                if errors.Cause(err) != modelcmd.ErrConnTimedOut {
 
107
                        logger.Debugf("unable to open api: %s", err)
 
108
                }
 
109
                ctx.Infof("Unable to open API: %s\n", err)
 
110
                api = nil
 
111
        }
 
112
 
 
113
        // Obtain controller environ so we can clean up afterwards.
 
114
        controllerEnviron, err := c.getControllerEnviron(store, controllerName, api)
 
115
        if err != nil {
 
116
                return errors.Annotate(err, "getting controller environ")
 
117
        }
 
118
        // If we were unable to connect to the API, just destroy the controller through
 
119
        // the environs interface.
 
120
        if api == nil {
 
121
                ctx.Infof("Unable to connect to the API server. Destroying through provider.")
 
122
                return environs.Destroy(controllerName, controllerEnviron, store)
 
123
        }
 
124
 
 
125
        // Attempt to destroy the controller and all environments.
 
126
        err = api.DestroyController(true)
 
127
        if err != nil {
 
128
                ctx.Infof("Unable to destroy controller through the API: %s.  Destroying through provider.", err)
 
129
                return environs.Destroy(controllerName, controllerEnviron, store)
 
130
        }
 
131
 
 
132
        ctx.Infof("Destroying controller %q\nWaiting for resources to be reclaimed", controllerName)
 
133
 
 
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))
 
139
                }
 
140
        }
 
141
 
 
142
        ctx.Infof("All hosted models reclaimed, cleaning up controller machines")
 
143
 
 
144
        return environs.Destroy(controllerName, controllerEnviron, store)
 
145
}