github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/provider/common/availabilityzones_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common_test
     5  
     6  import (
     7  	"fmt"
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "launchpad.net/gocheck"
    11  
    12  	"github.com/juju/juju/environs"
    13  	"github.com/juju/juju/instance"
    14  	"github.com/juju/juju/provider/common"
    15  	coretesting "github.com/juju/juju/testing"
    16  )
    17  
    18  type AvailabilityZoneSuite struct {
    19  	coretesting.FakeJujuHomeSuite
    20  	env mockZonedEnviron
    21  }
    22  
    23  var _ = gc.Suite(&AvailabilityZoneSuite{})
    24  
    25  func (s *AvailabilityZoneSuite) SetUpSuite(c *gc.C) {
    26  	s.FakeJujuHomeSuite.SetUpSuite(c)
    27  
    28  	allInstances := make([]instance.Instance, 3)
    29  	for i := range allInstances {
    30  		allInstances[i] = &mockInstance{id: fmt.Sprintf("inst%d", i)}
    31  	}
    32  	s.env.allInstances = func() ([]instance.Instance, error) {
    33  		return allInstances, nil
    34  	}
    35  
    36  	availabilityZones := make([]common.AvailabilityZone, 3)
    37  	for i := range availabilityZones {
    38  		availabilityZones[i] = &mockAvailabilityZone{
    39  			name:      fmt.Sprintf("az%d", i),
    40  			available: i > 0,
    41  		}
    42  	}
    43  	s.env.availabilityZones = func() ([]common.AvailabilityZone, error) {
    44  		return availabilityZones, nil
    45  	}
    46  }
    47  
    48  func (s *AvailabilityZoneSuite) TestBestAvailabilityZoneAllocationsAllInstances(c *gc.C) {
    49  	var called int
    50  	s.PatchValue(&s.env.instanceAvailabilityZoneNames, func(ids []instance.Id) ([]string, error) {
    51  		c.Assert(ids, gc.DeepEquals, []instance.Id{"inst0", "inst1", "inst2"})
    52  		called++
    53  		return []string{"az0", "az1", "az2"}, nil
    54  	})
    55  	best, err := common.BestAvailabilityZoneAllocations(&s.env, nil)
    56  	c.Assert(called, gc.Equals, 1)
    57  	c.Assert(err, gc.IsNil)
    58  	// az0 is unavailable, so az1 and az2 come out as equal best.
    59  	c.Assert(best, gc.DeepEquals, map[string][]instance.Id{
    60  		"az1": []instance.Id{"inst1"},
    61  		"az2": []instance.Id{"inst2"},
    62  	})
    63  }
    64  
    65  func (s *AvailabilityZoneSuite) TestBestAvailabilityZoneAllocationsAllInstancesErrors(c *gc.C) {
    66  	resultErr := fmt.Errorf("oh noes")
    67  	s.PatchValue(&s.env.allInstances, func() ([]instance.Instance, error) {
    68  		return nil, resultErr
    69  	})
    70  	best, err := common.BestAvailabilityZoneAllocations(&s.env, nil)
    71  	c.Assert(err, gc.Equals, resultErr)
    72  	c.Assert(best, gc.HasLen, 0)
    73  }
    74  
    75  func (s *AvailabilityZoneSuite) TestBestAvailabilityZoneAllocationsPartialInstances(c *gc.C) {
    76  	var called int
    77  	s.PatchValue(&s.env.instanceAvailabilityZoneNames, func(ids []instance.Id) ([]string, error) {
    78  		c.Assert(ids, gc.DeepEquals, []instance.Id{"nichts", "inst1", "null", "inst2"})
    79  		called++
    80  		return []string{"", "az1", "", "az1"}, environs.ErrPartialInstances
    81  	})
    82  	best, err := common.BestAvailabilityZoneAllocations(&s.env, []instance.Id{"nichts", "inst1", "null", "inst2"})
    83  	c.Assert(called, gc.Equals, 1)
    84  	c.Assert(err, gc.IsNil)
    85  	// All known instances are in az1 and az0 is unavailable, so az2 is the best.
    86  	c.Assert(best, gc.DeepEquals, map[string][]instance.Id{"az2": nil})
    87  }
    88  
    89  func (s *AvailabilityZoneSuite) TestBestAvailabilityZoneAllocationsInstanceAvailabilityZonesErrors(c *gc.C) {
    90  	var returnErr error
    91  	var called int
    92  	s.PatchValue(&s.env.instanceAvailabilityZoneNames, func(ids []instance.Id) ([]string, error) {
    93  		called++
    94  		return nil, returnErr
    95  	})
    96  	errors := []error{environs.ErrNoInstances, fmt.Errorf("whatever")}
    97  	for i, err := range errors {
    98  		returnErr = err
    99  		best, err := common.BestAvailabilityZoneAllocations(&s.env, nil)
   100  		c.Assert(called, gc.Equals, i+1)
   101  		c.Assert(err, gc.Equals, returnErr)
   102  		c.Assert(best, gc.HasLen, 0)
   103  	}
   104  }
   105  
   106  func (s *AvailabilityZoneSuite) TestBestAvailabilityZoneAllocationsNoZones(c *gc.C) {
   107  	var calls []string
   108  	s.PatchValue(&s.env.instanceAvailabilityZoneNames, func(ids []instance.Id) ([]string, error) {
   109  		c.Assert(ids, gc.DeepEquals, []instance.Id{"inst0", "inst1", "inst2"})
   110  		calls = append(calls, "InstanceAvailabilityZoneNames")
   111  		return []string{"", "", ""}, nil
   112  	})
   113  	s.PatchValue(&s.env.availabilityZones, func() ([]common.AvailabilityZone, error) {
   114  		calls = append(calls, "AvailabilityZones")
   115  		return []common.AvailabilityZone{}, nil
   116  	})
   117  	best, err := common.BestAvailabilityZoneAllocations(&s.env, nil)
   118  	c.Assert(calls, gc.DeepEquals, []string{"InstanceAvailabilityZoneNames", "AvailabilityZones"})
   119  	c.Assert(err, gc.IsNil)
   120  	c.Assert(best, gc.HasLen, 0)
   121  }
   122  
   123  func (s *AvailabilityZoneSuite) TestBestAvailabilityZoneAllocationsErrors(c *gc.C) {
   124  	var calls []string
   125  	s.PatchValue(&s.env.instanceAvailabilityZoneNames, func(ids []instance.Id) ([]string, error) {
   126  		c.Assert(ids, gc.DeepEquals, []instance.Id{"inst0", "inst1", "inst2"})
   127  		calls = append(calls, "InstanceAvailabilityZoneNames")
   128  		return []string{"", "", ""}, nil
   129  	})
   130  	resultErr := fmt.Errorf("u can haz no az")
   131  	s.PatchValue(&s.env.availabilityZones, func() ([]common.AvailabilityZone, error) {
   132  		calls = append(calls, "AvailabilityZones")
   133  		return nil, resultErr
   134  	})
   135  	best, err := common.BestAvailabilityZoneAllocations(&s.env, nil)
   136  	c.Assert(calls, gc.DeepEquals, []string{"InstanceAvailabilityZoneNames", "AvailabilityZones"})
   137  	c.Assert(err, gc.Equals, resultErr)
   138  	c.Assert(best, gc.HasLen, 0)
   139  }
   140  
   141  func (s *AvailabilityZoneSuite) TestDistributeInstancesGroup(c *gc.C) {
   142  	expectedGroup := []instance.Id{"0", "1", "2"}
   143  	var called bool
   144  	s.PatchValue(common.InternalBestAvailabilityZoneAllocations, func(_ common.ZonedEnviron, group []instance.Id) (map[string][]instance.Id, error) {
   145  		c.Assert(group, gc.DeepEquals, expectedGroup)
   146  		called = true
   147  		return nil, nil
   148  	})
   149  	common.DistributeInstances(&s.env, nil, expectedGroup)
   150  	c.Assert(called, jc.IsTrue)
   151  }
   152  
   153  func (s *AvailabilityZoneSuite) TestDistributeInstancesGroupErrors(c *gc.C) {
   154  	resultErr := fmt.Errorf("whatever")
   155  	s.PatchValue(common.InternalBestAvailabilityZoneAllocations, func(_ common.ZonedEnviron, group []instance.Id) (map[string][]instance.Id, error) {
   156  		return nil, resultErr
   157  	})
   158  	_, err := common.DistributeInstances(&s.env, nil, nil)
   159  	c.Assert(err, gc.Equals, resultErr)
   160  }
   161  
   162  func (s *AvailabilityZoneSuite) TestDistributeInstances(c *gc.C) {
   163  	var bestAvailabilityZones map[string][]instance.Id
   164  	s.PatchValue(common.InternalBestAvailabilityZoneAllocations, func(_ common.ZonedEnviron, group []instance.Id) (map[string][]instance.Id, error) {
   165  		return bestAvailabilityZones, nil
   166  	})
   167  
   168  	type distributeInstancesTest struct {
   169  		bestAvailabilityZones map[string][]instance.Id
   170  		candidates            []instance.Id
   171  		eligible              []instance.Id
   172  	}
   173  
   174  	tests := []distributeInstancesTest{{
   175  		bestAvailabilityZones: map[string][]instance.Id{
   176  			"az0": []instance.Id{"i0"},
   177  			"az1": []instance.Id{"i1"},
   178  			"az2": []instance.Id{"i2"},
   179  		},
   180  		candidates: []instance.Id{"i2", "i3", "i4"},
   181  		eligible:   []instance.Id{"i2"},
   182  	}, {
   183  		bestAvailabilityZones: map[string][]instance.Id{
   184  			"az0": []instance.Id{"i0"},
   185  			"az1": []instance.Id{"i1"},
   186  			"az2": []instance.Id{"i2"},
   187  		},
   188  		candidates: []instance.Id{"i0", "i1", "i2"},
   189  		eligible:   []instance.Id{"i0", "i1", "i2"},
   190  	}, {
   191  		bestAvailabilityZones: map[string][]instance.Id{
   192  			"az0": []instance.Id{"i0"},
   193  			"az1": []instance.Id{"i1"},
   194  			"az2": []instance.Id{"i2"},
   195  		},
   196  		candidates: []instance.Id{"i3", "i4", "i5"},
   197  		eligible:   []instance.Id{},
   198  	}, {
   199  		bestAvailabilityZones: map[string][]instance.Id{
   200  			"az0": []instance.Id{"i0"},
   201  			"az1": []instance.Id{"i1"},
   202  			"az2": []instance.Id{"i2"},
   203  		},
   204  		candidates: []instance.Id{},
   205  		eligible:   []instance.Id{},
   206  	}, {
   207  		bestAvailabilityZones: map[string][]instance.Id{},
   208  		candidates:            []instance.Id{"i0"},
   209  		eligible:              []instance.Id{},
   210  	}}
   211  
   212  	for i, test := range tests {
   213  		c.Logf("test %d", i)
   214  		bestAvailabilityZones = test.bestAvailabilityZones
   215  		eligible, err := common.DistributeInstances(&s.env, test.candidates, nil)
   216  		c.Assert(err, gc.IsNil)
   217  		c.Assert(eligible, jc.SameContents, test.eligible)
   218  	}
   219  }