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 }