k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/service/service_test.go (about) 1 /* 2 Copyright 2022 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 service 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "fmt" 24 "testing" 25 "time" 26 27 corev1 "k8s.io/api/core/v1" 28 discoveryv1 "k8s.io/api/discovery/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/fields" 31 "k8s.io/apimachinery/pkg/labels" 32 "k8s.io/apimachinery/pkg/types" 33 "k8s.io/apimachinery/pkg/util/intstr" 34 utilrand "k8s.io/apimachinery/pkg/util/rand" 35 "k8s.io/apimachinery/pkg/util/wait" 36 "k8s.io/apimachinery/pkg/watch" 37 utilfeature "k8s.io/apiserver/pkg/util/feature" 38 "k8s.io/client-go/informers" 39 clientset "k8s.io/client-go/kubernetes" 40 "k8s.io/client-go/tools/cache" 41 watchtools "k8s.io/client-go/tools/watch" 42 "k8s.io/client-go/util/retry" 43 featuregatetesting "k8s.io/component-base/featuregate/testing" 44 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" 45 "k8s.io/kubernetes/pkg/controller/endpointslice" 46 "k8s.io/kubernetes/pkg/features" 47 "k8s.io/kubernetes/test/integration/framework" 48 "k8s.io/kubernetes/test/utils/format" 49 "k8s.io/kubernetes/test/utils/ktesting" 50 "k8s.io/utils/ptr" 51 ) 52 53 // Test_ExternalNameServiceStopsDefaultingInternalTrafficPolicy tests that Services no longer default 54 // the internalTrafficPolicy field when Type is ExternalName. This test exists due to historic reasons where 55 // the internalTrafficPolicy field was being defaulted in older versions. New versions stop defaulting the 56 // field and drop on read, but for compatibility reasons we still accept the field. 57 func Test_ExternalNameServiceStopsDefaultingInternalTrafficPolicy(t *testing.T) { 58 server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) 59 defer server.TearDownFn() 60 61 client, err := clientset.NewForConfig(server.ClientConfig) 62 if err != nil { 63 t.Fatalf("Error creating clientset: %v", err) 64 } 65 66 ns := framework.CreateNamespaceOrDie(client, "test-external-name-drops-internal-traffic-policy", t) 67 defer framework.DeleteNamespaceOrDie(client, ns, t) 68 69 service := &corev1.Service{ 70 ObjectMeta: metav1.ObjectMeta{ 71 Name: "test-123", 72 }, 73 Spec: corev1.ServiceSpec{ 74 Type: corev1.ServiceTypeExternalName, 75 ExternalName: "foo.bar.com", 76 }, 77 } 78 79 service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{}) 80 if err != nil { 81 t.Fatalf("Error creating test service: %v", err) 82 } 83 84 if service.Spec.InternalTrafficPolicy != nil { 85 t.Errorf("service internalTrafficPolicy should be droppped but is set: %v", service.Spec.InternalTrafficPolicy) 86 } 87 88 service, err = client.CoreV1().Services(ns.Name).Get(context.TODO(), service.Name, metav1.GetOptions{}) 89 if err != nil { 90 t.Fatalf("error getting service: %v", err) 91 } 92 93 if service.Spec.InternalTrafficPolicy != nil { 94 t.Errorf("service internalTrafficPolicy should be droppped but is set: %v", service.Spec.InternalTrafficPolicy) 95 } 96 } 97 98 // Test_ExternalNameServiceDropsInternalTrafficPolicy tests that Services accepts the internalTrafficPolicy field on Create, 99 // but drops the field on read. This test exists due to historic reasons where the internalTrafficPolicy field was being defaulted 100 // in older versions. New versions stop defaulting the field and drop on read, but for compatibility reasons we still accept the field. 101 func Test_ExternalNameServiceDropsInternalTrafficPolicy(t *testing.T) { 102 server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) 103 defer server.TearDownFn() 104 105 client, err := clientset.NewForConfig(server.ClientConfig) 106 if err != nil { 107 t.Fatalf("Error creating clientset: %v", err) 108 } 109 110 ns := framework.CreateNamespaceOrDie(client, "test-external-name-drops-internal-traffic-policy", t) 111 defer framework.DeleteNamespaceOrDie(client, ns, t) 112 113 internalTrafficPolicy := corev1.ServiceInternalTrafficPolicyCluster 114 service := &corev1.Service{ 115 ObjectMeta: metav1.ObjectMeta{ 116 Name: "test-123", 117 }, 118 Spec: corev1.ServiceSpec{ 119 Type: corev1.ServiceTypeExternalName, 120 ExternalName: "foo.bar.com", 121 InternalTrafficPolicy: &internalTrafficPolicy, 122 }, 123 } 124 125 service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{}) 126 if err != nil { 127 t.Fatalf("Error creating test service: %v", err) 128 } 129 130 if service.Spec.InternalTrafficPolicy != nil { 131 t.Errorf("service internalTrafficPolicy should be droppped but is set: %v", service.Spec.InternalTrafficPolicy) 132 } 133 134 service, err = client.CoreV1().Services(ns.Name).Get(context.TODO(), service.Name, metav1.GetOptions{}) 135 if err != nil { 136 t.Fatalf("error getting service: %v", err) 137 } 138 139 if service.Spec.InternalTrafficPolicy != nil { 140 t.Errorf("service internalTrafficPolicy should be droppped but is set: %v", service.Spec.InternalTrafficPolicy) 141 } 142 } 143 144 // Test_ConvertingToExternalNameServiceDropsInternalTrafficPolicy tests that converting a Service to Type=ExternalName 145 // results in the internalTrafficPolicy field being dropped.This test exists due to historic reasons where the internalTrafficPolicy 146 // field was being defaulted in older versions. New versions stop defaulting the field and drop on read, but for compatibility reasons 147 // we still accept the field. 148 func Test_ConvertingToExternalNameServiceDropsInternalTrafficPolicy(t *testing.T) { 149 server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) 150 defer server.TearDownFn() 151 152 client, err := clientset.NewForConfig(server.ClientConfig) 153 if err != nil { 154 t.Fatalf("Error creating clientset: %v", err) 155 } 156 157 ns := framework.CreateNamespaceOrDie(client, "test-external-name-drops-internal-traffic-policy", t) 158 defer framework.DeleteNamespaceOrDie(client, ns, t) 159 160 service := &corev1.Service{ 161 ObjectMeta: metav1.ObjectMeta{ 162 Name: "test-123", 163 }, 164 Spec: corev1.ServiceSpec{ 165 Type: corev1.ServiceTypeClusterIP, 166 Ports: []corev1.ServicePort{{ 167 Port: int32(80), 168 }}, 169 Selector: map[string]string{ 170 "foo": "bar", 171 }, 172 }, 173 } 174 175 service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{}) 176 if err != nil { 177 t.Fatalf("Error creating test service: %v", err) 178 } 179 180 if *service.Spec.InternalTrafficPolicy != corev1.ServiceInternalTrafficPolicyCluster { 181 t.Error("service internalTrafficPolicy was not set for clusterIP Service") 182 } 183 184 newService := service.DeepCopy() 185 newService.Spec.Type = corev1.ServiceTypeExternalName 186 newService.Spec.ExternalName = "foo.bar.com" 187 188 service, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), newService, metav1.UpdateOptions{}) 189 if err != nil { 190 t.Fatalf("error updating service: %v", err) 191 } 192 193 if service.Spec.InternalTrafficPolicy != nil { 194 t.Errorf("service internalTrafficPolicy should be droppped but is set: %v", service.Spec.InternalTrafficPolicy) 195 } 196 197 service, err = client.CoreV1().Services(ns.Name).Get(context.TODO(), service.Name, metav1.GetOptions{}) 198 if err != nil { 199 t.Fatalf("error getting service: %v", err) 200 } 201 202 if service.Spec.InternalTrafficPolicy != nil { 203 t.Errorf("service internalTrafficPolicy should be droppped but is set: %v", service.Spec.InternalTrafficPolicy) 204 } 205 } 206 207 // Test_RemovingExternalIPsFromClusterIPServiceDropsExternalTrafficPolicy tests that removing externalIPs from a 208 // ClusterIP Service results in the externalTrafficPolicy field being dropped. 209 func Test_RemovingExternalIPsFromClusterIPServiceDropsExternalTrafficPolicy(t *testing.T) { 210 server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) 211 defer server.TearDownFn() 212 213 client, err := clientset.NewForConfig(server.ClientConfig) 214 if err != nil { 215 t.Fatalf("Error creating clientset: %v", err) 216 } 217 218 ns := framework.CreateNamespaceOrDie(client, "test-removing-external-ips-drops-external-traffic-policy", t) 219 defer framework.DeleteNamespaceOrDie(client, ns, t) 220 221 service := &corev1.Service{ 222 ObjectMeta: metav1.ObjectMeta{ 223 Name: "test-123", 224 }, 225 Spec: corev1.ServiceSpec{ 226 Type: corev1.ServiceTypeClusterIP, 227 Ports: []corev1.ServicePort{{ 228 Port: int32(80), 229 }}, 230 Selector: map[string]string{ 231 "foo": "bar", 232 }, 233 ExternalIPs: []string{"1.1.1.1"}, 234 }, 235 } 236 237 service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{}) 238 if err != nil { 239 t.Fatalf("Error creating test service: %v", err) 240 } 241 242 if service.Spec.ExternalTrafficPolicy != corev1.ServiceExternalTrafficPolicyCluster { 243 t.Error("service externalTrafficPolicy was not set for clusterIP Service with externalIPs") 244 } 245 246 // externalTrafficPolicy should be dropped after removing externalIPs. 247 newService := service.DeepCopy() 248 newService.Spec.ExternalIPs = []string{} 249 250 service, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), newService, metav1.UpdateOptions{}) 251 if err != nil { 252 t.Fatalf("error updating service: %v", err) 253 } 254 255 if service.Spec.ExternalTrafficPolicy != "" { 256 t.Errorf("service externalTrafficPolicy should be droppped but is set: %v", service.Spec.ExternalTrafficPolicy) 257 } 258 259 service, err = client.CoreV1().Services(ns.Name).Get(context.TODO(), service.Name, metav1.GetOptions{}) 260 if err != nil { 261 t.Fatalf("error getting service: %v", err) 262 } 263 264 if service.Spec.ExternalTrafficPolicy != "" { 265 t.Errorf("service externalTrafficPolicy should be droppped but is set: %v", service.Spec.ExternalTrafficPolicy) 266 } 267 268 // externalTrafficPolicy should be set after adding externalIPs again. 269 newService = service.DeepCopy() 270 newService.Spec.ExternalIPs = []string{"1.1.1.1"} 271 272 service, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), newService, metav1.UpdateOptions{}) 273 if err != nil { 274 t.Fatalf("error updating service: %v", err) 275 } 276 277 if service.Spec.ExternalTrafficPolicy != corev1.ServiceExternalTrafficPolicyCluster { 278 t.Error("service externalTrafficPolicy was not set for clusterIP Service with externalIPs") 279 } 280 281 service, err = client.CoreV1().Services(ns.Name).Get(context.TODO(), service.Name, metav1.GetOptions{}) 282 if err != nil { 283 t.Fatalf("error getting service: %v", err) 284 } 285 286 if service.Spec.ExternalTrafficPolicy != corev1.ServiceExternalTrafficPolicyCluster { 287 t.Error("service externalTrafficPolicy was not set for clusterIP Service with externalIPs") 288 } 289 } 290 291 // Test transitions involving the `trafficDistribution` field in Service spec. 292 func Test_TransitionsForTrafficDistribution(t *testing.T) { 293 294 //////////////////////////////////////////////////////////////////////////// 295 // Setup components, like kube-apiserver and EndpointSlice controller. 296 //////////////////////////////////////////////////////////////////////////// 297 298 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceTrafficDistribution, true) 299 300 // Disable ServiceAccount admission plugin as we don't have serviceaccount controller running. 301 server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd()) 302 defer server.TearDownFn() 303 304 client, err := clientset.NewForConfig(server.ClientConfig) 305 if err != nil { 306 t.Fatalf("Error creating clientset: %v", err) 307 } 308 309 resyncPeriod := 12 * time.Hour 310 informers := informers.NewSharedInformerFactory(client, resyncPeriod) 311 312 ctx := ktesting.Init(t) 313 defer ctx.Cancel("test has completed") 314 epsController := endpointslice.NewController( 315 ctx, 316 informers.Core().V1().Pods(), 317 informers.Core().V1().Services(), 318 informers.Core().V1().Nodes(), 319 informers.Discovery().V1().EndpointSlices(), 320 int32(100), 321 client, 322 1*time.Second, 323 ) 324 325 informers.Start(ctx.Done()) 326 go epsController.Run(ctx, 1) 327 328 //////////////////////////////////////////////////////////////////////////// 329 // Create a namespace, node, pod in the node, and a service exposing the pod. 330 //////////////////////////////////////////////////////////////////////////// 331 332 ns := framework.CreateNamespaceOrDie(client, "test-service-traffic-distribution", t) 333 defer framework.DeleteNamespaceOrDie(client, ns, t) 334 335 node := &corev1.Node{ 336 ObjectMeta: metav1.ObjectMeta{ 337 Name: "fake-node", 338 Labels: map[string]string{ 339 corev1.LabelTopologyZone: "fake-zone-1", 340 }, 341 }, 342 } 343 344 pod := &corev1.Pod{ 345 ObjectMeta: metav1.ObjectMeta{ 346 Name: "test-pod", 347 Namespace: ns.GetName(), 348 Labels: map[string]string{ 349 "foo": "bar", 350 }, 351 }, 352 Spec: corev1.PodSpec{ 353 NodeName: node.GetName(), 354 Containers: []corev1.Container{ 355 { 356 Name: "fake-name", 357 Image: "fake-image", 358 Ports: []corev1.ContainerPort{ 359 { 360 Name: "port-443", 361 ContainerPort: 443, 362 }, 363 }, 364 }, 365 }, 366 }, 367 Status: corev1.PodStatus{ 368 Phase: corev1.PodRunning, 369 Conditions: []corev1.PodCondition{ 370 { 371 Type: corev1.PodReady, 372 Status: corev1.ConditionTrue, 373 }, 374 }, 375 PodIP: "10.0.0.1", 376 PodIPs: []corev1.PodIP{ 377 { 378 IP: "10.0.0.1", 379 }, 380 }, 381 }, 382 } 383 384 svc := &corev1.Service{ 385 ObjectMeta: metav1.ObjectMeta{ 386 Name: "test-service", 387 Namespace: ns.GetName(), 388 }, 389 Spec: corev1.ServiceSpec{ 390 Selector: map[string]string{ 391 "foo": "bar", 392 }, 393 Ports: []corev1.ServicePort{ 394 {Name: "port-443", Port: 443, Protocol: "TCP", TargetPort: intstr.FromInt32(443)}, 395 }, 396 }, 397 } 398 399 _, err = client.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{}) 400 if err != nil { 401 t.Fatalf("Failed to create test node: %v", err) 402 } 403 _, err = client.CoreV1().Pods(ns.Name).Create(ctx, pod, metav1.CreateOptions{}) 404 if err != nil { 405 t.Fatalf("Failed to create test ready pod: %v", err) 406 } 407 _, err = client.CoreV1().Pods(ns.Name).UpdateStatus(ctx, pod, metav1.UpdateOptions{}) 408 if err != nil { 409 t.Fatalf("Failed to update status for test pod to Ready: %v", err) 410 } 411 _, err = client.CoreV1().Services(ns.Name).Create(ctx, svc, metav1.CreateOptions{}) 412 if err != nil { 413 t.Fatalf("Failed to create test service: %v", err) 414 } 415 416 //////////////////////////////////////////////////////////////////////////// 417 // Assert that without the presence of `trafficDistribution` field and the 418 // service.kubernetes.io/topology-mode=Auto annotation, there are no zone 419 // hints in EndpointSlice. 420 //////////////////////////////////////////////////////////////////////////// 421 422 // logsBuffer captures logs during assertions which multiple retires. These 423 // will only be printed if the assertion failed. 424 logsBuffer := &bytes.Buffer{} 425 426 endpointSlicesHaveNoHints := func(ctx context.Context) (bool, error) { 427 slices, err := client.DiscoveryV1().EndpointSlices(ns.GetName()).List(ctx, metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", discoveryv1.LabelServiceName, svc.GetName())}) 428 if err != nil { 429 fmt.Fprintf(logsBuffer, "failed to list EndpointSlices for service %q: %v\n", svc.GetName(), err) 430 return false, nil 431 } 432 if slices == nil || len(slices.Items) == 0 { 433 fmt.Fprintf(logsBuffer, "no EndpointSlices returned for service %q\n", svc.GetName()) 434 return false, nil 435 } 436 fmt.Fprintf(logsBuffer, "EndpointSlices=\n%v\n", format.Object(slices, 1 /* indent one level */)) 437 438 for _, slice := range slices.Items { 439 for _, endpoint := range slice.Endpoints { 440 var ip string 441 if len(endpoint.Addresses) > 0 { 442 ip = endpoint.Addresses[0] 443 } 444 if endpoint.Hints != nil && len(endpoint.Hints.ForZones) != 0 { 445 fmt.Fprintf(logsBuffer, "endpoint with ip %v has hint %+v, want no hint\n", ip, endpoint.Hints) 446 return false, nil 447 } 448 } 449 } 450 return true, nil 451 } 452 453 err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, endpointSlicesHaveNoHints) 454 if err != nil { 455 t.Logf("logsBuffer=\n%v", logsBuffer) 456 t.Fatalf("Error waiting for EndpointSlices to have same zone hints: %v", err) 457 } 458 logsBuffer.Reset() 459 460 //////////////////////////////////////////////////////////////////////////// 461 // Update the service by setting the `trafficDistribution: PreferLocal` field 462 // 463 // Assert that the respective EndpointSlices get the same-zone hints. 464 //////////////////////////////////////////////////////////////////////////// 465 466 trafficDist := corev1.ServiceTrafficDistributionPreferClose 467 svc.Spec.TrafficDistribution = &trafficDist 468 _, err = client.CoreV1().Services(ns.Name).Update(ctx, svc, metav1.UpdateOptions{}) 469 if err != nil { 470 t.Fatalf("Failed to update test service with 'trafficDistribution: PreferLocal': %v", err) 471 } 472 473 endpointSlicesHaveSameZoneHints := func(ctx context.Context) (bool, error) { 474 slices, err := client.DiscoveryV1().EndpointSlices(ns.GetName()).List(ctx, metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", discoveryv1.LabelServiceName, svc.GetName())}) 475 if err != nil { 476 fmt.Fprintf(logsBuffer, "failed to list EndpointSlices for service %q: %v\n", svc.GetName(), err) 477 return false, nil 478 } 479 if slices == nil || len(slices.Items) == 0 { 480 fmt.Fprintf(logsBuffer, "no EndpointSlices returned for service %q\n", svc.GetName()) 481 return false, nil 482 } 483 fmt.Fprintf(logsBuffer, "EndpointSlices=\n%v\n", format.Object(slices, 1 /* indent one level */)) 484 485 for _, slice := range slices.Items { 486 for _, endpoint := range slice.Endpoints { 487 var ip string 488 if len(endpoint.Addresses) > 0 { 489 ip = endpoint.Addresses[0] 490 } 491 var zone string 492 if endpoint.Zone != nil { 493 zone = *endpoint.Zone 494 } 495 if endpoint.Hints == nil || len(endpoint.Hints.ForZones) != 1 || endpoint.Hints.ForZones[0].Name != zone { 496 fmt.Fprintf(logsBuffer, "endpoint with ip %v does not have the correct hint, want hint for zone %q\n", ip, zone) 497 return false, nil 498 } 499 } 500 } 501 return true, nil 502 } 503 504 err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, endpointSlicesHaveSameZoneHints) 505 if err != nil { 506 t.Logf("logsBuffer=\n%v", logsBuffer) 507 t.Fatalf("Error waiting for EndpointSlices to have same zone hints: %v", err) 508 } 509 logsBuffer.Reset() 510 511 //////////////////////////////////////////////////////////////////////////// 512 // Update the service with the service.kubernetes.io/topology-mode=Auto 513 // annotation. 514 // 515 // Assert that the EndpointSlice for service have no hints once 516 // service.kubernetes.io/topology-mode=Auto takes affect, since topology 517 // annotation would not work with only one service pod. 518 //////////////////////////////////////////////////////////////////////////// 519 svc.Annotations = map[string]string{corev1.AnnotationTopologyMode: "Auto"} 520 _, err = client.CoreV1().Services(ns.Name).Update(ctx, svc, metav1.UpdateOptions{}) 521 if err != nil { 522 t.Fatalf("Failed to update test service with 'service.kubernetes.io/topology-mode=Auto' annotation: %v", err) 523 } 524 525 err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, endpointSlicesHaveNoHints) 526 if err != nil { 527 t.Logf("logsBuffer=\n%v", logsBuffer) 528 t.Fatalf("Error waiting for EndpointSlices to have no hints: %v", err) 529 } 530 logsBuffer.Reset() 531 532 //////////////////////////////////////////////////////////////////////////// 533 // Remove the annotation service.kubernetes.io/topology-mode=Auto from the 534 // service. 535 // 536 // Assert that EndpointSlice for service again has the correct same-zone 537 // hints because of the `trafficDistribution: PreferLocal` field. 538 //////////////////////////////////////////////////////////////////////////// 539 svc.Annotations = map[string]string{} 540 _, err = client.CoreV1().Services(ns.Name).Update(ctx, svc, metav1.UpdateOptions{}) 541 if err != nil { 542 t.Fatalf("Failed to remove annotation 'service.kubernetes.io/topology-mode=Auto' from service: %v", err) 543 } 544 545 err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, endpointSlicesHaveSameZoneHints) 546 if err != nil { 547 t.Logf("logsBuffer=\n%v", logsBuffer) 548 t.Fatalf("Error waiting for EndpointSlices to have same zone hints: %v", err) 549 } 550 logsBuffer.Reset() 551 552 //////////////////////////////////////////////////////////////////////////// 553 // Remove the field `trafficDistribution: PreferLocal` from the service. 554 // 555 // Assert that EndpointSlice for service again has no zone hints. 556 //////////////////////////////////////////////////////////////////////////// 557 svc.Spec.TrafficDistribution = nil 558 _, err = client.CoreV1().Services(ns.Name).Update(ctx, svc, metav1.UpdateOptions{}) 559 if err != nil { 560 t.Fatalf("Failed to remove annotation 'service.kubernetes.io/topology-mode=Auto' from service: %v", err) 561 } 562 563 err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, endpointSlicesHaveNoHints) 564 if err != nil { 565 t.Logf("logsBuffer=\n%v", logsBuffer) 566 t.Fatalf("Error waiting for EndpointSlices to have no hints: %v", err) 567 } 568 logsBuffer.Reset() 569 } 570 571 func Test_ServiceClusterIPSelector(t *testing.T) { 572 server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) 573 defer server.TearDownFn() 574 575 ctx, cancel := context.WithCancel(context.Background()) 576 defer cancel() 577 578 client, err := clientset.NewForConfig(server.ClientConfig) 579 if err != nil { 580 t.Fatalf("Error creating clientset: %v", err) 581 } 582 583 ns := framework.CreateNamespaceOrDie(client, "test-external-name-drops-internal-traffic-policy", t) 584 defer framework.DeleteNamespaceOrDie(client, ns, t) 585 586 // create headless service 587 service := &corev1.Service{ 588 ObjectMeta: metav1.ObjectMeta{ 589 Name: "test-headless", 590 Namespace: ns.Name, 591 }, 592 Spec: corev1.ServiceSpec{ 593 ClusterIP: corev1.ClusterIPNone, 594 Type: corev1.ServiceTypeClusterIP, 595 Ports: []corev1.ServicePort{{ 596 Port: int32(80), 597 }}, 598 Selector: map[string]string{ 599 "foo": "bar", 600 }, 601 }, 602 } 603 604 _, err = client.CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{}) 605 if err != nil { 606 t.Fatalf("Error creating test service: %v", err) 607 } 608 609 // informer to watch only non-headless services 610 kubeInformers := informers.NewSharedInformerFactoryWithOptions(client, 0, informers.WithTweakListOptions(func(options *metav1.ListOptions) { 611 options.FieldSelector = fields.OneTermNotEqualSelector("spec.clusterIP", corev1.ClusterIPNone).String() 612 })) 613 614 serviceInformer := kubeInformers.Core().V1().Services().Informer() 615 serviceLister := kubeInformers.Core().V1().Services().Lister() 616 serviceHasSynced := serviceInformer.HasSynced 617 if _, err = serviceInformer.AddEventHandler( 618 cache.ResourceEventHandlerFuncs{ 619 AddFunc: func(obj interface{}) { 620 svc := obj.(*corev1.Service) 621 t.Logf("Added Service %#v", svc) 622 }, 623 UpdateFunc: func(oldObj, newObj interface{}) { 624 oldSvc := oldObj.(*corev1.Service) 625 newSvc := newObj.(*corev1.Service) 626 t.Logf("Updated Service %#v to %#v", oldSvc, newSvc) 627 }, 628 DeleteFunc: func(obj interface{}) { 629 svc := obj.(*corev1.Service) 630 t.Logf("Deleted Service %#v", svc) 631 }, 632 }, 633 ); err != nil { 634 t.Fatalf("Error adding service informer handler: %v", err) 635 } 636 kubeInformers.Start(ctx.Done()) 637 cache.WaitForCacheSync(ctx.Done(), serviceHasSynced) 638 svcs, err := serviceLister.List(labels.Everything()) 639 if err != nil { 640 t.Fatalf("Error listing services: %v", err) 641 } 642 // only the kubernetes.default service expected 643 if len(svcs) != 1 || svcs[0].Name != "kubernetes" { 644 t.Fatalf("expected 1 services, got %d", len(svcs)) 645 } 646 647 // create a new service with ClusterIP 648 service2 := service.DeepCopy() 649 service2.Spec.ClusterIP = "" 650 service2.Name = "test-clusterip" 651 _, err = client.CoreV1().Services(ns.Name).Create(ctx, service2, metav1.CreateOptions{}) 652 if err != nil { 653 t.Fatalf("Error creating test service: %v", err) 654 } 655 656 err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, func(ctx context.Context) (done bool, err error) { 657 svc, err := serviceLister.Services(service2.Namespace).Get(service2.Name) 658 if svc == nil || err != nil { 659 return false, nil 660 } 661 return true, nil 662 }) 663 if err != nil { 664 t.Fatalf("Error waiting for test service test-clusterip: %v", err) 665 } 666 667 // mutate the Service to drop the ClusterIP, theoretically ClusterIP is inmutable but ... 668 service.Spec.ExternalName = "test" 669 service.Spec.Type = corev1.ServiceTypeExternalName 670 _, err = client.CoreV1().Services(ns.Name).Update(ctx, service, metav1.UpdateOptions{}) 671 if err != nil { 672 t.Fatalf("Error creating test service: %v", err) 673 } 674 675 err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, func(ctx context.Context) (done bool, err error) { 676 svc, err := serviceLister.Services(service.Namespace).Get(service.Name) 677 if svc == nil || err != nil { 678 return false, nil 679 } 680 return true, nil 681 }) 682 if err != nil { 683 t.Fatalf("Error waiting for test service without ClusterIP: %v", err) 684 } 685 686 // mutate the Service to get the ClusterIP again 687 service.Spec.ExternalName = "" 688 service.Spec.ClusterIP = "" 689 service.Spec.Type = corev1.ServiceTypeClusterIP 690 _, err = client.CoreV1().Services(ns.Name).Update(ctx, service, metav1.UpdateOptions{}) 691 if err != nil { 692 t.Fatalf("Error creating test service: %v", err) 693 } 694 695 err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 10*time.Second, true, func(ctx context.Context) (done bool, err error) { 696 svc, err := serviceLister.Services(service.Namespace).Get(service.Name) 697 if svc == nil || err != nil { 698 return false, nil 699 } 700 return true, nil 701 }) 702 if err != nil { 703 t.Fatalf("Error waiting for test service with ClusterIP: %v", err) 704 } 705 } 706 707 // Repro https://github.com/kubernetes/kubernetes/issues/123853 708 func Test_ServiceWatchUntil(t *testing.T) { 709 svcReadyTimeout := 30 * time.Second 710 711 server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) 712 defer server.TearDownFn() 713 714 ctx, cancel := context.WithCancel(context.Background()) 715 defer cancel() 716 717 client, err := clientset.NewForConfig(server.ClientConfig) 718 if err != nil { 719 t.Fatalf("Error creating clientset: %v", err) 720 } 721 722 ns := framework.CreateNamespaceOrDie(client, "test-service-watchuntil", t) 723 defer framework.DeleteNamespaceOrDie(client, ns, t) 724 725 testSvcName := "test-service-" + utilrand.String(5) 726 testSvcLabels := map[string]string{"test-service-static": "true"} 727 testSvcLabelsFlat := "test-service-static=true" 728 729 w := &cache.ListWatch{ 730 WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { 731 options.LabelSelector = testSvcLabelsFlat 732 return client.CoreV1().Services(ns.Name).Watch(ctx, options) 733 }, 734 } 735 736 svcList, err := client.CoreV1().Services("").List(ctx, metav1.ListOptions{LabelSelector: testSvcLabelsFlat}) 737 if err != nil { 738 t.Fatalf("failed to list Services: %v", err) 739 } 740 // create service 741 service := &corev1.Service{ 742 ObjectMeta: metav1.ObjectMeta{ 743 Name: testSvcName, 744 Labels: testSvcLabels, 745 }, 746 Spec: corev1.ServiceSpec{ 747 Type: "LoadBalancer", 748 Ports: []corev1.ServicePort{{ 749 Name: "http", 750 Protocol: corev1.ProtocolTCP, 751 Port: int32(80), 752 TargetPort: intstr.FromInt32(80), 753 }}, 754 LoadBalancerClass: ptr.To[string]("example.com/internal-vip"), 755 }, 756 } 757 _, err = client.CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{}) 758 if err != nil { 759 t.Fatalf("Error creating test service: %v", err) 760 } 761 762 ctxUntil, cancel := context.WithTimeout(ctx, svcReadyTimeout) 763 defer cancel() 764 _, err = watchtools.Until(ctxUntil, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { 765 if svc, ok := event.Object.(*corev1.Service); ok { 766 found := svc.ObjectMeta.Name == service.ObjectMeta.Name && 767 svc.ObjectMeta.Namespace == ns.Name && 768 svc.Labels["test-service-static"] == "true" 769 if !found { 770 t.Logf("observed Service %v in namespace %v with labels: %v & ports %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels, svc.Spec.Ports) 771 return false, nil 772 } 773 t.Logf("Found Service %v in namespace %v with labels: %v & ports %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels, svc.Spec.Ports) 774 return found, nil 775 } 776 t.Logf("Observed event: %+v", event.Object) 777 return false, nil 778 }) 779 if err != nil { 780 t.Fatalf("Error service not found: %v", err) 781 } 782 783 t.Log("patching the ServiceStatus") 784 lbStatus := corev1.LoadBalancerStatus{ 785 Ingress: []corev1.LoadBalancerIngress{{IP: "203.0.113.1"}}, 786 } 787 lbStatusJSON, err := json.Marshal(lbStatus) 788 if err != nil { 789 t.Fatalf("Error marshalling status: %v", err) 790 } 791 _, err = client.CoreV1().Services(ns.Name).Patch(ctx, testSvcName, types.MergePatchType, 792 []byte(`{"metadata":{"annotations":{"patchedstatus":"true"}},"status":{"loadBalancer":`+string(lbStatusJSON)+`}}`), 793 metav1.PatchOptions{}, "status") 794 if err != nil { 795 t.Fatalf("Could not patch service status: %v", err) 796 } 797 798 t.Log("watching for the Service to be patched") 799 ctxUntil, cancel = context.WithTimeout(ctx, svcReadyTimeout) 800 defer cancel() 801 802 _, err = watchtools.Until(ctxUntil, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { 803 if svc, ok := event.Object.(*corev1.Service); ok { 804 found := svc.ObjectMeta.Name == service.ObjectMeta.Name && 805 svc.ObjectMeta.Namespace == ns.Name && 806 svc.Annotations["patchedstatus"] == "true" 807 if !found { 808 t.Logf("observed Service %v in namespace %v with annotations: %v & LoadBalancer: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.LoadBalancer) 809 return false, nil 810 } 811 t.Logf("Found Service %v in namespace %v with annotations: %v & LoadBalancer: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.LoadBalancer) 812 return found, nil 813 } 814 t.Logf("Observed event: %+v", event.Object) 815 return false, nil 816 }) 817 if err != nil { 818 t.Fatalf("failed to locate Service %v in namespace %v", service.ObjectMeta.Name, ns) 819 } 820 t.Logf("Service %s has service status patched", testSvcName) 821 822 t.Log("updating the ServiceStatus") 823 824 var statusToUpdate, updatedStatus *corev1.Service 825 err = retry.RetryOnConflict(retry.DefaultRetry, func() error { 826 statusToUpdate, err = client.CoreV1().Services(ns.Name).Get(ctx, testSvcName, metav1.GetOptions{}) 827 if err != nil { 828 return err 829 } 830 831 statusToUpdate.Status.Conditions = append(statusToUpdate.Status.Conditions, metav1.Condition{ 832 Type: "StatusUpdate", 833 Status: metav1.ConditionTrue, 834 Reason: "E2E", 835 Message: "Set from e2e test", 836 }) 837 838 updatedStatus, err = client.CoreV1().Services(ns.Name).UpdateStatus(ctx, statusToUpdate, metav1.UpdateOptions{}) 839 return err 840 }) 841 if err != nil { 842 t.Fatalf("\n\n Failed to UpdateStatus. %v\n\n", err) 843 } 844 t.Logf("updatedStatus.Conditions: %#v", updatedStatus.Status.Conditions) 845 846 t.Log("watching for the Service to be updated") 847 ctxUntil, cancel = context.WithTimeout(ctx, svcReadyTimeout) 848 defer cancel() 849 _, err = watchtools.Until(ctxUntil, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { 850 if svc, ok := event.Object.(*corev1.Service); ok { 851 found := svc.ObjectMeta.Name == service.ObjectMeta.Name && 852 svc.ObjectMeta.Namespace == ns.Name && 853 svc.Annotations["patchedstatus"] == "true" 854 if !found { 855 t.Logf("Observed Service %v in namespace %v with annotations: %v & Conditions: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.LoadBalancer) 856 return false, nil 857 } 858 for _, cond := range svc.Status.Conditions { 859 if cond.Type == "StatusUpdate" && 860 cond.Reason == "E2E" && 861 cond.Message == "Set from e2e test" { 862 t.Logf("Found Service %v in namespace %v with annotations: %v & Conditions: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.Conditions) 863 return found, nil 864 } else { 865 t.Logf("Observed Service %v in namespace %v with annotations: %v & Conditions: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.LoadBalancer) 866 return false, nil 867 } 868 } 869 } 870 t.Logf("Observed event: %+v", event.Object) 871 return false, nil 872 }) 873 if err != nil { 874 t.Fatalf("failed to locate Service %v in namespace %v", service.ObjectMeta.Name, ns) 875 } 876 t.Logf("Service %s has service status updated", testSvcName) 877 878 t.Log("patching the service") 879 servicePatchPayload, err := json.Marshal(corev1.Service{ 880 ObjectMeta: metav1.ObjectMeta{ 881 Labels: map[string]string{ 882 "test-service": "patched", 883 }, 884 }, 885 }) 886 887 _, err = client.CoreV1().Services(ns.Name).Patch(ctx, testSvcName, types.StrategicMergePatchType, []byte(servicePatchPayload), metav1.PatchOptions{}) 888 if err != nil { 889 t.Fatalf("failed to patch service. %v", err) 890 } 891 892 t.Log("watching for the Service to be patched") 893 ctxUntil, cancel = context.WithTimeout(ctx, svcReadyTimeout) 894 defer cancel() 895 _, err = watchtools.Until(ctxUntil, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { 896 if svc, ok := event.Object.(*corev1.Service); ok { 897 found := svc.ObjectMeta.Name == service.ObjectMeta.Name && 898 svc.ObjectMeta.Namespace == ns.Name && 899 svc.Labels["test-service"] == "patched" 900 if !found { 901 t.Logf("observed Service %v in namespace %v with labels: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels) 902 return false, nil 903 } 904 t.Logf("Found Service %v in namespace %v with labels: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels) 905 return found, nil 906 } 907 t.Logf("Observed event: %+v", event.Object) 908 return false, nil 909 }) 910 if err != nil { 911 t.Fatalf("failed to locate Service %v in namespace %v", service.ObjectMeta.Name, ns) 912 } 913 914 t.Logf("Service %s patched", testSvcName) 915 916 t.Log("deleting the service") 917 err = client.CoreV1().Services(ns.Name).Delete(ctx, testSvcName, metav1.DeleteOptions{}) 918 if err != nil { 919 t.Fatalf("failed to delete the Service. %v", err) 920 } 921 922 t.Log("watching for the Service to be deleted") 923 ctxUntil, cancel = context.WithTimeout(ctx, svcReadyTimeout) 924 defer cancel() 925 _, err = watchtools.Until(ctxUntil, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { 926 switch event.Type { 927 case watch.Deleted: 928 if svc, ok := event.Object.(*corev1.Service); ok { 929 found := svc.ObjectMeta.Name == service.ObjectMeta.Name && 930 svc.ObjectMeta.Namespace == ns.Name && 931 svc.Labels["test-service-static"] == "true" 932 if !found { 933 t.Logf("observed Service %v in namespace %v with labels: %v & annotations: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels, svc.Annotations) 934 return false, nil 935 } 936 t.Logf("Found Service %v in namespace %v with labels: %v & annotations: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels, svc.Annotations) 937 return found, nil 938 } 939 default: 940 t.Logf("Observed event: %+v", event.Type) 941 } 942 return false, nil 943 }) 944 if err != nil { 945 t.Fatalf("failed to delete Service %v in namespace %v", service.ObjectMeta.Name, ns) 946 } 947 t.Logf("Service %s deleted", testSvcName) 948 }