k8s.io/kubernetes@v1.29.3/test/e2e/common/util.go (about) 1 /* 2 Copyright 2016 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 common 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "text/template" 24 "time" 25 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/util/intstr" 29 "k8s.io/apimachinery/pkg/util/sets" 30 "k8s.io/apimachinery/pkg/util/wait" 31 clientset "k8s.io/client-go/kubernetes" 32 "k8s.io/kubernetes/test/e2e/framework" 33 e2erc "k8s.io/kubernetes/test/e2e/framework/rc" 34 imageutils "k8s.io/kubernetes/test/utils/image" 35 36 "github.com/onsi/ginkgo/v2" 37 ) 38 39 // TODO: Cleanup this file. 40 41 // Suite represents test suite. 42 type Suite string 43 44 const ( 45 // E2E represents a test suite for e2e. 46 E2E Suite = "e2e" 47 // NodeE2E represents a test suite for node e2e. 48 NodeE2E Suite = "node e2e" 49 ) 50 51 // CurrentSuite represents current test suite. 52 var CurrentSuite Suite 53 54 // PrePulledImages are a list of images used in e2e/common tests. These images should be prepulled 55 // before tests starts, so that the tests won't fail due image pulling flakes. 56 // Currently, this is only used by node e2e test and E2E tests. 57 // See also updateImageAllowList() in ../../e2e_node/image_list.go 58 // TODO(random-liu): Change the image puller pod to use similar mechanism. 59 var PrePulledImages = sets.NewString( 60 imageutils.GetE2EImage(imageutils.Agnhost), 61 imageutils.GetE2EImage(imageutils.BusyBox), 62 imageutils.GetE2EImage(imageutils.IpcUtils), 63 imageutils.GetE2EImage(imageutils.Nginx), 64 imageutils.GetE2EImage(imageutils.Httpd), 65 imageutils.GetE2EImage(imageutils.VolumeNFSServer), 66 imageutils.GetE2EImage(imageutils.NonRoot), 67 ) 68 69 // WindowsPrePulledImages are a list of images used in e2e/common tests. These images should be prepulled 70 // before tests starts, so that the tests won't fail due image pulling flakes. These images also have 71 // Windows support. Currently, this is only used by E2E tests. 72 var WindowsPrePulledImages = sets.NewString( 73 imageutils.GetE2EImage(imageutils.Agnhost), 74 imageutils.GetE2EImage(imageutils.BusyBox), 75 imageutils.GetE2EImage(imageutils.Nginx), 76 imageutils.GetE2EImage(imageutils.Httpd), 77 ) 78 79 type testImagesStruct struct { 80 AgnhostImage string 81 BusyBoxImage string 82 KittenImage string 83 NautilusImage string 84 NginxImage string 85 NginxNewImage string 86 HttpdImage string 87 HttpdNewImage string 88 PauseImage string 89 RedisImage string 90 } 91 92 var testImages testImagesStruct 93 94 func init() { 95 testImages = testImagesStruct{ 96 imageutils.GetE2EImage(imageutils.Agnhost), 97 imageutils.GetE2EImage(imageutils.BusyBox), 98 imageutils.GetE2EImage(imageutils.Kitten), 99 imageutils.GetE2EImage(imageutils.Nautilus), 100 imageutils.GetE2EImage(imageutils.Nginx), 101 imageutils.GetE2EImage(imageutils.NginxNew), 102 imageutils.GetE2EImage(imageutils.Httpd), 103 imageutils.GetE2EImage(imageutils.HttpdNew), 104 imageutils.GetE2EImage(imageutils.Pause), 105 imageutils.GetE2EImage(imageutils.Redis), 106 } 107 } 108 109 // SubstituteImageName replaces image name in content. 110 func SubstituteImageName(content string) string { 111 contentWithImageName := new(bytes.Buffer) 112 tmpl, err := template.New("imagemanifest").Parse(content) 113 if err != nil { 114 framework.Failf("Failed Parse the template: %v", err) 115 } 116 err = tmpl.Execute(contentWithImageName, testImages) 117 if err != nil { 118 framework.Failf("Failed executing template: %v", err) 119 } 120 return contentWithImageName.String() 121 } 122 123 func svcByName(name string, port int) *v1.Service { 124 return &v1.Service{ 125 ObjectMeta: metav1.ObjectMeta{ 126 Name: name, 127 }, 128 Spec: v1.ServiceSpec{ 129 Type: v1.ServiceTypeNodePort, 130 Selector: map[string]string{ 131 "name": name, 132 }, 133 Ports: []v1.ServicePort{{ 134 Port: int32(port), 135 TargetPort: intstr.FromInt32(int32(port)), 136 }}, 137 }, 138 } 139 } 140 141 // NewSVCByName creates a service by name. 142 func NewSVCByName(c clientset.Interface, ns, name string) error { 143 const testPort = 9376 144 _, err := c.CoreV1().Services(ns).Create(context.TODO(), svcByName(name, testPort), metav1.CreateOptions{}) 145 return err 146 } 147 148 // NewRCByName creates a replication controller with a selector by name of name. 149 func NewRCByName(c clientset.Interface, ns, name string, replicas int32, gracePeriod *int64, containerArgs []string) (*v1.ReplicationController, error) { 150 ginkgo.By(fmt.Sprintf("creating replication controller %s", name)) 151 152 if containerArgs == nil { 153 containerArgs = []string{"serve-hostname"} 154 } 155 156 return c.CoreV1().ReplicationControllers(ns).Create(context.TODO(), rcByNamePort( 157 name, replicas, framework.ServeHostnameImage, containerArgs, 9376, v1.ProtocolTCP, map[string]string{}, gracePeriod), metav1.CreateOptions{}) 158 } 159 160 // RestartNodes restarts specific nodes. 161 func RestartNodes(c clientset.Interface, nodes []v1.Node) error { 162 // Build mapping from zone to nodes in that zone. 163 nodeNamesByZone := make(map[string][]string) 164 for i := range nodes { 165 node := &nodes[i] 166 zone := framework.TestContext.CloudConfig.Zone 167 if z, ok := node.Labels[v1.LabelFailureDomainBetaZone]; ok { 168 zone = z 169 } else if z, ok := node.Labels[v1.LabelTopologyZone]; ok { 170 zone = z 171 } 172 nodeNamesByZone[zone] = append(nodeNamesByZone[zone], node.Name) 173 } 174 175 // Reboot the nodes. 176 for zone, nodeNames := range nodeNamesByZone { 177 args := []string{ 178 "compute", 179 fmt.Sprintf("--project=%s", framework.TestContext.CloudConfig.ProjectID), 180 "instances", 181 "reset", 182 } 183 args = append(args, nodeNames...) 184 args = append(args, fmt.Sprintf("--zone=%s", zone)) 185 stdout, stderr, err := framework.RunCmd("gcloud", args...) 186 if err != nil { 187 return fmt.Errorf("error restarting nodes: %s\nstdout: %s\nstderr: %s", err, stdout, stderr) 188 } 189 } 190 191 // Wait for their boot IDs to change. 192 for i := range nodes { 193 node := &nodes[i] 194 if err := wait.Poll(30*time.Second, framework.RestartNodeReadyAgainTimeout, func() (bool, error) { 195 newNode, err := c.CoreV1().Nodes().Get(context.TODO(), node.Name, metav1.GetOptions{}) 196 if err != nil { 197 return false, fmt.Errorf("error getting node info after reboot: %w", err) 198 } 199 return node.Status.NodeInfo.BootID != newNode.Status.NodeInfo.BootID, nil 200 }); err != nil { 201 return fmt.Errorf("error waiting for node %s boot ID to change: %w", node.Name, err) 202 } 203 } 204 return nil 205 } 206 207 // rcByNamePort returns a ReplicationController with specified name and port 208 func rcByNamePort(name string, replicas int32, image string, containerArgs []string, port int, protocol v1.Protocol, 209 labels map[string]string, gracePeriod *int64) *v1.ReplicationController { 210 211 return e2erc.ByNameContainer(name, replicas, labels, v1.Container{ 212 Name: name, 213 Image: image, 214 Args: containerArgs, 215 Ports: []v1.ContainerPort{{ContainerPort: int32(port), Protocol: protocol}}, 216 }, gracePeriod) 217 }