github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/controller/kill.go (about) 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 119 // If we were unable to connect to the API, just destroy the controller through 120 // the environs interface. 121 if api == nil { 122 ctx.Infof("Unable to connect to the API server. Destroying through provider.") 123 return environs.Destroy(controllerName, controllerEnviron, store) 124 } 125 126 // Attempt to destroy the controller and all environments. 127 err = api.DestroyController(true) 128 if err != nil { 129 ctx.Infof("Unable to destroy controller through the API: %s. Destroying through provider.", err) 130 return environs.Destroy(controllerName, controllerEnviron, store) 131 } 132 133 ctx.Infof("Destroying controller %q\nWaiting for resources to be reclaimed", controllerName) 134 135 updateStatus := newTimedStatusUpdater(ctx, api, controllerDetails.ControllerUUID) 136 for ctrStatus, envsStatus := updateStatus(0); hasUnDeadModels(envsStatus); ctrStatus, envsStatus = updateStatus(2 * time.Second) { 137 ctx.Infof(fmtCtrStatus(ctrStatus)) 138 for _, envStatus := range envsStatus { 139 ctx.Verbosef(fmtModelStatus(envStatus)) 140 } 141 } 142 143 ctx.Infof("All hosted models reclaimed, cleaning up controller machines") 144 145 return environs.Destroy(controllerName, controllerEnviron, store) 146 }