github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/cmd/juju/controller/kill_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package controller_test
     5  
     6  import (
     7  	"bytes"
     8  	"time"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/names"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils/clock"
    15  	gc "gopkg.in/check.v1"
    16  
    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"
    27  )
    28  
    29  type KillSuite struct {
    30  	baseDestroySuite
    31  }
    32  
    33  var _ = gc.Suite(&KillSuite{})
    34  
    35  func (s *KillSuite) runKillCommand(c *gc.C, args ...string) (*cmd.Context, error) {
    36  	return testing.RunCommand(c, s.newKillCommand(), args...)
    37  }
    38  
    39  func (s *KillSuite) newKillCommand() cmd.Command {
    40  	return controller.NewKillCommandForTest(
    41  		s.api, s.clientapi, s.store, s.apierror, &mockClock{}, nil)
    42  }
    43  
    44  func (s *KillSuite) TestKillNoControllerNameError(c *gc.C) {
    45  	_, err := s.runKillCommand(c)
    46  	c.Assert(err, gc.ErrorMatches, "no controller specified")
    47  }
    48  
    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")
    52  }
    53  
    54  func (s *KillSuite) TestKillUnknownArgument(c *gc.C) {
    55  	_, err := s.runKillCommand(c, "model", "whoops")
    56  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["whoops"\]`)
    57  }
    58  
    59  func (s *KillSuite) TestKillUnknownController(c *gc.C) {
    60  	_, err := s.runKillCommand(c, "foo")
    61  	c.Assert(err, gc.ErrorMatches, `controller foo not found`)
    62  }
    63  
    64  func (s *KillSuite) TestKillCannotConnectToAPISucceeds(c *gc.C) {
    65  	s.apierror = errors.New("connection refused")
    66  	ctx, err := s.runKillCommand(c, "local.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, "local.test1", s.store)
    70  }
    71  
    72  func (s *KillSuite) TestKillWithAPIConnection(c *gc.C) {
    73  	_, err := s.runKillCommand(c, "local.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, "local.test1", s.store)
    78  }
    79  
    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",
    86  	)
    87  	checkControllerExistsInStore(c, "test3", s.store)
    88  }
    89  
    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 bootstrap config from API: controller \"test3\" not found",
    95  	)
    96  	checkControllerExistsInStore(c, "test3", s.store)
    97  }
    98  
    99  func (s *KillSuite) TestKillDestroysControllerWithAPIError(c *gc.C) {
   100  	s.api.SetErrors(errors.New("some destroy error"))
   101  	ctx, err := s.runKillCommand(c, "local.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, "local.test1", s.store)
   106  }
   107  
   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)
   112  	ctx.Stdout = &stdout
   113  	ctx.Stdin = &stdin
   114  
   115  	// Ensure confirmation is requested if "-y" is not specified.
   116  	stdin.WriteString("n")
   117  	_, errc := cmdtesting.RunCommand(ctx, s.newKillCommand(), "local.test1")
   118  	select {
   119  	case err := <-errc:
   120  		c.Check(err, gc.ErrorMatches, "controller destruction aborted")
   121  	case <-time.After(testing.LongWait):
   122  		c.Fatalf("command took too long")
   123  	}
   124  	c.Check(testing.Stdout(ctx), gc.Matches, "WARNING!.*local.test1(.|\n)*")
   125  	checkControllerExistsInStore(c, "local.test1", s.store)
   126  }
   127  
   128  func (s *KillSuite) TestKillCommandControllerAlias(c *gc.C) {
   129  	_, err := testing.RunCommand(c, s.newKillCommand(), "local.test1", "-y")
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	checkControllerRemovedFromStore(c, "local.test1:test1", s.store)
   132  }
   133  
   134  func (s *KillSuite) TestKillAPIPermErrFails(c *gc.C) {
   135  	testDialer := func(_ jujuclient.ClientStore, controllerName, accountName, modelName string) (api.Connection, error) {
   136  		return nil, common.ErrPerm
   137  	}
   138  	cmd := controller.NewKillCommandForTest(nil, nil, s.store, nil, clock.WallClock, modelcmd.OpenFunc(testDialer))
   139  	_, err := testing.RunCommand(c, cmd, "local.test1", "-y")
   140  	c.Assert(err, gc.ErrorMatches, "cannot destroy controller: permission denied")
   141  	checkControllerExistsInStore(c, "local.test1", s.store)
   142  }
   143  
   144  func (s *KillSuite) TestKillEarlyAPIConnectionTimeout(c *gc.C) {
   145  	clock := &mockClock{}
   146  
   147  	stop := make(chan struct{})
   148  	defer close(stop)
   149  	testDialer := func(_ jujuclient.ClientStore, controllerName, accountName, modelName string) (api.Connection, error) {
   150  		<-stop
   151  		return nil, errors.New("kill command waited too long")
   152  	}
   153  
   154  	cmd := controller.NewKillCommandForTest(nil, nil, s.store, nil, clock, modelcmd.OpenFunc(testDialer))
   155  	ctx, err := testing.RunCommand(c, cmd, "local.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, "local.test1", s.store)
   159  	// Check that we were actually told to wait for 10s.
   160  	c.Assert(clock.wait, gc.Equals, 10*time.Second)
   161  }
   162  
   163  // mockClock will panic if anything but After is called
   164  type mockClock struct {
   165  	clock.Clock
   166  	wait time.Duration
   167  }
   168  
   169  func (m *mockClock) After(duration time.Duration) <-chan time.Time {
   170  	m.wait = duration
   171  	return time.After(time.Millisecond)
   172  }
   173  
   174  func (s *KillSuite) TestControllerStatus(c *gc.C) {
   175  	s.api.allEnvs = []base.UserModel{
   176  		{Name: "admin",
   177  			UUID:  "123",
   178  			Owner: names.NewUserTag("admin").String(),
   179  		}, {Name: "env1",
   180  			UUID:  "456",
   181  			Owner: names.NewUserTag("bob").String(),
   182  		}, {Name: "env2",
   183  			UUID:  "789",
   184  			Owner: names.NewUserTag("jo").String(),
   185  		},
   186  	}
   187  
   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{
   193  			UUID:               env.UUID,
   194  			Life:               params.Dying,
   195  			HostedMachineCount: 2,
   196  			ServiceCount:       1,
   197  			Owner:              owner.Canonical(),
   198  		}
   199  	}
   200  
   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)
   207  
   208  	for i, expected := range []struct {
   209  		Owner              string
   210  		Name               string
   211  		Life               params.Life
   212  		HostedMachineCount int
   213  		ServiceCount       int
   214  	}{
   215  		{
   216  			Owner:              "bob@local",
   217  			Name:               "env1",
   218  			Life:               params.Dying,
   219  			HostedMachineCount: 2,
   220  			ServiceCount:       1,
   221  		}, {
   222  			Owner:              "jo@local",
   223  			Name:               "env2",
   224  			Life:               params.Dying,
   225  			HostedMachineCount: 2,
   226  			ServiceCount:       1,
   227  		},
   228  	} {
   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)
   234  	}
   235  
   236  }
   237  
   238  func (s *KillSuite) TestFmtControllerStatus(c *gc.C) {
   239  	data := controller.CtrData{
   240  		"uuid",
   241  		params.Alive,
   242  		3,
   243  		20,
   244  		8,
   245  	}
   246  	out := controller.FmtCtrStatus(data)
   247  	c.Assert(out, gc.Equals, "Waiting on 3 models, 20 machines, 8 services")
   248  }
   249  
   250  func (s *KillSuite) TestFmtEnvironStatus(c *gc.C) {
   251  	data := controller.ModelData{
   252  		"uuid",
   253  		"owner@local",
   254  		"envname",
   255  		params.Dying,
   256  		8,
   257  		1,
   258  	}
   259  
   260  	out := controller.FmtModelStatus(data)
   261  	c.Assert(out, gc.Equals, "\towner@local/envname (dying), 8 machines, 1 service")
   262  }