github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  )
    25  
    26  func TestAll(t *stdtesting.T) {
    27  	coretesting.MgoTestPackage(t)
    28  }
    29  
    30  type clientSuite struct {
    31  	testing.JujuConnSuite
    32  
    33  	resources  *common.Resources
    34  	authoriser apiservertesting.FakeAuthorizer
    35  	haServer   *highavailability.HighAvailabilityAPI
    36  	pingers    []*presence.Pinger
    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 cpu-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.pingers = []*presence.Pinger{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) TearDownTest(c *gc.C) {
    85  	for _, pinger := range s.pingers {
    86  		assertKill(c, pinger)
    87  	}
    88  	s.JujuConnSuite.TearDownTest(c)
    89  }
    90  
    91  func (s *clientSuite) setAgentPresence(c *gc.C, machineId string) *presence.Pinger {
    92  	m, err := s.State.Machine(machineId)
    93  	c.Assert(err, jc.ErrorIsNil)
    94  	pinger, err := m.SetAgentPresence()
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	s.State.StartSync()
    97  	err = m.WaitAgentPresence(coretesting.LongWait)
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	return pinger
   100  }
   101  
   102  func (s *clientSuite) enableHA(
   103  	c *gc.C, numControllers int, cons constraints.Value, series string, placement []string,
   104  ) (params.ControllersChanges, error) {
   105  	return enableHA(c, s.haServer, numControllers, cons, series, placement)
   106  }
   107  
   108  func enableHA(
   109  	c *gc.C, haServer *highavailability.HighAvailabilityAPI, numControllers int, cons constraints.Value, series string, placement []string,
   110  ) (params.ControllersChanges, error) {
   111  	arg := params.ControllersSpecs{
   112  		Specs: []params.ControllersSpec{{
   113  			NumControllers: numControllers,
   114  			Constraints:    cons,
   115  			Series:         series,
   116  			Placement:      placement,
   117  		}}}
   118  	results, err := haServer.EnableHA(arg)
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	c.Assert(results.Results, gc.HasLen, 1)
   121  	result := results.Results[0]
   122  	// We explicitly return nil here so we can do typed nil checking
   123  	// of the result like normal.
   124  	err = nil
   125  	if result.Error != nil {
   126  		err = result.Error
   127  	}
   128  	return result.Result, err
   129  }
   130  
   131  func (s *clientSuite) TestEnableHASeries(c *gc.C) {
   132  	machines, err := s.State.AllMachines()
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	c.Assert(machines, gc.HasLen, 1)
   135  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   136  
   137  	enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, nil)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   140  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   141  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   142  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   143  
   144  	machines, err = s.State.AllMachines()
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	c.Assert(machines, gc.HasLen, 3)
   147  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   148  	c.Assert(machines[1].Series(), gc.Equals, "quantal")
   149  	c.Assert(machines[2].Series(), gc.Equals, "quantal")
   150  
   151  	pingerB := s.setAgentPresence(c, "1")
   152  	defer assertKill(c, pingerB)
   153  
   154  	pingerC := s.setAgentPresence(c, "2")
   155  	defer assertKill(c, pingerC)
   156  
   157  	enableHAResult, err = s.enableHA(c, 5, emptyCons, "non-default", nil)
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1", "machine-2"})
   160  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-3", "machine-4"})
   161  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   162  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   163  
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	machines, err = s.State.AllMachines()
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	c.Assert(machines, gc.HasLen, 5)
   168  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   169  	c.Assert(machines[1].Series(), gc.Equals, "quantal")
   170  	c.Assert(machines[2].Series(), gc.Equals, "quantal")
   171  	c.Assert(machines[3].Series(), gc.Equals, "non-default")
   172  	c.Assert(machines[4].Series(), gc.Equals, "non-default")
   173  }
   174  
   175  func (s *clientSuite) TestEnableHAConstraints(c *gc.C) {
   176  	enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil)
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   179  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   180  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   181  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   182  
   183  	machines, err := s.State.AllMachines()
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	c.Assert(machines, gc.HasLen, 3)
   186  	expectedCons := []constraints.Value{
   187  		controllerCons,
   188  		constraints.MustParse("mem=4G"),
   189  		constraints.MustParse("mem=4G"),
   190  	}
   191  	for i, m := range machines {
   192  		cons, err := m.Constraints()
   193  		c.Assert(err, jc.ErrorIsNil)
   194  		c.Check(cons, gc.DeepEquals, expectedCons[i])
   195  	}
   196  }
   197  
   198  func (s *clientSuite) TestEnableHAEmptyConstraints(c *gc.C) {
   199  	enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, nil)
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   202  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   203  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   204  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   205  
   206  	machines, err := s.State.AllMachines()
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	c.Assert(machines, gc.HasLen, 3)
   209  	for _, m := range machines {
   210  		cons, err := m.Constraints()
   211  		c.Assert(err, jc.ErrorIsNil)
   212  		c.Check(cons, gc.DeepEquals, controllerCons)
   213  	}
   214  }
   215  
   216  func (s *clientSuite) TestBlockMakeHA(c *gc.C) {
   217  	// Block all changes.
   218  	s.BlockAllChanges(c, "TestBlockEnableHA")
   219  
   220  	enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil)
   221  	s.AssertBlocked(c, err, "TestBlockEnableHA")
   222  
   223  	c.Assert(enableHAResult.Maintained, gc.HasLen, 0)
   224  	c.Assert(enableHAResult.Added, gc.HasLen, 0)
   225  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   226  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   227  
   228  	machines, err := s.State.AllMachines()
   229  	c.Assert(err, jc.ErrorIsNil)
   230  	c.Assert(machines, gc.HasLen, 1)
   231  }
   232  
   233  func (s *clientSuite) TestEnableHAPlacement(c *gc.C) {
   234  	placement := []string{"valid"}
   235  	enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, placement)
   236  	c.Assert(err, jc.ErrorIsNil)
   237  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   238  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   239  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   240  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   241  
   242  	machines, err := s.State.AllMachines()
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	c.Assert(machines, gc.HasLen, 3)
   245  	expectedCons := []constraints.Value{
   246  		controllerCons,
   247  		constraints.MustParse("mem=4G"),
   248  		constraints.MustParse("mem=4G"),
   249  	}
   250  	expectedPlacement := []string{"", "valid", ""}
   251  	for i, m := range machines {
   252  		cons, err := m.Constraints()
   253  		c.Assert(err, jc.ErrorIsNil)
   254  		c.Check(cons, gc.DeepEquals, expectedCons[i])
   255  		c.Check(m.Placement(), gc.Equals, expectedPlacement[i])
   256  	}
   257  }
   258  
   259  func (s *clientSuite) TestEnableHAPlacementTo(c *gc.C) {
   260  	machine1Cons := constraints.MustParse("mem=8G")
   261  	_, err := s.State.AddMachines(state.MachineTemplate{
   262  		Series:      "quantal",
   263  		Jobs:        []state.MachineJob{state.JobHostUnits},
   264  		Constraints: machine1Cons,
   265  	})
   266  	s.pingers = append(s.pingers, s.setAgentPresence(c, "1"))
   267  
   268  	_, err = s.State.AddMachine("quantal", state.JobHostUnits)
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	s.pingers = append(s.pingers, s.setAgentPresence(c, "2"))
   271  
   272  	placement := []string{"1", "2"}
   273  	enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, placement)
   274  	c.Assert(err, jc.ErrorIsNil)
   275  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   276  	c.Assert(enableHAResult.Added, gc.HasLen, 0)
   277  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   278  	c.Assert(enableHAResult.Converted, gc.DeepEquals, []string{"machine-1", "machine-2"})
   279  
   280  	machines, err := s.State.AllMachines()
   281  	c.Assert(err, jc.ErrorIsNil)
   282  	c.Assert(machines, gc.HasLen, 3)
   283  	expectedCons := []constraints.Value{
   284  		controllerCons,
   285  		machine1Cons,
   286  		{},
   287  	}
   288  	expectedPlacement := []string{"", "", ""}
   289  	for i, m := range machines {
   290  		cons, err := m.Constraints()
   291  		c.Assert(err, jc.ErrorIsNil)
   292  		c.Check(cons, gc.DeepEquals, expectedCons[i])
   293  		c.Check(m.Placement(), gc.Equals, expectedPlacement[i])
   294  	}
   295  }
   296  
   297  func (s *clientSuite) TestEnableHA0Preserves(c *gc.C) {
   298  	// A value of 0 says either "if I'm not HA, make me HA" or "preserve my
   299  	// current HA settings".
   300  	enableHAResult, err := s.enableHA(c, 0, emptyCons, defaultSeries, nil)
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   303  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   304  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   305  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   306  
   307  	machines, err := s.State.AllMachines()
   308  	c.Assert(machines, gc.HasLen, 3)
   309  
   310  	pingerB := s.setAgentPresence(c, "1")
   311  	defer assertKill(c, pingerB)
   312  
   313  	// Now, we keep agent 1 alive, but not agent 2, calling
   314  	// EnableHA(0) again will cause us to start another machine
   315  	enableHAResult, err = s.enableHA(c, 0, emptyCons, defaultSeries, nil)
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1"})
   318  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-3"})
   319  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   320  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   321  
   322  	machines, err = s.State.AllMachines()
   323  	c.Assert(err, jc.ErrorIsNil)
   324  	c.Assert(machines, gc.HasLen, 4)
   325  }
   326  
   327  func (s *clientSuite) TestEnableHA0Preserves5(c *gc.C) {
   328  	// Start off with 5 servers
   329  	enableHAResult, err := s.enableHA(c, 5, emptyCons, defaultSeries, nil)
   330  	c.Assert(err, jc.ErrorIsNil)
   331  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   332  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2", "machine-3", "machine-4"})
   333  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   334  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   335  
   336  	machines, err := s.State.AllMachines()
   337  	c.Assert(machines, gc.HasLen, 5)
   338  	pingerB := s.setAgentPresence(c, "1")
   339  	defer assertKill(c, pingerB)
   340  
   341  	pingerC := s.setAgentPresence(c, "2")
   342  	defer assertKill(c, pingerC)
   343  
   344  	pingerD := s.setAgentPresence(c, "3")
   345  	defer assertKill(c, pingerD)
   346  	// Keeping all alive but one, will bring up 1 more server to preserve 5
   347  	enableHAResult, err = s.enableHA(c, 0, emptyCons, defaultSeries, nil)
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1",
   350  		"machine-2", "machine-3"})
   351  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-5"})
   352  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   353  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   354  
   355  	machines, err = s.State.AllMachines()
   356  	c.Assert(machines, gc.HasLen, 6)
   357  	c.Assert(err, jc.ErrorIsNil)
   358  }
   359  
   360  func (s *clientSuite) TestEnableHAErrors(c *gc.C) {
   361  	enableHAResult, err := s.enableHA(c, -1, emptyCons, defaultSeries, nil)
   362  	c.Assert(err, gc.ErrorMatches, "number of controllers must be odd and non-negative")
   363  
   364  	enableHAResult, err = s.enableHA(c, 3, emptyCons, defaultSeries, nil)
   365  	c.Assert(err, jc.ErrorIsNil)
   366  	c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   367  	c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   368  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   369  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   370  
   371  	_, err = s.enableHA(c, 1, emptyCons, defaultSeries, nil)
   372  	c.Assert(err, gc.ErrorMatches, "failed to create new controller machines: cannot reduce controller count")
   373  }
   374  
   375  func (s *clientSuite) TestEnableHAHostedEnvErrors(c *gc.C) {
   376  	st2 := s.Factory.MakeModel(c, &factory.ModelParams{ConfigAttrs: coretesting.Attrs{"controller": false}})
   377  	defer st2.Close()
   378  
   379  	haServer, err := highavailability.NewHighAvailabilityAPI(st2, s.resources, s.authoriser)
   380  	c.Assert(err, jc.ErrorIsNil)
   381  
   382  	enableHAResult, err := enableHA(c, haServer, 3, constraints.MustParse("mem=4G"), defaultSeries, nil)
   383  	c.Assert(errors.Cause(err), gc.ErrorMatches, "unsupported with hosted models")
   384  
   385  	c.Assert(enableHAResult.Maintained, gc.HasLen, 0)
   386  	c.Assert(enableHAResult.Added, gc.HasLen, 0)
   387  	c.Assert(enableHAResult.Removed, gc.HasLen, 0)
   388  	c.Assert(enableHAResult.Converted, gc.HasLen, 0)
   389  
   390  	machines, err := st2.AllMachines()
   391  	c.Assert(err, jc.ErrorIsNil)
   392  	c.Assert(machines, gc.HasLen, 0)
   393  }