github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/subnets_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  	"sort"
     9  	"sync"
    10  	"sync/atomic"
    11  
    12  	"github.com/juju/errors"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/network"
    17  	"github.com/juju/juju/state"
    18  )
    19  
    20  type SubnetSuite struct {
    21  	ConnSuite
    22  }
    23  
    24  var _ = gc.Suite(&SubnetSuite{})
    25  
    26  func (s *SubnetSuite) TestAddSubnetSucceedsWithFullyPopulatedInfo(c *gc.C) {
    27  	subnetInfo := state.SubnetInfo{
    28  		ProviderId:        "foo",
    29  		CIDR:              "192.168.1.0/24",
    30  		VLANTag:           79,
    31  		AllocatableIPLow:  "192.168.1.0",
    32  		AllocatableIPHigh: "192.168.1.1",
    33  		AvailabilityZone:  "Timbuktu",
    34  		SpaceName:         "foo",
    35  	}
    36  
    37  	subnet, err := s.State.AddSubnet(subnetInfo)
    38  	c.Assert(err, jc.ErrorIsNil)
    39  	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)
    40  
    41  	// check it's been stored in state by fetching it back again
    42  	subnetFromDB, err := s.State.Subnet("192.168.1.0/24")
    43  	c.Assert(err, jc.ErrorIsNil)
    44  	s.assertSubnetMatchesInfo(c, subnetFromDB, subnetInfo)
    45  }
    46  
    47  func (s *SubnetSuite) assertSubnetMatchesInfo(c *gc.C, subnet *state.Subnet, info state.SubnetInfo) {
    48  	c.Assert(subnet.ProviderId(), gc.Equals, info.ProviderId)
    49  	c.Assert(subnet.CIDR(), gc.Equals, info.CIDR)
    50  	c.Assert(subnet.VLANTag(), gc.Equals, info.VLANTag)
    51  	c.Assert(subnet.AllocatableIPLow(), gc.Equals, info.AllocatableIPLow)
    52  	c.Assert(subnet.AllocatableIPHigh(), gc.Equals, info.AllocatableIPHigh)
    53  	c.Assert(subnet.AvailabilityZone(), gc.Equals, info.AvailabilityZone)
    54  	c.Assert(subnet.String(), gc.Equals, info.CIDR)
    55  	c.Assert(subnet.GoString(), gc.Equals, info.CIDR)
    56  	c.Assert(subnet.SpaceName(), gc.Equals, info.SpaceName)
    57  }
    58  
    59  func (s *SubnetSuite) TestAddSubnetFailsWithEmptyCIDR(c *gc.C) {
    60  	subnetInfo := state.SubnetInfo{}
    61  	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "missing CIDR")
    62  }
    63  
    64  func (s *SubnetSuite) assertAddSubnetForInfoFailsWithSuffix(c *gc.C, subnetInfo state.SubnetInfo, errorSuffix string) error {
    65  	subnet, err := s.State.AddSubnet(subnetInfo)
    66  	errorMessage := fmt.Sprintf("adding subnet %q: %s", subnetInfo.CIDR, errorSuffix)
    67  	c.Assert(err, gc.ErrorMatches, errorMessage)
    68  	c.Assert(subnet, gc.IsNil)
    69  	return err
    70  }
    71  
    72  func (s *SubnetSuite) TestAddSubnetFailsWithInvalidCIDR(c *gc.C) {
    73  	subnetInfo := state.SubnetInfo{CIDR: "foobar"}
    74  	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "invalid CIDR address: foobar")
    75  }
    76  
    77  func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeVLANTag(c *gc.C) {
    78  	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", VLANTag: 4095}
    79  	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, "invalid VLAN tag 4095: must be between 0 and 4094")
    80  }
    81  
    82  func (s *SubnetSuite) TestAddSubnetFailsWhenAllocatableIPHighSetButAllocatableIPLowNotSet(c *gc.C) {
    83  	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", AllocatableIPHigh: "192.168.0.1"}
    84  	s.assertAddSubnetForInfoFailsWithSuffix(
    85  		c, subnetInfo,
    86  		"either both AllocatableIPLow and AllocatableIPHigh must be set or neither set",
    87  	)
    88  }
    89  
    90  func (s *SubnetSuite) TestAddSubnetFailsWhenAllocatableIPLowSetButAllocatableIPHighNotSet(c *gc.C) {
    91  	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24", AllocatableIPLow: "192.168.0.1"}
    92  	s.assertAddSubnetForInfoFailsWithSuffix(
    93  		c, subnetInfo,
    94  		"either both AllocatableIPLow and AllocatableIPHigh must be set or neither set",
    95  	)
    96  }
    97  
    98  func (s *SubnetSuite) TestAddSubnetFailsWithInvalidAllocatableIPHigh(c *gc.C) {
    99  	subnetInfo := state.SubnetInfo{
   100  		CIDR:              "192.168.0.1/24",
   101  		AllocatableIPLow:  "192.168.0.1",
   102  		AllocatableIPHigh: "foobar",
   103  	}
   104  	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPHigh "foobar"`)
   105  }
   106  
   107  func (s *SubnetSuite) TestAddSubnetFailsWithInvalidAllocatableIPLow(c *gc.C) {
   108  	subnetInfo := state.SubnetInfo{
   109  		CIDR:              "192.168.0.1/24",
   110  		AllocatableIPLow:  "foobar",
   111  		AllocatableIPHigh: "192.168.0.1",
   112  	}
   113  	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPLow "foobar"`)
   114  }
   115  
   116  func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeAllocatableIPHigh(c *gc.C) {
   117  	subnetInfo := state.SubnetInfo{
   118  		CIDR:              "192.168.0.1/24",
   119  		AllocatableIPLow:  "192.168.0.1",
   120  		AllocatableIPHigh: "172.168.1.0",
   121  	}
   122  	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPHigh "172.168.1.0"`)
   123  }
   124  
   125  func (s *SubnetSuite) TestAddSubnetFailsWithOutOfRangeAllocatableIPLow(c *gc.C) {
   126  	subnetInfo := state.SubnetInfo{
   127  		CIDR:              "192.168.0.1/24",
   128  		AllocatableIPLow:  "172.168.1.0",
   129  		AllocatableIPHigh: "192.168.0.10",
   130  	}
   131  	s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `invalid AllocatableIPLow "172.168.1.0"`)
   132  }
   133  
   134  func (s *SubnetSuite) TestAddSubnetFailsWithAlreadyExistsForDuplicateCIDRInSameModel(c *gc.C) {
   135  	subnetInfo := state.SubnetInfo{CIDR: "192.168.0.1/24"}
   136  	subnet, err := s.State.AddSubnet(subnetInfo)
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	s.assertSubnetMatchesInfo(c, subnet, subnetInfo)
   139  
   140  	err = s.assertAddSubnetForInfoFailsWithSuffix(c, subnetInfo, `subnet "192.168.0.1/24" already exists`)
   141  	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
   142  }
   143  
   144  func (s *SubnetSuite) TestAddSubnetSucceedsForDuplicateCIDRInDifferentModels(c *gc.C) {
   145  	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24"}
   146  	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24"}
   147  	subnet1State := s.NewStateForModelNamed(c, "other-model")
   148  
   149  	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
   150  	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
   151  	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
   152  }
   153  
   154  func (s *SubnetSuite) addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c *gc.C, info1, info2 state.SubnetInfo, otherState *state.State) (*state.Subnet, *state.Subnet) {
   155  	subnet1, err := otherState.AddSubnet(info1)
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	subnet2, err := s.State.AddSubnet(info2)
   158  	c.Assert(err, jc.ErrorIsNil)
   159  
   160  	return subnet1, subnet2
   161  }
   162  
   163  func (s *SubnetSuite) TestAddSubnetFailsWhenProviderIdNotUniqueInSameModel(c *gc.C) {
   164  	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: "foo"}
   165  	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: "foo"}
   166  
   167  	s.addTwoSubnetsAndAssertSecondFailsWithSuffix(c, subnetInfo1, subnetInfo2, `ProviderId "foo" not unique`)
   168  }
   169  
   170  func (s *SubnetSuite) addTwoSubnetsAndAssertSecondFailsWithSuffix(c *gc.C, info1, info2 state.SubnetInfo, errorSuffix string) {
   171  	s.addTwoSubnetsInDifferentModelsAndAssertSecondFailsWithSuffix(c, info1, info2, s.State, errorSuffix)
   172  }
   173  
   174  func (s *SubnetSuite) addTwoSubnetsInDifferentModelsAndAssertSecondFailsWithSuffix(c *gc.C, info1, info2 state.SubnetInfo, otherState *state.State, errorSuffix string) {
   175  	_, err := otherState.AddSubnet(info1)
   176  	c.Assert(err, jc.ErrorIsNil)
   177  
   178  	s.assertAddSubnetForInfoFailsWithSuffix(c, info2, errorSuffix)
   179  }
   180  
   181  func (s *SubnetSuite) TestAddSubnetSucceedsWhenProviderIdNotUniqueInDifferentModels(c *gc.C) {
   182  	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: "foo"}
   183  	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: "foo"}
   184  	subnet1State := s.NewStateForModelNamed(c, "other-model")
   185  
   186  	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
   187  	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
   188  	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
   189  }
   190  
   191  func (s *SubnetSuite) TestAddSubnetSucceedsForDifferentCIDRsAndEmptyProviderIdInSameModel(c *gc.C) {
   192  	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: ""}
   193  	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: ""}
   194  
   195  	subnet1, subnet2 := s.addTwoSubnetsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2)
   196  	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
   197  	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
   198  }
   199  
   200  func (s *SubnetSuite) addTwoSubnetsAssertSuccessAndReturnBoth(c *gc.C, info1, info2 state.SubnetInfo) (*state.Subnet, *state.Subnet) {
   201  	return s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, info1, info2, s.State)
   202  }
   203  
   204  func (s *SubnetSuite) TestAddSubnetSucceedsForDifferentCIDRsAndEmptyProviderIdInDifferentModels(c *gc.C) {
   205  	subnetInfo1 := state.SubnetInfo{CIDR: "192.168.0.1/24", ProviderId: ""}
   206  	subnetInfo2 := state.SubnetInfo{CIDR: "10.0.0.0/24", ProviderId: ""}
   207  	subnet1State := s.NewStateForModelNamed(c, "other-model")
   208  
   209  	subnet1, subnet2 := s.addTwoSubnetsInDifferentModelsAssertSuccessAndReturnBoth(c, subnetInfo1, subnetInfo2, subnet1State)
   210  	s.assertSubnetMatchesInfo(c, subnet1, subnetInfo1)
   211  	s.assertSubnetMatchesInfo(c, subnet2, subnetInfo2)
   212  }
   213  
   214  func (s *SubnetSuite) TestEnsureDeadSetsLifeToDeadWhenAlive(c *gc.C) {
   215  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   216  
   217  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   218  	s.refreshAndAssertSubnetLifeIs(c, subnet, state.Dead)
   219  }
   220  
   221  func (s *SubnetSuite) addAliveSubnet(c *gc.C, cidr string) *state.Subnet {
   222  	subnetInfo := state.SubnetInfo{CIDR: cidr}
   223  	subnet, err := s.State.AddSubnet(subnetInfo)
   224  	c.Assert(err, jc.ErrorIsNil)
   225  	c.Assert(subnet.Life(), gc.Equals, state.Alive)
   226  
   227  	return subnet
   228  }
   229  
   230  func (s *SubnetSuite) ensureDeadAndAssertLifeIsDead(c *gc.C, subnet *state.Subnet) {
   231  	err := subnet.EnsureDead()
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	c.Assert(subnet.Life(), gc.Equals, state.Dead)
   234  }
   235  
   236  func (s *SubnetSuite) refreshAndAssertSubnetLifeIs(c *gc.C, subnet *state.Subnet, expectedLife state.Life) {
   237  	err := subnet.Refresh()
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	c.Assert(subnet.Life(), gc.Equals, expectedLife)
   240  }
   241  
   242  func (s *SubnetSuite) TestEnsureDeadSetsLifeToDeadWhenNotAlive(c *gc.C) {
   243  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   244  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   245  
   246  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   247  }
   248  
   249  func (s *SubnetSuite) TestRemoveFailsIfStillAlive(c *gc.C) {
   250  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   251  
   252  	err := subnet.Remove()
   253  	c.Assert(err, gc.ErrorMatches, `cannot remove subnet "192.168.0.1/24": subnet is not dead`)
   254  	s.refreshAndAssertSubnetLifeIs(c, subnet, state.Alive)
   255  }
   256  
   257  func (s *SubnetSuite) TestRemoveSucceedsWhenSubnetIsNotAlive(c *gc.C) {
   258  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   259  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   260  
   261  	s.removeSubnetAndAssertNotFound(c, subnet)
   262  }
   263  
   264  func (s *SubnetSuite) removeSubnetAndAssertNotFound(c *gc.C, subnet *state.Subnet) {
   265  	err := subnet.Remove()
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	s.assertSubnetWithCIDRNotFound(c, subnet.CIDR())
   268  }
   269  
   270  func (s *SubnetSuite) assertSubnetWithCIDRNotFound(c *gc.C, cidr string) {
   271  	_, err := s.State.Subnet(cidr)
   272  	s.assertSubnetNotFoundError(c, err)
   273  }
   274  
   275  func (s *SubnetSuite) assertSubnetNotFoundError(c *gc.C, err error) {
   276  	c.Assert(err, gc.ErrorMatches, "subnet .* not found")
   277  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   278  }
   279  
   280  func (s *SubnetSuite) TestRemoveSucceedsWhenCalledTwice(c *gc.C) {
   281  	subnet := s.addAliveSubnet(c, "192.168.0.1/24")
   282  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   283  	s.removeSubnetAndAssertNotFound(c, subnet)
   284  
   285  	err := subnet.Remove()
   286  	c.Assert(err, gc.ErrorMatches, `cannot remove subnet "192.168.0.1/24": not found or not dead`)
   287  }
   288  
   289  func (s *SubnetSuite) TestRemoveKillsAddedIPAddresses(c *gc.C) {
   290  	subnet := s.addAliveSubnet(c, "192.168.1.0/24")
   291  	s.addIPAddressForSubnet(c, "192.168.1.0", subnet)
   292  	s.addIPAddressForSubnet(c, "192.168.1.1", subnet)
   293  
   294  	err := subnet.EnsureDead()
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	err = subnet.Remove()
   297  	c.Assert(err, jc.ErrorIsNil)
   298  
   299  	s.assertIPAddressNotFound(c, "192.168.1.0")
   300  	s.assertIPAddressNotFound(c, "192.168.1.1")
   301  }
   302  
   303  func (s *SubnetSuite) addIPAddressForSubnet(c *gc.C, ipAddress string, subnet *state.Subnet) {
   304  	_, err := s.State.AddIPAddress(network.NewAddress(ipAddress), subnet.ID())
   305  	c.Assert(err, jc.ErrorIsNil)
   306  }
   307  
   308  func (s *SubnetSuite) assertIPAddressNotFound(c *gc.C, ipAddress string) {
   309  	_, err := s.State.IPAddress(ipAddress)
   310  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   311  }
   312  
   313  func (s *SubnetSuite) TestRefreshUpdatesStaleDocData(c *gc.C) {
   314  	subnet := s.addAliveSubnet(c, "fc00::/64")
   315  	subnetCopy, err := s.State.Subnet("fc00::/64")
   316  	c.Assert(err, jc.ErrorIsNil)
   317  
   318  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   319  	c.Assert(subnetCopy.Life(), gc.Equals, state.Alive)
   320  
   321  	err = subnetCopy.Refresh()
   322  	c.Assert(err, jc.ErrorIsNil)
   323  	c.Assert(subnetCopy.Life(), gc.Equals, state.Dead)
   324  }
   325  
   326  func (s *SubnetSuite) TestRefreshFailsWithNotFoundWhenRemoved(c *gc.C) {
   327  	subnet := s.addAliveSubnet(c, "192.168.1.0/24")
   328  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   329  	s.removeSubnetAndAssertNotFound(c, subnet)
   330  
   331  	err := subnet.Refresh()
   332  	s.assertSubnetNotFoundError(c, err)
   333  }
   334  
   335  func (s *SubnetSuite) TestAllSubnets(c *gc.C) {
   336  	subnetInfos := []state.SubnetInfo{
   337  		{CIDR: "192.168.1.0/24"},
   338  		{CIDR: "8.8.8.0/24", SpaceName: "bar"},
   339  		{CIDR: "10.0.2.0/24", ProviderId: "foo"},
   340  		{CIDR: "2001:db8::/64", AvailabilityZone: "zone1"},
   341  	}
   342  
   343  	for _, info := range subnetInfos {
   344  		_, err := s.State.AddSubnet(info)
   345  		c.Assert(err, jc.ErrorIsNil)
   346  	}
   347  
   348  	subnets, err := s.State.AllSubnets()
   349  	c.Assert(err, jc.ErrorIsNil)
   350  	c.Assert(subnets, gc.HasLen, len(subnetInfos))
   351  
   352  	for i, subnet := range subnets {
   353  		c.Assert(subnet.CIDR(), gc.Equals, subnetInfos[i].CIDR)
   354  		c.Assert(subnet.ProviderId(), gc.Equals, subnetInfos[i].ProviderId)
   355  		c.Assert(subnet.SpaceName(), gc.Equals, subnetInfos[i].SpaceName)
   356  		c.Assert(subnet.AvailabilityZone(), gc.Equals, subnetInfos[i].AvailabilityZone)
   357  	}
   358  }
   359  
   360  func (s *SubnetSuite) TestPickNewAddressNoAddresses(c *gc.C) {
   361  	subnet := s.addAliveSubnet(c, "192.168.1.0/24")
   362  
   363  	_, err := subnet.PickNewAddress()
   364  	c.Assert(err, gc.ErrorMatches, "no allocatable IP addresses for subnet .*")
   365  }
   366  
   367  func (s *SubnetSuite) TestPickNewAddressWhenSubnetIsDead(c *gc.C) {
   368  	subnet := s.addSubnetWithAllocatableIPHigh(c, "192.168.1.0")
   369  	s.ensureDeadAndAssertLifeIsDead(c, subnet)
   370  
   371  	_, err := subnet.PickNewAddress()
   372  	c.Assert(err, gc.ErrorMatches,
   373  		`cannot pick address: subnet "192.168.1.0/24" is not alive`,
   374  	)
   375  }
   376  
   377  func (s *SubnetSuite) addSubnetWithAllocatableIPHigh(c *gc.C, allocatableHigh string) *state.Subnet {
   378  	subnetInfo := state.SubnetInfo{
   379  		CIDR:              "192.168.1.0/24",
   380  		AllocatableIPLow:  "192.168.1.0",
   381  		AllocatableIPHigh: allocatableHigh,
   382  	}
   383  	subnet, err := s.State.AddSubnet(subnetInfo)
   384  	c.Assert(err, jc.ErrorIsNil)
   385  	return subnet
   386  }
   387  
   388  func (s *SubnetSuite) TestPickNewAddressAddressesExhausted(c *gc.C) {
   389  	subnet := s.addSubnetWithAllocatableIPHigh(c, "192.168.1.0")
   390  	s.addIPAddressForSubnet(c, "192.168.1.0", subnet)
   391  
   392  	_, err := subnet.PickNewAddress()
   393  	c.Assert(err, gc.ErrorMatches, "allocatable IP addresses exhausted for subnet .*")
   394  }
   395  
   396  func (s *SubnetSuite) TestPickNewAddressOneAddress(c *gc.C) {
   397  	subnet := s.addSubnetWithAllocatableIPHigh(c, "192.168.1.0")
   398  
   399  	addr, err := subnet.PickNewAddress()
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	c.Assert(addr.Value(), gc.Equals, "192.168.1.0")
   402  }
   403  
   404  func (s *SubnetSuite) TestPickNewAddressSkipsAllocated(c *gc.C) {
   405  	subnet := s.addSubnetWithAllocatableIPHigh(c, "192.168.1.1")
   406  	s.addIPAddressForSubnet(c, "192.168.1.0", subnet)
   407  
   408  	ipAddr, err := subnet.PickNewAddress()
   409  	c.Assert(err, jc.ErrorIsNil)
   410  	c.Assert(ipAddr.Value(), gc.Equals, "192.168.1.1")
   411  }
   412  
   413  func (s *SubnetSuite) TestPickNewAddressRace(c *gc.C) {
   414  	// represents 192.168.1.0
   415  	initialIP := uint32(3232235776)
   416  	var index int32 = -1
   417  	addresses := []uint32{initialIP, initialIP, initialIP + 1}
   418  
   419  	// the first two calls will get the same address (which simulates the
   420  	// inherent race condition in the code). The third call will get
   421  	// a new one. We should see two different addresses come out of the
   422  	// two calls: i.e. we will have detected the race condition and tried
   423  	// again.
   424  	mockPickAddress := func(_, _ uint32, _ map[uint32]bool) uint32 {
   425  		theIndex := atomic.AddInt32(&index, 1)
   426  		return addresses[theIndex]
   427  	}
   428  	s.PatchValue(&state.PickAddress, &mockPickAddress)
   429  
   430  	// 192.168.1.0 and 192.168.1.1 are the only valid addresses
   431  	subnet := s.addSubnetWithAllocatableIPHigh(c, "192.168.1.1")
   432  
   433  	waiter := sync.WaitGroup{}
   434  	waiter.Add(2)
   435  
   436  	var firstResult *state.IPAddress
   437  	var firstError error
   438  	var secondResult *state.IPAddress
   439  	var secondError error
   440  	go func() {
   441  		firstResult, firstError = subnet.PickNewAddress()
   442  		waiter.Done()
   443  	}()
   444  	go func() {
   445  		secondResult, secondError = subnet.PickNewAddress()
   446  		waiter.Done()
   447  	}()
   448  	waiter.Wait()
   449  
   450  	c.Assert(firstError, jc.ErrorIsNil)
   451  	c.Assert(secondError, jc.ErrorIsNil)
   452  	c.Assert(firstResult, gc.NotNil)
   453  	c.Assert(secondResult, gc.NotNil)
   454  
   455  	ipAddresses := []string{firstResult.Value(), secondResult.Value()}
   456  	sort.Strings(ipAddresses)
   457  
   458  	expected := []string{"192.168.1.0", "192.168.1.1"}
   459  	c.Assert(ipAddresses, jc.DeepEquals, expected)
   460  }