github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cmd/juju/commands/destroyenvironment_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package commands 5 6 import ( 7 "bytes" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/cmd/envcmd" 15 cmdtesting "github.com/juju/juju/cmd/testing" 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/environs/configstore" 18 "github.com/juju/juju/instance" 19 "github.com/juju/juju/juju/testing" 20 "github.com/juju/juju/provider/dummy" 21 coretesting "github.com/juju/juju/testing" 22 "github.com/juju/juju/testing/factory" 23 ) 24 25 type destroyEnvSuite struct { 26 testing.JujuConnSuite 27 CmdBlockHelper 28 } 29 30 func (s *destroyEnvSuite) SetUpTest(c *gc.C) { 31 s.JujuConnSuite.SetUpTest(c) 32 s.CmdBlockHelper = NewCmdBlockHelper(s.APIState) 33 c.Assert(s.CmdBlockHelper, gc.NotNil) 34 s.AddCleanup(func(*gc.C) { s.CmdBlockHelper.Close() }) 35 } 36 37 var _ = gc.Suite(&destroyEnvSuite{}) 38 39 func (s *destroyEnvSuite) TestDestroyEnvironmentCommand(c *gc.C) { 40 // Prepare the environment so we can destroy it. 41 _, err := environs.PrepareFromName("dummyenv", envcmd.BootstrapContext(cmdtesting.NullContext(c)), s.ConfigStore) 42 c.Assert(err, jc.ErrorIsNil) 43 44 // check environment is mandatory 45 opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand)) 46 c.Check(<-errc, gc.Equals, NoEnvironmentError) 47 48 // normal destroy 49 opc, errc = cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "dummyenv", "--yes") 50 c.Check(<-errc, gc.IsNil) 51 c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") 52 53 // Verify that the environment information has been removed. 54 _, err = s.ConfigStore.ReadInfo("dummyenv") 55 c.Assert(err, jc.Satisfies, errors.IsNotFound) 56 } 57 58 // startEnvironment prepare the environment so we can destroy it. 59 func (s *destroyEnvSuite) startEnvironment(c *gc.C, desiredEnvName string) { 60 _, err := environs.PrepareFromName(desiredEnvName, envcmd.BootstrapContext(cmdtesting.NullContext(c)), s.ConfigStore) 61 c.Assert(err, jc.ErrorIsNil) 62 } 63 64 func (s *destroyEnvSuite) checkDestroyEnvironment(c *gc.C, blocked, force bool) { 65 //Setup environment 66 envName := "dummyenv" 67 s.startEnvironment(c, envName) 68 if blocked { 69 s.BlockDestroyEnvironment(c, "checkDestroyEnvironment") 70 } 71 opc := make(chan dummy.Operation) 72 errc := make(chan error) 73 if force { 74 opc, errc = cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), envName, "--yes", "--force") 75 } else { 76 opc, errc = cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), envName, "--yes") 77 } 78 if force || !blocked { 79 c.Check(<-errc, gc.IsNil) 80 c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, envName) 81 // Verify that the environment information has been removed. 82 _, err := s.ConfigStore.ReadInfo(envName) 83 c.Assert(err, jc.Satisfies, errors.IsNotFound) 84 } else { 85 c.Check(<-errc, gc.Not(gc.IsNil)) 86 c.Check((<-opc), gc.IsNil) 87 // Verify that the environment information has not been removed. 88 _, err := s.ConfigStore.ReadInfo(envName) 89 c.Assert(err, jc.ErrorIsNil) 90 } 91 } 92 93 func (s *destroyEnvSuite) TestDestroyLockedEnvironment(c *gc.C) { 94 // lock environment: can't destroy locked environment 95 s.checkDestroyEnvironment(c, true, false) 96 } 97 98 func (s *destroyEnvSuite) TestDestroyUnlockedEnvironment(c *gc.C) { 99 s.checkDestroyEnvironment(c, false, false) 100 } 101 102 func (s *destroyEnvSuite) TestForceDestroyLockedEnvironment(c *gc.C) { 103 s.checkDestroyEnvironment(c, true, true) 104 } 105 106 func (s *destroyEnvSuite) TestForceDestroyUnlockedEnvironment(c *gc.C) { 107 s.checkDestroyEnvironment(c, false, true) 108 } 109 110 func (s *destroyEnvSuite) TestDestroyEnvironmentCommandEFlag(c *gc.C) { 111 // Prepare the environment so we can destroy it. 112 _, err := environs.PrepareFromName("dummyenv", envcmd.BootstrapContext(cmdtesting.NullContext(c)), s.ConfigStore) 113 c.Assert(err, jc.ErrorIsNil) 114 115 // check that either environment or the flag is mandatory 116 opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand)) 117 c.Check(<-errc, gc.Equals, NoEnvironmentError) 118 119 // We don't allow them to supply both entries at the same time 120 opc, errc = cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "-e", "dummyenv", "dummyenv", "--yes") 121 c.Check(<-errc, gc.Equals, DoubleEnvironmentError) 122 // We treat --environment the same way 123 opc, errc = cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "--environment", "dummyenv", "dummyenv", "--yes") 124 c.Check(<-errc, gc.Equals, DoubleEnvironmentError) 125 126 // destroy using the -e flag 127 opc, errc = cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "-e", "dummyenv", "--yes") 128 c.Check(<-errc, gc.IsNil) 129 c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") 130 131 // Verify that the environment information has been removed. 132 _, err = s.ConfigStore.ReadInfo("dummyenv") 133 c.Assert(err, jc.Satisfies, errors.IsNotFound) 134 } 135 136 func (s *destroyEnvSuite) TestDestroyEnvironmentCommandEmptyJenv(c *gc.C) { 137 oldinfo, err := s.ConfigStore.ReadInfo("dummyenv") 138 info := s.ConfigStore.CreateInfo("dummy-no-bootstrap") 139 info.SetAPICredentials(oldinfo.APICredentials()) 140 info.SetAPIEndpoint(oldinfo.APIEndpoint()) 141 err = info.Write() 142 c.Assert(err, jc.ErrorIsNil) 143 144 opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "dummy-no-bootstrap", "--yes") 145 c.Check(<-errc, gc.IsNil) 146 c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") 147 148 // Verify that the environment information has been removed. 149 _, err = s.ConfigStore.ReadInfo("dummyenv") 150 c.Assert(err, jc.Satisfies, errors.IsNotFound) 151 } 152 153 func (s *destroyEnvSuite) TestDestroyEnvironmentCommandNonStateServer(c *gc.C) { 154 s.setupHostedEnviron(c, "dummy-non-state-server") 155 opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "dummy-non-state-server", "--yes") 156 c.Check(<-errc, gc.IsNil) 157 // Check that there are no operations on the provider, we do not want to call 158 // Destroy on it. 159 c.Check(<-opc, gc.IsNil) 160 161 _, err := s.ConfigStore.ReadInfo("dummy-non-state-server") 162 c.Assert(err, jc.Satisfies, errors.IsNotFound) 163 } 164 165 func (s *destroyEnvSuite) TestForceDestroyEnvironmentCommandOnNonStateServerFails(c *gc.C) { 166 s.setupHostedEnviron(c, "dummy-non-state-server") 167 opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "dummy-non-state-server", "--yes", "--force") 168 c.Check(<-errc, gc.ErrorMatches, "cannot force destroy environment without bootstrap information") 169 c.Check(<-opc, gc.IsNil) 170 171 serverInfo, err := s.ConfigStore.ReadInfo("dummy-non-state-server") 172 c.Assert(err, jc.ErrorIsNil) 173 c.Assert(serverInfo, gc.Not(gc.IsNil)) 174 } 175 176 func (s *destroyEnvSuite) TestForceDestroyEnvironmentCommandOnNonStateServerNoConfimFails(c *gc.C) { 177 s.setupHostedEnviron(c, "dummy-non-state-server") 178 opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "dummy-non-state-server", "--force") 179 c.Check(<-errc, gc.ErrorMatches, "cannot force destroy environment without bootstrap information") 180 c.Check(<-opc, gc.IsNil) 181 182 serverInfo, err := s.ConfigStore.ReadInfo("dummy-non-state-server") 183 c.Assert(err, jc.ErrorIsNil) 184 c.Assert(serverInfo, gc.Not(gc.IsNil)) 185 } 186 187 func (s *destroyEnvSuite) TestDestroyEnvironmentCommandTwiceOnNonStateServer(c *gc.C) { 188 s.setupHostedEnviron(c, "dummy-non-state-server") 189 oldInfo, err := s.ConfigStore.ReadInfo("dummy-non-state-server") 190 c.Assert(err, jc.ErrorIsNil) 191 192 opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "dummy-non-state-server", "--yes") 193 c.Check(<-errc, gc.IsNil) 194 c.Check(<-opc, gc.IsNil) 195 196 _, err = s.ConfigStore.ReadInfo("dummy-non-state-server") 197 c.Assert(err, jc.Satisfies, errors.IsNotFound) 198 199 // Simluate another client calling destroy on the same environment. This 200 // client will have a local cache of the environ info, so write it back out. 201 info := s.ConfigStore.CreateInfo("dummy-non-state-server") 202 info.SetAPIEndpoint(oldInfo.APIEndpoint()) 203 info.SetAPICredentials(oldInfo.APICredentials()) 204 err = info.Write() 205 c.Assert(err, jc.ErrorIsNil) 206 207 // Call destroy again. 208 context, err := coretesting.RunCommand(c, new(DestroyEnvironmentCommand), "dummy-non-state-server", "--yes") 209 c.Assert(err, jc.ErrorIsNil) 210 c.Assert(coretesting.Stderr(context), gc.Equals, "environment not found, removing config file\n") 211 212 // Check that the client's cached info has been removed. 213 _, err = s.ConfigStore.ReadInfo("dummy-non-state-server") 214 c.Assert(err, jc.Satisfies, errors.IsNotFound) 215 } 216 217 func (s *destroyEnvSuite) setupHostedEnviron(c *gc.C, name string) { 218 st := s.Factory.MakeEnvironment(c, &factory.EnvParams{ 219 Name: name, 220 Prepare: true, 221 ConfigAttrs: coretesting.Attrs{"state-server": false}, 222 }) 223 defer st.Close() 224 225 ports, err := st.APIHostPorts() 226 c.Assert(err, jc.ErrorIsNil) 227 info := s.ConfigStore.CreateInfo(name) 228 endpoint := configstore.APIEndpoint{ 229 CACert: st.CACert(), 230 EnvironUUID: st.EnvironUUID(), 231 Addresses: []string{ports[0][0].String()}, 232 } 233 info.SetAPIEndpoint(endpoint) 234 235 ssinfo, err := s.ConfigStore.ReadInfo("dummyenv") 236 c.Assert(err, jc.ErrorIsNil) 237 info.SetAPICredentials(ssinfo.APICredentials()) 238 err = info.Write() 239 c.Assert(err, jc.ErrorIsNil) 240 } 241 242 func (s *destroyEnvSuite) TestDestroyEnvironmentCommandBroken(c *gc.C) { 243 oldinfo, err := s.ConfigStore.ReadInfo("dummyenv") 244 c.Assert(err, jc.ErrorIsNil) 245 bootstrapConfig := oldinfo.BootstrapConfig() 246 apiEndpoint := oldinfo.APIEndpoint() 247 apiCredentials := oldinfo.APICredentials() 248 err = oldinfo.Destroy() 249 c.Assert(err, jc.ErrorIsNil) 250 newinfo := s.ConfigStore.CreateInfo("dummyenv") 251 252 bootstrapConfig["broken"] = "Destroy" 253 newinfo.SetBootstrapConfig(bootstrapConfig) 254 newinfo.SetAPIEndpoint(apiEndpoint) 255 newinfo.SetAPICredentials(apiCredentials) 256 err = newinfo.Write() 257 c.Assert(err, jc.ErrorIsNil) 258 259 // Prepare the environment so we can destroy it. 260 _, err = environs.PrepareFromName("dummyenv", envcmd.BootstrapContext(cmdtesting.NullContext(c)), s.ConfigStore) 261 c.Assert(err, jc.ErrorIsNil) 262 263 // destroy with broken environment 264 opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), new(DestroyEnvironmentCommand), "dummyenv", "--yes") 265 op, ok := (<-opc).(dummy.OpDestroy) 266 c.Assert(ok, jc.IsTrue) 267 c.Assert(op.Error, gc.ErrorMatches, ".*dummy.Destroy is broken") 268 c.Check(<-errc, gc.ErrorMatches, ".*dummy.Destroy is broken") 269 c.Check(<-opc, gc.IsNil) 270 } 271 272 func (*destroyEnvSuite) TestDestroyEnvironmentCommandConfirmationFlag(c *gc.C) { 273 com := new(DestroyEnvironmentCommand) 274 c.Check(coretesting.InitCommand(com, []string{"dummyenv"}), gc.IsNil) 275 c.Check(com.assumeYes, jc.IsFalse) 276 277 com = new(DestroyEnvironmentCommand) 278 c.Check(coretesting.InitCommand(com, []string{"dummyenv", "-y"}), gc.IsNil) 279 c.Check(com.assumeYes, jc.IsTrue) 280 281 com = new(DestroyEnvironmentCommand) 282 c.Check(coretesting.InitCommand(com, []string{"dummyenv", "--yes"}), gc.IsNil) 283 c.Check(com.assumeYes, jc.IsTrue) 284 } 285 286 func (s *destroyEnvSuite) TestDestroyEnvironmentCommandConfirmation(c *gc.C) { 287 var stdin, stdout bytes.Buffer 288 ctx, err := cmd.DefaultContext() 289 c.Assert(err, jc.ErrorIsNil) 290 ctx.Stdout = &stdout 291 ctx.Stdin = &stdin 292 293 // Prepare the environment so we can destroy it. 294 env, err := environs.PrepareFromName("dummyenv", envcmd.BootstrapContext(cmdtesting.NullContext(c)), s.ConfigStore) 295 c.Assert(err, jc.ErrorIsNil) 296 297 assertEnvironNotDestroyed(c, env, s.ConfigStore) 298 299 // Ensure confirmation is requested if "-y" is not specified. 300 stdin.WriteString("n") 301 opc, errc := cmdtesting.RunCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv") 302 c.Check(<-errc, gc.ErrorMatches, "environment destruction aborted") 303 c.Check(<-opc, gc.IsNil) 304 c.Check(stdout.String(), gc.Matches, "WARNING!.*dummyenv.*\\(type: dummy\\)(.|\n)*") 305 assertEnvironNotDestroyed(c, env, s.ConfigStore) 306 307 // EOF on stdin: equivalent to answering no. 308 stdin.Reset() 309 stdout.Reset() 310 opc, errc = cmdtesting.RunCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv") 311 c.Check(<-opc, gc.IsNil) 312 c.Check(<-errc, gc.ErrorMatches, "environment destruction aborted") 313 assertEnvironNotDestroyed(c, env, s.ConfigStore) 314 315 // "--yes" passed: no confirmation request. 316 stdin.Reset() 317 stdout.Reset() 318 opc, errc = cmdtesting.RunCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv", "--yes") 319 c.Check(<-errc, gc.IsNil) 320 c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") 321 c.Check(stdout.String(), gc.Equals, "") 322 assertEnvironDestroyed(c, env, s.ConfigStore) 323 324 // Any of casing of "y" and "yes" will confirm. 325 for _, answer := range []string{"y", "Y", "yes", "YES"} { 326 // Prepare the environment so we can destroy it. 327 s.Reset(c) 328 env, err := environs.PrepareFromName("dummyenv", envcmd.BootstrapContext(cmdtesting.NullContext(c)), s.ConfigStore) 329 c.Assert(err, jc.ErrorIsNil) 330 331 stdin.Reset() 332 stdout.Reset() 333 stdin.WriteString(answer) 334 opc, errc = cmdtesting.RunCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv") 335 c.Check(<-errc, gc.IsNil) 336 c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") 337 c.Check(stdout.String(), gc.Matches, "WARNING!.*dummyenv.*\\(type: dummy\\)(.|\n)*") 338 assertEnvironDestroyed(c, env, s.ConfigStore) 339 } 340 } 341 342 func assertEnvironDestroyed(c *gc.C, env environs.Environ, store configstore.Storage) { 343 _, err := store.ReadInfo(env.Config().Name()) 344 c.Assert(err, jc.Satisfies, errors.IsNotFound) 345 346 _, err = env.Instances([]instance.Id{"invalid"}) 347 c.Assert(err, gc.ErrorMatches, "environment has been destroyed") 348 } 349 350 func assertEnvironNotDestroyed(c *gc.C, env environs.Environ, store configstore.Storage) { 351 info, err := store.ReadInfo(env.Config().Name()) 352 c.Assert(err, jc.ErrorIsNil) 353 c.Assert(info.Initialized(), jc.IsTrue) 354 355 _, err = environs.NewFromName(env.Config().Name(), store) 356 c.Assert(err, jc.ErrorIsNil) 357 }