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