github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/juju/model/destroy_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package model_test
     5  
     6  import (
     7  	"bytes"
     8  	"time"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/errors"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/names.v2"
    15  
    16  	"github.com/juju/juju/api/base"
    17  	"github.com/juju/juju/apiserver/common"
    18  	"github.com/juju/juju/apiserver/params"
    19  	"github.com/juju/juju/cmd/juju/model"
    20  	"github.com/juju/juju/cmd/modelcmd"
    21  	cmdtesting "github.com/juju/juju/cmd/testing"
    22  	"github.com/juju/juju/jujuclient"
    23  	"github.com/juju/juju/jujuclient/jujuclienttesting"
    24  	_ "github.com/juju/juju/provider/dummy"
    25  	"github.com/juju/juju/testing"
    26  )
    27  
    28  type DestroySuite struct {
    29  	testing.FakeJujuXDGDataHomeSuite
    30  	api   *fakeAPI
    31  	store *jujuclienttesting.MemStore
    32  	sleep func(time.Duration)
    33  }
    34  
    35  var _ = gc.Suite(&DestroySuite{})
    36  
    37  // fakeDestroyAPI mocks out the cient API
    38  type fakeAPI struct {
    39  	err             error
    40  	env             map[string]interface{}
    41  	statusCallCount int
    42  	modelInfoErr    []*params.Error
    43  }
    44  
    45  func (f *fakeAPI) Close() error { return nil }
    46  
    47  func (f *fakeAPI) DestroyModel(names.ModelTag) error {
    48  	return f.err
    49  }
    50  
    51  func (f *fakeAPI) ModelStatus(models ...names.ModelTag) ([]base.ModelStatus, error) {
    52  	var err *params.Error = &params.Error{Code: params.CodeNotFound}
    53  	if f.statusCallCount < len(f.modelInfoErr) {
    54  		err = f.modelInfoErr[f.statusCallCount]
    55  	}
    56  	f.statusCallCount++
    57  	return []base.ModelStatus{{}}, err
    58  }
    59  
    60  func (s *DestroySuite) SetUpTest(c *gc.C) {
    61  	s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
    62  	s.api = &fakeAPI{}
    63  	s.api.err = nil
    64  
    65  	s.store = jujuclienttesting.NewMemStore()
    66  	s.store.CurrentControllerName = "test1"
    67  	s.store.Controllers["test1"] = jujuclient.ControllerDetails{ControllerUUID: "test1-uuid"}
    68  	s.store.Models["test1"] = &jujuclient.ControllerModels{
    69  		Models: map[string]jujuclient.ModelDetails{
    70  			"admin/test1": {"test1-uuid"},
    71  			"admin/test2": {"test2-uuid"},
    72  		},
    73  	}
    74  	s.store.Accounts["test1"] = jujuclient.AccountDetails{
    75  		User: "admin",
    76  	}
    77  	s.sleep = func(time.Duration) {}
    78  }
    79  
    80  func (s *DestroySuite) runDestroyCommand(c *gc.C, args ...string) (*cmd.Context, error) {
    81  	cmd := model.NewDestroyCommandForTest(s.api, noOpRefresh, s.store, s.sleep)
    82  	return testing.RunCommand(c, cmd, args...)
    83  }
    84  
    85  func (s *DestroySuite) NewDestroyCommand() cmd.Command {
    86  	return model.NewDestroyCommandForTest(s.api, noOpRefresh, s.store, s.sleep)
    87  }
    88  
    89  func checkModelExistsInStore(c *gc.C, name string, store jujuclient.ClientStore) {
    90  	controller, model := modelcmd.SplitModelName(name)
    91  	_, err := store.ModelByName(controller, model)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  }
    94  
    95  func checkModelRemovedFromStore(c *gc.C, name string, store jujuclient.ClientStore) {
    96  	controller, model := modelcmd.SplitModelName(name)
    97  	_, err := store.ModelByName(controller, model)
    98  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    99  }
   100  
   101  func (s *DestroySuite) TestDestroyNoModelNameError(c *gc.C) {
   102  	_, err := s.runDestroyCommand(c)
   103  	c.Assert(err, gc.ErrorMatches, "no model specified")
   104  }
   105  
   106  func (s *DestroySuite) TestDestroyBadFlags(c *gc.C) {
   107  	_, err := s.runDestroyCommand(c, "-n")
   108  	c.Assert(err, gc.ErrorMatches, "flag provided but not defined: -n")
   109  }
   110  
   111  func (s *DestroySuite) TestDestroyUnknownArgument(c *gc.C) {
   112  	_, err := s.runDestroyCommand(c, "model", "whoops")
   113  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["whoops"\]`)
   114  }
   115  
   116  func (s *DestroySuite) TestDestroyUnknownModelCallsRefresh(c *gc.C) {
   117  	called := false
   118  	refresh := func(jujuclient.ClientStore, string) error {
   119  		called = true
   120  		return nil
   121  	}
   122  
   123  	cmd := model.NewDestroyCommandForTest(s.api, refresh, s.store, s.sleep)
   124  	_, err := testing.RunCommand(c, cmd, "foo")
   125  	c.Check(called, jc.IsTrue)
   126  	c.Check(err, gc.ErrorMatches, `cannot read model info: model test1:admin/foo not found`)
   127  }
   128  
   129  func (s *DestroySuite) TestDestroyCannotConnectToAPI(c *gc.C) {
   130  	s.api.err = errors.New("connection refused")
   131  	_, err := s.runDestroyCommand(c, "test2", "-y")
   132  	c.Assert(err, gc.ErrorMatches, "cannot destroy model: connection refused")
   133  	c.Check(c.GetTestLog(), jc.Contains, "failed to destroy model \"test2\"")
   134  	checkModelExistsInStore(c, "test1:admin/test2", s.store)
   135  }
   136  
   137  func (s *DestroySuite) TestSystemDestroyFails(c *gc.C) {
   138  	_, err := s.runDestroyCommand(c, "test1", "-y")
   139  	c.Assert(err, gc.ErrorMatches, `"test1" is a controller; use 'juju destroy-controller' to destroy it`)
   140  	checkModelExistsInStore(c, "test1:admin/test1", s.store)
   141  }
   142  
   143  func (s *DestroySuite) TestDestroy(c *gc.C) {
   144  	checkModelExistsInStore(c, "test1:admin/test2", s.store)
   145  	_, err := s.runDestroyCommand(c, "test2", "-y")
   146  	c.Assert(err, jc.ErrorIsNil)
   147  	checkModelRemovedFromStore(c, "test1:admin/test2", s.store)
   148  }
   149  
   150  func (s *DestroySuite) TestDestroyBlocks(c *gc.C) {
   151  	checkModelExistsInStore(c, "test1:admin/test2", s.store)
   152  	s.api.modelInfoErr = []*params.Error{{}, {Code: params.CodeNotFound}}
   153  	_, err := s.runDestroyCommand(c, "test2", "-y")
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	checkModelRemovedFromStore(c, "test1:admin/test2", s.store)
   156  	c.Assert(s.api.statusCallCount, gc.Equals, 1)
   157  }
   158  
   159  func (s *DestroySuite) TestFailedDestroyModel(c *gc.C) {
   160  	s.api.err = errors.New("permission denied")
   161  	_, err := s.runDestroyCommand(c, "test1:test2", "-y")
   162  	c.Assert(err, gc.ErrorMatches, "cannot destroy model: permission denied")
   163  	checkModelExistsInStore(c, "test1:admin/test2", s.store)
   164  }
   165  
   166  func (s *DestroySuite) resetModel(c *gc.C) {
   167  	s.store.Models["test1"] = &jujuclient.ControllerModels{
   168  		Models: map[string]jujuclient.ModelDetails{
   169  			"admin/test1": {"test1-uuid"},
   170  			"admin/test2": {"test2-uuid"},
   171  		},
   172  	}
   173  }
   174  
   175  func (s *DestroySuite) TestDestroyCommandConfirmation(c *gc.C) {
   176  	var stdin, stdout bytes.Buffer
   177  	ctx, err := cmd.DefaultContext()
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	ctx.Stdout = &stdout
   180  	ctx.Stdin = &stdin
   181  
   182  	// Ensure confirmation is requested if "-y" is not specified.
   183  	stdin.WriteString("n")
   184  	_, errc := cmdtesting.RunCommand(ctx, s.NewDestroyCommand(), "test2")
   185  	select {
   186  	case err := <-errc:
   187  		c.Check(err, gc.ErrorMatches, "model destruction: aborted")
   188  	case <-time.After(testing.LongWait):
   189  		c.Fatalf("command took too long")
   190  	}
   191  	c.Check(testing.Stdout(ctx), gc.Matches, "WARNING!.*test2(.|\n)*")
   192  	checkModelExistsInStore(c, "test1:admin/test1", s.store)
   193  
   194  	// EOF on stdin: equivalent to answering no.
   195  	stdin.Reset()
   196  	stdout.Reset()
   197  	_, errc = cmdtesting.RunCommand(ctx, s.NewDestroyCommand(), "test2")
   198  	select {
   199  	case err := <-errc:
   200  		c.Check(err, gc.ErrorMatches, "model destruction: aborted")
   201  	case <-time.After(testing.LongWait):
   202  		c.Fatalf("command took too long")
   203  	}
   204  	c.Check(testing.Stdout(ctx), gc.Matches, "WARNING!.*test2(.|\n)*")
   205  	checkModelExistsInStore(c, "test1:admin/test2", s.store)
   206  
   207  	for _, answer := range []string{"y", "Y", "yes", "YES"} {
   208  		stdin.Reset()
   209  		stdout.Reset()
   210  		stdin.WriteString(answer)
   211  		_, errc = cmdtesting.RunCommand(ctx, s.NewDestroyCommand(), "test2")
   212  		select {
   213  		case err := <-errc:
   214  			c.Check(err, jc.ErrorIsNil)
   215  		case <-time.After(testing.LongWait):
   216  			c.Fatalf("command took too long")
   217  		}
   218  		checkModelRemovedFromStore(c, "test1:admin/test2", s.store)
   219  
   220  		// Add the test2 model back into the store for the next test
   221  		s.resetModel(c)
   222  	}
   223  }
   224  
   225  func (s *DestroySuite) TestBlockedDestroy(c *gc.C) {
   226  	s.api.err = common.OperationBlockedError("TestBlockedDestroy")
   227  	_, err := s.runDestroyCommand(c, "test2", "-y")
   228  	testing.AssertOperationWasBlocked(c, err, ".*TestBlockedDestroy.*")
   229  }