k8s.io/kubernetes@v1.29.3/pkg/registry/core/service/ipallocator/cidrallocator_test.go (about)

     1  /*
     2  Copyright 2023 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  	"context"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  	"k8s.io/apimachinery/pkg/util/wait"
    30  	"k8s.io/client-go/informers"
    31  	"k8s.io/client-go/kubernetes/fake"
    32  	k8stesting "k8s.io/client-go/testing"
    33  	netutils "k8s.io/utils/net"
    34  )
    35  
    36  func newTestMetaAllocator() (*MetaAllocator, error) {
    37  	client := fake.NewSimpleClientset()
    38  
    39  	informerFactory := informers.NewSharedInformerFactory(client, 0*time.Second)
    40  	serviceCIDRInformer := informerFactory.Networking().V1alpha1().ServiceCIDRs()
    41  	serviceCIDRStore := serviceCIDRInformer.Informer().GetIndexer()
    42  	serviceCIDRInformer.Informer().HasSynced()
    43  	ipInformer := informerFactory.Networking().V1alpha1().IPAddresses()
    44  	ipStore := ipInformer.Informer().GetIndexer()
    45  
    46  	client.PrependReactor("create", "servicecidrs", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) {
    47  		cidr := action.(k8stesting.CreateAction).GetObject().(*networkingv1alpha1.ServiceCIDR)
    48  		_, exists, err := serviceCIDRStore.GetByKey(cidr.Name)
    49  		if exists && err != nil {
    50  			return false, nil, fmt.Errorf("cidr already exist")
    51  		}
    52  		cidr.Generation = 1
    53  		err = serviceCIDRStore.Add(cidr)
    54  		return false, cidr, err
    55  	}))
    56  	client.PrependReactor("delete", "servicecidrs", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) {
    57  		name := action.(k8stesting.DeleteAction).GetName()
    58  		obj, exists, err := serviceCIDRStore.GetByKey(name)
    59  		cidr := &networkingv1alpha1.ServiceCIDR{}
    60  		if exists && err == nil {
    61  			cidr = obj.(*networkingv1alpha1.ServiceCIDR)
    62  			err = serviceCIDRStore.Delete(cidr)
    63  		}
    64  		return false, cidr, err
    65  	}))
    66  
    67  	client.PrependReactor("create", "ipaddresses", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) {
    68  		ip := action.(k8stesting.CreateAction).GetObject().(*networkingv1alpha1.IPAddress)
    69  		_, exists, err := ipStore.GetByKey(ip.Name)
    70  		if exists && err != nil {
    71  			return false, nil, fmt.Errorf("ip already exist")
    72  		}
    73  		ip.Generation = 1
    74  		err = ipStore.Add(ip)
    75  		return false, ip, err
    76  	}))
    77  	client.PrependReactor("delete", "ipaddresses", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) {
    78  		name := action.(k8stesting.DeleteAction).GetName()
    79  		obj, exists, err := ipStore.GetByKey(name)
    80  		ip := &networkingv1alpha1.IPAddress{}
    81  		if exists && err == nil {
    82  			ip = obj.(*networkingv1alpha1.IPAddress)
    83  			err = ipStore.Delete(ip)
    84  		}
    85  		return false, ip, err
    86  	}))
    87  
    88  	c, err := NewMetaAllocator(client.NetworkingV1alpha1(), serviceCIDRInformer, ipInformer, false)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	// we can not force the state of the informers to be synced without racing
    93  	// so we run our worker here
    94  	go wait.Until(c.runWorker, time.Second, c.internalStopCh)
    95  	return c, nil
    96  }
    97  
    98  func TestCIDRAllocateMultiple(t *testing.T) {
    99  	r, err := newTestMetaAllocator()
   100  	if err != nil {
   101  		t.Fatal(err)
   102  	}
   103  	defer r.Destroy()
   104  
   105  	if f := r.Free(); f != 0 {
   106  		t.Errorf("free: %d", f)
   107  	}
   108  	if _, err := r.AllocateNext(); err == nil {
   109  		t.Error(err)
   110  	}
   111  
   112  	cidr := newServiceCIDR("test", "192.168.0.0/28")
   113  	_, err = r.client.ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{})
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	r.addServiceCIDR(cidr)
   118  	// wait for the cidr to be processed and set the informer synced
   119  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   120  		allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.1"))
   121  		if err != nil {
   122  			t.Logf("unexpected error %v", err)
   123  			return false, nil
   124  		}
   125  		allocator.ipAddressSynced = func() bool { return true }
   126  		return allocator.ready.Load(), nil
   127  	})
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	found := sets.NewString()
   132  	count := 0
   133  	for r.Free() > 0 {
   134  		ip, err := r.AllocateNext()
   135  		if err != nil {
   136  			t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err)
   137  		}
   138  		count++
   139  		if found.Has(ip.String()) {
   140  			t.Fatalf("allocated %s twice: %d", ip, count)
   141  		}
   142  		found.Insert(ip.String())
   143  	}
   144  	if count != 14 {
   145  		t.Fatalf("expected 14 IPs got %d", count)
   146  	}
   147  	if _, err := r.AllocateNext(); err == nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	cidr2 := newServiceCIDR("test2", "10.0.0.0/28")
   152  	_, err = r.client.ServiceCIDRs().Create(context.Background(), cidr2, metav1.CreateOptions{})
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	r.addServiceCIDR(cidr2)
   157  	// wait for the cidr to be processed
   158  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   159  		allocator, err := r.getAllocator(netutils.ParseIPSloppy("10.0.0.11"))
   160  		if err != nil {
   161  			return false, nil
   162  		}
   163  		allocator.ipAddressSynced = func() bool { return true }
   164  		return allocator.ready.Load(), nil
   165  	})
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  	// allocate one IP from the new allocator
   170  	err = r.Allocate(netutils.ParseIPSloppy("10.0.0.11"))
   171  	if err != nil {
   172  		t.Fatalf("error allocating IP 10.0.0.11 from new allocator: %v", err)
   173  	}
   174  	count++
   175  	for r.Free() > 0 {
   176  		ip, err := r.AllocateNext()
   177  		if err != nil {
   178  			t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err)
   179  		}
   180  		count++
   181  		if found.Has(ip.String()) {
   182  			t.Fatalf("allocated %s twice: %d", ip, count)
   183  		}
   184  		found.Insert(ip.String())
   185  	}
   186  	if count != 28 {
   187  		t.Fatalf("expected 28 IPs got %d", count)
   188  	}
   189  	if _, err := r.AllocateNext(); err == nil {
   190  		t.Fatal(err)
   191  	}
   192  
   193  }
   194  
   195  func TestCIDRAllocateShadow(t *testing.T) {
   196  	r, err := newTestMetaAllocator()
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	defer r.Destroy()
   201  
   202  	if f := r.Free(); f != 0 {
   203  		t.Errorf("free: %d", f)
   204  	}
   205  	if _, err := r.AllocateNext(); err == nil {
   206  		t.Error(err)
   207  	}
   208  
   209  	cidr := newServiceCIDR("test", "192.168.1.0/24")
   210  	_, err = r.client.ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{})
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	r.addServiceCIDR(cidr)
   215  	// wait for the cidr to be processed and set the informer synced
   216  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   217  		allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.1.0"))
   218  		if err != nil {
   219  			return false, nil
   220  		}
   221  		allocator.ipAddressSynced = func() bool { return true }
   222  		return allocator.ready.Load(), nil
   223  	})
   224  	if err != nil {
   225  		t.Fatal(err)
   226  	}
   227  	// allocate one IP from the new allocator
   228  	err = r.Allocate(netutils.ParseIPSloppy("192.168.1.0"))
   229  	if err == nil {
   230  		t.Fatalf("unexpected allocation for IP 192.168.1.0")
   231  	}
   232  
   233  	if f := r.Used(); f != 0 {
   234  		t.Errorf("used: %d", f)
   235  	}
   236  
   237  	cidr2 := newServiceCIDR("test2", "192.168.0.0/16")
   238  	_, err = r.client.ServiceCIDRs().Create(context.Background(), cidr2, metav1.CreateOptions{})
   239  	if err != nil {
   240  		t.Fatal(err)
   241  	}
   242  	r.addServiceCIDR(cidr2)
   243  	// wait for the cidr to be processed
   244  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   245  		allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.0"))
   246  		if err != nil {
   247  			return false, nil
   248  		}
   249  		allocator.ipAddressSynced = func() bool { return true }
   250  		return allocator.ready.Load(), nil
   251  	})
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	// allocate one IP from the new allocator
   256  	err = r.Allocate(netutils.ParseIPSloppy("192.168.1.0"))
   257  	if err != nil {
   258  		t.Fatalf("error allocating IP 192.168.1.0 from new allocator: %v", err)
   259  	}
   260  
   261  	if f := r.Used(); f != 1 {
   262  		t.Errorf("used: %d", f)
   263  	}
   264  
   265  }
   266  
   267  func TestCIDRAllocateGrow(t *testing.T) {
   268  	r, err := newTestMetaAllocator()
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	defer r.Destroy()
   273  
   274  	if f := r.Free(); f != 0 {
   275  		t.Errorf("free: %d", f)
   276  	}
   277  	if _, err := r.AllocateNext(); err == nil {
   278  		t.Error(err)
   279  	}
   280  
   281  	cidr := newServiceCIDR("test", "192.168.0.0/28")
   282  	_, err = r.client.ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{})
   283  	if err != nil {
   284  		t.Fatal(err)
   285  	}
   286  	r.addServiceCIDR(cidr)
   287  	// wait for the cidr to be processed
   288  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   289  		allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.1"))
   290  		if err != nil {
   291  			return false, nil
   292  		}
   293  		allocator.ipAddressSynced = func() bool { return true }
   294  		return allocator.ready.Load(), nil
   295  	})
   296  	if err != nil {
   297  		t.Fatal(err)
   298  	}
   299  	found := sets.NewString()
   300  	count := 0
   301  	for r.Free() > 0 {
   302  		ip, err := r.AllocateNext()
   303  		if err != nil {
   304  			t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err)
   305  		}
   306  		count++
   307  		if found.Has(ip.String()) {
   308  			t.Fatalf("allocated %s twice: %d", ip, count)
   309  		}
   310  		found.Insert(ip.String())
   311  	}
   312  	if count != 14 {
   313  		t.Fatalf("expected 14 IPs got %d", count)
   314  	}
   315  	if _, err := r.AllocateNext(); err == nil {
   316  		t.Fatal(err)
   317  	}
   318  
   319  	cidr2 := newServiceCIDR("test2", "192.168.0.0/24")
   320  	_, err = r.client.ServiceCIDRs().Create(context.Background(), cidr2, metav1.CreateOptions{})
   321  	if err != nil {
   322  		t.Fatal(err)
   323  	}
   324  	r.addServiceCIDR(cidr2)
   325  	// wait for the cidr to be processed
   326  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   327  		allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.253"))
   328  		if err != nil {
   329  			return false, nil
   330  		}
   331  		allocator.ipAddressSynced = func() bool { return true }
   332  		return allocator.ready.Load(), nil
   333  	})
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  
   338  	for r.Free() > 0 {
   339  		ip, err := r.AllocateNext()
   340  		if err != nil {
   341  			t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err)
   342  		}
   343  		count++
   344  		if found.Has(ip.String()) {
   345  			t.Fatalf("allocated %s twice: %d", ip, count)
   346  		}
   347  		found.Insert(ip.String())
   348  	}
   349  	if count != 254 {
   350  		t.Fatalf("expected 254 IPs got %d", count)
   351  	}
   352  	if _, err := r.AllocateNext(); err == nil {
   353  		t.Fatal(err)
   354  	}
   355  
   356  }
   357  
   358  func TestCIDRAllocateShrink(t *testing.T) {
   359  	r, err := newTestMetaAllocator()
   360  	if err != nil {
   361  		t.Fatal(err)
   362  	}
   363  	defer r.Destroy()
   364  
   365  	if f := r.Free(); f != 0 {
   366  		t.Errorf("free: %d", f)
   367  	}
   368  	if _, err := r.AllocateNext(); err == nil {
   369  		t.Error(err)
   370  	}
   371  
   372  	cidr := newServiceCIDR("test", "192.168.0.0/24")
   373  	_, err = r.client.ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{})
   374  	if err != nil {
   375  		t.Fatal(err)
   376  	}
   377  	r.addServiceCIDR(cidr)
   378  	// wait for the cidr to be processed
   379  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   380  		allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.1"))
   381  		if err != nil {
   382  			return false, nil
   383  		}
   384  		allocator.ipAddressSynced = func() bool { return true }
   385  		return allocator.ready.Load(), nil
   386  	})
   387  	if err != nil {
   388  		t.Fatal(err)
   389  	}
   390  	found := sets.NewString()
   391  	count := 0
   392  	for r.Free() > 0 {
   393  		ip, err := r.AllocateNext()
   394  		if err != nil {
   395  			t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err)
   396  		}
   397  		count++
   398  		if found.Has(ip.String()) {
   399  			t.Fatalf("allocated %s twice: %d", ip, count)
   400  		}
   401  		found.Insert(ip.String())
   402  	}
   403  	if count != 254 {
   404  		t.Fatalf("expected 254 IPs got %d", count)
   405  	}
   406  	if _, err := r.AllocateNext(); err == nil {
   407  		t.Fatal(err)
   408  	}
   409  	for _, ip := range found.List() {
   410  		err = r.Release(netutils.ParseIPSloppy(ip))
   411  		if err != nil {
   412  			t.Fatalf("unexpected error releasing ip %s", err)
   413  		}
   414  	}
   415  	if r.Used() > 0 {
   416  		t.Fatalf("expected allocator to be empty, got %d", r.Free())
   417  	}
   418  	cidr2 := newServiceCIDR("cidr2", "192.168.0.0/28")
   419  	_, err = r.client.ServiceCIDRs().Create(context.Background(), cidr2, metav1.CreateOptions{})
   420  	if err != nil {
   421  		t.Fatal(err)
   422  	}
   423  	r.addServiceCIDR(cidr2)
   424  	err = r.client.ServiceCIDRs().Delete(context.Background(), cidr.Name, metav1.DeleteOptions{})
   425  	if err != nil {
   426  		t.Fatal(err)
   427  	}
   428  	r.deleteServiceCIDR(cidr)
   429  
   430  	// wait for the cidr to be processed (delete ServiceCIDR)
   431  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   432  		_, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.253"))
   433  		if err != nil {
   434  			return true, nil
   435  		}
   436  
   437  		return false, nil
   438  	})
   439  	if err != nil {
   440  		t.Fatal(err)
   441  	}
   442  	// wait for the cidr to be processed (create ServiceCIDR)
   443  	err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
   444  		allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.1"))
   445  		if err != nil {
   446  			return false, nil
   447  		}
   448  		allocator.ipAddressSynced = func() bool { return true }
   449  		return allocator.ready.Load(), nil
   450  	})
   451  	if err != nil {
   452  		t.Fatal(err)
   453  	}
   454  	count = 0
   455  	for r.Free() > 0 {
   456  		_, err := r.AllocateNext()
   457  		if err != nil {
   458  			t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err)
   459  		}
   460  		count++
   461  	}
   462  	if count != 14 {
   463  		t.Fatalf("expected 14 IPs got %d", count)
   464  	}
   465  	if _, err := r.AllocateNext(); err == nil {
   466  		t.Fatal(err)
   467  	}
   468  
   469  }
   470  
   471  // TODO: add IPv6 and dual stack test cases
   472  func newServiceCIDR(name, cidr string) *networkingv1alpha1.ServiceCIDR {
   473  	return &networkingv1alpha1.ServiceCIDR{
   474  		ObjectMeta: metav1.ObjectMeta{
   475  			Name: name,
   476  		},
   477  		Spec: networkingv1alpha1.ServiceCIDRSpec{
   478  			CIDRs: []string{cidr},
   479  		},
   480  		Status: networkingv1alpha1.ServiceCIDRStatus{
   481  			Conditions: []metav1.Condition{
   482  				{
   483  					Type:   string(networkingv1alpha1.ServiceCIDRConditionReady),
   484  					Status: metav1.ConditionTrue,
   485  				},
   486  			},
   487  		},
   488  	}
   489  }