...

Source file src/k8s.io/kubernetes/pkg/registry/core/service/ipallocator/bitmap_test.go

Documentation: k8s.io/kubernetes/pkg/registry/core/service/ipallocator

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package ipallocator
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  	"testing"
    23  
    24  	"k8s.io/apimachinery/pkg/util/sets"
    25  	"k8s.io/component-base/metrics/testutil"
    26  	api "k8s.io/kubernetes/pkg/apis/core"
    27  	netutils "k8s.io/utils/net"
    28  )
    29  
    30  func TestAllocate(t *testing.T) {
    31  	testCases := []struct {
    32  		name             string
    33  		cidr             string
    34  		family           api.IPFamily
    35  		free             int
    36  		released         string
    37  		outOfRange       []string
    38  		alreadyAllocated string
    39  	}{
    40  		{
    41  			name:     "IPv4",
    42  			cidr:     "192.168.1.0/24",
    43  			family:   api.IPv4Protocol,
    44  			free:     254,
    45  			released: "192.168.1.5",
    46  			outOfRange: []string{
    47  				"192.168.0.1",   // not in 192.168.1.0/24
    48  				"192.168.1.0",   // reserved (base address)
    49  				"192.168.1.255", // reserved (broadcast address)
    50  				"192.168.2.2",   // not in 192.168.1.0/24
    51  			},
    52  			alreadyAllocated: "192.168.1.1",
    53  		},
    54  		{
    55  			name:     "IPv4 large",
    56  			cidr:     "10.0.0.0/15",
    57  			family:   api.IPv4Protocol,
    58  			free:     131070,
    59  			released: "10.0.0.5",
    60  			outOfRange: []string{
    61  				"10.0.0.0",      // reserved (base address)
    62  				"10.15.255.255", // reserved (broadcast address)
    63  				"10.255.255.2",  // not in range
    64  			},
    65  			alreadyAllocated: "10.0.0.1",
    66  		},
    67  		{
    68  			name:     "IPv6",
    69  			cidr:     "2001:db8:1::/48",
    70  			family:   api.IPv6Protocol,
    71  			free:     65535,
    72  			released: "2001:db8:1::5",
    73  			outOfRange: []string{
    74  				"2001:db8::1",     // not in 2001:db8:1::/48
    75  				"2001:db8:1::",    // reserved (base address)
    76  				"2001:db8:1::1:0", // not in the low 16 bits of 2001:db8:1::/48
    77  				"2001:db8:2::2",   // not in 2001:db8:1::/48
    78  			},
    79  			alreadyAllocated: "2001:db8:1::1",
    80  		},
    81  	}
    82  	for _, tc := range testCases {
    83  		t.Run(tc.name, func(t *testing.T) {
    84  			_, cidr, err := netutils.ParseCIDRSloppy(tc.cidr)
    85  			if err != nil {
    86  				t.Fatal(err)
    87  			}
    88  			r, err := NewInMemory(cidr)
    89  			if err != nil {
    90  				t.Fatal(err)
    91  			}
    92  			if f := r.Free(); f != tc.free {
    93  				t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, tc.free, f)
    94  			}
    95  
    96  			rCIDR := r.CIDR()
    97  			if rCIDR.String() != tc.cidr {
    98  				t.Errorf("[%s] wrong CIDR: expected %v, got %v", tc.name, tc.cidr, rCIDR.String())
    99  			}
   100  
   101  			if r.IPFamily() != tc.family {
   102  				t.Errorf("[%s] wrong IP family: expected %v, got %v", tc.name, tc.family, r.IPFamily())
   103  			}
   104  
   105  			if f := r.Used(); f != 0 {
   106  				t.Errorf("[%s]: wrong used: expected %d, got %d", tc.name, 0, f)
   107  			}
   108  			found := sets.NewString()
   109  			count := 0
   110  			for r.Free() > 0 {
   111  				ip, err := r.AllocateNext()
   112  				if err != nil {
   113  					t.Fatalf("[%s] error @ %d: %v", tc.name, count, err)
   114  				}
   115  				count++
   116  				if !cidr.Contains(ip) {
   117  					t.Fatalf("[%s] allocated %s which is outside of %s", tc.name, ip, cidr)
   118  				}
   119  				if found.Has(ip.String()) {
   120  					t.Fatalf("[%s] allocated %s twice @ %d", tc.name, ip, count)
   121  				}
   122  				found.Insert(ip.String())
   123  			}
   124  			if _, err := r.AllocateNext(); err != ErrFull {
   125  				t.Fatal(err)
   126  			}
   127  
   128  			released := netutils.ParseIPSloppy(tc.released)
   129  			if err := r.Release(released); err != nil {
   130  				t.Fatal(err)
   131  			}
   132  			if f := r.Free(); f != 1 {
   133  				t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, 1, f)
   134  			}
   135  			if f := r.Used(); f != (tc.free - 1) {
   136  				t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, tc.free-1, f)
   137  			}
   138  			ip, err := r.AllocateNext()
   139  			if err != nil {
   140  				t.Fatal(err)
   141  			}
   142  			if !released.Equal(ip) {
   143  				t.Errorf("[%s] unexpected %s : %s", tc.name, ip, released)
   144  			}
   145  
   146  			if err := r.Release(released); err != nil {
   147  				t.Fatal(err)
   148  			}
   149  			for _, outOfRange := range tc.outOfRange {
   150  				err = r.Allocate(netutils.ParseIPSloppy(outOfRange))
   151  				if _, ok := err.(*ErrNotInRange); !ok {
   152  					t.Fatal(err)
   153  				}
   154  			}
   155  			if err := r.Allocate(netutils.ParseIPSloppy(tc.alreadyAllocated)); err != ErrAllocated {
   156  				t.Fatal(err)
   157  			}
   158  			if f := r.Free(); f != 1 {
   159  				t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, 1, f)
   160  			}
   161  			if f := r.Used(); f != (tc.free - 1) {
   162  				t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, tc.free-1, f)
   163  			}
   164  			if err := r.Allocate(released); err != nil {
   165  				t.Fatal(err)
   166  			}
   167  			if f := r.Free(); f != 0 {
   168  				t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, 0, f)
   169  			}
   170  			if f := r.Used(); f != tc.free {
   171  				t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, tc.free, f)
   172  			}
   173  		})
   174  	}
   175  }
   176  
   177  func TestAllocateTiny(t *testing.T) {
   178  	_, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/32")
   179  	if err != nil {
   180  		t.Fatal(err)
   181  	}
   182  	r, err := NewInMemory(cidr)
   183  	if err != nil {
   184  		t.Fatal(err)
   185  	}
   186  	if f := r.Free(); f != 0 {
   187  		t.Errorf("free: %d", f)
   188  	}
   189  	if _, err := r.AllocateNext(); err != ErrFull {
   190  		t.Error(err)
   191  	}
   192  }
   193  
   194  func TestAllocateReserved(t *testing.T) {
   195  	_, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/25")
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	r, err := NewInMemory(cidr)
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	// allocate all addresses on the dynamic block
   204  	// subnet /25 = 128 ; dynamic block size is min(max(16,128/16),256) = 16
   205  	dynamicOffset := calculateRangeOffset(cidr)
   206  	dynamicBlockSize := r.max - dynamicOffset
   207  	for i := 0; i < dynamicBlockSize; i++ {
   208  		if _, err := r.AllocateNext(); err != nil {
   209  			t.Errorf("Unexpected error trying to allocate: %v", err)
   210  		}
   211  	}
   212  	for i := dynamicOffset; i < r.max; i++ {
   213  		ip := fmt.Sprintf("192.168.1.%d", i+1)
   214  		if !r.Has(netutils.ParseIPSloppy(ip)) {
   215  			t.Errorf("IP %s expected to be allocated", ip)
   216  		}
   217  	}
   218  	if f := r.Free(); f != dynamicOffset {
   219  		t.Errorf("expected %d free addresses, got %d", dynamicOffset, f)
   220  	}
   221  	// allocate all addresses on the static block
   222  	for i := 0; i < dynamicOffset; i++ {
   223  		ip := fmt.Sprintf("192.168.1.%d", i+1)
   224  		if err := r.Allocate(netutils.ParseIPSloppy(ip)); err != nil {
   225  			t.Errorf("Unexpected error trying to allocate IP %s: %v", ip, err)
   226  		}
   227  	}
   228  	if f := r.Free(); f != 0 {
   229  		t.Errorf("expected free equal to 0 got: %d", f)
   230  	}
   231  	// release one address in the allocated block and another a new one randomly
   232  	if err := r.Release(netutils.ParseIPSloppy("192.168.1.10")); err != nil {
   233  		t.Fatalf("Unexpected error trying to release ip 192.168.1.10: %v", err)
   234  	}
   235  	if _, err := r.AllocateNext(); err != nil {
   236  		t.Error(err)
   237  	}
   238  	if f := r.Free(); f != 0 {
   239  		t.Errorf("expected free equal to 0 got: %d", f)
   240  	}
   241  }
   242  
   243  func TestAllocateSmall(t *testing.T) {
   244  	_, cidr, err := netutils.ParseCIDRSloppy("192.168.1.240/30")
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	r, err := NewInMemory(cidr)
   249  	if err != nil {
   250  		t.Fatal(err)
   251  	}
   252  	if f := r.Free(); f != 2 {
   253  		t.Errorf("expected free equal to 2 got: %d", f)
   254  	}
   255  	found := sets.NewString()
   256  	for i := 0; i < 2; i++ {
   257  		ip, err := r.AllocateNext()
   258  		if err != nil {
   259  			t.Fatal(err)
   260  		}
   261  		if found.Has(ip.String()) {
   262  			t.Fatalf("address %s has been already allocated", ip)
   263  		}
   264  		found.Insert(ip.String())
   265  	}
   266  	for s := range found {
   267  		if !r.Has(netutils.ParseIPSloppy(s)) {
   268  			t.Fatalf("missing: %s", s)
   269  		}
   270  		if err := r.Allocate(netutils.ParseIPSloppy(s)); err != ErrAllocated {
   271  			t.Fatal(err)
   272  		}
   273  	}
   274  	for i := 0; i < 100; i++ {
   275  		if _, err := r.AllocateNext(); err != ErrFull {
   276  			t.Fatalf("suddenly became not-full: %#v", r)
   277  		}
   278  	}
   279  
   280  	if f := r.Free(); f != 0 {
   281  		t.Errorf("expected free equal to 0 got: %d", f)
   282  	}
   283  
   284  	if r.max != 2 {
   285  		t.Fatalf("expected range equal to 2, got: %v", r)
   286  	}
   287  }
   288  
   289  func TestForEach(t *testing.T) {
   290  	_, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/24")
   291  	if err != nil {
   292  		t.Fatal(err)
   293  	}
   294  
   295  	testCases := []sets.String{
   296  		sets.NewString(),
   297  		sets.NewString("192.168.1.1"),
   298  		sets.NewString("192.168.1.1", "192.168.1.254"),
   299  		sets.NewString("192.168.1.1", "192.168.1.128", "192.168.1.254"),
   300  	}
   301  
   302  	for i, tc := range testCases {
   303  		r, err := NewInMemory(cidr)
   304  		if err != nil {
   305  			t.Fatal(err)
   306  		}
   307  		for ips := range tc {
   308  			ip := netutils.ParseIPSloppy(ips)
   309  			if err := r.Allocate(ip); err != nil {
   310  				t.Errorf("[%d] error allocating IP %v: %v", i, ip, err)
   311  			}
   312  			if !r.Has(ip) {
   313  				t.Errorf("[%d] expected IP %v allocated", i, ip)
   314  			}
   315  		}
   316  		calls := sets.NewString()
   317  		r.ForEach(func(ip net.IP) {
   318  			calls.Insert(ip.String())
   319  		})
   320  		if len(calls) != len(tc) {
   321  			t.Errorf("[%d] expected %d calls, got %d", i, len(tc), len(calls))
   322  		}
   323  		if !calls.Equal(tc) {
   324  			t.Errorf("[%d] expected calls to equal testcase: %v vs %v", i, calls.List(), tc.List())
   325  		}
   326  	}
   327  }
   328  
   329  func TestSnapshot(t *testing.T) {
   330  	_, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/24")
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  	r, err := NewInMemory(cidr)
   335  	if err != nil {
   336  		t.Fatal(err)
   337  	}
   338  	ip := []net.IP{}
   339  	for i := 0; i < 10; i++ {
   340  		n, err := r.AllocateNext()
   341  		if err != nil {
   342  			t.Fatal(err)
   343  		}
   344  		ip = append(ip, n)
   345  	}
   346  
   347  	var dst api.RangeAllocation
   348  	err = r.Snapshot(&dst)
   349  	if err != nil {
   350  		t.Fatal(err)
   351  	}
   352  
   353  	_, network, err := netutils.ParseCIDRSloppy(dst.Range)
   354  	if err != nil {
   355  		t.Fatal(err)
   356  	}
   357  
   358  	if !network.IP.Equal(cidr.IP) || network.Mask.String() != cidr.Mask.String() {
   359  		t.Fatalf("mismatched networks: %s : %s", network, cidr)
   360  	}
   361  
   362  	_, otherCidr, err := netutils.ParseCIDRSloppy("192.168.2.0/24")
   363  	if err != nil {
   364  		t.Fatal(err)
   365  	}
   366  	_, err = NewInMemory(otherCidr)
   367  	if err != nil {
   368  		t.Fatal(err)
   369  	}
   370  	if err := r.Restore(otherCidr, dst.Data); err != ErrMismatchedNetwork {
   371  		t.Fatal(err)
   372  	}
   373  	other, err := NewInMemory(network)
   374  	if err != nil {
   375  		t.Fatal(err)
   376  	}
   377  	if err := other.Restore(network, dst.Data); err != nil {
   378  		t.Fatal(err)
   379  	}
   380  
   381  	for _, n := range ip {
   382  		if !other.Has(n) {
   383  			t.Errorf("restored range does not have %s", n)
   384  		}
   385  	}
   386  	if other.Free() != r.Free() {
   387  		t.Errorf("counts do not match: %d", other.Free())
   388  	}
   389  }
   390  
   391  func TestNewFromSnapshot(t *testing.T) {
   392  	_, cidr, err := netutils.ParseCIDRSloppy("192.168.0.0/24")
   393  	if err != nil {
   394  		t.Fatal(err)
   395  	}
   396  	r, err := NewInMemory(cidr)
   397  	if err != nil {
   398  		t.Fatal(err)
   399  	}
   400  	allocated := []net.IP{}
   401  	for i := 0; i < 128; i++ {
   402  		ip, err := r.AllocateNext()
   403  		if err != nil {
   404  			t.Fatal(err)
   405  		}
   406  		allocated = append(allocated, ip)
   407  	}
   408  
   409  	snapshot := api.RangeAllocation{}
   410  	if err = r.Snapshot(&snapshot); err != nil {
   411  		t.Fatal(err)
   412  	}
   413  
   414  	r, err = NewFromSnapshot(&snapshot)
   415  	if err != nil {
   416  		t.Fatal(err)
   417  	}
   418  
   419  	if x := r.Free(); x != 126 {
   420  		t.Fatalf("expected 126 free IPs, got %d", x)
   421  	}
   422  	if x := r.Used(); x != 128 {
   423  		t.Fatalf("expected 128 used IPs, got %d", x)
   424  	}
   425  
   426  	for _, ip := range allocated {
   427  		if !r.Has(ip) {
   428  			t.Fatalf("expected IP to be allocated, but it was not")
   429  		}
   430  	}
   431  }
   432  
   433  func TestClusterIPMetrics(t *testing.T) {
   434  	clearMetrics()
   435  	// create IPv4 allocator
   436  	cidrIPv4 := "10.0.0.0/24"
   437  	_, clusterCIDRv4, _ := netutils.ParseCIDRSloppy(cidrIPv4)
   438  	a, err := NewInMemory(clusterCIDRv4)
   439  	if err != nil {
   440  		t.Fatalf("unexpected error creating CidrSet: %v", err)
   441  	}
   442  	a.EnableMetrics()
   443  	// create IPv6 allocator
   444  	cidrIPv6 := "2001:db8::/112"
   445  	_, clusterCIDRv6, _ := netutils.ParseCIDRSloppy(cidrIPv6)
   446  	b, err := NewInMemory(clusterCIDRv6)
   447  	b.EnableMetrics()
   448  	if err != nil {
   449  		t.Fatalf("unexpected error creating CidrSet: %v", err)
   450  	}
   451  
   452  	// Check initial state
   453  	em := testMetrics{
   454  		free:      0,
   455  		used:      0,
   456  		allocated: 0,
   457  		errors:    0,
   458  	}
   459  	expectMetrics(t, cidrIPv4, em)
   460  	em = testMetrics{
   461  		free:      0,
   462  		used:      0,
   463  		allocated: 0,
   464  		errors:    0,
   465  	}
   466  	expectMetrics(t, cidrIPv6, em)
   467  
   468  	// allocate 2 IPv4 addresses
   469  	found := sets.NewString()
   470  	for i := 0; i < 2; i++ {
   471  		ip, err := a.AllocateNext()
   472  		if err != nil {
   473  			t.Fatal(err)
   474  		}
   475  		if found.Has(ip.String()) {
   476  			t.Fatalf("already reserved: %s", ip)
   477  		}
   478  		found.Insert(ip.String())
   479  	}
   480  
   481  	em = testMetrics{
   482  		free:      252,
   483  		used:      2,
   484  		allocated: 2,
   485  		errors:    0,
   486  	}
   487  	expectMetrics(t, cidrIPv4, em)
   488  
   489  	// try to allocate the same IP addresses
   490  	for s := range found {
   491  		if !a.Has(netutils.ParseIPSloppy(s)) {
   492  			t.Fatalf("missing: %s", s)
   493  		}
   494  		if err := a.Allocate(netutils.ParseIPSloppy(s)); err != ErrAllocated {
   495  			t.Fatal(err)
   496  		}
   497  	}
   498  	em = testMetrics{
   499  		free:      252,
   500  		used:      2,
   501  		allocated: 2,
   502  		errors:    2,
   503  	}
   504  	expectMetrics(t, cidrIPv4, em)
   505  
   506  	// release the addresses allocated
   507  	for s := range found {
   508  		if !a.Has(netutils.ParseIPSloppy(s)) {
   509  			t.Fatalf("missing: %s", s)
   510  		}
   511  		if err := a.Release(netutils.ParseIPSloppy(s)); err != nil {
   512  			t.Fatal(err)
   513  		}
   514  	}
   515  	em = testMetrics{
   516  		free:      254,
   517  		used:      0,
   518  		allocated: 2,
   519  		errors:    2,
   520  	}
   521  	expectMetrics(t, cidrIPv4, em)
   522  
   523  	// allocate 264 addresses for each allocator
   524  	// the full range and 10 more (254 + 10 = 264) for IPv4
   525  	for i := 0; i < 264; i++ {
   526  		a.AllocateNext()
   527  		b.AllocateNext()
   528  	}
   529  	em = testMetrics{
   530  		free:      0,
   531  		used:      254,
   532  		allocated: 256, // this is a counter, we already had 2 allocations and we did 254 more
   533  		errors:    12,
   534  	}
   535  	expectMetrics(t, cidrIPv4, em)
   536  	em = testMetrics{
   537  		free:      65271, // IPv6 clusterIP range is capped to 2^16 and consider the broadcast address as valid
   538  		used:      264,
   539  		allocated: 264,
   540  		errors:    0,
   541  	}
   542  	expectMetrics(t, cidrIPv6, em)
   543  }
   544  
   545  func TestClusterIPAllocatedMetrics(t *testing.T) {
   546  	clearMetrics()
   547  	// create IPv4 allocator
   548  	cidrIPv4 := "10.0.0.0/25"
   549  	_, clusterCIDRv4, _ := netutils.ParseCIDRSloppy(cidrIPv4)
   550  	a, err := NewInMemory(clusterCIDRv4)
   551  	if err != nil {
   552  		t.Fatalf("unexpected error creating CidrSet: %v", err)
   553  	}
   554  	a.EnableMetrics()
   555  
   556  	em := testMetrics{
   557  		free:      0,
   558  		used:      0,
   559  		allocated: 0,
   560  		errors:    0,
   561  	}
   562  	expectMetrics(t, cidrIPv4, em)
   563  
   564  	// allocate 2 dynamic IPv4 addresses
   565  	found := sets.NewString()
   566  	for i := 0; i < 2; i++ {
   567  		ip, err := a.AllocateNext()
   568  		if err != nil {
   569  			t.Fatal(err)
   570  		}
   571  		if found.Has(ip.String()) {
   572  			t.Fatalf("already reserved: %s", ip)
   573  		}
   574  		found.Insert(ip.String())
   575  	}
   576  
   577  	dynamic_allocated, err := testutil.GetCounterMetricValue(clusterIPAllocations.WithLabelValues(cidrIPv4, "dynamic"))
   578  	if err != nil {
   579  		t.Errorf("failed to get %s value, err: %v", clusterIPAllocations.Name, err)
   580  	}
   581  	if dynamic_allocated != 2 {
   582  		t.Fatalf("Expected 2 received %f", dynamic_allocated)
   583  	}
   584  
   585  	// try to allocate the same IP addresses
   586  	for s := range found {
   587  		if !a.Has(netutils.ParseIPSloppy(s)) {
   588  			t.Fatalf("missing: %s", s)
   589  		}
   590  		if err := a.Allocate(netutils.ParseIPSloppy(s)); err != ErrAllocated {
   591  			t.Fatal(err)
   592  		}
   593  	}
   594  
   595  	static_errors, err := testutil.GetCounterMetricValue(clusterIPAllocationErrors.WithLabelValues(cidrIPv4, "static"))
   596  	if err != nil {
   597  		t.Errorf("failed to get %s value, err: %v", clusterIPAllocationErrors.Name, err)
   598  	}
   599  	if static_errors != 2 {
   600  		t.Fatalf("Expected 2 received %f", dynamic_allocated)
   601  	}
   602  }
   603  
   604  func TestMetricsDisabled(t *testing.T) {
   605  	// create metrics enabled allocator
   606  	cidrIPv4 := "10.0.0.0/24"
   607  	_, clusterCIDRv4, _ := netutils.ParseCIDRSloppy(cidrIPv4)
   608  	a, err := NewInMemory(clusterCIDRv4)
   609  	if err != nil {
   610  		t.Fatalf("unexpected error creating CidrSet: %v", err)
   611  	}
   612  	a.EnableMetrics()
   613  
   614  	// create metrics disabled allocator with same CIDR
   615  	// this metrics should be ignored
   616  	b, err := NewInMemory(clusterCIDRv4)
   617  	if err != nil {
   618  		t.Fatalf("unexpected error creating CidrSet: %v", err)
   619  	}
   620  
   621  	// Check initial state
   622  	em := testMetrics{
   623  		free:      0,
   624  		used:      0,
   625  		allocated: 0,
   626  		errors:    0,
   627  	}
   628  	expectMetrics(t, cidrIPv4, em)
   629  
   630  	// allocate in metrics enabled allocator
   631  	for i := 0; i < 100; i++ {
   632  		_, err := a.AllocateNext()
   633  		if err != nil {
   634  			t.Fatal(err)
   635  		}
   636  	}
   637  	em = testMetrics{
   638  		free:      154,
   639  		used:      100,
   640  		allocated: 100,
   641  		errors:    0,
   642  	}
   643  	expectMetrics(t, cidrIPv4, em)
   644  
   645  	// allocate in metrics disabled allocator
   646  	for i := 0; i < 200; i++ {
   647  		_, err := b.AllocateNext()
   648  		if err != nil {
   649  			t.Fatal(err)
   650  		}
   651  	}
   652  	// the metrics should not be changed
   653  	expectMetrics(t, cidrIPv4, em)
   654  }
   655  
   656  // Metrics helpers
   657  func clearMetrics() {
   658  	clusterIPAllocated.Reset()
   659  	clusterIPAvailable.Reset()
   660  	clusterIPAllocations.Reset()
   661  	clusterIPAllocationErrors.Reset()
   662  }
   663  
   664  type testMetrics struct {
   665  	free      float64
   666  	used      float64
   667  	allocated float64
   668  	errors    float64
   669  }
   670  
   671  func expectMetrics(t *testing.T, label string, em testMetrics) {
   672  	var m testMetrics
   673  	var err error
   674  	m.free, err = testutil.GetGaugeMetricValue(clusterIPAvailable.WithLabelValues(label))
   675  	if err != nil {
   676  		t.Errorf("failed to get %s value, err: %v", clusterIPAvailable.Name, err)
   677  	}
   678  	m.used, err = testutil.GetGaugeMetricValue(clusterIPAllocated.WithLabelValues(label))
   679  	if err != nil {
   680  		t.Errorf("failed to get %s value, err: %v", clusterIPAllocated.Name, err)
   681  	}
   682  	static_allocated, err := testutil.GetCounterMetricValue(clusterIPAllocations.WithLabelValues(label, "static"))
   683  	if err != nil {
   684  		t.Errorf("failed to get %s value, err: %v", clusterIPAllocations.Name, err)
   685  	}
   686  	static_errors, err := testutil.GetCounterMetricValue(clusterIPAllocationErrors.WithLabelValues(label, "static"))
   687  	if err != nil {
   688  		t.Errorf("failed to get %s value, err: %v", clusterIPAllocationErrors.Name, err)
   689  	}
   690  	dynamic_allocated, err := testutil.GetCounterMetricValue(clusterIPAllocations.WithLabelValues(label, "dynamic"))
   691  	if err != nil {
   692  		t.Errorf("failed to get %s value, err: %v", clusterIPAllocations.Name, err)
   693  	}
   694  	dynamic_errors, err := testutil.GetCounterMetricValue(clusterIPAllocationErrors.WithLabelValues(label, "dynamic"))
   695  	if err != nil {
   696  		t.Errorf("failed to get %s value, err: %v", clusterIPAllocationErrors.Name, err)
   697  	}
   698  
   699  	m.allocated = static_allocated + dynamic_allocated
   700  	m.errors = static_errors + dynamic_errors
   701  
   702  	if m != em {
   703  		t.Fatalf("metrics error: expected %v, received %v", em, m)
   704  	}
   705  }
   706  
   707  func TestDryRun(t *testing.T) {
   708  	testCases := []struct {
   709  		name   string
   710  		cidr   string
   711  		family api.IPFamily
   712  	}{{
   713  		name:   "IPv4",
   714  		cidr:   "192.168.1.0/24",
   715  		family: api.IPv4Protocol,
   716  	}, {
   717  		name:   "IPv6",
   718  		cidr:   "2001:db8:1::/48",
   719  		family: api.IPv6Protocol,
   720  	}}
   721  	for _, tc := range testCases {
   722  		t.Run(tc.name, func(t *testing.T) {
   723  			_, cidr, err := netutils.ParseCIDRSloppy(tc.cidr)
   724  			if err != nil {
   725  				t.Fatalf("unexpected failure: %v", err)
   726  			}
   727  			r, err := NewInMemory(cidr)
   728  			if err != nil {
   729  				t.Fatalf("unexpected failure: %v", err)
   730  			}
   731  
   732  			baseUsed := r.Used()
   733  
   734  			rCIDR := r.DryRun().CIDR()
   735  			if rCIDR.String() != tc.cidr {
   736  				t.Errorf("allocator returned a different cidr")
   737  			}
   738  
   739  			if r.DryRun().IPFamily() != tc.family {
   740  				t.Errorf("allocator returned wrong IP family")
   741  			}
   742  
   743  			expectUsed := func(t *testing.T, r *Range, expect int) {
   744  				t.Helper()
   745  				if u := r.Used(); u != expect {
   746  					t.Errorf("unexpected used count: got %d, wanted %d", u, expect)
   747  				}
   748  			}
   749  			expectUsed(t, r, baseUsed)
   750  
   751  			err = r.DryRun().Allocate(netutils.AddIPOffset(netutils.BigForIP(cidr.IP), 1))
   752  			if err != nil {
   753  				t.Fatalf("unexpected failure: %v", err)
   754  			}
   755  			expectUsed(t, r, baseUsed)
   756  
   757  			_, err = r.DryRun().AllocateNext()
   758  			if err != nil {
   759  				t.Fatalf("unexpected failure: %v", err)
   760  			}
   761  			expectUsed(t, r, baseUsed)
   762  
   763  			if err := r.DryRun().Release(cidr.IP); err != nil {
   764  				t.Fatalf("unexpected failure: %v", err)
   765  			}
   766  			expectUsed(t, r, baseUsed)
   767  		})
   768  	}
   769  }
   770  
   771  func Test_calculateRangeOffset(t *testing.T) {
   772  	// default $min = 16, $max = 256 and $step = 16.
   773  	tests := []struct {
   774  		name string
   775  		cidr string
   776  		want int
   777  	}{
   778  		{
   779  			name: "full mask IPv4",
   780  			cidr: "192.168.1.1/32",
   781  			want: 0,
   782  		},
   783  		{
   784  			name: "full mask IPv6",
   785  			cidr: "fd00::1/128",
   786  			want: 0,
   787  		},
   788  		{
   789  			name: "very small mask IPv4",
   790  			cidr: "192.168.1.1/30",
   791  			want: 0,
   792  		},
   793  		{
   794  			name: "very small mask IPv6",
   795  			cidr: "fd00::1/126",
   796  			want: 0,
   797  		},
   798  		{
   799  			name: "small mask IPv4",
   800  			cidr: "192.168.1.1/28",
   801  			want: 0,
   802  		},
   803  		{
   804  			name: "small mask IPv4",
   805  			cidr: "192.168.1.1/27",
   806  			want: 16,
   807  		},
   808  		{
   809  			name: "small mask IPv6",
   810  			cidr: "fd00::1/124",
   811  			want: 0,
   812  		},
   813  		{
   814  			name: "small mask IPv6",
   815  			cidr: "fd00::1/122",
   816  			want: 16,
   817  		},
   818  		{
   819  			name: "medium mask IPv4",
   820  			cidr: "192.168.1.1/22",
   821  			want: 64,
   822  		},
   823  		{
   824  			name: "medium mask IPv6",
   825  			cidr: "fd00::1/118",
   826  			want: 64,
   827  		},
   828  		{
   829  			name: "large mask IPv4",
   830  			cidr: "192.168.1.1/8",
   831  			want: 256,
   832  		},
   833  		{
   834  			name: "large mask IPv6",
   835  			cidr: "fd00::1/12",
   836  			want: 256,
   837  		},
   838  	}
   839  	for _, tt := range tests {
   840  		t.Run(tt.name, func(t *testing.T) {
   841  
   842  			_, cidr, err := netutils.ParseCIDRSloppy(tt.cidr)
   843  			if err != nil {
   844  				t.Fatalf("Unexpected error parsing CIDR %s: %v", tt.cidr, err)
   845  			}
   846  			if got := calculateRangeOffset(cidr); got != tt.want {
   847  				t.Errorf("DynamicRangeOffset() = %v, want %v", got, tt.want)
   848  			}
   849  		})
   850  	}
   851  }
   852  
   853  // cpu: Intel(R) Xeon(R) CPU E5-2678 v3 @ 2.50GHz
   854  // BenchmarkAllocateNextIPv4
   855  // BenchmarkAllocateNextIPv4-24    	 1175304	       870.9 ns/op	    1337 B/op	      11 allocs/op
   856  func BenchmarkAllocateNextIPv4Size1048574(b *testing.B) {
   857  	_, cidr, err := netutils.ParseCIDRSloppy("10.0.0.0/12")
   858  	if err != nil {
   859  		b.Fatal(err)
   860  	}
   861  	r, err := NewInMemory(cidr)
   862  	if err != nil {
   863  		b.Fatal(err)
   864  	}
   865  	for n := 0; n < b.N; n++ {
   866  		r.AllocateNext()
   867  	}
   868  }
   869  
   870  // This is capped to 65535
   871  // cpu: Intel(R) Xeon(R) CPU E5-2678 v3 @ 2.50GHz
   872  // BenchmarkAllocateNextIPv6
   873  // BenchmarkAllocateNextIPv6-24    	 5779431	       194.0 ns/op	      18 B/op	       2 allocs/op
   874  func BenchmarkAllocateNextIPv6Size65535(b *testing.B) {
   875  	_, cidr, err := netutils.ParseCIDRSloppy("fd00::/24")
   876  	if err != nil {
   877  		b.Fatal(err)
   878  	}
   879  	r, err := NewInMemory(cidr)
   880  	if err != nil {
   881  		b.Fatal(err)
   882  	}
   883  	for n := 0; n < b.N; n++ {
   884  		r.AllocateNext()
   885  	}
   886  }
   887  

View as plain text