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