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  }