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  }