k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/framework/service/resource.go (about) 1 /* 2 Copyright 2019 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 "context" 21 "fmt" 22 "time" 23 24 v1 "k8s.io/api/core/v1" 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/labels" 28 "k8s.io/apimachinery/pkg/util/intstr" 29 clientset "k8s.io/client-go/kubernetes" 30 restclient "k8s.io/client-go/rest" 31 "k8s.io/kubernetes/test/e2e/framework" 32 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 33 testutils "k8s.io/kubernetes/test/utils" 34 ) 35 36 // GetServicesProxyRequest returns a request for a service proxy. 37 func GetServicesProxyRequest(c clientset.Interface, request *restclient.Request) (*restclient.Request, error) { 38 return request.Resource("services").SubResource("proxy"), nil 39 } 40 41 // CreateServiceSpec returns a Service object for testing. 42 func CreateServiceSpec(serviceName, externalName string, isHeadless bool, selector map[string]string) *v1.Service { 43 headlessService := &v1.Service{ 44 ObjectMeta: metav1.ObjectMeta{ 45 Name: serviceName, 46 }, 47 Spec: v1.ServiceSpec{ 48 Selector: selector, 49 }, 50 } 51 if externalName != "" { 52 headlessService.Spec.Type = v1.ServiceTypeExternalName 53 headlessService.Spec.ExternalName = externalName 54 } else { 55 headlessService.Spec.Ports = []v1.ServicePort{ 56 {Port: 80, Name: "http", Protocol: v1.ProtocolTCP}, 57 } 58 } 59 if isHeadless { 60 headlessService.Spec.ClusterIP = "None" 61 } 62 return headlessService 63 } 64 65 // UpdateService fetches a service, calls the update function on it, 66 // and then attempts to send the updated service. It retries up to 2 67 // times in the face of timeouts and conflicts. 68 func UpdateService(ctx context.Context, c clientset.Interface, namespace, serviceName string, update func(*v1.Service)) (*v1.Service, error) { 69 var service *v1.Service 70 var err error 71 for i := 0; i < 3; i++ { 72 service, err = c.CoreV1().Services(namespace).Get(ctx, serviceName, metav1.GetOptions{}) 73 if err != nil { 74 return service, err 75 } 76 77 update(service) 78 79 service, err = c.CoreV1().Services(namespace).Update(ctx, service, metav1.UpdateOptions{}) 80 81 if !apierrors.IsConflict(err) && !apierrors.IsServerTimeout(err) { 82 return service, err 83 } 84 } 85 return service, err 86 } 87 88 // CleanupServiceResources cleans up service Type=LoadBalancer resources. 89 func CleanupServiceResources(ctx context.Context, c clientset.Interface, loadBalancerName, region, zone string) { 90 framework.TestContext.CloudConfig.Provider.CleanupServiceResources(ctx, c, loadBalancerName, region, zone) 91 } 92 93 // GetIngressPoint returns a host on which ingress serves. 94 func GetIngressPoint(ing *v1.LoadBalancerIngress) string { 95 host := ing.IP 96 if host == "" { 97 host = ing.Hostname 98 } 99 return host 100 } 101 102 // GetServiceLoadBalancerCreationTimeout returns a timeout value for creating a load balancer of a service. 103 func GetServiceLoadBalancerCreationTimeout(ctx context.Context, cs clientset.Interface) time.Duration { 104 nodes, err := e2enode.GetReadySchedulableNodes(ctx, cs) 105 framework.ExpectNoError(err) 106 if len(nodes.Items) > LargeClusterMinNodesNumber { 107 return loadBalancerCreateTimeoutLarge 108 } 109 return loadBalancerCreateTimeoutDefault 110 } 111 112 // GetServiceLoadBalancerPropagationTimeout returns a timeout value for propagating a load balancer of a service. 113 func GetServiceLoadBalancerPropagationTimeout(ctx context.Context, cs clientset.Interface) time.Duration { 114 nodes, err := e2enode.GetReadySchedulableNodes(ctx, cs) 115 framework.ExpectNoError(err) 116 if len(nodes.Items) > LargeClusterMinNodesNumber { 117 return loadBalancerPropagationTimeoutLarge 118 } 119 return loadBalancerPropagationTimeoutDefault 120 } 121 122 // CreateServiceForSimpleAppWithPods is a convenience wrapper to create a service and its matching pods all at once. 123 func CreateServiceForSimpleAppWithPods(ctx context.Context, c clientset.Interface, contPort int, svcPort int, namespace, appName string, podSpec func(n v1.Node) v1.PodSpec, count int, block bool) (*v1.Service, error) { 124 var err error 125 theService := CreateServiceForSimpleApp(ctx, c, contPort, svcPort, namespace, appName) 126 e2enode.CreatePodsPerNodeForSimpleApp(ctx, c, namespace, appName, podSpec, count) 127 if block { 128 err = testutils.WaitForPodsWithLabelRunning(c, namespace, labels.SelectorFromSet(labels.Set(theService.Spec.Selector))) 129 } 130 return theService, err 131 } 132 133 // CreateServiceForSimpleApp returns a service that selects/exposes pods (send -1 ports if no exposure needed) with an app label. 134 func CreateServiceForSimpleApp(ctx context.Context, c clientset.Interface, contPort, svcPort int, namespace, appName string) *v1.Service { 135 if appName == "" { 136 panic(fmt.Sprintf("no app name provided")) 137 } 138 139 serviceSelector := map[string]string{ 140 "app": appName + "-pod", 141 } 142 143 // For convenience, user sending ports are optional. 144 portsFunc := func() []v1.ServicePort { 145 if contPort < 1 || svcPort < 1 { 146 return nil 147 } 148 return []v1.ServicePort{{ 149 Protocol: v1.ProtocolTCP, 150 Port: int32(svcPort), 151 TargetPort: intstr.FromInt32(int32(contPort)), 152 }} 153 } 154 framework.Logf("Creating a service-for-%v for selecting app=%v-pod", appName, appName) 155 service, err := c.CoreV1().Services(namespace).Create(ctx, &v1.Service{ 156 ObjectMeta: metav1.ObjectMeta{ 157 Name: "service-for-" + appName, 158 Labels: map[string]string{ 159 "app": appName + "-service", 160 }, 161 }, 162 Spec: v1.ServiceSpec{ 163 Ports: portsFunc(), 164 Selector: serviceSelector, 165 }, 166 }, metav1.CreateOptions{}) 167 framework.ExpectNoError(err) 168 return service 169 }