github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/destroyenvironment.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "bufio" 8 stderrors "errors" 9 "fmt" 10 "io" 11 "strings" 12 13 "github.com/juju/cmd" 14 "github.com/juju/errors" 15 "launchpad.net/gnuflag" 16 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/cmd/envcmd" 19 "github.com/juju/juju/cmd/juju/block" 20 "github.com/juju/juju/environs" 21 "github.com/juju/juju/environs/configstore" 22 "github.com/juju/juju/juju" 23 ) 24 25 var NoEnvironmentError = stderrors.New("no environment specified") 26 var DoubleEnvironmentError = stderrors.New("you cannot supply both -e and the envname as a positional argument") 27 28 // DestroyEnvironmentCommand destroys an environment. 29 type DestroyEnvironmentCommand struct { 30 envcmd.EnvCommandBase 31 cmd.CommandBase 32 envName string 33 assumeYes bool 34 force bool 35 } 36 37 func (c *DestroyEnvironmentCommand) Info() *cmd.Info { 38 return &cmd.Info{ 39 Name: "destroy-environment", 40 Args: "<environment name>", 41 Purpose: "terminate all machines and other associated resources for an environment", 42 } 43 } 44 45 func (c *DestroyEnvironmentCommand) SetFlags(f *gnuflag.FlagSet) { 46 f.BoolVar(&c.assumeYes, "y", false, "Do not ask for confirmation") 47 f.BoolVar(&c.assumeYes, "yes", false, "") 48 f.BoolVar(&c.force, "force", false, "Forcefully destroy the environment, directly through the environment provider") 49 f.StringVar(&c.envName, "e", "", "juju environment to operate in") 50 f.StringVar(&c.envName, "environment", "", "juju environment to operate in") 51 } 52 53 func (c *DestroyEnvironmentCommand) Init(args []string) error { 54 if c.envName != "" { 55 logger.Warningf("-e/--environment flag is deprecated in 1.18, " + 56 "please supply environment as a positional parameter") 57 // They supplied the -e flag 58 if len(args) == 0 { 59 // We're happy, we have enough information 60 return nil 61 } 62 // You can't supply -e ENV and ENV as a positional argument 63 return DoubleEnvironmentError 64 } 65 // No -e flag means they must supply the environment positionally 66 switch len(args) { 67 case 0: 68 return NoEnvironmentError 69 case 1: 70 c.envName = args[0] 71 return nil 72 default: 73 return cmd.CheckEmpty(args[1:]) 74 } 75 } 76 77 func (c *DestroyEnvironmentCommand) Run(ctx *cmd.Context) (result error) { 78 store, err := configstore.Default() 79 if err != nil { 80 return fmt.Errorf("cannot open environment info storage: %v", err) 81 } 82 environ, err := environs.NewFromName(c.envName, store) 83 if err != nil { 84 if environs.IsEmptyConfig(err) { 85 // Delete the .jenv file and call it done. 86 ctx.Infof("removing empty environment file") 87 return environs.DestroyInfo(c.envName, store) 88 } 89 return err 90 } 91 if !c.assumeYes { 92 fmt.Fprintf(ctx.Stdout, destroyEnvMsg, c.envName, environ.Config().Type()) 93 94 scanner := bufio.NewScanner(ctx.Stdin) 95 scanner.Scan() 96 err := scanner.Err() 97 if err != nil && err != io.EOF { 98 return fmt.Errorf("Environment destruction aborted: %s", err) 99 } 100 answer := strings.ToLower(scanner.Text()) 101 if answer != "y" && answer != "yes" { 102 return stderrors.New("environment destruction aborted") 103 } 104 } 105 // If --force is supplied, then don't attempt to use the API. 106 // This is necessary to destroy broken environments, where the 107 // API server is inaccessible or faulty. 108 if !c.force { 109 defer func() { 110 result = c.ensureUserFriendlyErrorLog(result) 111 }() 112 apiclient, err := juju.NewAPIClientFromName(c.envName) 113 if err != nil { 114 return errors.Annotate(err, "cannot connect to API") 115 } 116 defer apiclient.Close() 117 err = apiclient.DestroyEnvironment() 118 if cmdErr := processDestroyError(err); cmdErr != nil { 119 return cmdErr 120 } 121 } 122 return environs.Destroy(environ, store) 123 } 124 125 // processDestroyError determines how to format error message based on its code. 126 // Note that CodeNotImplemented errors have not be propogated in previous implementation. 127 // This behaviour was preserved. 128 func processDestroyError(err error) error { 129 if err == nil || params.IsCodeNotImplemented(err) { 130 return nil 131 } 132 if params.IsCodeOperationBlocked(err) { 133 return err 134 } 135 return errors.Annotate(err, "destroying environment") 136 } 137 138 // ensureUserFriendlyErrorLog ensures that error will be logged and displayed 139 // in a user-friendly manner with readable and digestable error message. 140 func (c *DestroyEnvironmentCommand) ensureUserFriendlyErrorLog(err error) error { 141 if err == nil { 142 return nil 143 } 144 if params.IsCodeOperationBlocked(err) { 145 return block.ProcessBlockedError(err, block.BlockDestroy) 146 } 147 logger.Errorf(stdFailureMsg, c.envName) 148 return err 149 } 150 151 var destroyEnvMsg = ` 152 WARNING! this command will destroy the %q environment (type: %s) 153 This includes all machines, services, data and other resources. 154 155 Continue [y/N]? `[1:] 156 157 var stdFailureMsg = `failed to destroy environment %q 158 159 If the environment is unusable, then you may run 160 161 juju destroy-environment --force 162 163 to forcefully destroy the environment. Upon doing so, review 164 your environment provider console for any resources that need 165 to be cleaned up. Using force will also by-pass destroy-envrionment block. 166 167 `