24
24
"github.com/juju/juju/cmd/modelcmd"
25
25
"github.com/juju/juju/environs"
26
26
"github.com/juju/juju/environs/config"
27
"github.com/juju/juju/environs/configstore"
27
"github.com/juju/juju/jujuclient"
30
30
// NewDestroyCommand returns a command to destroy a controller.
92
92
// Run implements Command.Run
93
93
func (c *destroyCommand) Run(ctx *cmd.Context) error {
94
store, err := configstore.Default()
96
return errors.Annotate(err, "cannot open controller info storage")
99
94
controllerName := c.ControllerName()
100
cfgInfo, err := store.ReadInfo(configstore.EnvironInfoName(
101
controllerName, configstore.AdminModelName(controllerName),
95
store := c.ClientStore()
96
controllerDetails, err := store.ControllerByName(controllerName)
104
98
return errors.Annotate(err, "cannot read controller info")
107
// Verify that we're destroying a controller
108
apiEndpoint := cfgInfo.APIEndpoint()
109
if apiEndpoint.ServerUUID != "" && apiEndpoint.ModelUUID != apiEndpoint.ServerUUID {
110
return errors.Errorf("%q is not a controller; use juju destroy-model to destroy it", c.ControllerName())
113
101
if !c.assumeYes {
114
102
if err = confirmDestruction(ctx, c.ControllerName()); err != nil {
125
113
defer api.Close()
127
// Obtain bootstrap / controller environ information
128
controllerEnviron, err := c.getControllerModel(cfgInfo, api)
115
// Obtain controller environ so we can clean up afterwards.
116
controllerEnviron, err := c.getControllerEnviron(store, controllerName, api)
130
return errors.Annotate(err, "cannot obtain bootstrap information")
118
return errors.Annotate(err, "getting controller environ")
133
121
// Attempt to destroy the controller.
140
128
if c.destroyModels {
141
129
ctx.Infof("Waiting for hosted model resources to be reclaimed.")
143
updateStatus := newTimedStatusUpdater(ctx, api, apiEndpoint.ModelUUID)
131
updateStatus := newTimedStatusUpdater(ctx, api, controllerDetails.ControllerUUID)
144
132
for ctrStatus, modelsStatus := updateStatus(0); hasUnDeadModels(modelsStatus); ctrStatus, modelsStatus = updateStatus(2 * time.Second) {
145
133
ctx.Infof(fmtCtrStatus(ctrStatus))
146
134
for _, model := range modelsStatus {
151
139
ctx.Infof("All hosted models reclaimed, cleaning up controller machines")
153
return environs.Destroy(c.ControllerName(), controllerEnviron, store, c.ClientStore())
141
return environs.Destroy(c.ControllerName(), controllerEnviron, store)
156
144
// ensureUserFriendlyErrorLog ensures that error will be logged and displayed
255
243
return controller.NewClient(root), nil
258
func (c *destroyCommandBase) getClientAPI() (destroyClientAPI, error) {
259
if c.clientapi != nil {
260
return c.clientapi, nil
262
root, err := c.NewAPIRoot()
264
return nil, errors.Trace(err)
266
return root.Client(), nil
269
246
// SetFlags implements Command.SetFlags.
270
247
func (c *destroyCommandBase) SetFlags(f *gnuflag.FlagSet) {
271
248
f.BoolVar(&c.assumeYes, "y", false, "Do not ask for confirmation")
287
// getControllerModel gets the bootstrap information required to destroy the
288
// model by first checking the config store, then querying the API if
289
// the information is not in the store.
290
func (c *destroyCommandBase) getControllerModel(info configstore.EnvironInfo, sysAPI destroyControllerAPI) (_ environs.Environ, err error) {
291
bootstrapCfg := info.BootstrapConfig()
292
if bootstrapCfg == nil {
264
// getControllerEnviron returns the Environ for the controller model.
266
// getControllerEnviron gets the information required to get the
267
// Environ by first checking the config store, then querying the
268
// API if the information is not in the store.
269
func (c *destroyCommandBase) getControllerEnviron(
270
store jujuclient.ClientStore, controllerName string, sysAPI destroyControllerAPI,
271
) (_ environs.Environ, err error) {
272
cfg, err := modelcmd.NewGetBootstrapConfigFunc(store)(controllerName)
273
if errors.IsNotFound(err) {
293
274
if sysAPI == nil {
294
return nil, errors.New("unable to get bootstrap information from API")
296
bootstrapCfg, err = sysAPI.ModelConfig()
275
return nil, errors.New(
276
"unable to get bootstrap information from client store or API",
279
bootstrapConfig, err := sysAPI.ModelConfig()
281
return nil, errors.Annotate(err, "getting bootstrap config from API")
283
cfg, err = config.New(config.NoDefaults, bootstrapConfig)
298
285
return nil, errors.Trace(err)
302
cfg, err := config.New(config.NoDefaults, bootstrapCfg)
304
return nil, errors.Trace(err)
287
} else if err != nil {
288
return nil, errors.Annotate(err, "getting bootstrap config from client store")
306
290
return environs.New(cfg)