github.com/geofffranks/garden-linux@v0.0.0-20160715111146-26c893169cfa/network/bridgemgr/mgr_test.go (about)

     1  package bridgemgr_test
     2  
     3  import (
     4  	"errors"
     5  	"net"
     6  
     7  	"code.cloudfoundry.org/garden-linux/network/bridgemgr"
     8  	. "github.com/onsi/ginkgo"
     9  	. "github.com/onsi/gomega"
    10  )
    11  
    12  var _ = Describe("BridgeNamePool", func() {
    13  	var subnet1 *net.IPNet
    14  	var subnet2 *net.IPNet
    15  	var fakeBuilder *builder
    16  	var fakeLister *lister
    17  
    18  	var mgr bridgemgr.BridgeManager
    19  
    20  	BeforeEach(func() {
    21  		_, subnet1, _ = net.ParseCIDR("1.2.3.4/30")
    22  		_, subnet2, _ = net.ParseCIDR("1.2.3.4/29")
    23  
    24  		fakeBuilder = &builder{}
    25  		fakeLister = &lister{}
    26  		mgr = bridgemgr.New("pr", fakeBuilder, fakeLister)
    27  	})
    28  
    29  	Describe("reserving", func() {
    30  		Context("when no bridge names have been assigned", func() {
    31  			It("assigns a prefixed name", func() {
    32  				name, err := mgr.Reserve(subnet1, "container1")
    33  
    34  				Expect(err).ToNot(HaveOccurred())
    35  				Expect(name).To(MatchRegexp("^pr"))
    36  			})
    37  
    38  			It("creates the bridge", func() {
    39  				name, err := mgr.Reserve(subnet1, "container-name")
    40  				Expect(err).ToNot(HaveOccurred())
    41  				Expect(fakeBuilder.CreatedBridges).To(ContainElement(createParams{
    42  					name:   name,
    43  					subnet: subnet1,
    44  					ip:     net.ParseIP("1.2.3.5").To4(),
    45  				}))
    46  			})
    47  
    48  			Context("when creating the bridge fails", func() {
    49  				BeforeEach(func() {
    50  					fakeBuilder.CreateReturns = errors.New("Bananas")
    51  				})
    52  
    53  				It("returns the error", func() {
    54  					_, err := mgr.Reserve(subnet1, "container1")
    55  					Expect(err).To(HaveOccurred())
    56  				})
    57  
    58  				Context("when the same subnet is acquired later", func() {
    59  					It("retries the creation", func() {
    60  						mgr.Reserve(subnet1, "container1")
    61  						mgr.Reserve(subnet1, "container1")
    62  
    63  						Expect(fakeBuilder.CreatedBridges).To(HaveLen(2))
    64  					})
    65  				})
    66  			})
    67  		})
    68  
    69  		Context("when a subnet has already been assigned a bridge name", func() {
    70  			It("reuses the same name", func() {
    71  				name1, err := mgr.Reserve(subnet1, "container1")
    72  				Expect(err).ToNot(HaveOccurred())
    73  
    74  				name2, err := mgr.Reserve(subnet1, "container2")
    75  				Expect(err).ToNot(HaveOccurred())
    76  
    77  				Expect(name2).To(Equal(name1))
    78  				Expect(fakeBuilder.CreatedBridges).To(HaveLen(1))
    79  			})
    80  		})
    81  
    82  		Context("when a bridge is acquired for a different subnet", func() {
    83  			It("assigns a new bridge name", func() {
    84  				name1, err := mgr.Reserve(subnet1, "container1")
    85  				Expect(err).ToNot(HaveOccurred())
    86  
    87  				name2, err := mgr.Reserve(subnet2, "container2")
    88  				Expect(err).ToNot(HaveOccurred())
    89  
    90  				Expect(name2).ToNot(Equal(name1))
    91  				Expect(fakeBuilder.CreatedBridges).To(HaveLen(2))
    92  			})
    93  		})
    94  	})
    95  
    96  	Describe("releasing", func() {
    97  		Context("when a container releases its bridge", func() {
    98  			var name string
    99  
   100  			Context("and there are still containers in the subnet", func() {
   101  				BeforeEach(func() {
   102  					_, err := mgr.Reserve(subnet1, "container1")
   103  					Expect(err).ToNot(HaveOccurred())
   104  					name, err = mgr.Reserve(subnet1, "container2")
   105  					Expect(err).ToNot(HaveOccurred())
   106  
   107  					Expect(mgr.Release(name, "container1")).To(Succeed())
   108  				})
   109  
   110  				It("reuses the existing bridge name on the next Reserve", func() {
   111  					newName, err := mgr.Reserve(subnet1, "container3")
   112  					Expect(err).ToNot(HaveOccurred())
   113  					Expect(newName).To(Equal(name))
   114  				})
   115  
   116  				It("does not destroy the bridge", func() {
   117  					Expect(fakeBuilder.Destroyed).ToNot(ContainElement(name))
   118  				})
   119  			})
   120  
   121  			Context("and it is the final container using the bridge", func() {
   122  				var name string
   123  				BeforeEach(func() {
   124  					var err error
   125  					name, err = mgr.Reserve(subnet1, "container1")
   126  					Expect(err).ToNot(HaveOccurred())
   127  
   128  					Expect(mgr.Release(name, "container1")).To(Succeed())
   129  				})
   130  
   131  				It("assigns a new bridge name on the next Reserve", func() {
   132  					newName, err := mgr.Reserve(subnet1, "container2")
   133  					Expect(err).ToNot(HaveOccurred())
   134  					Expect(newName).ToNot(Equal(name))
   135  				})
   136  
   137  				It("destroys the bridge with the passed destroyer", func() {
   138  					Expect(mgr.Release("some-bridge", "container1")).To(Succeed())
   139  					Expect(fakeBuilder.Destroyed).To(ContainElement("some-bridge"))
   140  				})
   141  
   142  				Context("when the destroyer returns an error", func() {
   143  					It("returns an error", func() {
   144  						fakeBuilder.DestroyReturns = errors.New("bboom ")
   145  						Expect(mgr.Release("some-bridge", "container1")).ToNot(Succeed())
   146  					})
   147  				})
   148  			})
   149  
   150  			Context("and it has not previously been acquired (e.g. when releasing an unknown bridge during recovery)", func() {
   151  				It("destroys the bridge with the passed destroyer", func() {
   152  					Expect(mgr.Release("some-bridge", "container1")).To(Succeed())
   153  					Expect(fakeBuilder.Destroyed).To(ContainElement("some-bridge"))
   154  				})
   155  			})
   156  		})
   157  	})
   158  
   159  	Describe("rereserving", func() {
   160  		It("returns an error if the bridge name is empty", func() {
   161  			Expect(mgr.Rereserve("", subnet1, "some-id")).To(MatchError("bridgemgr: re-reserving bridge: bridge name must not be empty"))
   162  		})
   163  
   164  		Context("when a bridge name is rereserved", func() {
   165  			It("returns an error if the reacquired subnet is already assigned to another bridge name", func() {
   166  				name, err := mgr.Reserve(subnet1, "")
   167  				Expect(err).ToNot(HaveOccurred())
   168  
   169  				Expect(mgr.Rereserve(name, subnet2, "")).ToNot(Succeed())
   170  			})
   171  
   172  			Context("when the bridge could be reacquired", func() {
   173  				BeforeEach(func() {
   174  					Expect(mgr.Rereserve("my-bridge", subnet1, "my-container")).To(Succeed())
   175  				})
   176  
   177  				Context("when a bridge name is acquired for the same subnet", func() {
   178  					It("reuses the bridge name", func() {
   179  						name, err := mgr.Reserve(subnet1, "another-container")
   180  						Expect(err).ToNot(HaveOccurred())
   181  
   182  						Expect(name).To(Equal("my-bridge"))
   183  					})
   184  
   185  					Context("when it is released", func() {
   186  						It("does not destroy the bridge, since the reacquired container is still using it", func() {
   187  							Expect(mgr.Release("my-bridge", "another-container")).To(Succeed())
   188  							Expect(fakeBuilder.Destroyed).ToNot(ContainElement("my-bridge"))
   189  						})
   190  					})
   191  				})
   192  
   193  				Context("when it is released", func() {
   194  					It("destroys the bridge with the passed destroyer", func() {
   195  						Expect(mgr.Release("my-bridge", "my-container")).To(Succeed())
   196  						Expect(fakeBuilder.Destroyed).To(ContainElement("my-bridge"))
   197  					})
   198  				})
   199  			})
   200  		})
   201  	})
   202  
   203  	Describe("pruning", func() {
   204  		Context("when listing bridges fails", func() {
   205  			BeforeEach(func() {
   206  				fakeLister.ListReturns = errors.New("o no")
   207  			})
   208  
   209  			It("returns a wrapped error", func() {
   210  				Expect(mgr.Prune()).To(MatchError("bridgemgr: pruning bridges: o no"))
   211  			})
   212  		})
   213  
   214  		Context("when there are no bridges", func() {
   215  			It("does not destroy any bridges", func() {
   216  				Expect(mgr.Prune()).To(Succeed())
   217  				Expect(fakeBuilder.Destroyed).To(HaveLen(0))
   218  			})
   219  		})
   220  
   221  		Context("when there are multiple bridges", func() {
   222  			BeforeEach(func() {
   223  				fakeLister.Bridges = []string{"doesnotmatch", "pr-123", "pr-234"}
   224  				mgr.Rereserve("pr-234", subnet1, "somecontainerid")
   225  				Expect(mgr.Prune()).To(Succeed())
   226  			})
   227  
   228  			It("destroys bridges with the prefix", func() {
   229  				Expect(fakeBuilder.Destroyed).To(ContainElement("pr-123"))
   230  			})
   231  
   232  			It("does not destroy bridges without the prefix", func() {
   233  				Expect(fakeBuilder.Destroyed).ToNot(ContainElement("doesnotmatch"))
   234  			})
   235  
   236  			It("does not destroy bridges which are reserved", func() {
   237  				Expect(fakeBuilder.Destroyed).ToNot(ContainElement("pr-234"))
   238  			})
   239  		})
   240  	})
   241  })
   242  
   243  type builder struct {
   244  	CreatedBridges []createParams
   245  	CreateReturns  error
   246  	Destroyed      []string
   247  	DestroyReturns error
   248  }
   249  
   250  type createParams struct {
   251  	name   string
   252  	subnet *net.IPNet
   253  	ip     net.IP
   254  }
   255  
   256  func (c *builder) Create(name string, ip net.IP, subnet *net.IPNet) (*net.Interface, error) {
   257  	c.CreatedBridges = append(c.CreatedBridges, createParams{
   258  		name:   name,
   259  		subnet: subnet,
   260  		ip:     ip,
   261  	})
   262  
   263  	return nil, c.CreateReturns
   264  }
   265  
   266  func (d *builder) Destroy(name string) error {
   267  	d.Destroyed = append(d.Destroyed, name)
   268  	return d.DestroyReturns
   269  }
   270  
   271  type lister struct {
   272  	Bridges     []string
   273  	ListReturns error
   274  }
   275  
   276  func (l *lister) List() ([]string, error) {
   277  	return l.Bridges, l.ListReturns
   278  }