github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/network/subnets/subnets_test.go (about)

     1  package subnets_test
     2  
     3  import (
     4  	"net"
     5  	"runtime"
     6  
     7  	"github.com/cloudfoundry-incubator/garden-linux/linux_backend"
     8  	"github.com/cloudfoundry-incubator/garden-linux/network/subnets"
     9  
    10  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/gomega"
    12  )
    13  
    14  var _ = Describe("Subnet Pool", func() {
    15  	var subnetpool subnets.Subnets
    16  	var defaultSubnetPool *net.IPNet
    17  
    18  	JustBeforeEach(func() {
    19  		var err error
    20  		subnetpool, err = subnets.NewSubnets(defaultSubnetPool)
    21  		Expect(err).ToNot(HaveOccurred())
    22  	})
    23  
    24  	Describe("Capacity", func() {
    25  		Context("when the dynamic allocation net is empty", func() {
    26  			BeforeEach(func() {
    27  				defaultSubnetPool = subnetPool("10.2.3.0/32")
    28  			})
    29  
    30  			It("returns zero", func() {
    31  				Expect(subnetpool.Capacity()).To(Equal(0))
    32  			})
    33  		})
    34  
    35  		Context("when the dynamic allocation net is non-empty", func() {
    36  			BeforeEach(func() {
    37  				defaultSubnetPool = subnetPool("10.2.3.0/27")
    38  			})
    39  
    40  			It("returns the correct number of subnets initially and repeatedly", func() {
    41  				Expect(subnetpool.Capacity()).To(Equal(8))
    42  				Expect(subnetpool.Capacity()).To(Equal(8))
    43  			})
    44  
    45  			It("returns the correct capacity after allocating subnets", func() {
    46  				cap := subnetpool.Capacity()
    47  
    48  				_, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
    49  				Expect(err).ToNot(HaveOccurred())
    50  
    51  				Expect(subnetpool.Capacity()).To(Equal(cap))
    52  
    53  				_, err = subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
    54  				Expect(err).ToNot(HaveOccurred())
    55  
    56  				Expect(subnetpool.Capacity()).To(Equal(cap))
    57  			})
    58  		})
    59  	})
    60  
    61  	Describe("Allocating and Releasing", func() {
    62  		Describe("Static Subnet Allocation", func() {
    63  			Context("when the requested subnet is within the dynamic allocation range", func() {
    64  				BeforeEach(func() {
    65  					defaultSubnetPool = subnetPool("10.2.3.0/29")
    66  				})
    67  
    68  				It("returns an appropriate error", func() {
    69  					_, static := networkParms("10.2.3.4/30")
    70  
    71  					_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
    72  					Expect(err).To(MatchError("the requested subnet (10.2.3.4/30) overlaps the dynamic allocation range (10.2.3.0/29)"))
    73  				})
    74  			})
    75  
    76  			Context("when the requested subnet subsumes the dynamic allocation range", func() {
    77  				BeforeEach(func() {
    78  					defaultSubnetPool = subnetPool("10.2.3.4/30")
    79  				})
    80  
    81  				It("returns an appropriate error", func() {
    82  					_, static := networkParms("10.2.3.0/24")
    83  
    84  					_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
    85  					Expect(err).To(HaveOccurred())
    86  					Expect(err).To(MatchError("the requested subnet (10.2.3.0/24) overlaps the dynamic allocation range (10.2.3.4/30)"))
    87  				})
    88  			})
    89  
    90  			Context("when the requested subnet is not within the dynamic allocation range", func() {
    91  				BeforeEach(func() {
    92  					defaultSubnetPool = subnetPool("10.2.3.0/29")
    93  				})
    94  
    95  				Context("allocating a static subnet", func() {
    96  					Context("and a static IP", func() {
    97  						It("returns an error if the IP is not inside the subnet", func() {
    98  							_, static := networkParms("11.0.0.0/8")
    99  
   100  							ip := net.ParseIP("9.0.0.1")
   101  							_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   102  							Expect(err).To(Equal(subnets.ErrInvalidIP))
   103  						})
   104  
   105  						It("returns the same subnet and IP if the IP is inside the subnet", func() {
   106  							_, static := networkParms("11.0.0.0/8")
   107  
   108  							ip := net.ParseIP("11.0.0.1")
   109  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   110  							Expect(err).ToNot(HaveOccurred())
   111  
   112  							Expect(network.Subnet).To(Equal(static))
   113  							Expect(network.IP).To(Equal(ip))
   114  						})
   115  
   116  						It("does not allow the same IP to be requested twice", func() {
   117  							_, static := networkParms("11.0.0.0/8")
   118  
   119  							ip := net.ParseIP("11.0.0.1")
   120  							_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   121  							Expect(err).ToNot(HaveOccurred())
   122  
   123  							_, static = networkParms("11.0.0.0/8") // make sure we get a new pointer
   124  							_, err = subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   125  							Expect(err).To(Equal(subnets.ErrIPAlreadyAcquired))
   126  						})
   127  
   128  						It("allows two IPs to be serially requested in the same subnet", func() {
   129  							_, static := networkParms("11.0.0.0/8")
   130  
   131  							ip := net.ParseIP("11.0.0.1")
   132  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   133  							Expect(err).ToNot(HaveOccurred())
   134  							Expect(network.Subnet).To(Equal(static))
   135  							Expect(network.IP).To(Equal(ip))
   136  
   137  							ip2 := net.ParseIP("11.0.0.2")
   138  
   139  							_, static = networkParms("11.0.0.0/8") // make sure we get a new pointer
   140  							network2, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip2})
   141  							Expect(err).ToNot(HaveOccurred())
   142  							Expect(network2.Subnet).To(Equal(static))
   143  							Expect(network2.IP).To(Equal(ip2))
   144  						})
   145  
   146  						It("when an IP is allocated from a subnet but released in between, it should be treated as new both times", func() {
   147  							_, static := networkParms("11.0.0.0/8")
   148  
   149  							ip := net.ParseIP("11.0.0.1")
   150  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   151  							Expect(err).ToNot(HaveOccurred())
   152  							Expect(network.Subnet).To(Equal(static))
   153  							Expect(network.IP).To(Equal(ip))
   154  
   155  							err = subnetpool.Release(network)
   156  							Expect(err).ToNot(HaveOccurred())
   157  
   158  							_, static = networkParms("11.0.0.0/8") // make sure we get a new pointer
   159  							network, err = subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   160  							Expect(err).ToNot(HaveOccurred())
   161  							Expect(network.Subnet).To(Equal(static))
   162  							Expect(network.IP).To(Equal(ip))
   163  						})
   164  
   165  						It("prevents dynamic allocation of the same IP", func() {
   166  							_, static := networkParms("11.0.0.0/8")
   167  
   168  							ip := net.ParseIP("11.0.0.2")
   169  							_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   170  							Expect(err).ToNot(HaveOccurred())
   171  
   172  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   173  							Expect(err).ToNot(HaveOccurred())
   174  							Expect(network.IP.String()).To(Equal("11.0.0.1"))
   175  
   176  							network, err = subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   177  							Expect(err).ToNot(HaveOccurred())
   178  							Expect(network.IP.String()).To(Equal("11.0.0.3"))
   179  						})
   180  
   181  						Describe("errors", func() {
   182  							It("fails if a static subnet is requested specifying an IP address which clashes with the gateway IP address", func() {
   183  								_, static := networkParms("11.0.0.0/8")
   184  								gateway := net.ParseIP("11.255.255.254")
   185  								_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{gateway})
   186  								Expect(err).To(MatchError(subnets.ErrIPEqualsGateway))
   187  							})
   188  
   189  							It("fails if a static subnet is requested specifying an IP address which clashes with the broadcast IP address", func() {
   190  								_, static := networkParms("11.0.0.0/8")
   191  								max := net.ParseIP("11.255.255.255")
   192  								_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{max})
   193  								Expect(err).To(MatchError(subnets.ErrIPEqualsBroadcast))
   194  							})
   195  						})
   196  					})
   197  
   198  					Context("and a dynamic IP", func() {
   199  						It("does not return an error", func() {
   200  							_, static := networkParms("11.0.0.0/8")
   201  
   202  							_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   203  							Expect(err).ToNot(HaveOccurred())
   204  						})
   205  
   206  						It("returns the first available IP", func() {
   207  							_, static := networkParms("11.0.0.0/8")
   208  
   209  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   210  							Expect(err).ToNot(HaveOccurred())
   211  
   212  							Expect(network.IP.String()).To(Equal("11.0.0.1"))
   213  						})
   214  
   215  						It("returns distinct IPs", func() {
   216  							_, static := networkParms("11.0.0.0/22")
   217  
   218  							seen := make(map[string]bool)
   219  							var err error
   220  							for err == nil {
   221  								network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   222  
   223  								if err != nil {
   224  									Expect(err).To(Equal(subnets.ErrInsufficientIPs))
   225  									break
   226  								}
   227  
   228  								Expect(seen).ToNot(HaveKey(network.IP.String()))
   229  								seen[network.IP.String()] = true
   230  							}
   231  						})
   232  
   233  						It("returns all IPs except gateway, minimum and broadcast", func() {
   234  							_, static := networkParms("11.0.0.0/23")
   235  
   236  							var err error
   237  							count := 0
   238  							for err == nil {
   239  								if _, err = subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector); err != nil {
   240  									Expect(err).To(Equal(subnets.ErrInsufficientIPs))
   241  								}
   242  
   243  								count++
   244  							}
   245  
   246  							Expect(count).To(Equal(510))
   247  						})
   248  
   249  						It("causes static alocation to fail if it tries to allocate the same IP afterwards", func() {
   250  							_, static := networkParms("11.0.0.0/8")
   251  
   252  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   253  							Expect(err).ToNot(HaveOccurred())
   254  
   255  							_, err = subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{network.IP})
   256  							Expect(err).To(Equal(subnets.ErrIPAlreadyAcquired))
   257  						})
   258  					})
   259  				})
   260  
   261  				Context("after all IPs are allocated from a subnet, a subsequent request for the same subnet", func() {
   262  					var (
   263  						static *net.IPNet
   264  						ips    [5]net.IP
   265  					)
   266  
   267  					JustBeforeEach(func() {
   268  						var err error
   269  						_, static, err = net.ParseCIDR("10.9.3.0/29")
   270  						Expect(err).ToNot(HaveOccurred())
   271  
   272  						for i := 0; i < 5; i++ {
   273  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   274  							Expect(err).ToNot(HaveOccurred())
   275  
   276  							ips[i] = network.IP
   277  						}
   278  					})
   279  
   280  					It("returns an appropriate error", func() {
   281  						_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   282  						Expect(err).To(HaveOccurred())
   283  						Expect(err).To(Equal(subnets.ErrInsufficientIPs))
   284  					})
   285  
   286  					Context("but after it is released", func() {
   287  						It("dynamically allocates the released IP again", func() {
   288  							err := subnetpool.Release(&linux_backend.Network{static, ips[3]})
   289  							Expect(err).ToNot(HaveOccurred())
   290  
   291  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   292  							Expect(err).ToNot(HaveOccurred())
   293  							Expect(network.IP).To(Equal(ips[3]))
   294  						})
   295  
   296  						It("allows static allocation again", func() {
   297  							err := subnetpool.Release(&linux_backend.Network{static, ips[3]})
   298  							Expect(err).ToNot(HaveOccurred())
   299  
   300  							_, err = subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ips[3]})
   301  							Expect(err).ToNot(HaveOccurred())
   302  						})
   303  					})
   304  				})
   305  
   306  				Context("after a subnet has been allocated, a subsequent request for an overlapping subnet which begins on the same ip", func() {
   307  					var (
   308  						firstSubnetPool  *net.IPNet
   309  						firstContainerIP net.IP
   310  						secondSubnetPool *net.IPNet
   311  					)
   312  
   313  					JustBeforeEach(func() {
   314  						var err error
   315  						firstContainerIP, firstSubnetPool = networkParms("10.9.3.0/30")
   316  						Expect(err).ToNot(HaveOccurred())
   317  
   318  						_, secondSubnetPool = networkParms("10.9.3.0/29")
   319  						Expect(err).ToNot(HaveOccurred())
   320  
   321  						_, err = subnetpool.Acquire(subnets.StaticSubnetSelector{firstSubnetPool}, subnets.DynamicIPSelector)
   322  						Expect(err).ToNot(HaveOccurred())
   323  					})
   324  
   325  					It("returns an appropriate error", func() {
   326  						_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{secondSubnetPool}, subnets.DynamicIPSelector)
   327  						Expect(err).To(MatchError("the requested subnet (10.9.3.0/29) overlaps an existing subnet (10.9.3.0/30)"))
   328  					})
   329  				})
   330  
   331  				Context("after a subnet has been allocated, a subsequent request for an overlapping subnet", func() {
   332  					var (
   333  						firstSubnetPool  *net.IPNet
   334  						firstContainerIP net.IP
   335  						secondSubnetPool *net.IPNet
   336  					)
   337  
   338  					JustBeforeEach(func() {
   339  						var err error
   340  						firstContainerIP, firstSubnetPool = networkParms("10.9.3.4/30")
   341  						Expect(err).ToNot(HaveOccurred())
   342  
   343  						_, secondSubnetPool = networkParms("10.9.3.0/29")
   344  						Expect(err).ToNot(HaveOccurred())
   345  
   346  						_, err = subnetpool.Acquire(subnets.StaticSubnetSelector{firstSubnetPool}, subnets.DynamicIPSelector)
   347  						Expect(err).ToNot(HaveOccurred())
   348  					})
   349  
   350  					It("returns an appropriate error", func() {
   351  						_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{secondSubnetPool}, subnets.DynamicIPSelector)
   352  						Expect(err).To(MatchError("the requested subnet (10.9.3.0/29) overlaps an existing subnet (10.9.3.4/30)"))
   353  					})
   354  
   355  					Context("but after it is released", func() {
   356  						It("allows allocation again", func() {
   357  							err := subnetpool.Release(&linux_backend.Network{firstSubnetPool, firstContainerIP})
   358  							Expect(err).ToNot(HaveOccurred())
   359  
   360  							_, err = subnetpool.Acquire(subnets.StaticSubnetSelector{secondSubnetPool}, subnets.DynamicIPSelector)
   361  							Expect(err).ToNot(HaveOccurred())
   362  						})
   363  					})
   364  				})
   365  
   366  				Context("requesting a specific IP address in a static subnet", func() {
   367  					It("does not return an error", func() {
   368  						_, static := networkParms("10.9.3.6/29")
   369  
   370  						_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   371  						Expect(err).ToNot(HaveOccurred())
   372  					})
   373  				})
   374  
   375  			})
   376  		})
   377  
   378  		Describe("Dynamic /30 Subnet Allocation", func() {
   379  			Context("when the pool does not have sufficient IPs to allocate a subnet", func() {
   380  				BeforeEach(func() {
   381  					defaultSubnetPool = subnetPool("10.2.3.0/31")
   382  				})
   383  
   384  				It("the first request returns an error", func() {
   385  					_, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   386  					Expect(err).To(HaveOccurred())
   387  				})
   388  			})
   389  
   390  			Context("when the pool has sufficient IPs to allocate a single subnet", func() {
   391  				BeforeEach(func() {
   392  					defaultSubnetPool = subnetPool("10.2.3.0/30")
   393  				})
   394  
   395  				Context("the first request", func() {
   396  					It("succeeds, and returns a /30 network within the subnet", func() {
   397  						network, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   398  						Expect(err).ToNot(HaveOccurred())
   399  
   400  						Expect(network.Subnet).ToNot(BeNil())
   401  						Expect(network.Subnet.String()).To(Equal("10.2.3.0/30"))
   402  					})
   403  				})
   404  
   405  				Context("subsequent requests", func() {
   406  					It("fails, and return an err", func() {
   407  						_, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   408  						Expect(err).ToNot(HaveOccurred())
   409  
   410  						_, err = subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   411  						Expect(err).To(HaveOccurred())
   412  					})
   413  				})
   414  
   415  				Context("when an allocated network is released", func() {
   416  					It("a subsequent allocation succeeds, and returns the first network again", func() {
   417  						// first
   418  						network, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   419  						Expect(err).ToNot(HaveOccurred())
   420  
   421  						// second - will fail (sanity check)
   422  						_, err = subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   423  						Expect(err).To(HaveOccurred())
   424  
   425  						// release
   426  						err = subnetpool.Release(&linux_backend.Network{network.Subnet, network.IP})
   427  						Expect(err).ToNot(HaveOccurred())
   428  
   429  						// third - should work now because of release
   430  						network2, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   431  						Expect(err).ToNot(HaveOccurred())
   432  
   433  						Expect(network2.Subnet).ToNot(BeNil())
   434  						Expect(network2.Subnet.String()).To(Equal(network.Subnet.String()))
   435  					})
   436  
   437  					Context("and it is not the last IP in the subnet", func() {
   438  						It("returns gone=false", func() {
   439  							_, static := networkParms("10.3.3.0/29")
   440  
   441  							_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   442  							Expect(err).ToNot(HaveOccurred())
   443  
   444  							network, err := subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.DynamicIPSelector)
   445  							Expect(err).ToNot(HaveOccurred())
   446  
   447  							err = subnetpool.Release(&linux_backend.Network{network.Subnet, network.IP})
   448  							Expect(err).ToNot(HaveOccurred())
   449  						})
   450  					})
   451  				})
   452  
   453  				Context("when a network is released twice", func() {
   454  					It("returns an error", func() {
   455  						// first
   456  						network, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   457  						Expect(err).ToNot(HaveOccurred())
   458  
   459  						// release
   460  						err = subnetpool.Release(&linux_backend.Network{network.Subnet, network.IP})
   461  						Expect(err).ToNot(HaveOccurred())
   462  
   463  						// release again
   464  						err = subnetpool.Release(&linux_backend.Network{network.Subnet, network.IP})
   465  						Expect(err).To(HaveOccurred())
   466  						Expect(err).To(Equal(subnets.ErrReleasedUnallocatedSubnet))
   467  					})
   468  				})
   469  			})
   470  
   471  			Context("when the pool has sufficient IPs to allocate two /30 subnets", func() {
   472  				BeforeEach(func() {
   473  					defaultSubnetPool = subnetPool("10.2.3.0/29")
   474  				})
   475  
   476  				Context("the second request", func() {
   477  					It("succeeds", func() {
   478  						_, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   479  						Expect(err).ToNot(HaveOccurred())
   480  
   481  						_, err = subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   482  						Expect(err).ToNot(HaveOccurred())
   483  					})
   484  
   485  					It("returns the second /30 network within the subnet", func() {
   486  						_, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   487  						Expect(err).ToNot(HaveOccurred())
   488  
   489  						network, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   490  						Expect(err).ToNot(HaveOccurred())
   491  
   492  						Expect(network.Subnet).ToNot(BeNil())
   493  						Expect(network.Subnet.String()).To(Equal("10.2.3.4/30"))
   494  					})
   495  				})
   496  
   497  				It("allocates distinct networks concurrently", func() {
   498  					prev := runtime.GOMAXPROCS(2)
   499  					defer runtime.GOMAXPROCS(prev)
   500  
   501  					Consistently(func() bool {
   502  						_, network, err := net.ParseCIDR("10.0.0.0/29")
   503  						Expect(err).ToNot(HaveOccurred())
   504  
   505  						subnetpool, err := subnets.NewSubnets(network)
   506  						Expect(err).ToNot(HaveOccurred())
   507  
   508  						out := make(chan *net.IPNet)
   509  						go func(out chan *net.IPNet) {
   510  							defer GinkgoRecover()
   511  							n1, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   512  							Expect(err).ToNot(HaveOccurred())
   513  							out <- n1.Subnet
   514  						}(out)
   515  
   516  						go func(out chan *net.IPNet) {
   517  							defer GinkgoRecover()
   518  							n1, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   519  							Expect(err).ToNot(HaveOccurred())
   520  							out <- n1.Subnet
   521  						}(out)
   522  
   523  						a := <-out
   524  						b := <-out
   525  						return a.IP.Equal(b.IP)
   526  					}, "100ms", "2ms").ShouldNot(BeTrue())
   527  				})
   528  
   529  				It("correctly handles concurrent release of the same network", func() {
   530  					prev := runtime.GOMAXPROCS(2)
   531  					defer runtime.GOMAXPROCS(prev)
   532  
   533  					Consistently(func() bool {
   534  						_, network, err := net.ParseCIDR("10.0.0.0/29")
   535  						Expect(err).ToNot(HaveOccurred())
   536  
   537  						subnetpool, err := subnets.NewSubnets(network)
   538  						Expect(err).ToNot(HaveOccurred())
   539  
   540  						acquired, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.DynamicIPSelector)
   541  						Expect(err).ToNot(HaveOccurred())
   542  
   543  						out := make(chan error)
   544  						go func(out chan error) {
   545  							defer GinkgoRecover()
   546  							err := subnetpool.Release(&linux_backend.Network{acquired.Subnet, acquired.IP})
   547  							out <- err
   548  						}(out)
   549  
   550  						go func(out chan error) {
   551  							defer GinkgoRecover()
   552  							err := subnetpool.Release(&linux_backend.Network{acquired.Subnet, acquired.IP})
   553  							out <- err
   554  						}(out)
   555  
   556  						a := <-out
   557  						b := <-out
   558  						return (a == nil) != (b == nil)
   559  					}, "200ms", "2ms").Should(BeTrue())
   560  				})
   561  
   562  				It("correctly handles concurrent allocation of the same network", func() {
   563  					prev := runtime.GOMAXPROCS(2)
   564  					defer runtime.GOMAXPROCS(prev)
   565  
   566  					Consistently(func() bool {
   567  						network := subnetPool("10.0.0.0/29")
   568  
   569  						subnetpool, err := subnets.NewSubnets(network)
   570  						Expect(err).ToNot(HaveOccurred())
   571  
   572  						ip, n1 := networkParms("10.1.0.0/30")
   573  
   574  						out := make(chan error)
   575  						go func(out chan error) {
   576  							defer GinkgoRecover()
   577  							_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{n1}, subnets.StaticIPSelector{ip})
   578  							out <- err
   579  						}(out)
   580  
   581  						go func(out chan error) {
   582  							defer GinkgoRecover()
   583  							_, err := subnetpool.Acquire(subnets.StaticSubnetSelector{n1}, subnets.StaticIPSelector{ip})
   584  							out <- err
   585  						}(out)
   586  
   587  						a := <-out
   588  						b := <-out
   589  						return (a == nil) != (b == nil)
   590  					}, "200ms", "2ms").Should(BeTrue())
   591  				})
   592  			})
   593  		})
   594  
   595  		Describe("Removeing", func() {
   596  			BeforeEach(func() {
   597  				defaultSubnetPool = subnetPool("10.2.3.0/29")
   598  			})
   599  
   600  			Context("an allocation outside the dynamic allocation net", func() {
   601  				It("recovers the first time", func() {
   602  					_, static := networkParms("10.9.3.4/30")
   603  
   604  					err := subnetpool.Remove(&linux_backend.Network{static, net.ParseIP("10.9.3.5")})
   605  					Expect(err).ToNot(HaveOccurred())
   606  				})
   607  
   608  				It("does not allow recovering twice", func() {
   609  					_, static := networkParms("10.9.3.4/30")
   610  
   611  					err := subnetpool.Remove(&linux_backend.Network{static, net.ParseIP("10.9.3.5")})
   612  					Expect(err).ToNot(HaveOccurred())
   613  
   614  					err = subnetpool.Remove(&linux_backend.Network{static, net.ParseIP("10.9.3.5")})
   615  					Expect(err).To(HaveOccurred())
   616  				})
   617  
   618  				It("does not allow allocating after recovery", func() {
   619  					_, static := networkParms("10.9.3.4/30")
   620  
   621  					ip := net.ParseIP("10.9.3.5")
   622  					err := subnetpool.Remove(&linux_backend.Network{static, ip})
   623  					Expect(err).ToNot(HaveOccurred())
   624  
   625  					_, err = subnetpool.Acquire(subnets.StaticSubnetSelector{static}, subnets.StaticIPSelector{ip})
   626  					Expect(err).To(HaveOccurred())
   627  				})
   628  
   629  				It("does not allow recovering without an explicit IP", func() {
   630  					_, static := networkParms("10.9.3.4/30")
   631  
   632  					err := subnetpool.Remove(&linux_backend.Network{static, nil})
   633  					Expect(err).To(HaveOccurred())
   634  				})
   635  			})
   636  
   637  			Context("an allocation inside the dynamic allocation net", func() {
   638  				It("recovers the first time", func() {
   639  					_, static := networkParms("10.2.3.4/30")
   640  
   641  					err := subnetpool.Remove(&linux_backend.Network{static, net.ParseIP("10.2.3.5")})
   642  					Expect(err).ToNot(HaveOccurred())
   643  				})
   644  
   645  				It("does not allow recovering twice", func() {
   646  					_, static := networkParms("10.2.3.4/30")
   647  
   648  					err := subnetpool.Remove(&linux_backend.Network{static, net.ParseIP("10.2.3.5")})
   649  					Expect(err).ToNot(HaveOccurred())
   650  
   651  					err = subnetpool.Remove(&linux_backend.Network{static, net.ParseIP("10.2.3.5")})
   652  					Expect(err).To(HaveOccurred())
   653  				})
   654  
   655  				It("does not dynamically allocate a recovered network", func() {
   656  					_, static := networkParms("10.2.3.4/30")
   657  
   658  					err := subnetpool.Remove(&linux_backend.Network{static, net.ParseIP("10.2.3.1")})
   659  					Expect(err).ToNot(HaveOccurred())
   660  
   661  					network, err := subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.StaticIPSelector{net.ParseIP("10.2.3.1")})
   662  					Expect(err).ToNot(HaveOccurred())
   663  					Expect(network.Subnet.String()).To(Equal("10.2.3.0/30"))
   664  
   665  					_, err = subnetpool.Acquire(subnets.DynamicSubnetSelector, subnets.StaticIPSelector{net.ParseIP("10.2.3.1")})
   666  					Expect(err).To(Equal(subnets.ErrInsufficientSubnets))
   667  				})
   668  			})
   669  
   670  		})
   671  
   672  	})
   673  })
   674  
   675  func subnetPool(networkString string) *net.IPNet {
   676  	_, subnetPool := networkParms(networkString)
   677  	return subnetPool
   678  }
   679  
   680  func networkParms(networkString string) (net.IP, *net.IPNet) {
   681  	containerIP, subnet, err := net.ParseCIDR(networkString)
   682  	Expect(err).ToNot(HaveOccurred())
   683  	if containerIP.Equal(subnet.IP) {
   684  		containerIP = nextIP(containerIP)
   685  	}
   686  	return containerIP, subnet
   687  }
   688  
   689  func nextIP(ip net.IP) net.IP {
   690  	next := net.ParseIP(ip.String())
   691  	inc(next)
   692  	return next
   693  }
   694  
   695  func inc(ip net.IP) {
   696  	for j := len(ip) - 1; j >= 0; j-- {
   697  		ip[j]++
   698  		if ip[j] > 0 {
   699  			break
   700  		}
   701  	}
   702  }