github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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  	"github.com/juju/juju/apiserver/highavailability"
    15  	"github.com/juju/juju/apiserver/params"
    16  	apiservertesting "github.com/juju/juju/apiserver/testing"
    17  	"github.com/juju/juju/constraints"
    18  	"github.com/juju/juju/juju/testing"
    19  	"github.com/juju/juju/state"
    20  	"github.com/juju/juju/state/presence"
    21  	coretesting "github.com/juju/juju/testing"
    22  )
    23  
    24  func TestAll(t *stdtesting.T) {
    25  	coretesting.MgoTestPackage(t)
    26  }
    27  
    28  type clientSuite struct {
    29  	testing.JujuConnSuite
    30  
    31  	resources  *common.Resources
    32  	authoriser apiservertesting.FakeAuthorizer
    33  	haServer   *highavailability.HighAvailabilityAPI
    34  	pinger     *presence.Pinger
    35  }
    36  
    37  type Killer interface {
    38  	Kill() error
    39  }
    40  
    41  var _ = gc.Suite(&clientSuite{})
    42  
    43  func assertKill(c *gc.C, killer Killer) {
    44  	c.Assert(killer.Kill(), gc.IsNil)
    45  }
    46  
    47  var (
    48  	emptyCons     = constraints.Value{}
    49  	defaultSeries = ""
    50  )
    51  
    52  func (s *clientSuite) SetUpTest(c *gc.C) {
    53  	s.JujuConnSuite.SetUpTest(c)
    54  	s.resources = common.NewResources()
    55  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    56  
    57  	s.authoriser = apiservertesting.FakeAuthorizer{
    58  		Tag:            s.AdminUserTag(c),
    59  		EnvironManager: true,
    60  	}
    61  
    62  	var err error
    63  	s.haServer, err = highavailability.NewHighAvailabilityAPI(s.State, s.resources, s.authoriser)
    64  	c.Assert(err, jc.ErrorIsNil)
    65  
    66  	_, err = s.State.AddMachine("quantal", state.JobManageEnviron)
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	// We have to ensure the agents are alive, or EnsureAvailability will
    69  	// create more to replace them.
    70  	s.pinger = s.setAgentPresence(c, "0")
    71  }
    72  
    73  func (s *clientSuite) TearDownTest(c *gc.C) {
    74  	assertKill(c, s.pinger)
    75  	s.JujuConnSuite.TearDownTest(c)
    76  }
    77  
    78  func (s *clientSuite) setAgentPresence(c *gc.C, machineId string) *presence.Pinger {
    79  	m, err := s.State.Machine(machineId)
    80  	c.Assert(err, jc.ErrorIsNil)
    81  	pinger, err := m.SetAgentPresence()
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	s.State.StartSync()
    84  	err = m.WaitAgentPresence(coretesting.LongWait)
    85  	c.Assert(err, jc.ErrorIsNil)
    86  	return pinger
    87  }
    88  
    89  func (s *clientSuite) ensureAvailability(
    90  	c *gc.C, numStateServers int, cons constraints.Value, series string, placement []string,
    91  ) (params.StateServersChanges, error) {
    92  
    93  	arg := params.StateServersSpecs{
    94  		Specs: []params.StateServersSpec{{
    95  			NumStateServers: numStateServers,
    96  			Constraints:     cons,
    97  			Series:          series,
    98  			Placement:       placement,
    99  		}}}
   100  	results, err := s.haServer.EnsureAvailability(arg)
   101  	c.Assert(err, jc.ErrorIsNil)
   102  	c.Assert(results.Results, gc.HasLen, 1)
   103  	result := results.Results[0]
   104  	// We explicitly return nil here so we can do typed nil checking
   105  	// of the result like normal.
   106  	err = nil
   107  	if result.Error != nil {
   108  		err = result.Error
   109  	}
   110  	return result.Result, err
   111  }
   112  
   113  func (s *clientSuite) TestEnsureAvailabilitySeries(c *gc.C) {
   114  	machines, err := s.State.AllMachines()
   115  	c.Assert(err, jc.ErrorIsNil)
   116  	c.Assert(machines, gc.HasLen, 1)
   117  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   118  
   119  	ensureAvailabilityResult, err := s.ensureAvailability(c, 3, emptyCons, defaultSeries, nil)
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   122  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   123  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   124  
   125  	machines, err = s.State.AllMachines()
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	c.Assert(machines, gc.HasLen, 3)
   128  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   129  	c.Assert(machines[1].Series(), gc.Equals, "quantal")
   130  	c.Assert(machines[2].Series(), gc.Equals, "quantal")
   131  
   132  	pingerB := s.setAgentPresence(c, "1")
   133  	defer assertKill(c, pingerB)
   134  
   135  	pingerC := s.setAgentPresence(c, "2")
   136  	defer assertKill(c, pingerC)
   137  
   138  	ensureAvailabilityResult, err = s.ensureAvailability(c, 5, emptyCons, "non-default", nil)
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1", "machine-2"})
   141  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-3", "machine-4"})
   142  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   143  
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	machines, err = s.State.AllMachines()
   146  	c.Assert(err, jc.ErrorIsNil)
   147  	c.Assert(machines, gc.HasLen, 5)
   148  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   149  	c.Assert(machines[1].Series(), gc.Equals, "quantal")
   150  	c.Assert(machines[2].Series(), gc.Equals, "quantal")
   151  	c.Assert(machines[3].Series(), gc.Equals, "non-default")
   152  	c.Assert(machines[4].Series(), gc.Equals, "non-default")
   153  }
   154  
   155  func (s *clientSuite) TestEnsureAvailabilityConstraints(c *gc.C) {
   156  	ensureAvailabilityResult, err := s.ensureAvailability(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   159  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   160  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   161  
   162  	machines, err := s.State.AllMachines()
   163  	c.Assert(err, jc.ErrorIsNil)
   164  	c.Assert(machines, gc.HasLen, 3)
   165  	expectedCons := []constraints.Value{
   166  		{},
   167  		constraints.MustParse("mem=4G"),
   168  		constraints.MustParse("mem=4G"),
   169  	}
   170  	for i, m := range machines {
   171  		cons, err := m.Constraints()
   172  		c.Assert(err, jc.ErrorIsNil)
   173  		c.Check(cons, gc.DeepEquals, expectedCons[i])
   174  	}
   175  }
   176  
   177  func (s *clientSuite) TestBlockEnsureAvailability(c *gc.C) {
   178  	// Block all changes.
   179  	err := s.State.UpdateEnvironConfig(map[string]interface{}{"block-all-changes": true}, nil, nil)
   180  	c.Assert(err, jc.ErrorIsNil)
   181  
   182  	ensureAvailabilityResult, err := s.ensureAvailability(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil)
   183  	c.Assert(errors.Cause(err), gc.DeepEquals, common.ErrOperationBlocked)
   184  
   185  	c.Assert(ensureAvailabilityResult.Maintained, gc.HasLen, 0)
   186  	c.Assert(ensureAvailabilityResult.Added, gc.HasLen, 0)
   187  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   188  
   189  	machines, err := s.State.AllMachines()
   190  	c.Assert(err, jc.ErrorIsNil)
   191  	c.Assert(machines, gc.HasLen, 1)
   192  }
   193  
   194  func (s *clientSuite) TestEnsureAvailabilityPlacement(c *gc.C) {
   195  	placement := []string{"valid"}
   196  	ensureAvailabilityResult, err := s.ensureAvailability(c, 3, constraints.MustParse("mem=4G"), defaultSeries, placement)
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   199  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   200  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   201  
   202  	machines, err := s.State.AllMachines()
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	c.Assert(machines, gc.HasLen, 3)
   205  	expectedCons := []constraints.Value{
   206  		{},
   207  		constraints.MustParse("mem=4G"),
   208  		constraints.MustParse("mem=4G"),
   209  	}
   210  	expectedPlacement := []string{"", "valid", ""}
   211  	for i, m := range machines {
   212  		cons, err := m.Constraints()
   213  		c.Assert(err, jc.ErrorIsNil)
   214  		c.Check(cons, gc.DeepEquals, expectedCons[i])
   215  		c.Check(m.Placement(), gc.Equals, expectedPlacement[i])
   216  	}
   217  }
   218  
   219  func (s *clientSuite) TestEnsureAvailability0Preserves(c *gc.C) {
   220  	// A value of 0 says either "if I'm not HA, make me HA" or "preserve my
   221  	// current HA settings".
   222  	ensureAvailabilityResult, err := s.ensureAvailability(c, 0, emptyCons, defaultSeries, nil)
   223  	c.Assert(err, jc.ErrorIsNil)
   224  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   225  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   226  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   227  
   228  	machines, err := s.State.AllMachines()
   229  	c.Assert(machines, gc.HasLen, 3)
   230  
   231  	pingerB := s.setAgentPresence(c, "1")
   232  	defer assertKill(c, pingerB)
   233  
   234  	// Now, we keep agent 1 alive, but not agent 2, calling
   235  	// EnsureAvailability(0) again will cause us to start another machine
   236  	ensureAvailabilityResult, err = s.ensureAvailability(c, 0, emptyCons, defaultSeries, nil)
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1"})
   239  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-3"})
   240  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   241  
   242  	machines, err = s.State.AllMachines()
   243  	c.Assert(machines, gc.HasLen, 4)
   244  }
   245  
   246  func (s *clientSuite) TestEnsureAvailability0Preserves5(c *gc.C) {
   247  	// Start off with 5 servers
   248  	ensureAvailabilityResult, err := s.ensureAvailability(c, 5, emptyCons, defaultSeries, nil)
   249  	c.Assert(err, jc.ErrorIsNil)
   250  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   251  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2", "machine-3", "machine-4"})
   252  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   253  
   254  	machines, err := s.State.AllMachines()
   255  	c.Assert(machines, gc.HasLen, 5)
   256  	pingerB := s.setAgentPresence(c, "1")
   257  	defer assertKill(c, pingerB)
   258  
   259  	pingerC := s.setAgentPresence(c, "2")
   260  	defer assertKill(c, pingerC)
   261  
   262  	pingerD := s.setAgentPresence(c, "3")
   263  	defer assertKill(c, pingerD)
   264  	// Keeping all alive but one, will bring up 1 more server to preserve 5
   265  	ensureAvailabilityResult, err = s.ensureAvailability(c, 0, emptyCons, defaultSeries, nil)
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1",
   268  		"machine-2", "machine-3"})
   269  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-5"})
   270  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   271  
   272  	machines, err = s.State.AllMachines()
   273  	c.Assert(machines, gc.HasLen, 6)
   274  	c.Assert(err, jc.ErrorIsNil)
   275  }
   276  
   277  func (s *clientSuite) TestEnsureAvailabilityErrors(c *gc.C) {
   278  	ensureAvailabilityResult, err := s.ensureAvailability(c, -1, emptyCons, defaultSeries, nil)
   279  	c.Assert(err, gc.ErrorMatches, "number of state servers must be odd and non-negative")
   280  
   281  	ensureAvailabilityResult, err = s.ensureAvailability(c, 3, emptyCons, defaultSeries, nil)
   282  	c.Assert(err, jc.ErrorIsNil)
   283  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"})
   284  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
   285  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   286  
   287  	_, err = s.ensureAvailability(c, 1, emptyCons, defaultSeries, nil)
   288  	c.Assert(err, gc.ErrorMatches, "failed to create new state server machines: cannot reduce state server count")
   289  }