github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/highavailability/highavailability_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package highavailability_test
     5  
     6  import (
     7  	stdtesting "testing"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	commontesting "github.com/juju/juju/apiserver/common/testing"
    15  	"github.com/juju/juju/apiserver/highavailability"
    16  	"github.com/juju/juju/apiserver/params"
    17  	apiservertesting "github.com/juju/juju/apiserver/testing"
    18  	"github.com/juju/juju/constraints"
    19  	"github.com/juju/juju/juju/testing"
    20  	"github.com/juju/juju/state"
    21  	"github.com/juju/juju/state/presence"
    22  	coretesting "github.com/juju/juju/testing"
    23  	"github.com/juju/juju/testing/factory"
    24  	"github.com/juju/juju/worker"
    25  )
    26  
    27  func TestAll(t *stdtesting.T) {
    28  	coretesting.MgoTestPackage(t)
    29  }
    30  
    31  type clientSuite struct {
    32  	testing.JujuConnSuite
    33  
    34  	resources  *common.Resources
    35  	authoriser apiservertesting.FakeAuthorizer
    36  	haServer   *highavailability.HighAvailabilityAPI
    37  
    38  	commontesting.BlockHelper
    39  }
    40  
    41  type KillerForTesting interface {
    42  	KillForTesting() error
    43  }
    44  
    45  var _ = gc.Suite(&clientSuite{})
    46  
    47  func assertKill(c *gc.C, killer KillerForTesting) {
    48  	c.Assert(killer.KillForTesting(), gc.IsNil)
    49  }
    50  
    51  var (
    52  	emptyCons      = constraints.Value{}
    53  	controllerCons = constraints.MustParse("mem=16G cores=16")
    54  	defaultSeries  = ""
    55  )
    56  
    57  func (s *clientSuite) SetUpTest(c *gc.C) {
    58  	s.JujuConnSuite.SetUpTest(c)
    59  	s.resources = common.NewResources()
    60  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    61  
    62  	s.authoriser = apiservertesting.FakeAuthorizer{
    63  		Tag:            s.AdminUserTag(c),
    64  		EnvironManager: true,
    65  	}
    66  
    67  	var err error
    68  	s.haServer, err = highavailability.NewHighAvailabilityAPI(s.State, s.resources, s.authoriser)
    69  	c.Assert(err, jc.ErrorIsNil)
    70  
    71  	_, err = s.State.AddMachines(state.MachineTemplate{
    72  		Series:      "quantal",
    73  		Jobs:        []state.MachineJob{state.JobManageModel},
    74  		Constraints: controllerCons,
    75  	})
    76  	c.Assert(err, jc.ErrorIsNil)
    77  	// We have to ensure the agents are alive, or EnableHA will
    78  	// create more to replace them.
    79  	s.setAgentPresence(c, "0")
    80  	s.BlockHelper = commontesting.NewBlockHelper(s.APIState)
    81  	s.AddCleanup(func(*gc.C) { s.BlockHelper.Close() })
    82  }
    83  
    84  func (s *clientSuite) setAgentPresence(c *gc.C, machineId string) *presence.Pinger {
    85  	m, err := s.State.Machine(machineId)
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	pinger, err := m.SetAgentPresence()
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	s.AddCleanup(func(c *gc.C) {
    90  		c.Assert(worker.Stop(pinger), jc.ErrorIsNil)
    91  	})
    92  
    93  	s.State.StartSync()
    94  	err = m.WaitAgentPresence(coretesting.LongWait)
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	return pinger
    97  }
    98  
    99  func (s *clientSuite) enableHA(
   100  	c *gc.C, numControllers int, cons constraints.Value, series string, placement []string,
   101  ) (params.ControllersChanges, error) {
   102  	return enableHA(c, s.haServer, numControllers, cons, series, placement)
   103  }
   104  
   105  func enableHA(
   106  	c *gc.C, haServer *highavailability.HighAvailabilityAPI, numControllers int, cons constraints.Value, series string, placement []string,
   107  ) (params.ControllersChanges, error) {
   108  	arg := params.ControllersSpecs{
   109  		Specs: []params.ControllersSpec{{
   110  			NumControllers: numControllers,
   111  			Constraints:    cons,
   112  			Series:         series,
   113  			Placement:      placement,
   114  		}}}
   115  	results, err := haServer.EnableHA(arg)
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	c.Assert(results.Results, gc.HasLen, 1)
   118  	result := results.Results[0]
   119  	// We explicitly return nil here so we can do typed nil checking
   120  	// of the result like normal.
   121  	err = nil
   122  	if result.Error != nil {
   123  		err = result.Error
   124  	}
   125  	return result.Result, err
   126  }
   127  
   128  func (s *clientSuite) TestEnableHASeries(c *gc.C) {
   129  	machines, err := s.State.AllMachines()
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	c.Assert(machines, gc.HasLen, 1)
   132  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   133  
   134  	enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, nil)
   135  	c.Assert(err, jc.ErrorIsNil)
   136  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   137  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   138  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   139  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   140  
   141  	machines, err = s.State.AllMachines()
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	c.Assert(machines, gc.HasLen, 3)
   144  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   145  	c.Assert(machines[1].Series(), gc.Equals, "quantal")
   146  	c.Assert(machines[2].Series(), gc.Equals, "quantal")
   147  
   148  	s.setAgentPresence(c, "1")
   149  	s.setAgentPresence(c, "2")
   150  
   151  	enableHAResult, err = s.enableHA(c, 5, emptyCons, "non-default", nil)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1", "machine-2"})
   154  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-3", "machine-4"})
   155  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   156  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   157  
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	machines, err = s.State.AllMachines()
   160  	c.Assert(err, jc.ErrorIsNil)
   161  	c.Assert(machines, gc.HasLen, 5)
   162  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   163  	c.Assert(machines[1].Series(), gc.Equals, "quantal")
   164  	c.Assert(machines[2].Series(), gc.Equals, "quantal")
   165  	c.Assert(machines[3].Series(), gc.Equals, "non-default")
   166  	c.Assert(machines[4].Series(), gc.Equals, "non-default")
   167  }
   168  
   169  func (s *clientSuite) TestEnableHAConstraints(c *gc.C) {
   170  	enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil)
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   173  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   174  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   175  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   176  
   177  	machines, err := s.State.AllMachines()
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	c.Assert(machines, gc.HasLen, 3)
   180  	expectedCons := []constraints.Value{
   181  		controllerCons,
   182  		constraints.MustParse("mem=4G"),
   183  		constraints.MustParse("mem=4G"),
   184  	}
   185  	for i, m := range machines {
   186  		cons, err := m.Constraints()
   187  		c.Assert(err, jc.ErrorIsNil)
   188  		c.Check(cons, gc.DeepEquals, expectedCons[i])
   189  	}
   190  }
   191  
   192  func (s *clientSuite) TestEnableHAEmptyConstraints(c *gc.C) {
   193  	enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, nil)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   196  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   197  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   198  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   199  
   200  	machines, err := s.State.AllMachines()
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	c.Assert(machines, gc.HasLen, 3)
   203  	for _, m := range machines {
   204  		cons, err := m.Constraints()
   205  		c.Assert(err, jc.ErrorIsNil)
   206  		c.Check(cons, gc.DeepEquals, controllerCons)
   207  	}
   208  }
   209  
   210  func (s *clientSuite) TestBlockMakeHA(c *gc.C) {
   211  	// Block all changes.
   212  	s.BlockAllChanges(c, "TestBlockEnableHA")
   213  
   214  	enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil)
   215  	s.AssertBlocked(c, err, "TestBlockEnableHA")
   216  
   217  	c.Assert(enableHAResult.Maintained, gc.HasLen, 0)
   218  	c.Assert(enableHAResult.Added, gc.HasLen, 0)
   219  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   220  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   221  
   222  	machines, err := s.State.AllMachines()
   223  	c.Assert(err, jc.ErrorIsNil)
   224  	c.Assert(machines, gc.HasLen, 1)
   225  }
   226  
   227  func (s *clientSuite) TestEnableHAPlacement(c *gc.C) {
   228  	placement := []string{"valid"}
   229  	enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, placement)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   232  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   233  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   234  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   235  
   236  	machines, err := s.State.AllMachines()
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	c.Assert(machines, gc.HasLen, 3)
   239  	expectedCons := []constraints.Value{
   240  		controllerCons,
   241  		constraints.MustParse("mem=4G"),
   242  		constraints.MustParse("mem=4G"),
   243  	}
   244  	expectedPlacement := []string{"", "valid", ""}
   245  	for i, m := range machines {
   246  		cons, err := m.Constraints()
   247  		c.Assert(err, jc.ErrorIsNil)
   248  		c.Check(cons, gc.DeepEquals, expectedCons[i])
   249  		c.Check(m.Placement(), gc.Equals, expectedPlacement[i])
   250  	}
   251  }
   252  
   253  func (s *clientSuite) TestEnableHAPlacementTo(c *gc.C) {
   254  	machine1Cons := constraints.MustParse("mem=8G")
   255  	_, err := s.State.AddMachines(state.MachineTemplate{
   256  		Series:      "quantal",
   257  		Jobs:        []state.MachineJob{state.JobHostUnits},
   258  		Constraints: machine1Cons,
   259  	})
   260  	s.setAgentPresence(c, "1")
   261  
   262  	_, err = s.State.AddMachine("quantal", state.JobHostUnits)
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	s.setAgentPresence(c, "2")
   265  
   266  	placement := []string{"1", "2"}
   267  	enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, placement)
   268  	c.Assert(err, jc.ErrorIsNil)
   269  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   270  	c.Assert(enableHAResult.Added, gc.HasLen, 0)
   271  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   272  	c.Assert(enableHAResult.Converted, gc.DeepEquals, []string{"machine-1", "machine-2"})
   273  
   274  	machines, err := s.State.AllMachines()
   275  	c.Assert(err, jc.ErrorIsNil)
   276  	c.Assert(machines, gc.HasLen, 3)
   277  	expectedCons := []constraints.Value{
   278  		controllerCons,
   279  		machine1Cons,
   280  		{},
   281  	}
   282  	expectedPlacement := []string{"", "", ""}
   283  	for i, m := range machines {
   284  		cons, err := m.Constraints()
   285  		c.Assert(err, jc.ErrorIsNil)
   286  		c.Check(cons, gc.DeepEquals, expectedCons[i])
   287  		c.Check(m.Placement(), gc.Equals, expectedPlacement[i])
   288  	}
   289  }
   290  
   291  func (s *clientSuite) TestEnableHA0Preserves(c *gc.C) {
   292  	// A value of 0 says either "if I'm not HA, make me HA" or "preserve my
   293  	// current HA settings".
   294  	enableHAResult, err := s.enableHA(c, 0, emptyCons, defaultSeries, nil)
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   297  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   298  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   299  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   300  
   301  	machines, err := s.State.AllMachines()
   302  	c.Assert(machines, gc.HasLen, 3)
   303  
   304  	s.setAgentPresence(c, "1")
   305  
   306  	// Now, we keep agent 1 alive, but not agent 2, calling
   307  	// EnableHA(0) again will cause us to start another machine
   308  	enableHAResult, err = s.enableHA(c, 0, emptyCons, defaultSeries, nil)
   309  	c.Assert(err, jc.ErrorIsNil)
   310  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1"})
   311  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-3"})
   312  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   313  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   314  
   315  	machines, err = s.State.AllMachines()
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	c.Assert(machines, gc.HasLen, 4)
   318  }
   319  
   320  func (s *clientSuite) TestEnableHA0Preserves5(c *gc.C) {
   321  	// Start off with 5 servers
   322  	enableHAResult, err := s.enableHA(c, 5, emptyCons, defaultSeries, nil)
   323  	c.Assert(err, jc.ErrorIsNil)
   324  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   325  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2", "machine-3", "machine-4"})
   326  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   327  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   328  
   329  	machines, err := s.State.AllMachines()
   330  	c.Assert(machines, gc.HasLen, 5)
   331  	s.setAgentPresence(c, "1")
   332  	s.setAgentPresence(c, "2")
   333  	s.setAgentPresence(c, "3")
   334  	// Keeping all alive but one, will bring up 1 more server to preserve 5
   335  	enableHAResult, err = s.enableHA(c, 0, emptyCons, defaultSeries, nil)
   336  	c.Assert(err, jc.ErrorIsNil)
   337  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1",
   338  		"machine-2", "machine-3"})
   339  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-5"})
   340  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   341  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   342  
   343  	machines, err = s.State.AllMachines()
   344  	c.Assert(machines, gc.HasLen, 6)
   345  	c.Assert(err, jc.ErrorIsNil)
   346  }
   347  
   348  func (s *clientSuite) TestEnableHAErrors(c *gc.C) {
   349  	enableHAResult, err := s.enableHA(c, -1, emptyCons, defaultSeries, nil)
   350  	c.Assert(err, gc.ErrorMatches, "number of controllers must be odd and non-negative")
   351  
   352  	enableHAResult, err = s.enableHA(c, 3, emptyCons, defaultSeries, nil)
   353  	c.Assert(err, jc.ErrorIsNil)
   354  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   355  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   356  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   357  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   358  
   359  	_, err = s.enableHA(c, 1, emptyCons, defaultSeries, nil)
   360  	c.Assert(err, gc.ErrorMatches, "failed to create new controller machines: cannot reduce controller count")
   361  }
   362  
   363  func (s *clientSuite) TestEnableHAHostedEnvErrors(c *gc.C) {
   364  	st2 := s.Factory.MakeModel(c, &factory.ModelParams{ConfigAttrs: coretesting.Attrs{"controller": false}})
   365  	defer st2.Close()
   366  
   367  	haServer, err := highavailability.NewHighAvailabilityAPI(st2, s.resources, s.authoriser)
   368  	c.Assert(err, jc.ErrorIsNil)
   369  
   370  	enableHAResult, err := enableHA(c, haServer, 3, constraints.MustParse("mem=4G"), defaultSeries, nil)
   371  	c.Assert(errors.Cause(err), gc.ErrorMatches, "unsupported with hosted models")
   372  
   373  	c.Assert(enableHAResult.Maintained, gc.HasLen, 0)
   374  	c.Assert(enableHAResult.Added, gc.HasLen, 0)
   375  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   376  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   377  
   378  	machines, err := st2.AllMachines()
   379  	c.Assert(err, jc.ErrorIsNil)
   380  	c.Assert(machines, gc.HasLen, 0)
   381  }
   382  
   383  func (s *clientSuite) TestEnableHAMultipleSpecs(c *gc.C) {
   384  	arg := params.ControllersSpecs{
   385  		Specs: []params.ControllersSpec{
   386  			{NumControllers: 3},
   387  			{NumControllers: 5},
   388  		},
   389  	}
   390  	results, err := s.haServer.EnableHA(arg)
   391  	c.Check(err, gc.ErrorMatches, "only one controller spec is supported")
   392  	c.Check(results.Results, gc.HasLen, 0)
   393  }
   394  
   395  func (s *clientSuite) TestEnableHANoSpecs(c *gc.C) {
   396  	arg := params.ControllersSpecs{
   397  		Specs: []params.ControllersSpec{},
   398  	}
   399  	results, err := s.haServer.EnableHA(arg)
   400  	c.Check(err, jc.ErrorIsNil)
   401  	c.Check(results.Results, gc.HasLen, 0)
   402  }