func (c *DestroyEnvironmentCommand) Run(ctx *cmd.Context) (result error) { store, err := configstore.Default() if err != nil { return fmt.Errorf("cannot open environment info storage: %v", err) } environ, err := environs.NewFromName(c.envName, store) if err != nil { if environs.IsEmptyConfig(err) { // Delete the .jenv file and call it done. ctx.Infof("removing empty environment file") return environs.DestroyInfo(c.envName, store) } return err } if !c.assumeYes { fmt.Fprintf(ctx.Stdout, destroyEnvMsg, environ.Name(), environ.Config().Type()) scanner := bufio.NewScanner(ctx.Stdin) scanner.Scan() err := scanner.Err() if err != nil && err != io.EOF { return fmt.Errorf("Environment destruction aborted: %s", err) } answer := strings.ToLower(scanner.Text()) if answer != "y" && answer != "yes" { return errors.New("environment destruction aborted") } } // If --force is supplied, then don't attempt to use the API. // This is necessary to destroy broken environments, where the // API server is inaccessible or faulty. if !c.force { defer func() { if result == nil { return } logger.Errorf(`failed to destroy environment %q If the environment is unusable, then you may run juju destroy-environment --force to forcefully destroy the environment. Upon doing so, review your environment provider console for any resources that need to be cleaned up. `, c.envName) }() apiclient, err := juju.NewAPIClientFromName(c.envName) if err != nil { return fmt.Errorf("cannot connect to API: %v", err) } defer apiclient.Close() err = apiclient.DestroyEnvironment() if err != nil && !params.IsCodeNotImplemented(err) { return fmt.Errorf("destroying environment: %v", err) } } return environs.Destroy(environ, store) }
func destroyEnvInfoProductionFunc( ctx *cmd.Context, cfgName string, store configstore.Storage, action string, ) { ctx.Infof("%s failed, cleaning up the environment.", action) if err := environs.DestroyInfo(cfgName, store); err != nil { logger.Errorf("the environment jenv file could not be cleaned up: %v", err) } }
// Run implements Command.Run func (c *destroyCommand) Run(ctx *cmd.Context) error { store, err := configstore.Default() if err != nil { return errors.Annotate(err, "cannot open environment info storage") } cfgInfo, err := store.ReadInfo(c.envName) if err != nil { return errors.Annotate(err, "cannot read environment info") } // Verify that we're not destroying a controller apiEndpoint := cfgInfo.APIEndpoint() if apiEndpoint.ServerUUID != "" && apiEndpoint.EnvironUUID == apiEndpoint.ServerUUID { return errors.Errorf("%q is a controller; use 'juju destroy-controller' to destroy it", c.envName) } if !c.assumeYes { fmt.Fprintf(ctx.Stdout, destroyEnvMsg, c.envName) if err := jujucmd.UserConfirmYes(ctx); err != nil { return errors.Annotate(err, "environment destruction") } } // Attempt to connect to the API. If we can't, fail the destroy. api, err := c.getAPI() if err != nil { return errors.Annotate(err, "cannot connect to API") } defer api.Close() // Attempt to destroy the environment. err = api.DestroyEnvironment() if err != nil { return c.handleError(errors.Annotate(err, "cannot destroy environment")) } return environs.DestroyInfo(c.envName, store) }
func (c *destroyEnvironmentCommand) Run(ctx *cmd.Context) (result error) { store, err := configstore.Default() if err != nil { return errors.Annotate(err, "cannot open environment info storage") } cfgInfo, err := store.ReadInfo(c.EnvName()) if err != nil { return errors.Annotate(err, "cannot read environment info") } var hasBootstrapCfg bool var serverEnviron environs.Environ if bootstrapCfg := cfgInfo.BootstrapConfig(); bootstrapCfg != nil { hasBootstrapCfg = true serverEnviron, err = getServerEnv(bootstrapCfg) if err != nil { return errors.Trace(err) } } if c.force { if hasBootstrapCfg { // If --force is supplied on a server environment, then don't // attempt to use the API. This is necessary to destroy broken // environments, where the API server is inaccessible or faulty. return environs.Destroy(serverEnviron, store) } else { // Force only makes sense on the server environment. return errors.Errorf("cannot force destroy environment without bootstrap information") } } apiclient, err := c.NewAPIClient() if err != nil { if errors.IsNotFound(err) { logger.Warningf("environment not found, removing config file") ctx.Infof("environment not found, removing config file") return environs.DestroyInfo(c.EnvName(), store) } return errors.Annotate(err, "cannot connect to API") } defer apiclient.Close() info, err := apiclient.EnvironmentInfo() if err != nil { return errors.Annotate(err, "cannot get information for environment") } if !c.assumeYes { fmt.Fprintf(ctx.Stdout, destroyEnvMsg, c.EnvName(), info.ProviderType) scanner := bufio.NewScanner(ctx.Stdin) scanner.Scan() err := scanner.Err() if err != nil && err != io.EOF { return errors.Annotate(err, "environment destruction aborted") } answer := strings.ToLower(scanner.Text()) if answer != "y" && answer != "yes" { return stderrors.New("environment destruction aborted") } } if info.UUID == info.ServerUUID { if !hasBootstrapCfg { // serverEnviron will be nil as we didn't have the jenv bootstrap // config to build it. But we do have a connection to the API // server, so get the config from there. bootstrapCfg, err := apiclient.EnvironmentGet() if err != nil { return errors.Annotate(err, "environment destruction failed") } serverEnviron, err = getServerEnv(bootstrapCfg) if err != nil { return errors.Annotate(err, "environment destruction failed") } } if err := c.destroyEnv(apiclient); err != nil { return errors.Annotate(err, "environment destruction failed") } if err := environs.Destroy(serverEnviron, store); err != nil { return errors.Annotate(err, "environment destruction failed") } return environs.DestroyInfo(c.EnvName(), store) } // If this is not the server environment, there is no bootstrap info and // we do not call Destroy on the provider. Destroying the environment via // the API and cleaning up the jenv file is sufficient. if err := c.destroyEnv(apiclient); err != nil { errors.Annotate(err, "cannot destroy environment") } return environs.DestroyInfo(c.EnvName(), store) }