k8s.io/kubernetes@v1.29.3/pkg/controller/nodeipam/ipam/multicidrset/multi_cidr_set_test.go (about)

     1  /*
     2  Copyright 2022 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 multicidrset
    18  
    19  import (
    20  	"net"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"k8s.io/component-base/metrics/testutil"
    25  	"k8s.io/klog/v2/ktesting"
    26  	utilnet "k8s.io/utils/net"
    27  )
    28  
    29  func allocateNext(s *MultiCIDRSet) (*net.IPNet, error) {
    30  	candidate, _, err := s.NextCandidate()
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	err = s.Occupy(candidate)
    36  
    37  	return candidate, err
    38  }
    39  
    40  func TestCIDRSetFullyAllocated(t *testing.T) {
    41  	cases := []struct {
    42  		clusterCIDRStr  string
    43  		perNodeHostBits int
    44  		expectedCIDR    string
    45  		description     string
    46  	}{
    47  		{
    48  			clusterCIDRStr:  "127.123.234.0/28",
    49  			perNodeHostBits: 4,
    50  			expectedCIDR:    "127.123.234.0/28",
    51  			description:     "Fully allocated CIDR with IPv4",
    52  		},
    53  		{
    54  			clusterCIDRStr:  "beef:1234::/112",
    55  			perNodeHostBits: 16,
    56  			expectedCIDR:    "beef:1234::/112",
    57  			description:     "Fully allocated CIDR with IPv6",
    58  		},
    59  	}
    60  	for _, tc := range cases {
    61  		_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr)
    62  		a, err := NewMultiCIDRSet(clusterCIDR, tc.perNodeHostBits)
    63  		if err != nil {
    64  			t.Fatalf("unexpected error: %v for %v", err, tc.description)
    65  		}
    66  		p, err := allocateNext(a)
    67  		if err != nil {
    68  			t.Fatalf("unexpected error: %v for %v", err, tc.description)
    69  		}
    70  		if p.String() != tc.expectedCIDR {
    71  			t.Fatalf("unexpected allocated cidr: %v, expecting %v for %v",
    72  				p.String(), tc.expectedCIDR, tc.description)
    73  		}
    74  
    75  		_, err = allocateNext(a)
    76  		if err == nil {
    77  			t.Fatalf("expected error because of fully-allocated range for %v", tc.description)
    78  		}
    79  
    80  		a.Release(p)
    81  
    82  		p, err = allocateNext(a)
    83  		if err != nil {
    84  			t.Fatalf("unexpected error: %v for %v", err, tc.description)
    85  		}
    86  		if p.String() != tc.expectedCIDR {
    87  			t.Fatalf("unexpected allocated cidr: %v, expecting %v for %v",
    88  				p.String(), tc.expectedCIDR, tc.description)
    89  		}
    90  		_, err = allocateNext(a)
    91  		if err == nil {
    92  			t.Fatalf("expected error because of fully-allocated range for %v", tc.description)
    93  		}
    94  	}
    95  }
    96  
    97  func TestIndexToCIDRBlock(t *testing.T) {
    98  	cases := []struct {
    99  		clusterCIDRStr  string
   100  		perNodeHostBits int
   101  		index           int
   102  		CIDRBlock       string
   103  		description     string
   104  	}{
   105  		{
   106  			clusterCIDRStr:  "127.123.3.0/16",
   107  			perNodeHostBits: 8,
   108  			index:           0,
   109  			CIDRBlock:       "127.123.0.0/24",
   110  			description:     "1st IP address indexed with IPv4",
   111  		},
   112  		{
   113  			clusterCIDRStr:  "127.123.0.0/16",
   114  			perNodeHostBits: 8,
   115  			index:           15,
   116  			CIDRBlock:       "127.123.15.0/24",
   117  			description:     "16th IP address indexed with IPv4",
   118  		},
   119  		{
   120  			clusterCIDRStr:  "192.168.5.219/28",
   121  			perNodeHostBits: 0,
   122  			index:           5,
   123  			CIDRBlock:       "192.168.5.213/32",
   124  			description:     "5th IP address indexed with IPv4",
   125  		},
   126  		{
   127  			clusterCIDRStr:  "2001:0db8:1234:3::/48",
   128  			perNodeHostBits: 64,
   129  			index:           0,
   130  			CIDRBlock:       "2001:db8:1234::/64",
   131  			description:     "1st IP address indexed with IPv6 /64",
   132  		},
   133  		{
   134  			clusterCIDRStr:  "2001:0db8:1234::/48",
   135  			perNodeHostBits: 64,
   136  			index:           15,
   137  			CIDRBlock:       "2001:db8:1234:f::/64",
   138  			description:     "16th IP address indexed with IPv6 /64",
   139  		},
   140  		{
   141  			clusterCIDRStr:  "2001:0db8:85a3::8a2e:0370:7334/50",
   142  			perNodeHostBits: 65,
   143  			index:           6425,
   144  			CIDRBlock:       "2001:db8:85a3:3232::/63",
   145  			description:     "6426th IP address indexed with IPv6 /63",
   146  		},
   147  		{
   148  			clusterCIDRStr:  "2001:0db8::/32",
   149  			perNodeHostBits: 80,
   150  			index:           0,
   151  			CIDRBlock:       "2001:db8::/48",
   152  			description:     "1st IP address indexed with IPv6 /48",
   153  		},
   154  		{
   155  			clusterCIDRStr:  "2001:0db8::/32",
   156  			perNodeHostBits: 80,
   157  			index:           15,
   158  			CIDRBlock:       "2001:db8:f::/48",
   159  			description:     "16th IP address indexed with IPv6 /48",
   160  		},
   161  		{
   162  			clusterCIDRStr:  "2001:0db8:85a3::8a2e:0370:7334/32",
   163  			perNodeHostBits: 80,
   164  			index:           6425,
   165  			CIDRBlock:       "2001:db8:1919::/48",
   166  			description:     "6426th IP address indexed with IPv6 /48",
   167  		},
   168  		{
   169  			clusterCIDRStr:  "2001:0db8:1234:ff00::/56",
   170  			perNodeHostBits: 56,
   171  			index:           0,
   172  			CIDRBlock:       "2001:db8:1234:ff00::/72",
   173  			description:     "1st IP address indexed with IPv6 /72",
   174  		},
   175  		{
   176  			clusterCIDRStr:  "2001:0db8:1234:ff00::/56",
   177  			perNodeHostBits: 56,
   178  			index:           15,
   179  			CIDRBlock:       "2001:db8:1234:ff00:f00::/72",
   180  			description:     "16th IP address indexed with IPv6 /72",
   181  		},
   182  		{
   183  			clusterCIDRStr:  "2001:0db8:1234:ff00::0370:7334/56",
   184  			perNodeHostBits: 56,
   185  			index:           6425,
   186  			CIDRBlock:       "2001:db8:1234:ff19:1900::/72",
   187  			description:     "6426th IP address indexed with IPv6 /72",
   188  		},
   189  		{
   190  			clusterCIDRStr:  "2001:0db8:1234:0:1234::/80",
   191  			perNodeHostBits: 32,
   192  			index:           0,
   193  			CIDRBlock:       "2001:db8:1234:0:1234::/96",
   194  			description:     "1st IP address indexed with IPv6 /96",
   195  		},
   196  		{
   197  			clusterCIDRStr:  "2001:0db8:1234:0:1234::/80",
   198  			perNodeHostBits: 32,
   199  			index:           15,
   200  			CIDRBlock:       "2001:db8:1234:0:1234:f::/96",
   201  			description:     "16th IP address indexed with IPv6 /96",
   202  		},
   203  		{
   204  			clusterCIDRStr:  "2001:0db8:1234:ff00::0370:7334/80",
   205  			perNodeHostBits: 32,
   206  			index:           6425,
   207  			CIDRBlock:       "2001:db8:1234:ff00:0:1919::/96",
   208  			description:     "6426th IP address indexed with IPv6 /96",
   209  		},
   210  	}
   211  	for _, tc := range cases {
   212  		_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr)
   213  		a, err := NewMultiCIDRSet(clusterCIDR, tc.perNodeHostBits)
   214  		if err != nil {
   215  			t.Fatalf("error for %v ", tc.description)
   216  		}
   217  		cidr, err := a.indexToCIDRBlock(tc.index)
   218  		if err != nil {
   219  			t.Fatalf("error for %v ", tc.description)
   220  		}
   221  		if cidr.String() != tc.CIDRBlock {
   222  			t.Fatalf("error for %v index %d %s", tc.description, tc.index, cidr.String())
   223  		}
   224  	}
   225  }
   226  
   227  func TestCIDRSet_RandomishAllocation(t *testing.T) {
   228  	cases := []struct {
   229  		clusterCIDRStr string
   230  		description    string
   231  	}{
   232  		{
   233  			clusterCIDRStr: "127.123.234.0/16",
   234  			description:    "RandomishAllocation with IPv4",
   235  		},
   236  		{
   237  			clusterCIDRStr: "beef:1234::/112",
   238  			description:    "RandomishAllocation with IPv6",
   239  		},
   240  	}
   241  	for _, tc := range cases {
   242  		_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr)
   243  		a, err := NewMultiCIDRSet(clusterCIDR, 8)
   244  		if err != nil {
   245  			t.Fatalf("Error allocating CIDRSet for %v", tc.description)
   246  		}
   247  		// allocate all the CIDRs.
   248  		var cidrs []*net.IPNet
   249  
   250  		for i := 0; i < 256; i++ {
   251  			if c, err := allocateNext(a); err == nil {
   252  				cidrs = append(cidrs, c)
   253  			} else {
   254  				t.Fatalf("unexpected error: %v for %v", err, tc.description)
   255  			}
   256  		}
   257  
   258  		_, err = allocateNext(a)
   259  		if err == nil {
   260  			t.Fatalf("expected error because of fully-allocated range for %v", tc.description)
   261  		}
   262  		// release all the CIDRs.
   263  		for i := 0; i < len(cidrs); i++ {
   264  			a.Release(cidrs[i])
   265  		}
   266  
   267  		// allocate the CIDRs again.
   268  		var rcidrs []*net.IPNet
   269  		for i := 0; i < 256; i++ {
   270  			if c, err := allocateNext(a); err == nil {
   271  				rcidrs = append(rcidrs, c)
   272  			} else {
   273  				t.Fatalf("unexpected error: %d, %v for %v", i, err, tc.description)
   274  			}
   275  		}
   276  		_, err = allocateNext(a)
   277  		if err == nil {
   278  			t.Fatalf("expected error because of fully-allocated range for %v", tc.description)
   279  		}
   280  
   281  		if !reflect.DeepEqual(cidrs, rcidrs) {
   282  			t.Fatalf("expected re-allocated cidrs are the same collection for %v", tc.description)
   283  		}
   284  	}
   285  }
   286  
   287  func TestCIDRSet_AllocationOccupied(t *testing.T) {
   288  	cases := []struct {
   289  		clusterCIDRStr string
   290  		description    string
   291  	}{
   292  		{
   293  			clusterCIDRStr: "127.123.234.0/16",
   294  			description:    "AllocationOccupied with IPv4",
   295  		},
   296  		{
   297  			clusterCIDRStr: "beef:1234::/112",
   298  			description:    "AllocationOccupied with IPv6",
   299  		},
   300  	}
   301  	for _, tc := range cases {
   302  		_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr)
   303  		a, err := NewMultiCIDRSet(clusterCIDR, 8)
   304  		if err != nil {
   305  			t.Fatalf("Error allocating CIDRSet for %v", tc.description)
   306  		}
   307  		// allocate all the CIDRs.
   308  		var cidrs []*net.IPNet
   309  		var numCIDRs = 256
   310  
   311  		for i := 0; i < numCIDRs; i++ {
   312  			if c, err := allocateNext(a); err == nil {
   313  				cidrs = append(cidrs, c)
   314  			} else {
   315  				t.Fatalf("unexpected error: %v for %v", err, tc.description)
   316  			}
   317  		}
   318  
   319  		_, err = allocateNext(a)
   320  		if err == nil {
   321  			t.Fatalf("expected error because of fully-allocated range for %v", tc.description)
   322  		}
   323  		// release all the CIDRs.
   324  		for i := 0; i < len(cidrs); i++ {
   325  			a.Release(cidrs[i])
   326  		}
   327  		// occupy the last 128 CIDRs.
   328  		for i := numCIDRs / 2; i < numCIDRs; i++ {
   329  			a.Occupy(cidrs[i])
   330  		}
   331  		// occupy the first of the last 128 again.
   332  		a.Occupy(cidrs[numCIDRs/2])
   333  
   334  		// allocate the first 128 CIDRs again.
   335  		var rcidrs []*net.IPNet
   336  		for i := 0; i < numCIDRs/2; i++ {
   337  			if c, err := allocateNext(a); err == nil {
   338  				rcidrs = append(rcidrs, c)
   339  			} else {
   340  				t.Fatalf("unexpected error: %d, %v for %v", i, err, tc.description)
   341  			}
   342  		}
   343  		_, err = allocateNext(a)
   344  		if err == nil {
   345  			t.Fatalf("expected error because of fully-allocated range for %v", tc.description)
   346  		}
   347  
   348  		// check Occupy() works properly.
   349  		for i := numCIDRs / 2; i < numCIDRs; i++ {
   350  			rcidrs = append(rcidrs, cidrs[i])
   351  		}
   352  		if !reflect.DeepEqual(cidrs, rcidrs) {
   353  			t.Fatalf("expected re-allocated cidrs are the same collection for %v", tc.description)
   354  		}
   355  	}
   356  }
   357  
   358  func TestDoubleOccupyRelease(t *testing.T) {
   359  	// Run a sequence of operations and check the number of occupied CIDRs
   360  	// after each one.
   361  	clusterCIDRStr := "10.42.0.0/16"
   362  	operations := []struct {
   363  		cidrStr     string
   364  		operation   string
   365  		numOccupied int
   366  	}{
   367  		// Occupy 1 element: +1
   368  		{
   369  			cidrStr:     "10.42.5.0/24",
   370  			operation:   "occupy",
   371  			numOccupied: 1,
   372  		},
   373  		// Occupy 1 more element: +1
   374  		{
   375  			cidrStr:     "10.42.9.0/24",
   376  			operation:   "occupy",
   377  			numOccupied: 2,
   378  		},
   379  		// Occupy 4 elements overlapping with one from the above: +3
   380  		{
   381  			cidrStr:     "10.42.8.0/22",
   382  			operation:   "occupy",
   383  			numOccupied: 5,
   384  		},
   385  		// Occupy an already-occupied element: no change
   386  		{
   387  			cidrStr:     "10.42.9.0/24",
   388  			operation:   "occupy",
   389  			numOccupied: 5,
   390  		},
   391  		// Release an coccupied element: -1
   392  		{
   393  			cidrStr:     "10.42.9.0/24",
   394  			operation:   "release",
   395  			numOccupied: 4,
   396  		},
   397  		// Release an unoccupied element: no change
   398  		{
   399  			cidrStr:     "10.42.9.0/24",
   400  			operation:   "release",
   401  			numOccupied: 4,
   402  		},
   403  		// Release 4 elements, only one of which is occupied: -1
   404  		{
   405  			cidrStr:     "10.42.4.0/22",
   406  			operation:   "release",
   407  			numOccupied: 3,
   408  		},
   409  	}
   410  	// Check that there are exactly that many allocatable CIDRs after all
   411  	// operations have been executed.
   412  	numAllocatable24s := (1 << 8) - 3
   413  
   414  	_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(clusterCIDRStr)
   415  	a, err := NewMultiCIDRSet(clusterCIDR, 8)
   416  	if err != nil {
   417  		t.Fatalf("Error allocating CIDRSet")
   418  	}
   419  
   420  	// Execute the operations.
   421  	for _, op := range operations {
   422  		_, cidr, _ := utilnet.ParseCIDRSloppy(op.cidrStr)
   423  		switch op.operation {
   424  		case "occupy":
   425  			a.Occupy(cidr)
   426  		case "release":
   427  			a.Release(cidr)
   428  		default:
   429  			t.Fatalf("test error: unknown operation %v", op.operation)
   430  		}
   431  		if a.allocatedCIDRs != op.numOccupied {
   432  			t.Fatalf("CIDR %v Expected %d occupied CIDRS, got %d", cidr, op.numOccupied, a.allocatedCIDRs)
   433  		}
   434  	}
   435  
   436  	// Make sure that we can allocate exactly `numAllocatable24s` elements.
   437  	for i := 0; i < numAllocatable24s; i++ {
   438  		_, err := allocateNext(a)
   439  		if err != nil {
   440  			t.Fatalf("Expected to be able to allocate %d CIDRS, failed after %d", numAllocatable24s, i)
   441  		}
   442  	}
   443  
   444  	_, err = allocateNext(a)
   445  	if err == nil {
   446  		t.Fatalf("Expected to be able to allocate exactly %d CIDRS, got one more", numAllocatable24s)
   447  	}
   448  }
   449  
   450  func TestGetBitforCIDR(t *testing.T) {
   451  	cases := []struct {
   452  		clusterCIDRStr  string
   453  		perNodeHostBits int
   454  		subNetCIDRStr   string
   455  		expectedBit     int
   456  		expectErr       bool
   457  		description     string
   458  	}{
   459  		{
   460  			clusterCIDRStr:  "127.0.0.0/8",
   461  			perNodeHostBits: 16,
   462  			subNetCIDRStr:   "127.0.0.0/16",
   463  			expectedBit:     0,
   464  			expectErr:       false,
   465  			description:     "Get 0 Bit with IPv4",
   466  		},
   467  		{
   468  			clusterCIDRStr:  "be00::/8",
   469  			perNodeHostBits: 112,
   470  			subNetCIDRStr:   "be00::/16",
   471  			expectedBit:     0,
   472  			expectErr:       false,
   473  			description:     "Get 0 Bit with IPv6",
   474  		},
   475  		{
   476  			clusterCIDRStr:  "127.0.0.0/8",
   477  			perNodeHostBits: 16,
   478  			subNetCIDRStr:   "127.123.0.0/16",
   479  			expectedBit:     123,
   480  			expectErr:       false,
   481  			description:     "Get 123rd Bit with IPv4",
   482  		},
   483  		{
   484  			clusterCIDRStr:  "be00::/8",
   485  			perNodeHostBits: 112,
   486  			subNetCIDRStr:   "beef::/16",
   487  			expectedBit:     0xef,
   488  			expectErr:       false,
   489  			description:     "Get xef Bit with IPv6",
   490  		},
   491  		{
   492  			clusterCIDRStr:  "127.0.0.0/8",
   493  			perNodeHostBits: 16,
   494  			subNetCIDRStr:   "127.168.0.0/16",
   495  			expectedBit:     168,
   496  			expectErr:       false,
   497  			description:     "Get 168th Bit with IPv4",
   498  		},
   499  		{
   500  			clusterCIDRStr:  "be00::/8",
   501  			perNodeHostBits: 112,
   502  			subNetCIDRStr:   "be68::/16",
   503  			expectedBit:     0x68,
   504  			expectErr:       false,
   505  			description:     "Get x68th Bit with IPv6",
   506  		},
   507  		{
   508  			clusterCIDRStr:  "127.0.0.0/8",
   509  			perNodeHostBits: 16,
   510  			subNetCIDRStr:   "127.224.0.0/16",
   511  			expectedBit:     224,
   512  			expectErr:       false,
   513  			description:     "Get 224th Bit with IPv4",
   514  		},
   515  		{
   516  			clusterCIDRStr:  "be00::/8",
   517  			perNodeHostBits: 112,
   518  			subNetCIDRStr:   "be24::/16",
   519  			expectedBit:     0x24,
   520  			expectErr:       false,
   521  			description:     "Get x24th Bit with IPv6",
   522  		},
   523  		{
   524  			clusterCIDRStr:  "192.168.0.0/16",
   525  			perNodeHostBits: 8,
   526  			subNetCIDRStr:   "192.168.12.0/24",
   527  			expectedBit:     12,
   528  			expectErr:       false,
   529  			description:     "Get 12th Bit with IPv4",
   530  		},
   531  		{
   532  			clusterCIDRStr:  "beef::/16",
   533  			perNodeHostBits: 104,
   534  			subNetCIDRStr:   "beef:1200::/24",
   535  			expectedBit:     0x12,
   536  			expectErr:       false,
   537  			description:     "Get x12th Bit with IPv6",
   538  		},
   539  		{
   540  			clusterCIDRStr:  "192.168.0.0/16",
   541  			perNodeHostBits: 8,
   542  			subNetCIDRStr:   "192.168.151.0/24",
   543  			expectedBit:     151,
   544  			expectErr:       false,
   545  			description:     "Get 151st Bit with IPv4",
   546  		},
   547  		{
   548  			clusterCIDRStr:  "beef::/16",
   549  			perNodeHostBits: 104,
   550  			subNetCIDRStr:   "beef:9700::/24",
   551  			expectedBit:     0x97,
   552  			expectErr:       false,
   553  			description:     "Get x97st Bit with IPv6",
   554  		},
   555  		{
   556  			clusterCIDRStr:  "192.168.0.0/16",
   557  			perNodeHostBits: 8,
   558  			subNetCIDRStr:   "127.168.224.0/24",
   559  			expectErr:       true,
   560  			description:     "Get error with IPv4",
   561  		},
   562  		{
   563  			clusterCIDRStr:  "beef::/16",
   564  			perNodeHostBits: 104,
   565  			subNetCIDRStr:   "2001:db00::/24",
   566  			expectErr:       true,
   567  			description:     "Get error with IPv6",
   568  		},
   569  	}
   570  
   571  	logger, _ := ktesting.NewTestContext(t)
   572  	for _, tc := range cases {
   573  		_, clusterCIDR, err := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr)
   574  		if err != nil {
   575  			t.Fatalf("unexpected error: %v for %v", err, tc.description)
   576  		}
   577  
   578  		cs, err := NewMultiCIDRSet(clusterCIDR, tc.perNodeHostBits)
   579  		if err != nil {
   580  			t.Fatalf("Error allocating CIDRSet for %v", tc.description)
   581  		}
   582  		_, subnetCIDR, err := utilnet.ParseCIDRSloppy(tc.subNetCIDRStr)
   583  		if err != nil {
   584  			t.Fatalf("unexpected error: %v for %v", err, tc.description)
   585  		}
   586  
   587  		got, err := cs.getIndexForIP(subnetCIDR.IP)
   588  		if err == nil && tc.expectErr {
   589  			logger.Error(nil, "Expected error but got null", "description", tc.description)
   590  			continue
   591  		}
   592  
   593  		if err != nil && !tc.expectErr {
   594  			logger.Error(err, "Unexpected error", "description", tc.description)
   595  			continue
   596  		}
   597  
   598  		if got != tc.expectedBit {
   599  			logger.Error(nil, "Unexpected value", "description", tc.description, "expected", tc.expectedBit, "got", got)
   600  		}
   601  	}
   602  }
   603  
   604  func TestCIDRSetv6(t *testing.T) {
   605  	cases := []struct {
   606  		clusterCIDRStr  string
   607  		perNodeHostBits int
   608  		expectedCIDR    string
   609  		expectedCIDR2   string
   610  		expectErr       bool
   611  		description     string
   612  	}{
   613  		{
   614  			clusterCIDRStr:  "127.0.0.0/8",
   615  			perNodeHostBits: 0,
   616  			expectErr:       false,
   617  			expectedCIDR:    "127.0.0.0/32",
   618  			expectedCIDR2:   "127.0.0.1/32",
   619  			description:     "Max cluster subnet size with IPv4",
   620  		},
   621  		{
   622  			clusterCIDRStr:  "beef:1234::/32",
   623  			perNodeHostBits: 79,
   624  			expectErr:       true,
   625  			description:     "Max cluster subnet size with IPv6",
   626  		},
   627  		{
   628  			clusterCIDRStr:  "2001:beef:1234:369b::/60",
   629  			perNodeHostBits: 64,
   630  			expectedCIDR:    "2001:beef:1234:3690::/64",
   631  			expectedCIDR2:   "2001:beef:1234:3691::/64",
   632  			expectErr:       false,
   633  			description:     "Allocate a few IPv6",
   634  		},
   635  	}
   636  	for _, tc := range cases {
   637  		t.Run(tc.description, func(t *testing.T) {
   638  			_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr)
   639  			a, err := NewMultiCIDRSet(clusterCIDR, tc.perNodeHostBits)
   640  			if gotErr := err != nil; gotErr != tc.expectErr {
   641  				t.Fatalf("NewMultiCIDRSet(%v, %v) = %v, %v; gotErr = %t, want %t", clusterCIDR, tc.perNodeHostBits, a, err, gotErr, tc.expectErr)
   642  			}
   643  			if a == nil {
   644  				return
   645  			}
   646  			p, err := allocateNext(a)
   647  			if err == nil && tc.expectErr {
   648  				t.Errorf("allocateNext(a) = nil, want error")
   649  			}
   650  			if err != nil && !tc.expectErr {
   651  				t.Errorf("allocateNext(a) = %+v, want no error", err)
   652  			}
   653  			if !tc.expectErr {
   654  				if p != nil && p.String() != tc.expectedCIDR {
   655  					t.Fatalf("allocateNext(a) got %+v, want %+v", p.String(), tc.expectedCIDR)
   656  				}
   657  			}
   658  			p2, err := allocateNext(a)
   659  			if err == nil && tc.expectErr {
   660  				t.Errorf("allocateNext(a) = nil, want error")
   661  			}
   662  			if err != nil && !tc.expectErr {
   663  				t.Errorf("allocateNext(a) = %+v, want no error", err)
   664  			}
   665  			if !tc.expectErr {
   666  				if p2 != nil && p2.String() != tc.expectedCIDR2 {
   667  					t.Fatalf("allocateNext(a) got %+v, want %+v", p2.String(), tc.expectedCIDR)
   668  				}
   669  			}
   670  		})
   671  	}
   672  }
   673  
   674  func TestMultiCIDRSetMetrics(t *testing.T) {
   675  	cidr := "10.0.0.0/16"
   676  	_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(cidr)
   677  	clearMetrics(map[string]string{"clusterCIDR": cidr})
   678  
   679  	// We have 256 free cidrs
   680  	a, err := NewMultiCIDRSet(clusterCIDR, 8)
   681  	if err != nil {
   682  		t.Fatalf("unexpected error creating MultiCIDRSet: %v", err)
   683  	}
   684  
   685  	clusterMaskSize, _ := clusterCIDR.Mask.Size()
   686  	max := getMaxCIDRs(24, clusterMaskSize)
   687  	em := testMetrics{
   688  		usage:      0,
   689  		allocs:     0,
   690  		releases:   0,
   691  		allocTries: 0,
   692  		max:        float64(max),
   693  	}
   694  	expectMetrics(t, cidr, em)
   695  
   696  	// Allocate next all.
   697  	for i := 1; i <= 256; i++ {
   698  		_, err := allocateNext(a)
   699  		if err != nil {
   700  			t.Fatalf("unexpected error allocating a new CIDR: %v", err)
   701  		}
   702  		em := testMetrics{
   703  			usage:      float64(i) / float64(256),
   704  			allocs:     float64(i),
   705  			releases:   0,
   706  			allocTries: 0,
   707  			max:        float64(max),
   708  		}
   709  		expectMetrics(t, cidr, em)
   710  	}
   711  	// Release all CIDRs.
   712  	a.Release(clusterCIDR)
   713  	em = testMetrics{
   714  		usage:      0,
   715  		allocs:     256,
   716  		releases:   256,
   717  		allocTries: 0,
   718  		max:        float64(max),
   719  	}
   720  	expectMetrics(t, cidr, em)
   721  
   722  	// Allocate all CIDRs.
   723  	a.Occupy(clusterCIDR)
   724  	em = testMetrics{
   725  		usage:      1,
   726  		allocs:     512,
   727  		releases:   256,
   728  		allocTries: 0,
   729  		max:        float64(max),
   730  	}
   731  	expectMetrics(t, cidr, em)
   732  
   733  }
   734  
   735  func TestMultiCIDRSetMetricsHistogram(t *testing.T) {
   736  	cidr := "10.0.0.0/16"
   737  	_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(cidr)
   738  	clearMetrics(map[string]string{"clusterCIDR": cidr})
   739  
   740  	// We have 256 free cidrs.
   741  	a, err := NewMultiCIDRSet(clusterCIDR, 8)
   742  	if err != nil {
   743  		t.Fatalf("unexpected error creating MultiCIDRSet: %v", err)
   744  	}
   745  
   746  	clusterMaskSize, _ := clusterCIDR.Mask.Size()
   747  	max := getMaxCIDRs(24, clusterMaskSize)
   748  	em := testMetrics{
   749  		usage:      0,
   750  		allocs:     0,
   751  		releases:   0,
   752  		allocTries: 0,
   753  		max:        float64(max),
   754  	}
   755  	expectMetrics(t, cidr, em)
   756  
   757  	// Allocate half of the range.
   758  	// Occupy does not update the nextCandidate.
   759  	_, halfClusterCIDR, _ := utilnet.ParseCIDRSloppy("10.0.0.0/17")
   760  	a.Occupy(halfClusterCIDR)
   761  	em = testMetrics{
   762  		usage:    0.5,
   763  		allocs:   128,
   764  		releases: 0,
   765  		max:      float64(max),
   766  	}
   767  	expectMetrics(t, cidr, em)
   768  	// Allocate next should iterate until the next free cidr
   769  	// that is exactly the same number we allocated previously.
   770  	_, err = allocateNext(a)
   771  	if err != nil {
   772  		t.Fatalf("unexpected error allocating a new CIDR: %v", err)
   773  	}
   774  	em = testMetrics{
   775  		usage:    float64(129) / float64(256),
   776  		allocs:   129,
   777  		releases: 0,
   778  		max:      float64(max),
   779  	}
   780  	expectMetrics(t, cidr, em)
   781  }
   782  
   783  func TestMultiCIDRSetMetricsDual(t *testing.T) {
   784  	// create IPv4 cidrSet.
   785  	cidrIPv4 := "10.0.0.0/16"
   786  	_, clusterCIDRv4, _ := utilnet.ParseCIDRSloppy(cidrIPv4)
   787  	clearMetrics(map[string]string{"clusterCIDR": cidrIPv4})
   788  
   789  	a, err := NewMultiCIDRSet(clusterCIDRv4, 8)
   790  	if err != nil {
   791  		t.Fatalf("unexpected error creating MultiCIDRSet: %v", err)
   792  	}
   793  
   794  	clusterMaskSize, _ := clusterCIDRv4.Mask.Size()
   795  	maxIPv4 := getMaxCIDRs(24, clusterMaskSize)
   796  	em := testMetrics{
   797  		usage:      0,
   798  		allocs:     0,
   799  		releases:   0,
   800  		allocTries: 0,
   801  		max:        float64(maxIPv4),
   802  	}
   803  	expectMetrics(t, cidrIPv4, em)
   804  
   805  	// create IPv6 cidrSet.
   806  	cidrIPv6 := "2001:db8::/48"
   807  	_, clusterCIDRv6, _ := utilnet.ParseCIDRSloppy(cidrIPv6)
   808  	clearMetrics(map[string]string{"clusterCIDR": cidrIPv6})
   809  
   810  	b, err := NewMultiCIDRSet(clusterCIDRv6, 64)
   811  	if err != nil {
   812  		t.Fatalf("unexpected error creating MultiCIDRSet: %v", err)
   813  	}
   814  
   815  	clusterMaskSize, _ = clusterCIDRv6.Mask.Size()
   816  	maxIPv6 := getMaxCIDRs(64, clusterMaskSize)
   817  	em = testMetrics{
   818  		usage:      0,
   819  		allocs:     0,
   820  		releases:   0,
   821  		allocTries: 0,
   822  		max:        float64(maxIPv6),
   823  	}
   824  	expectMetrics(t, cidrIPv6, em)
   825  
   826  	// Allocate all.
   827  	a.Occupy(clusterCIDRv4)
   828  	em = testMetrics{
   829  		usage:      1,
   830  		allocs:     256,
   831  		releases:   0,
   832  		allocTries: 0,
   833  		max:        float64(maxIPv4),
   834  	}
   835  	expectMetrics(t, cidrIPv4, em)
   836  
   837  	b.Occupy(clusterCIDRv6)
   838  	em = testMetrics{
   839  		usage:      1,
   840  		allocs:     65536,
   841  		releases:   0,
   842  		allocTries: 0,
   843  		max:        float64(maxIPv6),
   844  	}
   845  	expectMetrics(t, cidrIPv6, em)
   846  
   847  	// Release all.
   848  	a.Release(clusterCIDRv4)
   849  	em = testMetrics{
   850  		usage:      0,
   851  		allocs:     256,
   852  		releases:   256,
   853  		allocTries: 0,
   854  		max:        float64(maxIPv4),
   855  	}
   856  	expectMetrics(t, cidrIPv4, em)
   857  	b.Release(clusterCIDRv6)
   858  	em = testMetrics{
   859  		usage:      0,
   860  		allocs:     65536,
   861  		releases:   65536,
   862  		allocTries: 0,
   863  		max:        float64(maxIPv6),
   864  	}
   865  	expectMetrics(t, cidrIPv6, em)
   866  
   867  }
   868  
   869  func Test_getMaxCIDRs(t *testing.T) {
   870  	cidrIPv4 := "10.0.0.0/16"
   871  	_, clusterCIDRv4, _ := utilnet.ParseCIDRSloppy(cidrIPv4)
   872  
   873  	cidrIPv6 := "2001:db8::/48"
   874  	_, clusterCIDRv6, _ := utilnet.ParseCIDRSloppy(cidrIPv6)
   875  
   876  	tests := []struct {
   877  		name             string
   878  		subNetMaskSize   int
   879  		clusterCIDR      *net.IPNet
   880  		expectedMaxCIDRs int
   881  	}{
   882  		{
   883  			name:             "IPv4",
   884  			subNetMaskSize:   24,
   885  			clusterCIDR:      clusterCIDRv4,
   886  			expectedMaxCIDRs: 256,
   887  		},
   888  		{
   889  			name:             "IPv6",
   890  			subNetMaskSize:   64,
   891  			clusterCIDR:      clusterCIDRv6,
   892  			expectedMaxCIDRs: 65536,
   893  		},
   894  	}
   895  
   896  	for _, test := range tests {
   897  		t.Run(test.name, func(t *testing.T) {
   898  			clusterMaskSize, _ := test.clusterCIDR.Mask.Size()
   899  			maxCIDRs := getMaxCIDRs(test.subNetMaskSize, clusterMaskSize)
   900  			if test.expectedMaxCIDRs != maxCIDRs {
   901  				t.Errorf("incorrect maxCIDRs, expected: %d, got: %d", test.expectedMaxCIDRs, maxCIDRs)
   902  			}
   903  		})
   904  	}
   905  }
   906  
   907  // Metrics helpers.
   908  func clearMetrics(labels map[string]string) {
   909  	cidrSetAllocations.Delete(labels)
   910  	cidrSetReleases.Delete(labels)
   911  	cidrSetUsage.Delete(labels)
   912  	cidrSetAllocationTriesPerRequest.Delete(labels)
   913  	cidrSetMaxCidrs.Delete(labels)
   914  }
   915  
   916  type testMetrics struct {
   917  	usage      float64
   918  	allocs     float64
   919  	releases   float64
   920  	allocTries float64
   921  	max        float64
   922  }
   923  
   924  func expectMetrics(t *testing.T, label string, em testMetrics) {
   925  	var m testMetrics
   926  	var err error
   927  	m.usage, err = testutil.GetGaugeMetricValue(cidrSetUsage.WithLabelValues(label))
   928  	if err != nil {
   929  		t.Errorf("failed to get %s value, err: %v", cidrSetUsage.Name, err)
   930  	}
   931  	m.allocs, err = testutil.GetCounterMetricValue(cidrSetAllocations.WithLabelValues(label))
   932  	if err != nil {
   933  		t.Errorf("failed to get %s value, err: %v", cidrSetAllocations.Name, err)
   934  	}
   935  	m.releases, err = testutil.GetCounterMetricValue(cidrSetReleases.WithLabelValues(label))
   936  	if err != nil {
   937  		t.Errorf("failed to get %s value, err: %v", cidrSetReleases.Name, err)
   938  	}
   939  	m.allocTries, err = testutil.GetHistogramMetricValue(cidrSetAllocationTriesPerRequest.WithLabelValues(label))
   940  	if err != nil {
   941  		t.Errorf("failed to get %s value, err: %v", cidrSetAllocationTriesPerRequest.Name, err)
   942  	}
   943  	m.max, err = testutil.GetGaugeMetricValue(cidrSetMaxCidrs.WithLabelValues(label))
   944  	if err != nil {
   945  		t.Errorf("failed to get %s value, err: %v", cidrSetMaxCidrs.Name, err)
   946  	}
   947  
   948  	if m != em {
   949  		t.Fatalf("metrics error: expected %v, received %v", em, m)
   950  	}
   951  }
   952  
   953  // Benchmarks
   954  func benchmarkAllocateAllIPv6(cidr string, perNodeHostBits int, b *testing.B) {
   955  	_, clusterCIDR, _ := utilnet.ParseCIDRSloppy(cidr)
   956  	a, _ := NewMultiCIDRSet(clusterCIDR, perNodeHostBits)
   957  	for n := 0; n < b.N; n++ {
   958  		// Allocate the whole range + 1.
   959  		for i := 0; i <= a.MaxCIDRs; i++ {
   960  			allocateNext(a)
   961  		}
   962  		// Release all.
   963  		a.Release(clusterCIDR)
   964  	}
   965  }
   966  
   967  func BenchmarkAllocateAll_48_52(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/48", 52, b) }
   968  func BenchmarkAllocateAll_48_56(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/48", 56, b) }
   969  
   970  func BenchmarkAllocateAll_48_60(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/48", 60, b) }
   971  func BenchmarkAllocateAll_48_64(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/48", 64, b) }
   972  
   973  func BenchmarkAllocateAll_64_68(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/64", 68, b) }
   974  
   975  func BenchmarkAllocateAll_64_72(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/64", 72, b) }
   976  func BenchmarkAllocateAll_64_76(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/64", 76, b) }
   977  
   978  func BenchmarkAllocateAll_64_80(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/64", 80, b) }