1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package controller_test
11
"github.com/juju/errors"
12
jc "github.com/juju/testing/checkers"
13
"github.com/juju/utils/clock"
14
gc "gopkg.in/check.v1"
15
"gopkg.in/juju/names.v2"
17
"github.com/juju/juju/api"
18
"github.com/juju/juju/api/base"
19
"github.com/juju/juju/apiserver/common"
20
"github.com/juju/juju/apiserver/params"
21
"github.com/juju/juju/cmd/juju/controller"
22
"github.com/juju/juju/cmd/modelcmd"
23
cmdtesting "github.com/juju/juju/cmd/testing"
24
"github.com/juju/juju/jujuclient"
25
_ "github.com/juju/juju/provider/dummy"
26
"github.com/juju/juju/testing"
29
type KillSuite struct {
33
var _ = gc.Suite(&KillSuite{})
35
func (s *KillSuite) runKillCommand(c *gc.C, args ...string) (*cmd.Context, error) {
36
return testing.RunCommand(c, s.newKillCommand(), args...)
39
func (s *KillSuite) newKillCommand() cmd.Command {
40
return controller.NewKillCommandForTest(
41
s.api, s.clientapi, s.store, s.apierror, &mockClock{}, nil)
44
func (s *KillSuite) TestKillNoControllerNameError(c *gc.C) {
45
_, err := s.runKillCommand(c)
46
c.Assert(err, gc.ErrorMatches, "no controller specified")
49
func (s *KillSuite) TestKillBadFlags(c *gc.C) {
50
_, err := s.runKillCommand(c, "-n")
51
c.Assert(err, gc.ErrorMatches, "flag provided but not defined: -n")
54
func (s *KillSuite) TestKillUnknownArgument(c *gc.C) {
55
_, err := s.runKillCommand(c, "model", "whoops")
56
c.Assert(err, gc.ErrorMatches, `unrecognized args: \["whoops"\]`)
59
func (s *KillSuite) TestKillUnknownController(c *gc.C) {
60
_, err := s.runKillCommand(c, "foo")
61
c.Assert(err, gc.ErrorMatches, `controller foo not found`)
64
func (s *KillSuite) TestKillCannotConnectToAPISucceeds(c *gc.C) {
65
s.apierror = errors.New("connection refused")
66
ctx, err := s.runKillCommand(c, "test1", "-y")
67
c.Assert(err, jc.ErrorIsNil)
68
c.Check(testing.Stderr(ctx), jc.Contains, "Unable to open API: connection refused")
69
checkControllerRemovedFromStore(c, "test1", s.store)
72
func (s *KillSuite) TestKillWithAPIConnection(c *gc.C) {
73
_, err := s.runKillCommand(c, "test1", "-y")
74
c.Assert(err, jc.ErrorIsNil)
75
c.Assert(s.api.destroyAll, jc.IsTrue)
76
c.Assert(s.clientapi.destroycalled, jc.IsFalse)
77
checkControllerRemovedFromStore(c, "test1", s.store)
80
func (s *KillSuite) TestKillEnvironmentGetFailsWithoutAPIConnection(c *gc.C) {
81
s.apierror = errors.New("connection refused")
82
s.api.SetErrors(errors.NotFoundf(`controller "test3"`))
83
_, err := s.runKillCommand(c, "test3", "-y")
84
c.Assert(err, gc.ErrorMatches,
85
"getting controller environ: unable to get bootstrap information from client store or API",
87
checkControllerExistsInStore(c, "test3", s.store)
90
func (s *KillSuite) TestKillEnvironmentGetFailsWithAPIConnection(c *gc.C) {
91
s.api.SetErrors(errors.NotFoundf(`controller "test3"`))
92
_, err := s.runKillCommand(c, "test3", "-y")
93
c.Assert(err, gc.ErrorMatches,
94
"getting controller environ: getting model config from API: controller \"test3\" not found",
96
checkControllerExistsInStore(c, "test3", s.store)
99
func (s *KillSuite) TestKillDestroysControllerWithAPIError(c *gc.C) {
100
s.api.SetErrors(errors.New("some destroy error"))
101
ctx, err := s.runKillCommand(c, "test1", "-y")
102
c.Assert(err, jc.ErrorIsNil)
103
c.Check(testing.Stderr(ctx), jc.Contains, "Unable to destroy controller through the API: some destroy error. Destroying through provider.")
104
c.Assert(s.api.destroyAll, jc.IsTrue)
105
checkControllerRemovedFromStore(c, "test1", s.store)
108
func (s *KillSuite) TestKillCommandConfirmation(c *gc.C) {
109
var stdin, stdout bytes.Buffer
110
ctx, err := cmd.DefaultContext()
111
c.Assert(err, jc.ErrorIsNil)
115
// Ensure confirmation is requested if "-y" is not specified.
116
stdin.WriteString("n")
117
_, errc := cmdtesting.RunCommand(ctx, s.newKillCommand(), "test1")
120
c.Check(err, gc.ErrorMatches, "controller destruction aborted")
121
case <-time.After(testing.LongWait):
122
c.Fatalf("command took too long")
124
c.Check(testing.Stdout(ctx), gc.Matches, "WARNING!.*test1(.|\n)*")
125
checkControllerExistsInStore(c, "test1", s.store)
128
func (s *KillSuite) TestKillCommandControllerAlias(c *gc.C) {
129
_, err := testing.RunCommand(c, s.newKillCommand(), "test1", "-y")
130
c.Assert(err, jc.ErrorIsNil)
131
checkControllerRemovedFromStore(c, "test1:test1", s.store)
134
func (s *KillSuite) TestKillAPIPermErrFails(c *gc.C) {
135
testDialer := func(_ jujuclient.ClientStore, controllerName, modelName string) (api.Connection, error) {
136
return nil, common.ErrPerm
138
cmd := controller.NewKillCommandForTest(nil, nil, s.store, nil, clock.WallClock, modelcmd.OpenFunc(testDialer))
139
_, err := testing.RunCommand(c, cmd, "test1", "-y")
140
c.Assert(err, gc.ErrorMatches, "cannot destroy controller: permission denied")
141
checkControllerExistsInStore(c, "test1", s.store)
144
func (s *KillSuite) TestKillEarlyAPIConnectionTimeout(c *gc.C) {
145
clock := &mockClock{}
147
stop := make(chan struct{})
149
testDialer := func(_ jujuclient.ClientStore, controllerName, modelName string) (api.Connection, error) {
151
return nil, errors.New("kill command waited too long")
154
cmd := controller.NewKillCommandForTest(nil, nil, s.store, nil, clock, modelcmd.OpenFunc(testDialer))
155
ctx, err := testing.RunCommand(c, cmd, "test1", "-y")
156
c.Check(err, jc.ErrorIsNil)
157
c.Check(testing.Stderr(ctx), jc.Contains, "Unable to open API: open connection timed out")
158
checkControllerRemovedFromStore(c, "test1", s.store)
159
// Check that we were actually told to wait for 10s.
160
c.Assert(clock.wait, gc.Equals, 10*time.Second)
163
// mockClock will panic if anything but After is called
164
type mockClock struct {
169
func (m *mockClock) After(duration time.Duration) <-chan time.Time {
171
return time.After(time.Millisecond)
174
func (s *KillSuite) TestControllerStatus(c *gc.C) {
175
s.api.allEnvs = []base.UserModel{
178
Owner: names.NewUserTag("admin").String(),
181
Owner: names.NewUserTag("bob").String(),
184
Owner: names.NewUserTag("jo").String(),
188
s.api.envStatus = make(map[string]base.ModelStatus)
189
for _, env := range s.api.allEnvs {
190
owner, err := names.ParseUserTag(env.Owner)
191
c.Assert(err, jc.ErrorIsNil)
192
s.api.envStatus[env.UUID] = base.ModelStatus{
195
HostedMachineCount: 2,
197
Owner: owner.Canonical(),
201
ctrStatus, envsStatus, err := controller.NewData(s.api, "123")
202
c.Assert(err, jc.ErrorIsNil)
203
c.Assert(ctrStatus.HostedModelCount, gc.Equals, 2)
204
c.Assert(ctrStatus.HostedMachineCount, gc.Equals, 6)
205
c.Assert(ctrStatus.ServiceCount, gc.Equals, 3)
206
c.Assert(envsStatus, gc.HasLen, 2)
208
for i, expected := range []struct {
212
HostedMachineCount int
219
HostedMachineCount: 2,
225
HostedMachineCount: 2,
229
c.Assert(envsStatus[i].Owner, gc.Equals, expected.Owner)
230
c.Assert(envsStatus[i].Name, gc.Equals, expected.Name)
231
c.Assert(envsStatus[i].Life, gc.Equals, expected.Life)
232
c.Assert(envsStatus[i].HostedMachineCount, gc.Equals, expected.HostedMachineCount)
233
c.Assert(envsStatus[i].ServiceCount, gc.Equals, expected.ServiceCount)
238
func (s *KillSuite) TestFmtControllerStatus(c *gc.C) {
239
data := controller.CtrData{
246
out := controller.FmtCtrStatus(data)
247
c.Assert(out, gc.Equals, "Waiting on 3 models, 20 machines, 8 applications")
250
func (s *KillSuite) TestFmtEnvironStatus(c *gc.C) {
251
data := controller.ModelData{
260
out := controller.FmtModelStatus(data)
261
c.Assert(out, gc.Equals, "\towner@local/envname (dying), 8 machines, 1 application")