github.com/moby/docker@v26.1.3+incompatible/libnetwork/cnmallocator/networkallocator_test.go (about)

     1  package cnmallocator
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"testing"
     7  
     8  	"github.com/docker/docker/libnetwork/types"
     9  	"github.com/moby/swarmkit/v2/api"
    10  	"github.com/moby/swarmkit/v2/manager/allocator/networkallocator"
    11  	"gotest.tools/v3/assert"
    12  	is "gotest.tools/v3/assert/cmp"
    13  )
    14  
    15  func newNetworkAllocator(t *testing.T) networkallocator.NetworkAllocator {
    16  	na, err := (&Provider{}).NewAllocator(nil)
    17  	assert.Check(t, err)
    18  	assert.Check(t, na != nil)
    19  	return na
    20  }
    21  
    22  func TestNew(t *testing.T) {
    23  	newNetworkAllocator(t)
    24  }
    25  
    26  func TestAllocateInvalidIPAM(t *testing.T) {
    27  	na := newNetworkAllocator(t)
    28  	n := &api.Network{
    29  		ID: "testID",
    30  		Spec: api.NetworkSpec{
    31  			Annotations: api.Annotations{
    32  				Name: "test",
    33  			},
    34  			DriverConfig: &api.Driver{},
    35  			IPAM: &api.IPAMOptions{
    36  				Driver: &api.Driver{
    37  					Name: "invalidipam,",
    38  				},
    39  			},
    40  		},
    41  	}
    42  	err := na.Allocate(n)
    43  	assert.Check(t, is.ErrorContains(err, ""))
    44  }
    45  
    46  func TestAllocateInvalidDriver(t *testing.T) {
    47  	na := newNetworkAllocator(t)
    48  	n := &api.Network{
    49  		ID: "testID",
    50  		Spec: api.NetworkSpec{
    51  			Annotations: api.Annotations{
    52  				Name: "test",
    53  			},
    54  			DriverConfig: &api.Driver{
    55  				Name: "invaliddriver",
    56  			},
    57  		},
    58  	}
    59  
    60  	err := na.Allocate(n)
    61  	assert.Check(t, is.ErrorContains(err, ""))
    62  }
    63  
    64  func TestNetworkDoubleAllocate(t *testing.T) {
    65  	na := newNetworkAllocator(t)
    66  	n := &api.Network{
    67  		ID: "testID",
    68  		Spec: api.NetworkSpec{
    69  			Annotations: api.Annotations{
    70  				Name: "test",
    71  			},
    72  		},
    73  	}
    74  
    75  	err := na.Allocate(n)
    76  	assert.Check(t, err)
    77  
    78  	err = na.Allocate(n)
    79  	assert.Check(t, is.ErrorContains(err, ""))
    80  }
    81  
    82  func TestAllocateEmptyConfig(t *testing.T) {
    83  	na1 := newNetworkAllocator(t)
    84  	na2 := newNetworkAllocator(t)
    85  	n1 := &api.Network{
    86  		ID: "testID1",
    87  		Spec: api.NetworkSpec{
    88  			Annotations: api.Annotations{
    89  				Name: "test1",
    90  			},
    91  		},
    92  	}
    93  
    94  	n2 := &api.Network{
    95  		ID: "testID2",
    96  		Spec: api.NetworkSpec{
    97  			Annotations: api.Annotations{
    98  				Name: "test2",
    99  			},
   100  		},
   101  	}
   102  
   103  	err := na1.Allocate(n1)
   104  	assert.Check(t, err)
   105  	assert.Check(t, n1.IPAM.Configs != nil)
   106  	assert.Check(t, is.Equal(len(n1.IPAM.Configs), 1))
   107  	assert.Check(t, is.Equal(n1.IPAM.Configs[0].Range, ""))
   108  	assert.Check(t, is.Equal(len(n1.IPAM.Configs[0].Reserved), 0))
   109  
   110  	_, subnet11, err := net.ParseCIDR(n1.IPAM.Configs[0].Subnet)
   111  	assert.Check(t, err)
   112  
   113  	gwip11 := net.ParseIP(n1.IPAM.Configs[0].Gateway)
   114  	assert.Check(t, gwip11 != nil)
   115  
   116  	err = na1.Allocate(n2)
   117  	assert.Check(t, err)
   118  	assert.Check(t, n2.IPAM.Configs != nil)
   119  	assert.Check(t, is.Equal(len(n2.IPAM.Configs), 1))
   120  	assert.Check(t, is.Equal(n2.IPAM.Configs[0].Range, ""))
   121  	assert.Check(t, is.Equal(len(n2.IPAM.Configs[0].Reserved), 0))
   122  
   123  	_, subnet21, err := net.ParseCIDR(n2.IPAM.Configs[0].Subnet)
   124  	assert.Check(t, err)
   125  
   126  	gwip21 := net.ParseIP(n2.IPAM.Configs[0].Gateway)
   127  	assert.Check(t, gwip21 != nil)
   128  
   129  	// Allocate n1 ans n2 with another allocator instance but in
   130  	// intentionally reverse order.
   131  	err = na2.Allocate(n2)
   132  	assert.Check(t, err)
   133  	assert.Check(t, n2.IPAM.Configs != nil)
   134  	assert.Check(t, is.Equal(len(n2.IPAM.Configs), 1))
   135  	assert.Check(t, is.Equal(n2.IPAM.Configs[0].Range, ""))
   136  	assert.Check(t, is.Equal(len(n2.IPAM.Configs[0].Reserved), 0))
   137  
   138  	_, subnet22, err := net.ParseCIDR(n2.IPAM.Configs[0].Subnet)
   139  	assert.Check(t, err)
   140  	assert.Check(t, is.DeepEqual(subnet21, subnet22))
   141  
   142  	gwip22 := net.ParseIP(n2.IPAM.Configs[0].Gateway)
   143  	assert.Check(t, is.DeepEqual(gwip21, gwip22))
   144  
   145  	err = na2.Allocate(n1)
   146  	assert.Check(t, err)
   147  	assert.Check(t, n1.IPAM.Configs != nil)
   148  	assert.Check(t, is.Equal(len(n1.IPAM.Configs), 1))
   149  	assert.Check(t, is.Equal(n1.IPAM.Configs[0].Range, ""))
   150  	assert.Check(t, is.Equal(len(n1.IPAM.Configs[0].Reserved), 0))
   151  
   152  	_, subnet12, err := net.ParseCIDR(n1.IPAM.Configs[0].Subnet)
   153  	assert.Check(t, err)
   154  	assert.Check(t, is.DeepEqual(subnet11, subnet12))
   155  
   156  	gwip12 := net.ParseIP(n1.IPAM.Configs[0].Gateway)
   157  	assert.Check(t, is.DeepEqual(gwip11, gwip12))
   158  }
   159  
   160  func TestAllocateWithOneSubnet(t *testing.T) {
   161  	na := newNetworkAllocator(t)
   162  	n := &api.Network{
   163  		ID: "testID",
   164  		Spec: api.NetworkSpec{
   165  			Annotations: api.Annotations{
   166  				Name: "test",
   167  			},
   168  			DriverConfig: &api.Driver{},
   169  			IPAM: &api.IPAMOptions{
   170  				Driver: &api.Driver{},
   171  				Configs: []*api.IPAMConfig{
   172  					{
   173  						Subnet: "192.168.1.0/24",
   174  					},
   175  				},
   176  			},
   177  		},
   178  	}
   179  
   180  	err := na.Allocate(n)
   181  	assert.Check(t, err)
   182  	assert.Check(t, is.Equal(len(n.IPAM.Configs), 1))
   183  	assert.Check(t, is.Equal(n.IPAM.Configs[0].Range, ""))
   184  	assert.Check(t, is.Equal(len(n.IPAM.Configs[0].Reserved), 0))
   185  	assert.Check(t, is.Equal(n.IPAM.Configs[0].Subnet, "192.168.1.0/24"))
   186  
   187  	ip := net.ParseIP(n.IPAM.Configs[0].Gateway)
   188  	assert.Check(t, ip != nil)
   189  }
   190  
   191  func TestAllocateWithOneSubnetGateway(t *testing.T) {
   192  	na := newNetworkAllocator(t)
   193  	n := &api.Network{
   194  		ID: "testID",
   195  		Spec: api.NetworkSpec{
   196  			Annotations: api.Annotations{
   197  				Name: "test",
   198  			},
   199  			DriverConfig: &api.Driver{},
   200  			IPAM: &api.IPAMOptions{
   201  				Driver: &api.Driver{},
   202  				Configs: []*api.IPAMConfig{
   203  					{
   204  						Subnet:  "192.168.1.0/24",
   205  						Gateway: "192.168.1.1",
   206  					},
   207  				},
   208  			},
   209  		},
   210  	}
   211  
   212  	err := na.Allocate(n)
   213  	assert.Check(t, err)
   214  	assert.Check(t, is.Equal(len(n.IPAM.Configs), 1))
   215  	assert.Check(t, is.Equal(n.IPAM.Configs[0].Range, ""))
   216  	assert.Check(t, is.Equal(len(n.IPAM.Configs[0].Reserved), 0))
   217  	assert.Check(t, is.Equal(n.IPAM.Configs[0].Subnet, "192.168.1.0/24"))
   218  	assert.Check(t, is.Equal(n.IPAM.Configs[0].Gateway, "192.168.1.1"))
   219  }
   220  
   221  func TestAllocateWithOneSubnetInvalidGateway(t *testing.T) {
   222  	na := newNetworkAllocator(t)
   223  	n := &api.Network{
   224  		ID: "testID",
   225  		Spec: api.NetworkSpec{
   226  			Annotations: api.Annotations{
   227  				Name: "test",
   228  			},
   229  			DriverConfig: &api.Driver{},
   230  			IPAM: &api.IPAMOptions{
   231  				Driver: &api.Driver{},
   232  				Configs: []*api.IPAMConfig{
   233  					{
   234  						Subnet:  "192.168.1.0/24",
   235  						Gateway: "192.168.2.1",
   236  					},
   237  				},
   238  			},
   239  		},
   240  	}
   241  
   242  	err := na.Allocate(n)
   243  	assert.Check(t, is.ErrorContains(err, ""))
   244  }
   245  
   246  // TestAllocateWithSmallSubnet validates that /32 subnets don't produce an error,
   247  // as /31 and /32 subnets are supported by docker daemon, starting with
   248  // https://github.com/moby/moby/commit/3a938df4b570aad3bfb4d5342379582e872fc1a3,
   249  func TestAllocateWithSmallSubnet(t *testing.T) {
   250  	na := newNetworkAllocator(t)
   251  	n := &api.Network{
   252  		ID: "testID",
   253  		Spec: api.NetworkSpec{
   254  			Annotations: api.Annotations{
   255  				Name: "test",
   256  			},
   257  			DriverConfig: &api.Driver{},
   258  			IPAM: &api.IPAMOptions{
   259  				Driver: &api.Driver{},
   260  				Configs: []*api.IPAMConfig{
   261  					{
   262  						Subnet: "1.1.1.1/32",
   263  					},
   264  				},
   265  			},
   266  		},
   267  	}
   268  
   269  	err := na.Allocate(n)
   270  	assert.Check(t, err)
   271  }
   272  
   273  func TestAllocateWithTwoSubnetsNoGateway(t *testing.T) {
   274  	na := newNetworkAllocator(t)
   275  	n := &api.Network{
   276  		ID: "testID",
   277  		Spec: api.NetworkSpec{
   278  			Annotations: api.Annotations{
   279  				Name: "test",
   280  			},
   281  			DriverConfig: &api.Driver{},
   282  			IPAM: &api.IPAMOptions{
   283  				Driver: &api.Driver{},
   284  				Configs: []*api.IPAMConfig{
   285  					{
   286  						Subnet: "192.168.1.0/24",
   287  					},
   288  					{
   289  						Subnet: "192.168.2.0/24",
   290  					},
   291  				},
   292  			},
   293  		},
   294  	}
   295  
   296  	err := na.Allocate(n)
   297  	assert.Check(t, err)
   298  	assert.Check(t, is.Equal(len(n.IPAM.Configs), 2))
   299  	assert.Check(t, is.Equal(n.IPAM.Configs[0].Range, ""))
   300  	assert.Check(t, is.Equal(len(n.IPAM.Configs[0].Reserved), 0))
   301  	assert.Check(t, is.Equal(n.IPAM.Configs[0].Subnet, "192.168.1.0/24"))
   302  	assert.Check(t, is.Equal(n.IPAM.Configs[1].Range, ""))
   303  	assert.Check(t, is.Equal(len(n.IPAM.Configs[1].Reserved), 0))
   304  	assert.Check(t, is.Equal(n.IPAM.Configs[1].Subnet, "192.168.2.0/24"))
   305  
   306  	ip := net.ParseIP(n.IPAM.Configs[0].Gateway)
   307  	assert.Check(t, ip != nil)
   308  	ip = net.ParseIP(n.IPAM.Configs[1].Gateway)
   309  	assert.Check(t, ip != nil)
   310  }
   311  
   312  func TestFree(t *testing.T) {
   313  	na := newNetworkAllocator(t)
   314  	n := &api.Network{
   315  		ID: "testID",
   316  		Spec: api.NetworkSpec{
   317  			Annotations: api.Annotations{
   318  				Name: "test",
   319  			},
   320  			DriverConfig: &api.Driver{},
   321  			IPAM: &api.IPAMOptions{
   322  				Driver: &api.Driver{},
   323  				Configs: []*api.IPAMConfig{
   324  					{
   325  						Subnet:  "192.168.1.0/24",
   326  						Gateway: "192.168.1.1",
   327  					},
   328  				},
   329  			},
   330  		},
   331  	}
   332  
   333  	err := na.Allocate(n)
   334  	assert.Check(t, err)
   335  
   336  	err = na.Deallocate(n)
   337  	assert.Check(t, err)
   338  
   339  	// Reallocate again to make sure it succeeds.
   340  	err = na.Allocate(n)
   341  	assert.Check(t, err)
   342  }
   343  
   344  func TestAllocateTaskFree(t *testing.T) {
   345  	na1 := newNetworkAllocator(t)
   346  	na2 := newNetworkAllocator(t)
   347  	n1 := &api.Network{
   348  		ID: "testID1",
   349  		Spec: api.NetworkSpec{
   350  			Annotations: api.Annotations{
   351  				Name: "test1",
   352  			},
   353  			DriverConfig: &api.Driver{},
   354  			IPAM: &api.IPAMOptions{
   355  				Driver: &api.Driver{},
   356  				Configs: []*api.IPAMConfig{
   357  					{
   358  						Subnet:  "192.168.1.0/24",
   359  						Gateway: "192.168.1.1",
   360  					},
   361  				},
   362  			},
   363  		},
   364  	}
   365  
   366  	n2 := &api.Network{
   367  		ID: "testID2",
   368  		Spec: api.NetworkSpec{
   369  			Annotations: api.Annotations{
   370  				Name: "test2",
   371  			},
   372  			DriverConfig: &api.Driver{},
   373  			IPAM: &api.IPAMOptions{
   374  				Driver: &api.Driver{},
   375  				Configs: []*api.IPAMConfig{
   376  					{
   377  						Subnet:  "192.168.2.0/24",
   378  						Gateway: "192.168.2.1",
   379  					},
   380  				},
   381  			},
   382  		},
   383  	}
   384  
   385  	task1 := &api.Task{
   386  		Networks: []*api.NetworkAttachment{
   387  			{
   388  				Network: n1,
   389  			},
   390  			{
   391  				Network: n2,
   392  			},
   393  		},
   394  	}
   395  
   396  	task2 := &api.Task{
   397  		Networks: []*api.NetworkAttachment{
   398  			{
   399  				Network: n1,
   400  			},
   401  			{
   402  				Network: n2,
   403  			},
   404  		},
   405  	}
   406  
   407  	err := na1.Allocate(n1)
   408  	assert.Check(t, err)
   409  
   410  	err = na1.Allocate(n2)
   411  	assert.Check(t, err)
   412  
   413  	err = na1.AllocateTask(task1)
   414  	assert.Check(t, err)
   415  	assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 1))
   416  	assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 1))
   417  
   418  	_, subnet1, _ := net.ParseCIDR("192.168.1.0/24")
   419  	_, subnet2, _ := net.ParseCIDR("192.168.2.0/24")
   420  
   421  	// variable coding: network/task/allocator
   422  	ip111, _, err := net.ParseCIDR(task1.Networks[0].Addresses[0])
   423  	assert.Check(t, err)
   424  
   425  	ip211, _, err := net.ParseCIDR(task1.Networks[1].Addresses[0])
   426  	assert.Check(t, err)
   427  
   428  	assert.Check(t, is.Equal(subnet1.Contains(ip111), true))
   429  	assert.Check(t, is.Equal(subnet2.Contains(ip211), true))
   430  
   431  	err = na1.AllocateTask(task2)
   432  	assert.Check(t, err)
   433  	assert.Check(t, is.Equal(len(task2.Networks[0].Addresses), 1))
   434  	assert.Check(t, is.Equal(len(task2.Networks[1].Addresses), 1))
   435  
   436  	ip121, _, err := net.ParseCIDR(task2.Networks[0].Addresses[0])
   437  	assert.Check(t, err)
   438  
   439  	ip221, _, err := net.ParseCIDR(task2.Networks[1].Addresses[0])
   440  	assert.Check(t, err)
   441  
   442  	assert.Check(t, is.Equal(subnet1.Contains(ip121), true))
   443  	assert.Check(t, is.Equal(subnet2.Contains(ip221), true))
   444  
   445  	// Now allocate the same the same tasks in a second allocator
   446  	// but intentionally in reverse order.
   447  	err = na2.Allocate(n1)
   448  	assert.Check(t, err)
   449  
   450  	err = na2.Allocate(n2)
   451  	assert.Check(t, err)
   452  
   453  	err = na2.AllocateTask(task2)
   454  	assert.Check(t, err)
   455  	assert.Check(t, is.Equal(len(task2.Networks[0].Addresses), 1))
   456  	assert.Check(t, is.Equal(len(task2.Networks[1].Addresses), 1))
   457  
   458  	ip122, _, err := net.ParseCIDR(task2.Networks[0].Addresses[0])
   459  	assert.Check(t, err)
   460  
   461  	ip222, _, err := net.ParseCIDR(task2.Networks[1].Addresses[0])
   462  	assert.Check(t, err)
   463  
   464  	assert.Check(t, is.Equal(subnet1.Contains(ip122), true))
   465  	assert.Check(t, is.Equal(subnet2.Contains(ip222), true))
   466  	assert.Check(t, is.DeepEqual(ip121, ip122))
   467  	assert.Check(t, is.DeepEqual(ip221, ip222))
   468  
   469  	err = na2.AllocateTask(task1)
   470  	assert.Check(t, err)
   471  	assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 1))
   472  	assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 1))
   473  
   474  	ip112, _, err := net.ParseCIDR(task1.Networks[0].Addresses[0])
   475  	assert.Check(t, err)
   476  
   477  	ip212, _, err := net.ParseCIDR(task1.Networks[1].Addresses[0])
   478  	assert.Check(t, err)
   479  
   480  	assert.Check(t, is.Equal(subnet1.Contains(ip112), true))
   481  	assert.Check(t, is.Equal(subnet2.Contains(ip212), true))
   482  	assert.Check(t, is.DeepEqual(ip111, ip112))
   483  	assert.Check(t, is.DeepEqual(ip211, ip212))
   484  
   485  	// Deallocate task
   486  	err = na1.DeallocateTask(task1)
   487  	assert.Check(t, err)
   488  	assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 0))
   489  	assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 0))
   490  
   491  	// Try allocation after free
   492  	err = na1.AllocateTask(task1)
   493  	assert.Check(t, err)
   494  	assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 1))
   495  	assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 1))
   496  
   497  	ip111, _, err = net.ParseCIDR(task1.Networks[0].Addresses[0])
   498  	assert.Check(t, err)
   499  
   500  	ip211, _, err = net.ParseCIDR(task1.Networks[1].Addresses[0])
   501  	assert.Check(t, err)
   502  
   503  	assert.Check(t, is.Equal(subnet1.Contains(ip111), true))
   504  	assert.Check(t, is.Equal(subnet2.Contains(ip211), true))
   505  
   506  	err = na1.DeallocateTask(task1)
   507  	assert.Check(t, err)
   508  	assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 0))
   509  	assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 0))
   510  
   511  	// Try to free endpoints on an already freed task
   512  	err = na1.DeallocateTask(task1)
   513  	assert.Check(t, err)
   514  }
   515  
   516  func TestAllocateService(t *testing.T) {
   517  	na := newNetworkAllocator(t)
   518  	n := &api.Network{
   519  		ID: "testID",
   520  		Spec: api.NetworkSpec{
   521  			Annotations: api.Annotations{
   522  				Name: "test",
   523  			},
   524  		},
   525  	}
   526  
   527  	s := &api.Service{
   528  		ID: "testID1",
   529  		Spec: api.ServiceSpec{
   530  			Task: api.TaskSpec{
   531  				Networks: []*api.NetworkAttachmentConfig{
   532  					{
   533  						Target: "testID",
   534  					},
   535  				},
   536  			},
   537  			Endpoint: &api.EndpointSpec{
   538  				Ports: []*api.PortConfig{
   539  					{
   540  						Name:       "http",
   541  						TargetPort: 80,
   542  					},
   543  					{
   544  						Name:       "https",
   545  						TargetPort: 443,
   546  					},
   547  				},
   548  			},
   549  		},
   550  	}
   551  
   552  	err := na.Allocate(n)
   553  	assert.Check(t, err)
   554  	assert.Check(t, n.IPAM.Configs != nil)
   555  	assert.Check(t, is.Equal(len(n.IPAM.Configs), 1))
   556  	assert.Check(t, is.Equal(n.IPAM.Configs[0].Range, ""))
   557  	assert.Check(t, is.Equal(len(n.IPAM.Configs[0].Reserved), 0))
   558  
   559  	_, subnet, err := net.ParseCIDR(n.IPAM.Configs[0].Subnet)
   560  	assert.Check(t, err)
   561  
   562  	gwip := net.ParseIP(n.IPAM.Configs[0].Gateway)
   563  	assert.Check(t, gwip != nil)
   564  
   565  	err = na.AllocateService(s)
   566  	assert.Check(t, err)
   567  	assert.Check(t, is.Len(s.Endpoint.Ports, 0)) // Network allocator is not responsible for allocating ports.
   568  
   569  	assert.Check(t, is.Equal(1, len(s.Endpoint.VirtualIPs)))
   570  
   571  	assert.Check(t, is.DeepEqual(s.Endpoint.Spec, s.Spec.Endpoint))
   572  
   573  	ip, _, err := net.ParseCIDR(s.Endpoint.VirtualIPs[0].Addr)
   574  	assert.Check(t, err)
   575  
   576  	assert.Check(t, is.Equal(true, subnet.Contains(ip)))
   577  }
   578  
   579  func TestDeallocateServiceAllocateIngressMode(t *testing.T) {
   580  	na := newNetworkAllocator(t)
   581  
   582  	n := &api.Network{
   583  		ID: "testNetID1",
   584  		Spec: api.NetworkSpec{
   585  			Annotations: api.Annotations{
   586  				Name: "test",
   587  			},
   588  			Ingress: true,
   589  		},
   590  	}
   591  
   592  	err := na.Allocate(n)
   593  	assert.Check(t, err)
   594  
   595  	s := &api.Service{
   596  		ID: "testID1",
   597  		Spec: api.ServiceSpec{
   598  			Endpoint: &api.EndpointSpec{
   599  				Ports: []*api.PortConfig{
   600  					{
   601  						Name:          "some_tcp",
   602  						TargetPort:    1234,
   603  						PublishedPort: 1234,
   604  						PublishMode:   api.PublishModeIngress,
   605  					},
   606  				},
   607  			},
   608  		},
   609  		Endpoint: &api.Endpoint{},
   610  	}
   611  
   612  	s.Endpoint.VirtualIPs = append(s.Endpoint.VirtualIPs,
   613  		&api.Endpoint_VirtualIP{NetworkID: n.ID})
   614  
   615  	err = na.AllocateService(s)
   616  	assert.Check(t, err)
   617  	assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
   618  
   619  	err = na.DeallocateService(s)
   620  	assert.Check(t, err)
   621  	assert.Check(t, is.Len(s.Endpoint.Ports, 0))
   622  	assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 0))
   623  	// Allocate again.
   624  	s.Endpoint.VirtualIPs = append(s.Endpoint.VirtualIPs,
   625  		&api.Endpoint_VirtualIP{NetworkID: n.ID})
   626  
   627  	err = na.AllocateService(s)
   628  	assert.Check(t, err)
   629  	assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
   630  }
   631  
   632  func TestServiceNetworkUpdate(t *testing.T) {
   633  	na := newNetworkAllocator(t)
   634  
   635  	n1 := &api.Network{
   636  		ID: "testID1",
   637  		Spec: api.NetworkSpec{
   638  			Annotations: api.Annotations{
   639  				Name: "test",
   640  			},
   641  		},
   642  	}
   643  
   644  	n2 := &api.Network{
   645  		ID: "testID2",
   646  		Spec: api.NetworkSpec{
   647  			Annotations: api.Annotations{
   648  				Name: "test2",
   649  			},
   650  		},
   651  	}
   652  
   653  	// Allocate both networks
   654  	err := na.Allocate(n1)
   655  	assert.Check(t, err)
   656  
   657  	err = na.Allocate(n2)
   658  	assert.Check(t, err)
   659  
   660  	// Attach a network to a service spec nd allocate a service
   661  	s := &api.Service{
   662  		ID: "testID1",
   663  		Spec: api.ServiceSpec{
   664  			Task: api.TaskSpec{
   665  				Networks: []*api.NetworkAttachmentConfig{
   666  					{
   667  						Target: "testID1",
   668  					},
   669  				},
   670  			},
   671  			Endpoint: &api.EndpointSpec{
   672  				Mode: api.ResolutionModeVirtualIP,
   673  			},
   674  		},
   675  	}
   676  
   677  	err = na.AllocateService(s)
   678  	assert.Check(t, err)
   679  	assert.Check(t, na.IsServiceAllocated(s))
   680  	assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
   681  
   682  	// Now update the same service with another network
   683  	s.Spec.Task.Networks = append(s.Spec.Task.Networks, &api.NetworkAttachmentConfig{Target: "testID2"})
   684  
   685  	assert.Check(t, !na.IsServiceAllocated(s))
   686  	err = na.AllocateService(s)
   687  	assert.Check(t, err)
   688  
   689  	assert.Check(t, na.IsServiceAllocated(s))
   690  	assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 2))
   691  
   692  	s.Spec.Task.Networks = s.Spec.Task.Networks[:1]
   693  
   694  	// Check if service needs update and allocate with updated service spec
   695  	assert.Check(t, !na.IsServiceAllocated(s))
   696  
   697  	err = na.AllocateService(s)
   698  	assert.Check(t, err)
   699  	assert.Check(t, na.IsServiceAllocated(s))
   700  	assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
   701  
   702  	s.Spec.Task.Networks = s.Spec.Task.Networks[:0]
   703  	// Check if service needs update with all the networks removed and allocate with updated service spec
   704  	assert.Check(t, !na.IsServiceAllocated(s))
   705  
   706  	err = na.AllocateService(s)
   707  	assert.Check(t, err)
   708  	assert.Check(t, na.IsServiceAllocated(s))
   709  	assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 0))
   710  
   711  	// Attach a network and allocate service
   712  	s.Spec.Task.Networks = append(s.Spec.Task.Networks, &api.NetworkAttachmentConfig{Target: "testID2"})
   713  	assert.Check(t, !na.IsServiceAllocated(s))
   714  
   715  	err = na.AllocateService(s)
   716  	assert.Check(t, err)
   717  
   718  	assert.Check(t, na.IsServiceAllocated(s))
   719  	assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
   720  
   721  }
   722  
   723  type mockIpam struct {
   724  	actualIpamOptions map[string]string
   725  }
   726  
   727  func (a *mockIpam) GetDefaultAddressSpaces() (string, string, error) {
   728  	return "defaultAS", "defaultAS", nil
   729  }
   730  
   731  func (a *mockIpam) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
   732  	a.actualIpamOptions = options
   733  
   734  	poolCidr, _ := types.ParseCIDR(pool)
   735  	return fmt.Sprintf("%s/%s", "defaultAS", pool), poolCidr, nil, nil
   736  }
   737  
   738  func (a *mockIpam) ReleasePool(poolID string) error {
   739  	return nil
   740  }
   741  
   742  func (a *mockIpam) RequestAddress(poolID string, ip net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
   743  	return nil, nil, nil
   744  }
   745  
   746  func (a *mockIpam) ReleaseAddress(poolID string, ip net.IP) error {
   747  	return nil
   748  }
   749  
   750  func (a *mockIpam) IsBuiltIn() bool {
   751  	return true
   752  }
   753  
   754  func TestCorrectlyPassIPAMOptions(t *testing.T) {
   755  	var err error
   756  	expectedIpamOptions := map[string]string{"network-name": "freddie"}
   757  
   758  	na := newNetworkAllocator(t)
   759  	ipamDriver := &mockIpam{}
   760  
   761  	err = na.(*cnmNetworkAllocator).ipamRegistry.RegisterIpamDriver("mockipam", ipamDriver)
   762  	assert.Check(t, err)
   763  
   764  	n := &api.Network{
   765  		ID: "testID",
   766  		Spec: api.NetworkSpec{
   767  			Annotations: api.Annotations{
   768  				Name: "test",
   769  			},
   770  			DriverConfig: &api.Driver{},
   771  			IPAM: &api.IPAMOptions{
   772  				Driver: &api.Driver{
   773  					Name:    "mockipam",
   774  					Options: expectedIpamOptions,
   775  				},
   776  				Configs: []*api.IPAMConfig{
   777  					{
   778  						Subnet:  "192.168.1.0/24",
   779  						Gateway: "192.168.1.1",
   780  					},
   781  				},
   782  			},
   783  		},
   784  	}
   785  	err = na.Allocate(n)
   786  
   787  	assert.Check(t, is.DeepEqual(expectedIpamOptions, ipamDriver.actualIpamOptions))
   788  	assert.Check(t, err)
   789  }