k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/servicecidr/servicecidr_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 "net/netip" 23 "strings" 24 "testing" 25 "time" 26 27 v1 "k8s.io/api/core/v1" 28 networkingv1alpha1 "k8s.io/api/networking/v1alpha1" 29 apierrors "k8s.io/apimachinery/pkg/api/errors" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/util/wait" 32 utilfeature "k8s.io/apiserver/pkg/util/feature" 33 "k8s.io/client-go/informers" 34 "k8s.io/client-go/kubernetes" 35 featuregatetesting "k8s.io/component-base/featuregate/testing" 36 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" 37 "k8s.io/kubernetes/pkg/controller/servicecidrs" 38 "k8s.io/kubernetes/pkg/features" 39 "k8s.io/kubernetes/test/integration/framework" 40 ) 41 42 func TestServiceAllocNewServiceCIDR(t *testing.T) { 43 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MultiCIDRServiceAllocator, true) 44 45 etcdOptions := framework.SharedEtcd() 46 apiServerOptions := kubeapiservertesting.NewDefaultTestServerOptions() 47 s := kubeapiservertesting.StartTestServerOrDie(t, 48 apiServerOptions, 49 []string{ 50 "--runtime-config=networking.k8s.io/v1alpha1=true", 51 "--service-cluster-ip-range=192.168.0.0/29", 52 "--advertise-address=10.1.1.1", 53 "--disable-admission-plugins=ServiceAccount", 54 }, 55 etcdOptions) 56 defer s.TearDownFn() 57 58 client, err := kubernetes.NewForConfig(s.ClientConfig) 59 if err != nil { 60 t.Fatalf("Unexpected error: %v", err) 61 } 62 // ServiceCIDR controller 63 ctx, cancel := context.WithCancel(context.Background()) 64 defer cancel() 65 resyncPeriod := 12 * time.Hour 66 informerFactory := informers.NewSharedInformerFactory(client, resyncPeriod) 67 go servicecidrs.NewController( 68 ctx, 69 informerFactory.Networking().V1alpha1().ServiceCIDRs(), 70 informerFactory.Networking().V1alpha1().IPAddresses(), 71 client, 72 ).Run(ctx, 5) 73 informerFactory.Start(ctx.Done()) 74 75 // /29 = 6 services, kubernetes.default takes the first address 76 // make 5 more services to take up all IPs 77 for i := 0; i < 5; i++ { 78 if _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), makeService(fmt.Sprintf("service-%d", i)), metav1.CreateOptions{}); err != nil { 79 t.Fatal(err) 80 } 81 } 82 83 // Make another service. It will fail because we're out of cluster IPs 84 if _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), makeService("fail"), metav1.CreateOptions{}); err != nil { 85 if !strings.Contains(err.Error(), "range is full") { 86 t.Fatalf("unexpected error text: %v", err) 87 } 88 } else { 89 svcs, err := client.CoreV1().Services(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{}) 90 if err != nil { 91 t.Fatalf("unexpected error getting the services: %v", err) 92 } 93 allIPs := []string{} 94 for _, s := range svcs.Items { 95 allIPs = append(allIPs, s.Spec.ClusterIP) 96 } 97 t.Fatalf("unexpected creation success. The following IPs exist: %#v. It should only be possible to allocate 6 IP addresses in this cluster.\n\n%#v", allIPs, svcs) 98 } 99 100 // Add a new service CIDR to be able to create new IPs. 101 cidr := makeServiceCIDR("test2", "10.168.0.0/24", "") 102 if _, err := client.NetworkingV1alpha1().ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{}); err != nil { 103 t.Fatalf("got unexpected error: %v", err) 104 } 105 // wait ServiceCIDR is ready 106 if err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, time.Minute, false, func(ctx context.Context) (bool, error) { 107 cidr, err := client.NetworkingV1alpha1().ServiceCIDRs().Get(context.TODO(), cidr.Name, metav1.GetOptions{}) 108 if err != nil { 109 return false, err 110 } 111 return isServiceCIDRReady(cidr), nil 112 }); err != nil { 113 t.Fatalf("waiting for default service cidr ready condition set to false: %v", err) 114 } 115 // This time creating more Services should work 116 for i := 10; i < 150; i++ { 117 if _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), makeService(fmt.Sprintf("service-%d", i)), metav1.CreateOptions{}); err != nil { 118 t.Fatal(err) 119 } 120 } 121 } 122 123 // A ServiceCIDR can be deleted if there are no orphan IPs or if the existing IPs are contained in other ServiceCIDR 124 // that is not being deleted. 125 // The test starts the apiserver with the range "192.168.0.0/29" 126 // Create Services to fill the range 127 // Creates a new ServiceCIDR cidr1 with the same range as the one defined in the apiserver 128 // Deletes cidr1 object will work since its range is covered by the default ServiceCIDR created by the apiserver flags 129 // Creates a new cidr2 with a different range than cidr1 130 // Creates a new service so it picks an IPAddress on this range because "192.168.0.0/29" is full at this point 131 // Creates a new cidr3 that contains cidr2 132 // Deletes cidr2 since it is covered by cidr3 133 // Tries to delete cidr3 but is blocked since there is an IPAddress 134 // Deletes the Service with the IPAddress blocking the deletion 135 // cidr3 must not exist at this point 136 func TestServiceCIDRDeletion(t *testing.T) { 137 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MultiCIDRServiceAllocator, true) 138 cidr1 := "192.168.0.0/29" // same as the default 139 cidr2 := "10.0.0.0/24" // new range 140 cidr3 := "10.0.0.0/16" // contains cidr2 141 142 etcdOptions := framework.SharedEtcd() 143 apiServerOptions := kubeapiservertesting.NewDefaultTestServerOptions() 144 s := kubeapiservertesting.StartTestServerOrDie(t, 145 apiServerOptions, 146 []string{ 147 "--runtime-config=networking.k8s.io/v1alpha1=true", 148 "--service-cluster-ip-range=" + cidr1, 149 "--advertise-address=172.16.1.1", 150 "--disable-admission-plugins=ServiceAccount", 151 }, 152 etcdOptions) 153 defer s.TearDownFn() 154 155 client, err := kubernetes.NewForConfig(s.ClientConfig) 156 if err != nil { 157 t.Fatalf("Unexpected error: %v", err) 158 } 159 160 ns := framework.CreateNamespaceOrDie(client, "test-service-cidr-deletion", t) 161 defer framework.DeleteNamespaceOrDie(client, ns, t) 162 163 // ServiceCIDR controller 164 ctx, cancel := context.WithCancel(context.Background()) 165 defer cancel() 166 resyncPeriod := 12 * time.Hour 167 informerFactory := informers.NewSharedInformerFactory(client, resyncPeriod) 168 go servicecidrs.NewController( 169 ctx, 170 informerFactory.Networking().V1alpha1().ServiceCIDRs(), 171 informerFactory.Networking().V1alpha1().IPAddresses(), 172 client, 173 ).Run(ctx, 5) 174 informerFactory.Start(ctx.Done()) 175 176 // /29 = 6 services, kubernetes.default takes the first address 177 // make 5 more services to take up all IPs 178 for i := 0; i < 5; i++ { 179 if _, err := client.CoreV1().Services(ns.Name).Create(context.Background(), makeService(fmt.Sprintf("service-%d", i)), metav1.CreateOptions{}); err != nil { 180 t.Fatal(err) 181 } 182 } 183 // create a new ServiceCIDRs that overlaps the default one 184 _, err = client.NetworkingV1alpha1().ServiceCIDRs().Create(ctx, makeServiceCIDR("cidr1", cidr1, ""), metav1.CreateOptions{}) 185 if err != nil { 186 t.Fatal((err)) 187 } 188 // Wait until is ready. 189 if err := wait.PollUntilContextTimeout(context.Background(), 250*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { 190 cidr, err := client.NetworkingV1alpha1().ServiceCIDRs().Get(ctx, "cidr1", metav1.GetOptions{}) 191 if err != nil { 192 return false, nil 193 } 194 return isServiceCIDRReady(cidr), nil 195 }); err != nil { 196 t.Fatalf("cidr1 is not ready") 197 } 198 // we should be able to delete the ServiceCIDR despite it contains IP addresses as it overlaps with the default ServiceCIDR 199 err = client.NetworkingV1alpha1().ServiceCIDRs().Delete(ctx, "cidr1", metav1.DeleteOptions{}) 200 if err != nil { 201 t.Fatal((err)) 202 } 203 204 if err := wait.PollUntilContextTimeout(context.Background(), 250*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { 205 _, err := client.NetworkingV1alpha1().ServiceCIDRs().Get(ctx, "cidr1", metav1.GetOptions{}) 206 if err != nil && apierrors.IsNotFound(err) { 207 return true, nil 208 } 209 return false, nil 210 }); err != nil { 211 t.Fatalf("cidr1 has not been deleted") 212 } 213 214 // add a new ServiceCIDR with a new range 215 _, err = client.NetworkingV1alpha1().ServiceCIDRs().Create(ctx, makeServiceCIDR("cidr2", cidr2, ""), metav1.CreateOptions{}) 216 if err != nil { 217 t.Fatal((err)) 218 } 219 // wait the allocator process the new ServiceCIDR 220 // Wait until is ready. 221 if err := wait.PollUntilContextTimeout(context.Background(), 250*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { 222 cidr, err := client.NetworkingV1alpha1().ServiceCIDRs().Get(ctx, "cidr2", metav1.GetOptions{}) 223 if err != nil { 224 return false, nil 225 } 226 return isServiceCIDRReady(cidr), nil 227 }); err != nil { 228 t.Fatalf("cidr2 is not ready") 229 } 230 // create a new Service so it will take a new IP address from the new range 231 svc, err := client.CoreV1().Services(ns.Name).Create(context.Background(), makeService("new-cidr-service"), metav1.CreateOptions{}) 232 if err != nil { 233 t.Fatal(err) 234 } 235 236 if !cidrContainsIP(cidr2, svc.Spec.ClusterIP) { 237 t.Fatalf("Service %s expected to have an IP on range %s, got %s", svc.Name, cidr2, svc.Spec.ClusterIP) 238 } 239 240 // add a new ServiceCIDR that overlaps the existing one 241 _, err = client.NetworkingV1alpha1().ServiceCIDRs().Create(ctx, makeServiceCIDR("cidr3", cidr3, ""), metav1.CreateOptions{}) 242 if err != nil { 243 t.Fatal((err)) 244 } 245 // Wait until is ready. 246 if err := wait.PollUntilContextTimeout(context.Background(), 250*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { 247 cidr, err := client.NetworkingV1alpha1().ServiceCIDRs().Get(ctx, "cidr3", metav1.GetOptions{}) 248 if err != nil { 249 return false, nil 250 } 251 return isServiceCIDRReady(cidr), nil 252 }); err != nil { 253 t.Fatalf("cidr3 is not ready") 254 } 255 // we should be able to delete the ServiceCIDR2 despite it contains IP addresses as it is contained on ServiceCIDR3 256 err = client.NetworkingV1alpha1().ServiceCIDRs().Delete(ctx, "cidr2", metav1.DeleteOptions{}) 257 if err != nil { 258 t.Fatal((err)) 259 } 260 261 if err := wait.PollUntilContextTimeout(context.Background(), 250*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { 262 _, err := client.NetworkingV1alpha1().ServiceCIDRs().Get(ctx, "cidr2", metav1.GetOptions{}) 263 if err != nil && apierrors.IsNotFound(err) { 264 return true, nil 265 } 266 return false, nil 267 }); err != nil { 268 t.Fatalf("cidr2 has not been deleted") 269 } 270 271 // serviceCIDR3 will not be able to be deleted until the IPAddress is removed 272 err = client.NetworkingV1alpha1().ServiceCIDRs().Delete(ctx, "cidr3", metav1.DeleteOptions{}) 273 if err != nil { 274 t.Fatal((err)) 275 } 276 // Wait until is not ready. 277 if err := wait.PollUntilContextTimeout(context.Background(), 250*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { 278 cidr, err := client.NetworkingV1alpha1().ServiceCIDRs().Get(ctx, "cidr3", metav1.GetOptions{}) 279 if err != nil { 280 return false, nil 281 } 282 for _, condition := range cidr.Status.Conditions { 283 if condition.Type == networkingv1alpha1.ServiceCIDRConditionReady { 284 return condition.Status == metav1.ConditionStatus(metav1.ConditionFalse), nil 285 } 286 } 287 return false, nil 288 }); err != nil { 289 t.Fatalf("cidr3 is ready") 290 } 291 292 // delete the service blocking the deletion 293 if err := client.CoreV1().Services(ns.Name).Delete(context.Background(), "new-cidr-service", metav1.DeleteOptions{}); err != nil { 294 t.Fatal(err) 295 } 296 297 // cidr3 must not exist 298 if err := wait.PollUntilContextTimeout(context.Background(), 250*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { 299 _, err := client.NetworkingV1alpha1().ServiceCIDRs().Get(ctx, "cidr3", metav1.GetOptions{}) 300 if err != nil && apierrors.IsNotFound(err) { 301 return true, nil 302 } 303 return false, nil 304 }); err != nil { 305 t.Fatalf("cidr3 has not been deleted") 306 } 307 } 308 309 func makeServiceCIDR(name, primary, secondary string) *networkingv1alpha1.ServiceCIDR { 310 serviceCIDR := &networkingv1alpha1.ServiceCIDR{ 311 ObjectMeta: metav1.ObjectMeta{ 312 Name: name, 313 }, 314 Spec: networkingv1alpha1.ServiceCIDRSpec{}, 315 } 316 serviceCIDR.Spec.CIDRs = append(serviceCIDR.Spec.CIDRs, primary) 317 if secondary != "" { 318 serviceCIDR.Spec.CIDRs = append(serviceCIDR.Spec.CIDRs, secondary) 319 } 320 return serviceCIDR 321 } 322 323 func makeService(name string) *v1.Service { 324 return &v1.Service{ 325 ObjectMeta: metav1.ObjectMeta{ 326 Name: name, 327 }, 328 Spec: v1.ServiceSpec{ 329 Type: v1.ServiceTypeClusterIP, 330 Ports: []v1.ServicePort{ 331 {Port: 80}, 332 }, 333 }, 334 } 335 } 336 337 // returns true of the ServiceCIDRConditionReady is true 338 func isServiceCIDRReady(serviceCIDR *networkingv1alpha1.ServiceCIDR) bool { 339 if serviceCIDR == nil { 340 return false 341 } 342 343 for _, condition := range serviceCIDR.Status.Conditions { 344 if condition.Type == networkingv1alpha1.ServiceCIDRConditionReady { 345 return condition.Status == metav1.ConditionStatus(metav1.ConditionTrue) 346 } 347 } 348 349 return false 350 } 351 352 func cidrContainsIP(cidr, ip string) bool { 353 prefix := netip.MustParsePrefix(cidr) 354 address := netip.MustParseAddr(ip) 355 return prefix.Contains(address) 356 }