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 }