github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/distribution_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"fmt"
     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/instance"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  type InstanceDistributorSuite struct {
    18  	ConnSuite
    19  	distributor mockInstanceDistributor
    20  	wordpress   *state.Application
    21  	machines    []*state.Machine
    22  }
    23  
    24  var _ = gc.Suite(&InstanceDistributorSuite{})
    25  
    26  type mockInstanceDistributor struct {
    27  	candidates        []instance.Id
    28  	distributionGroup []instance.Id
    29  	result            []instance.Id
    30  	err               error
    31  }
    32  
    33  func (p *mockInstanceDistributor) DistributeInstances(candidates, distributionGroup []instance.Id) ([]instance.Id, error) {
    34  	p.candidates = candidates
    35  	p.distributionGroup = distributionGroup
    36  	result := p.result
    37  	if result == nil {
    38  		result = candidates
    39  	}
    40  	return result, p.err
    41  }
    42  
    43  func (s *InstanceDistributorSuite) SetUpTest(c *gc.C) {
    44  	s.ConnSuite.SetUpTest(c)
    45  	s.distributor = mockInstanceDistributor{}
    46  	s.policy.GetInstanceDistributor = func() (instance.Distributor, error) {
    47  		return &s.distributor, nil
    48  	}
    49  	s.wordpress = s.AddTestingService(
    50  		c,
    51  		"wordpress",
    52  		s.AddTestingCharm(c, "wordpress"),
    53  	)
    54  	s.machines = make([]*state.Machine, 3)
    55  	for i := range s.machines {
    56  		var err error
    57  		s.machines[i], err = s.State.AddOneMachine(state.MachineTemplate{
    58  			Series: "quantal",
    59  			Jobs:   []state.MachineJob{state.JobHostUnits},
    60  		})
    61  		c.Assert(err, jc.ErrorIsNil)
    62  	}
    63  }
    64  
    65  func (s *InstanceDistributorSuite) setupScenario(c *gc.C) {
    66  	// Assign a unit so we have a non-empty distribution group, and
    67  	// provision all instances so we have candidates.
    68  	unit, err := s.wordpress.AddUnit()
    69  	c.Assert(err, jc.ErrorIsNil)
    70  	err = unit.AssignToMachine(s.machines[0])
    71  	c.Assert(err, jc.ErrorIsNil)
    72  	for i, m := range s.machines {
    73  		instId := instance.Id(fmt.Sprintf("i-blah-%d", i))
    74  		err = m.SetProvisioned(instId, "fake-nonce", nil)
    75  		c.Assert(err, jc.ErrorIsNil)
    76  	}
    77  }
    78  
    79  func (s *InstanceDistributorSuite) TestDistributeInstances(c *gc.C) {
    80  	s.setupScenario(c)
    81  	unit, err := s.wordpress.AddUnit()
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	_, err = unit.AssignToCleanMachine()
    84  	c.Assert(err, jc.ErrorIsNil)
    85  	c.Assert(s.distributor.candidates, jc.SameContents, []instance.Id{"i-blah-1", "i-blah-2"})
    86  	c.Assert(s.distributor.distributionGroup, jc.SameContents, []instance.Id{"i-blah-0"})
    87  	s.distributor.result = []instance.Id{}
    88  	_, err = unit.AssignToCleanMachine()
    89  	c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse)
    90  }
    91  
    92  func (s *InstanceDistributorSuite) TestDistributeInstancesInvalidInstances(c *gc.C) {
    93  	s.setupScenario(c)
    94  	unit, err := s.wordpress.AddUnit()
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	s.distributor.result = []instance.Id{"notthere"}
    97  	_, err = unit.AssignToCleanMachine()
    98  	c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/1" to clean machine: invalid instance returned: notthere`)
    99  }
   100  
   101  func (s *InstanceDistributorSuite) TestDistributeInstancesNoEmptyMachines(c *gc.C) {
   102  	for i := range s.machines {
   103  		// Assign a unit so we have a non-empty distribution group.
   104  		unit, err := s.wordpress.AddUnit()
   105  		c.Assert(err, jc.ErrorIsNil)
   106  		m, err := unit.AssignToCleanMachine()
   107  		c.Assert(err, jc.ErrorIsNil)
   108  		instId := instance.Id(fmt.Sprintf("i-blah-%d", i))
   109  		err = m.SetProvisioned(instId, "fake-nonce", nil)
   110  		c.Assert(err, jc.ErrorIsNil)
   111  	}
   112  
   113  	// InstanceDistributor is not called if there are no empty instances.
   114  	s.distributor.err = fmt.Errorf("no assignment for you")
   115  	unit, err := s.wordpress.AddUnit()
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	_, err = unit.AssignToCleanMachine()
   118  	c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse)
   119  }
   120  
   121  func (s *InstanceDistributorSuite) TestDistributeInstancesErrors(c *gc.C) {
   122  	s.setupScenario(c)
   123  	unit, err := s.wordpress.AddUnit()
   124  	c.Assert(err, jc.ErrorIsNil)
   125  
   126  	// Ensure that assignment fails when DistributeInstances returns an error.
   127  	s.distributor.err = fmt.Errorf("no assignment for you")
   128  	_, err = unit.AssignToCleanMachine()
   129  	c.Assert(err, gc.ErrorMatches, ".*no assignment for you")
   130  	_, err = unit.AssignToCleanEmptyMachine()
   131  	c.Assert(err, gc.ErrorMatches, ".*no assignment for you")
   132  	// If the policy's InstanceDistributor method fails, that will be returned first.
   133  	s.policy.GetInstanceDistributor = func() (instance.Distributor, error) {
   134  		return nil, fmt.Errorf("incapable of InstanceDistributor")
   135  	}
   136  	_, err = unit.AssignToCleanMachine()
   137  	c.Assert(err, gc.ErrorMatches, ".*incapable of InstanceDistributor")
   138  }
   139  
   140  func (s *InstanceDistributorSuite) TestDistributeInstancesEmptyDistributionGroup(c *gc.C) {
   141  	s.distributor.err = fmt.Errorf("no assignment for you")
   142  
   143  	// InstanceDistributor is not called if the distribution group is empty.
   144  	unit0, err := s.wordpress.AddUnit()
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	_, err = unit0.AssignToCleanMachine()
   147  	c.Assert(err, jc.ErrorIsNil)
   148  
   149  	// Distribution group is still empty, because the machine assigned to has
   150  	// not been provisioned.
   151  	unit1, err := s.wordpress.AddUnit()
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	_, err = unit1.AssignToCleanMachine()
   154  	c.Assert(err, jc.ErrorIsNil)
   155  }
   156  
   157  func (s *InstanceDistributorSuite) TestInstanceDistributorUnimplemented(c *gc.C) {
   158  	s.setupScenario(c)
   159  	var distributorErr error
   160  	s.policy.GetInstanceDistributor = func() (instance.Distributor, error) {
   161  		return nil, distributorErr
   162  	}
   163  	unit, err := s.wordpress.AddUnit()
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	_, err = unit.AssignToCleanMachine()
   166  	c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/1" to clean machine: policy returned nil instance distributor without an error`)
   167  	distributorErr = errors.NotImplementedf("InstanceDistributor")
   168  	_, err = unit.AssignToCleanMachine()
   169  	c.Assert(err, jc.ErrorIsNil)
   170  }
   171  
   172  func (s *InstanceDistributorSuite) TestDistributeInstancesNoPolicy(c *gc.C) {
   173  	s.policy.GetInstanceDistributor = func() (instance.Distributor, error) {
   174  		c.Errorf("should not have been invoked")
   175  		return nil, nil
   176  	}
   177  	state.SetPolicy(s.State, nil)
   178  	unit, err := s.wordpress.AddUnit()
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	_, err = unit.AssignToCleanMachine()
   181  	c.Assert(err, jc.ErrorIsNil)
   182  }