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 = ¶ms.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 }