k8s.io/kubernetes@v1.29.3/test/integration/servicecidr/migration_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 servicecidr
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/util/wait"
    30  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    31  	"k8s.io/client-go/informers"
    32  	clientset "k8s.io/client-go/kubernetes"
    33  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    34  	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    35  	"k8s.io/kubernetes/pkg/controller/servicecidrs"
    36  	"k8s.io/kubernetes/pkg/controlplane/controller/defaultservicecidr"
    37  	"k8s.io/kubernetes/pkg/features"
    38  	"k8s.io/kubernetes/test/integration/framework"
    39  )
    40  
    41  // TestMigrateServiceCIDR validates the steps necessary to migrate a cluster default ServiceCIDR
    42  // including the existing kubernetes.default service.
    43  // 1. start apiserver with --service-cluster-ip-range 192.168.0.0/29"
    44  // 2. create services to use some addresses on the cidr
    45  // 3. create a temporary new ServiceCIDR 10.168.0.0/24 to migrate the cluster to it
    46  // 4. delete the default service CIDR so the allocators ignore it (it will be pending because of the finalizer and having still IPs)
    47  // 5. recreate the services, the allocator should pick the temporary ServiceCIDR
    48  // 6. start the new apiserver with the new ServiceCIDRs on the flags and shutdown the old one
    49  // 7. delete the kubernetes.default service, the new apiserver will recreate it within the new ServiceCIDR
    50  func TestMigrateServiceCIDR(t *testing.T) {
    51  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MultiCIDRServiceAllocator, true)()
    52  	ctx, cancelFn := context.WithCancel(context.Background())
    53  	defer cancelFn()
    54  
    55  	cidr1 := "192.168.0.0/29"
    56  	cidr2 := "10.168.0.0/24"
    57  
    58  	etcdOptions := framework.SharedEtcd()
    59  	apiServerOptions := kubeapiservertesting.NewDefaultTestServerOptions()
    60  	s1 := kubeapiservertesting.StartTestServerOrDie(t,
    61  		apiServerOptions,
    62  		[]string{
    63  			"--runtime-config=networking.k8s.io/v1alpha1=true",
    64  			"--service-cluster-ip-range=" + cidr1,
    65  			"--advertise-address=10.1.1.1",
    66  			"--disable-admission-plugins=ServiceAccount",
    67  		},
    68  		etcdOptions)
    69  
    70  	client1, err := clientset.NewForConfig(s1.ClientConfig)
    71  	if err != nil {
    72  		t.Fatalf("Unexpected error: %v", err)
    73  	}
    74  
    75  	ns := framework.CreateNamespaceOrDie(client1, "test-migrate-service-cidr", t)
    76  
    77  	resyncPeriod := 12 * time.Hour
    78  	informers1 := informers.NewSharedInformerFactory(client1, resyncPeriod)
    79  	// ServiceCIDR controller
    80  	go servicecidrs.NewController(
    81  		informers1.Networking().V1alpha1().ServiceCIDRs(),
    82  		informers1.Networking().V1alpha1().IPAddresses(),
    83  		client1,
    84  	).Run(ctx, 5)
    85  	informers1.Start(ctx.Done())
    86  
    87  	// the default serviceCIDR should have a finalizer and ready condition set to true
    88  	if err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, time.Minute, false, func(ctx context.Context) (bool, error) {
    89  		cidr, err := client1.NetworkingV1alpha1().ServiceCIDRs().Get(context.TODO(), defaultservicecidr.DefaultServiceCIDRName, metav1.GetOptions{})
    90  		if err != nil && !apierrors.IsNotFound(err) {
    91  			return false, err
    92  		}
    93  		if len(cidr.Finalizers) == 0 {
    94  			return false, nil
    95  		}
    96  
    97  		return isServiceCIDRReady(cidr), nil
    98  	}); err != nil {
    99  		t.Fatalf("waiting for default service cidr ready condition set to false: %v", err)
   100  	}
   101  
   102  	svc := func(i int) *v1.Service {
   103  		return &v1.Service{
   104  			ObjectMeta: metav1.ObjectMeta{
   105  				Name: fmt.Sprintf("svc-%v", i),
   106  			},
   107  			Spec: v1.ServiceSpec{
   108  				Type: v1.ServiceTypeClusterIP,
   109  				Ports: []v1.ServicePort{
   110  					{Port: 80},
   111  				},
   112  			},
   113  		}
   114  	}
   115  
   116  	// make 2 services , there will be still 3 free addresses
   117  	for i := 0; i < 2; i++ {
   118  		if _, err := client1.CoreV1().Services(ns.Name).Create(context.TODO(), svc(i), metav1.CreateOptions{}); err != nil {
   119  			t.Fatal(err)
   120  		}
   121  	}
   122  	// Add a new service CIDR to be able to migrate the apiserver
   123  	if _, err := client1.NetworkingV1alpha1().ServiceCIDRs().Create(context.Background(), makeServiceCIDR("migration-cidr", cidr2, ""), metav1.CreateOptions{}); err != nil {
   124  		t.Fatalf("got unexpected error: %v", err)
   125  	}
   126  
   127  	// wait ServiceCIDR is ready
   128  	if err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, time.Minute, false, func(ctx context.Context) (bool, error) {
   129  		cidr, err := client1.NetworkingV1alpha1().ServiceCIDRs().Get(context.TODO(), "migration-cidr", metav1.GetOptions{})
   130  		if err != nil && !apierrors.IsNotFound(err) {
   131  			return false, err
   132  		}
   133  		return isServiceCIDRReady(cidr), nil
   134  	}); err != nil {
   135  		t.Fatalf("waiting for default service cidr ready condition set to false: %v", err)
   136  	}
   137  
   138  	// delete the default ServiceCIDR so is no longer used for allocating IPs
   139  	if err := client1.NetworkingV1alpha1().ServiceCIDRs().Delete(context.Background(), defaultservicecidr.DefaultServiceCIDRName, metav1.DeleteOptions{}); err != nil {
   140  		t.Fatalf("got unexpected error: %v", err)
   141  	}
   142  
   143  	// the default serviceCIDR should be pending deletion with Ready condition set to false
   144  	if err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, time.Minute, false, func(ctx context.Context) (bool, error) {
   145  		cidr, err := client1.NetworkingV1alpha1().ServiceCIDRs().Get(context.TODO(), defaultservicecidr.DefaultServiceCIDRName, metav1.GetOptions{})
   146  		if err != nil && !apierrors.IsNotFound(err) {
   147  			return false, err
   148  		}
   149  		for _, condition := range cidr.Status.Conditions {
   150  			if condition.Type == networkingv1alpha1.ServiceCIDRConditionReady {
   151  				return condition.Status == metav1.ConditionFalse, nil
   152  			}
   153  		}
   154  		return false, nil
   155  	}); err != nil {
   156  		t.Fatalf("waiting for default service cidr ready condition set to false: %v", err)
   157  	}
   158  
   159  	// Migrate the services, delete the existing ones and recreate without specifying the ClusterIP
   160  	services, err := client1.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{})
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  	for _, svc := range services.Items {
   165  		// skip the default service since is managed by the apiserver
   166  		// and we want the new apiserver with the new service cidr to take over
   167  		if svc.Name == "kubernetes" {
   168  			continue
   169  		}
   170  		// wipe the necessary fields so we can recreate the Service
   171  		svc.ResourceVersion = ""
   172  		svc.Spec.ClusterIP = ""
   173  		svc.Spec.ClusterIPs = nil
   174  		svc.Status = v1.ServiceStatus{}
   175  		if err := client1.CoreV1().Services(svc.Namespace).Delete(context.Background(), svc.Name, metav1.DeleteOptions{}); err != nil {
   176  			t.Fatalf("got unexpected error: %v", err)
   177  		}
   178  		svc, err := client1.CoreV1().Services(svc.Namespace).Create(context.Background(), &svc, metav1.CreateOptions{})
   179  		if err != nil {
   180  			t.Fatalf("got unexpected error: %v", err)
   181  		}
   182  		if !cidrContainsIP(cidr2, svc.Spec.ClusterIP) {
   183  			t.Fatalf("Service expected to have an ip in range 10.168.0.0/24, got %s", svc.Spec.ClusterIP)
   184  		}
   185  	}
   186  
   187  	// start second apiserver with the new range and new service cidr controller
   188  	s2 := kubeapiservertesting.StartTestServerOrDie(t,
   189  		apiServerOptions,
   190  		[]string{
   191  			"--runtime-config=networking.k8s.io/v1alpha1=true",
   192  			"--service-cluster-ip-range=" + cidr2,
   193  			"--advertise-address=10.1.1.1",
   194  			"--disable-admission-plugins=ServiceAccount",
   195  		},
   196  		etcdOptions)
   197  	defer s2.TearDownFn()
   198  
   199  	client2, err := clientset.NewForConfig(s2.ClientConfig)
   200  	if err != nil {
   201  		t.Fatalf("Unexpected error: %v", err)
   202  	}
   203  	defer framework.DeleteNamespaceOrDie(client2, ns, t)
   204  
   205  	// switch the controller to the new apiserver
   206  	cancelFn()
   207  	s1.TearDownFn()
   208  
   209  	// ServiceCIDR controller
   210  	ctx2, cancelFn2 := context.WithCancel(context.Background())
   211  	defer cancelFn2()
   212  	informers2 := informers.NewSharedInformerFactory(client2, resyncPeriod)
   213  	go servicecidrs.NewController(
   214  		informers2.Networking().V1alpha1().ServiceCIDRs(),
   215  		informers2.Networking().V1alpha1().IPAddresses(),
   216  		client2,
   217  	).Run(ctx2, 5)
   218  	informers2.Start(ctx2.Done())
   219  
   220  	// delete the kubernetes.default service so the old DefaultServiceCIDR can be deleted
   221  	// and the new apiserver can take over
   222  	if err := client2.CoreV1().Services(metav1.NamespaceDefault).Delete(context.Background(), "kubernetes", metav1.DeleteOptions{}); err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	// the default serviceCIDR should  be the new one
   227  	if err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, time.Minute, false, func(ctx context.Context) (bool, error) {
   228  		cidr, err := client2.NetworkingV1alpha1().ServiceCIDRs().Get(context.TODO(), defaultservicecidr.DefaultServiceCIDRName, metav1.GetOptions{})
   229  		if err != nil && !apierrors.IsNotFound(err) {
   230  			return false, err
   231  		}
   232  
   233  		if len(cidr.Spec.CIDRs) == 0 {
   234  			return false, nil
   235  		}
   236  
   237  		if cidr.Spec.CIDRs[0] != cidr2 {
   238  			return false, nil
   239  		}
   240  
   241  		if len(cidr.Finalizers) == 0 {
   242  			return false, nil
   243  		}
   244  
   245  		for _, condition := range cidr.Status.Conditions {
   246  			if condition.Type == networkingv1alpha1.ServiceCIDRConditionReady {
   247  				return condition.Status == metav1.ConditionTrue, nil
   248  			}
   249  		}
   250  		return false, nil
   251  	}); err != nil {
   252  		t.Fatalf("waiting for default service cidr ready condition set to true: %v", err)
   253  	}
   254  
   255  	if err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, time.Minute, false, func(ctx context.Context) (bool, error) {
   256  		svc, err := client2.CoreV1().Services(metav1.NamespaceDefault).Get(context.TODO(), "kubernetes", metav1.GetOptions{})
   257  		if err != nil && !apierrors.IsNotFound(err) {
   258  			return false, err
   259  		}
   260  
   261  		if svc.Spec.ClusterIP != "10.168.0.1" {
   262  			return false, nil
   263  		}
   264  		return true, nil
   265  	}); err != nil {
   266  		t.Fatalf("waiting for default service kubernetes.default to be migrated: %v", err)
   267  	}
   268  
   269  	// The temporary ServiceCIDR can be deleted now since the Default ServiceCIDR will cover it
   270  	if err := client2.NetworkingV1alpha1().ServiceCIDRs().Delete(context.Background(), "migration-cidr", metav1.DeleteOptions{}); err != nil {
   271  		t.Fatalf("got unexpected error: %v", err)
   272  	}
   273  
   274  	// wait ServiceCIDR no longer exist
   275  	if err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, time.Minute, false, func(ctx context.Context) (bool, error) {
   276  		_, err := client2.NetworkingV1alpha1().ServiceCIDRs().Get(context.TODO(), "migration-cidr", metav1.GetOptions{})
   277  		if err != nil && !apierrors.IsNotFound(err) {
   278  			return false, nil
   279  		}
   280  		return true, nil
   281  	}); err != nil {
   282  		t.Fatalf("waiting for the migration service cidr to be deleted: %v", err)
   283  	}
   284  
   285  }