github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/commands/enableha_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  
    11  	"github.com/juju/cmd"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	goyaml "gopkg.in/yaml.v2"
    15  
    16  	"github.com/juju/juju/apiserver/common"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/cmd/modelcmd"
    19  	"github.com/juju/juju/constraints"
    20  	"github.com/juju/juju/instance"
    21  	"github.com/juju/juju/juju/testing"
    22  	"github.com/juju/juju/state"
    23  	coretesting "github.com/juju/juju/testing"
    24  	"github.com/juju/juju/testing/factory"
    25  )
    26  
    27  type EnableHASuite struct {
    28  	// TODO (cherylj) change this back to a FakeJujuXDGDataHomeSuite to
    29  	// remove the mongo dependency once ensure-availability is
    30  	// moved under a supercommand again.
    31  	testing.JujuConnSuite
    32  	fake *fakeHAClient
    33  }
    34  
    35  // invalidNumServers is a number of controllers that would
    36  // never be generated by the enable-ha command.
    37  const invalidNumServers = -2
    38  
    39  func (s *EnableHASuite) SetUpTest(c *gc.C) {
    40  	s.JujuConnSuite.SetUpTest(c)
    41  
    42  	// Initialize numControllers to an invalid number to validate
    43  	// that enable-ha doesn't call into the API when its
    44  	// pre-checks fail
    45  	s.fake = &fakeHAClient{numControllers: invalidNumServers}
    46  }
    47  
    48  type fakeHAClient struct {
    49  	numControllers int
    50  	cons           constraints.Value
    51  	err            error
    52  	placement      []string
    53  	result         params.ControllersChanges
    54  }
    55  
    56  func (f *fakeHAClient) Close() error {
    57  	return nil
    58  }
    59  
    60  func (f *fakeHAClient) EnableHA(numControllers int, cons constraints.Value, placement []string) (params.ControllersChanges, error) {
    61  
    62  	f.numControllers = numControllers
    63  	f.cons = cons
    64  	f.placement = placement
    65  
    66  	if f.err != nil {
    67  		return f.result, f.err
    68  	}
    69  
    70  	if numControllers == 1 {
    71  		return f.result, nil
    72  	}
    73  
    74  	// In the real HAClient, specifying a numControllers value of 0
    75  	// indicates that the default value (3) should be used
    76  	if numControllers == 0 {
    77  		numControllers = 3
    78  	}
    79  
    80  	f.result.Maintained = append(f.result.Maintained, "machine-0")
    81  
    82  	for _, p := range placement {
    83  		m, err := instance.ParsePlacement(p)
    84  		if err == nil && m.Scope == instance.MachineScope {
    85  			f.result.Converted = append(f.result.Converted, "machine-"+m.Directive)
    86  		}
    87  	}
    88  
    89  	// We may need to pretend that we added some machines.
    90  	for i := len(f.result.Converted) + 1; i < numControllers; i++ {
    91  		f.result.Added = append(f.result.Added, fmt.Sprintf("machine-%d", i))
    92  	}
    93  
    94  	return f.result, nil
    95  }
    96  
    97  var _ = gc.Suite(&EnableHASuite{})
    98  
    99  func (s *EnableHASuite) runEnableHA(c *gc.C, args ...string) (*cmd.Context, error) {
   100  	command := &enableHACommand{newHAClientFunc: func() (MakeHAClient, error) { return s.fake, nil }}
   101  	return coretesting.RunCommand(c, modelcmd.Wrap(command), args...)
   102  }
   103  
   104  func (s *EnableHASuite) TestEnableHA(c *gc.C) {
   105  	ctx, err := s.runEnableHA(c, "-n", "1")
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	c.Assert(coretesting.Stdout(ctx), gc.Equals, "\n")
   108  
   109  	c.Assert(s.fake.numControllers, gc.Equals, 1)
   110  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   111  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   112  }
   113  
   114  func (s *EnableHASuite) TestBlockEnableHA(c *gc.C) {
   115  	s.fake.err = common.OperationBlockedError("TestBlockEnableHA")
   116  	_, err := s.runEnableHA(c, "-n", "1")
   117  	coretesting.AssertOperationWasBlocked(c, err, ".*TestBlockEnableHA.*")
   118  }
   119  
   120  func (s *EnableHASuite) TestEnableHAFormatYaml(c *gc.C) {
   121  	expected := map[string][]string{
   122  		"maintained": {"0"},
   123  		"added":      {"1", "2"},
   124  	}
   125  
   126  	ctx, err := s.runEnableHA(c, "-n", "3", "--format", "yaml")
   127  	c.Assert(err, jc.ErrorIsNil)
   128  
   129  	c.Assert(s.fake.numControllers, gc.Equals, 3)
   130  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   131  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   132  
   133  	var result map[string][]string
   134  	err = goyaml.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result)
   135  	c.Assert(err, jc.ErrorIsNil)
   136  	c.Assert(result, gc.DeepEquals, expected)
   137  }
   138  
   139  func (s *EnableHASuite) TestEnableHAFormatJson(c *gc.C) {
   140  	expected := map[string][]string{
   141  		"maintained": {"0"},
   142  		"added":      {"1", "2"},
   143  	}
   144  
   145  	ctx, err := s.runEnableHA(c, "-n", "3", "--format", "json")
   146  	c.Assert(err, jc.ErrorIsNil)
   147  
   148  	c.Assert(s.fake.numControllers, gc.Equals, 3)
   149  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   150  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   151  
   152  	var result map[string][]string
   153  	err = json.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	c.Assert(result, gc.DeepEquals, expected)
   156  }
   157  
   158  func (s *EnableHASuite) TestEnableHAWithFive(c *gc.C) {
   159  	// Also test with -n 5 to validate numbers other than 1 and 3
   160  	ctx, err := s.runEnableHA(c, "-n", "5")
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   163  		"maintaining machines: 0\n"+
   164  			"adding machines: 1, 2, 3, 4\n\n")
   165  
   166  	c.Assert(s.fake.numControllers, gc.Equals, 5)
   167  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   168  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   169  }
   170  
   171  func (s *EnableHASuite) TestEnableHAWithConstraints(c *gc.C) {
   172  	ctx, err := s.runEnableHA(c, "--constraints", "mem=4G", "-n", "3")
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   175  		"maintaining machines: 0\n"+
   176  			"adding machines: 1, 2\n\n")
   177  
   178  	c.Assert(s.fake.numControllers, gc.Equals, 3)
   179  	expectedCons := constraints.MustParse("mem=4G")
   180  	c.Assert(s.fake.cons, gc.DeepEquals, expectedCons)
   181  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   182  }
   183  
   184  func (s *EnableHASuite) TestEnableHAWithPlacement(c *gc.C) {
   185  	ctx, err := s.runEnableHA(c, "--to", "valid", "-n", "3")
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   188  		"maintaining machines: 0\n"+
   189  			"adding machines: 1, 2\n\n")
   190  
   191  	c.Assert(s.fake.numControllers, gc.Equals, 3)
   192  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   193  	expectedPlacement := []string{"valid"}
   194  	c.Assert(s.fake.placement, gc.DeepEquals, expectedPlacement)
   195  }
   196  
   197  func (s *EnableHASuite) TestEnableHAErrors(c *gc.C) {
   198  	for _, n := range []int{-1, 2} {
   199  		_, err := s.runEnableHA(c, "-n", fmt.Sprint(n))
   200  		c.Assert(err, gc.ErrorMatches, "must specify a number of controllers odd and non-negative")
   201  	}
   202  
   203  	// Verify that enable-ha didn't call into the API
   204  	c.Assert(s.fake.numControllers, gc.Equals, invalidNumServers)
   205  }
   206  
   207  func (s *EnableHASuite) TestEnableHAAllows0(c *gc.C) {
   208  	// If the number of controllers is specified as "0", the API will
   209  	// then use the default number of 3.
   210  	ctx, err := s.runEnableHA(c, "-n", "0")
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   213  		"maintaining machines: 0\n"+
   214  			"adding machines: 1, 2\n\n")
   215  
   216  	c.Assert(s.fake.numControllers, gc.Equals, 0)
   217  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   218  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   219  }
   220  
   221  func (s *EnableHASuite) TestEnableHADefaultsTo0(c *gc.C) {
   222  	// If the number of controllers is not specified, we pass in 0 to the
   223  	// API.  The API will then use the default number of 3.
   224  	ctx, err := s.runEnableHA(c)
   225  	c.Assert(err, jc.ErrorIsNil)
   226  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   227  		"maintaining machines: 0\n"+
   228  			"adding machines: 1, 2\n\n")
   229  
   230  	c.Assert(s.fake.numControllers, gc.Equals, 0)
   231  	c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   232  	c.Assert(len(s.fake.placement), gc.Equals, 0)
   233  }
   234  
   235  func (s *EnableHASuite) TestEnableHAEndToEnd(c *gc.C) {
   236  	s.Factory.MakeMachine(c, &factory.MachineParams{
   237  		Jobs: []state.MachineJob{state.JobManageModel},
   238  	})
   239  	ctx, err := coretesting.RunCommand(c, newEnableHACommand(), "-n", "3")
   240  	c.Assert(err, jc.ErrorIsNil)
   241  
   242  	// Machine 0 is demoted because it hasn't reported its presence
   243  	c.Assert(coretesting.Stdout(ctx), gc.Equals,
   244  		"adding machines: 1, 2, 3\n"+
   245  			"demoting machines: 0\n\n")
   246  }
   247  
   248  func (s *EnableHASuite) TestEnableHAToExisting(c *gc.C) {
   249  	ctx, err := s.runEnableHA(c, "--to", "1,2")
   250  	c.Assert(err, jc.ErrorIsNil)
   251  	c.Check(coretesting.Stdout(ctx), gc.Equals, `
   252  maintaining machines: 0
   253  converting machines: 1, 2
   254  
   255  `[1:])
   256  
   257  	c.Check(s.fake.numControllers, gc.Equals, 0)
   258  	c.Check(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
   259  	c.Check(len(s.fake.placement), gc.Equals, 2)
   260  }
   261  
   262  func (s *EnableHASuite) TestEnableHADisallowsSeries(c *gc.C) {
   263  	// We don't allow --series as an argument. This test ensures it is not
   264  	// inadvertantly added back.
   265  	ctx, err := s.runEnableHA(c, "-n", "0", "--series", "xenian")
   266  	c.Assert(err, gc.ErrorMatches, "flag provided but not defined: --series")
   267  	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
   268  }