k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/utils/runners.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 utils
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"math"
    23  	"os"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	apps "k8s.io/api/apps/v1"
    29  	v1 "k8s.io/api/core/v1"
    30  	storagev1 "k8s.io/api/storage/v1"
    31  	storagev1beta1 "k8s.io/api/storage/v1beta1"
    32  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    33  	"k8s.io/apimachinery/pkg/api/resource"
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/fields"
    36  	"k8s.io/apimachinery/pkg/labels"
    37  	"k8s.io/apimachinery/pkg/runtime/schema"
    38  	"k8s.io/apimachinery/pkg/types"
    39  	"k8s.io/apimachinery/pkg/util/json"
    40  	"k8s.io/apimachinery/pkg/util/sets"
    41  	"k8s.io/apimachinery/pkg/util/strategicpatch"
    42  	"k8s.io/apimachinery/pkg/util/uuid"
    43  	"k8s.io/apimachinery/pkg/util/wait"
    44  	clientset "k8s.io/client-go/kubernetes"
    45  	scaleclient "k8s.io/client-go/scale"
    46  	"k8s.io/client-go/util/workqueue"
    47  	api "k8s.io/kubernetes/pkg/apis/core"
    48  	extensionsinternal "k8s.io/kubernetes/pkg/apis/extensions"
    49  	"k8s.io/utils/pointer"
    50  
    51  	"k8s.io/klog/v2"
    52  )
    53  
    54  const (
    55  	// String used to mark pod deletion
    56  	nonExist = "NonExist"
    57  )
    58  
    59  func removePtr(replicas *int32) int32 {
    60  	if replicas == nil {
    61  		return 0
    62  	}
    63  	return *replicas
    64  }
    65  
    66  func waitUntilPodIsScheduled(ctx context.Context, c clientset.Interface, name, namespace string, timeout time.Duration) (*v1.Pod, error) {
    67  	// Wait until it's scheduled
    68  	p, err := c.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{ResourceVersion: "0"})
    69  	if err == nil && p.Spec.NodeName != "" {
    70  		return p, nil
    71  	}
    72  	pollingPeriod := 200 * time.Millisecond
    73  	startTime := time.Now()
    74  	for startTime.Add(timeout).After(time.Now()) && ctx.Err() == nil {
    75  		time.Sleep(pollingPeriod)
    76  		p, err := c.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{ResourceVersion: "0"})
    77  		if err == nil && p.Spec.NodeName != "" {
    78  			return p, nil
    79  		}
    80  	}
    81  	return nil, fmt.Errorf("timed out after %v when waiting for pod %v/%v to start", timeout, namespace, name)
    82  }
    83  
    84  func RunPodAndGetNodeName(ctx context.Context, c clientset.Interface, pod *v1.Pod, timeout time.Duration) (string, error) {
    85  	name := pod.Name
    86  	namespace := pod.Namespace
    87  	if err := CreatePodWithRetries(c, namespace, pod); err != nil {
    88  		return "", err
    89  	}
    90  	p, err := waitUntilPodIsScheduled(ctx, c, name, namespace, timeout)
    91  	if err != nil {
    92  		return "", err
    93  	}
    94  	return p.Spec.NodeName, nil
    95  }
    96  
    97  type RunObjectConfig interface {
    98  	Run() error
    99  	GetName() string
   100  	GetNamespace() string
   101  	GetKind() schema.GroupKind
   102  	GetClient() clientset.Interface
   103  	GetScalesGetter() scaleclient.ScalesGetter
   104  	SetClient(clientset.Interface)
   105  	SetScalesClient(scaleclient.ScalesGetter)
   106  	GetReplicas() int
   107  	GetLabelValue(string) (string, bool)
   108  	GetGroupResource() schema.GroupResource
   109  	GetGroupVersionResource() schema.GroupVersionResource
   110  }
   111  
   112  type RCConfig struct {
   113  	Affinity                      *v1.Affinity
   114  	Client                        clientset.Interface
   115  	ScalesGetter                  scaleclient.ScalesGetter
   116  	Image                         string
   117  	Command                       []string
   118  	Name                          string
   119  	Namespace                     string
   120  	PollInterval                  time.Duration
   121  	Timeout                       time.Duration
   122  	PodStatusFile                 *os.File
   123  	Replicas                      int
   124  	CpuRequest                    int64 // millicores
   125  	CpuLimit                      int64 // millicores
   126  	MemRequest                    int64 // bytes
   127  	MemLimit                      int64 // bytes
   128  	GpuLimit                      int64 // count
   129  	ReadinessProbe                *v1.Probe
   130  	DNSPolicy                     *v1.DNSPolicy
   131  	PriorityClassName             string
   132  	TerminationGracePeriodSeconds *int64
   133  	Lifecycle                     *v1.Lifecycle
   134  	SchedulerName                 string
   135  
   136  	// Env vars, set the same for every pod.
   137  	Env map[string]string
   138  
   139  	// Extra labels and annotations added to every pod.
   140  	Labels      map[string]string
   141  	Annotations map[string]string
   142  
   143  	// Node selector for pods in the RC.
   144  	NodeSelector map[string]string
   145  
   146  	// Tolerations for pods in the RC.
   147  	Tolerations []v1.Toleration
   148  
   149  	// Ports to declare in the container (map of name to containerPort).
   150  	Ports map[string]int
   151  	// Ports to declare in the container as host and container ports.
   152  	HostPorts map[string]int
   153  
   154  	Volumes      []v1.Volume
   155  	VolumeMounts []v1.VolumeMount
   156  
   157  	// Pointer to a list of pods; if non-nil, will be set to a list of pods
   158  	// created by this RC by RunRC.
   159  	CreatedPods *[]*v1.Pod
   160  
   161  	// Maximum allowable container failures. If exceeded, RunRC returns an error.
   162  	// Defaults to replicas*0.1 if unspecified.
   163  	MaxContainerFailures *int
   164  	// Maximum allowed pod deletions count. If exceeded, RunRC returns an error.
   165  	// Defaults to 0.
   166  	MaxAllowedPodDeletions int
   167  
   168  	// If set to false starting RC will print progress, otherwise only errors will be printed.
   169  	Silent bool
   170  
   171  	// If set this function will be used to print log lines instead of klog.
   172  	LogFunc func(fmt string, args ...interface{})
   173  	// If set those functions will be used to gather data from Nodes - in integration tests where no
   174  	// kubelets are running those variables should be nil.
   175  	NodeDumpFunc      func(ctx context.Context, c clientset.Interface, nodeNames []string, logFunc func(fmt string, args ...interface{}))
   176  	ContainerDumpFunc func(ctx context.Context, c clientset.Interface, ns string, logFunc func(ftm string, args ...interface{}))
   177  
   178  	// Names of the secrets and configmaps to mount.
   179  	SecretNames    []string
   180  	ConfigMapNames []string
   181  
   182  	ServiceAccountTokenProjections int
   183  
   184  	// Additional containers to run in the pod
   185  	AdditionalContainers []v1.Container
   186  
   187  	// Security context for created pods
   188  	SecurityContext *v1.SecurityContext
   189  }
   190  
   191  func (rc *RCConfig) RCConfigLog(fmt string, args ...interface{}) {
   192  	if rc.LogFunc != nil {
   193  		rc.LogFunc(fmt, args...)
   194  	}
   195  	klog.Infof(fmt, args...)
   196  }
   197  
   198  type DeploymentConfig struct {
   199  	RCConfig
   200  }
   201  
   202  type ReplicaSetConfig struct {
   203  	RCConfig
   204  }
   205  
   206  type JobConfig struct {
   207  	RCConfig
   208  }
   209  
   210  // podInfo contains pod information useful for debugging e2e tests.
   211  type podInfo struct {
   212  	oldHostname string
   213  	oldPhase    string
   214  	hostname    string
   215  	phase       string
   216  }
   217  
   218  // podDiff is a map of pod name to podInfos
   219  type podDiff map[string]*podInfo
   220  
   221  // Print formats and prints the give podDiff.
   222  func (p podDiff) String(ignorePhases sets.String) string {
   223  	ret := ""
   224  	for name, info := range p {
   225  		if ignorePhases.Has(info.phase) {
   226  			continue
   227  		}
   228  		if info.phase == nonExist {
   229  			ret += fmt.Sprintf("Pod %v was deleted, had phase %v and host %v\n", name, info.oldPhase, info.oldHostname)
   230  			continue
   231  		}
   232  		phaseChange, hostChange := false, false
   233  		msg := fmt.Sprintf("Pod %v ", name)
   234  		if info.oldPhase != info.phase {
   235  			phaseChange = true
   236  			if info.oldPhase == nonExist {
   237  				msg += fmt.Sprintf("in phase %v ", info.phase)
   238  			} else {
   239  				msg += fmt.Sprintf("went from phase: %v -> %v ", info.oldPhase, info.phase)
   240  			}
   241  		}
   242  		if info.oldHostname != info.hostname {
   243  			hostChange = true
   244  			if info.oldHostname == nonExist || info.oldHostname == "" {
   245  				msg += fmt.Sprintf("assigned host %v ", info.hostname)
   246  			} else {
   247  				msg += fmt.Sprintf("went from host: %v -> %v ", info.oldHostname, info.hostname)
   248  			}
   249  		}
   250  		if phaseChange || hostChange {
   251  			ret += msg + "\n"
   252  		}
   253  	}
   254  	return ret
   255  }
   256  
   257  // DeletedPods returns a slice of pods that were present at the beginning
   258  // and then disappeared.
   259  func (p podDiff) DeletedPods() []string {
   260  	var deletedPods []string
   261  	for podName, podInfo := range p {
   262  		if podInfo.hostname == nonExist {
   263  			deletedPods = append(deletedPods, podName)
   264  		}
   265  	}
   266  	return deletedPods
   267  }
   268  
   269  // diff computes a podDiff given 2 lists of pods.
   270  func diff(oldPods []*v1.Pod, curPods []*v1.Pod) podDiff {
   271  	podInfoMap := podDiff{}
   272  
   273  	// New pods will show up in the curPods list but not in oldPods. They have oldhostname/phase == nonexist.
   274  	for _, pod := range curPods {
   275  		podInfoMap[pod.Name] = &podInfo{hostname: pod.Spec.NodeName, phase: string(pod.Status.Phase), oldHostname: nonExist, oldPhase: nonExist}
   276  	}
   277  
   278  	// Deleted pods will show up in the oldPods list but not in curPods. They have a hostname/phase == nonexist.
   279  	for _, pod := range oldPods {
   280  		if info, ok := podInfoMap[pod.Name]; ok {
   281  			info.oldHostname, info.oldPhase = pod.Spec.NodeName, string(pod.Status.Phase)
   282  		} else {
   283  			podInfoMap[pod.Name] = &podInfo{hostname: nonExist, phase: nonExist, oldHostname: pod.Spec.NodeName, oldPhase: string(pod.Status.Phase)}
   284  		}
   285  	}
   286  	return podInfoMap
   287  }
   288  
   289  // RunDeployment Launches (and verifies correctness) of a Deployment
   290  // and will wait for all pods it spawns to become "Running".
   291  // It's the caller's responsibility to clean up externally (i.e. use the
   292  // namespace lifecycle for handling Cleanup).
   293  func RunDeployment(ctx context.Context, config DeploymentConfig) error {
   294  	err := config.create()
   295  	if err != nil {
   296  		return err
   297  	}
   298  	return config.start(ctx)
   299  }
   300  
   301  func (config *DeploymentConfig) Run(ctx context.Context) error {
   302  	return RunDeployment(ctx, *config)
   303  }
   304  
   305  func (config *DeploymentConfig) GetKind() schema.GroupKind {
   306  	return extensionsinternal.Kind("Deployment")
   307  }
   308  
   309  func (config *DeploymentConfig) GetGroupResource() schema.GroupResource {
   310  	return extensionsinternal.Resource("deployments")
   311  }
   312  
   313  func (config *DeploymentConfig) GetGroupVersionResource() schema.GroupVersionResource {
   314  	return extensionsinternal.SchemeGroupVersion.WithResource("deployments")
   315  }
   316  
   317  func (config *DeploymentConfig) create() error {
   318  	deployment := &apps.Deployment{
   319  		ObjectMeta: metav1.ObjectMeta{
   320  			Name: config.Name,
   321  		},
   322  		Spec: apps.DeploymentSpec{
   323  			Replicas: pointer.Int32(int32(config.Replicas)),
   324  			Selector: &metav1.LabelSelector{
   325  				MatchLabels: map[string]string{
   326  					"name": config.Name,
   327  				},
   328  			},
   329  			Template: v1.PodTemplateSpec{
   330  				ObjectMeta: metav1.ObjectMeta{
   331  					Labels:      map[string]string{"name": config.Name},
   332  					Annotations: config.Annotations,
   333  				},
   334  				Spec: v1.PodSpec{
   335  					Affinity:                      config.Affinity,
   336  					TerminationGracePeriodSeconds: config.getTerminationGracePeriodSeconds(nil),
   337  					Containers: []v1.Container{
   338  						{
   339  							Name:            config.Name,
   340  							Image:           config.Image,
   341  							Command:         config.Command,
   342  							Ports:           []v1.ContainerPort{{ContainerPort: 80}},
   343  							Lifecycle:       config.Lifecycle,
   344  							SecurityContext: config.SecurityContext,
   345  						},
   346  					},
   347  				},
   348  			},
   349  		},
   350  	}
   351  
   352  	if len(config.AdditionalContainers) > 0 {
   353  		deployment.Spec.Template.Spec.Containers = append(deployment.Spec.Template.Spec.Containers, config.AdditionalContainers...)
   354  	}
   355  
   356  	if len(config.SecretNames) > 0 {
   357  		attachSecrets(&deployment.Spec.Template, config.SecretNames)
   358  	}
   359  	if len(config.ConfigMapNames) > 0 {
   360  		attachConfigMaps(&deployment.Spec.Template, config.ConfigMapNames)
   361  	}
   362  
   363  	for i := 0; i < config.ServiceAccountTokenProjections; i++ {
   364  		attachServiceAccountTokenProjection(&deployment.Spec.Template, fmt.Sprintf("tok-%d", i))
   365  	}
   366  
   367  	config.applyTo(&deployment.Spec.Template)
   368  
   369  	if err := CreateDeploymentWithRetries(config.Client, config.Namespace, deployment); err != nil {
   370  		return fmt.Errorf("error creating deployment: %v", err)
   371  	}
   372  	config.RCConfigLog("Created deployment with name: %v, namespace: %v, replica count: %v", deployment.Name, config.Namespace, removePtr(deployment.Spec.Replicas))
   373  	return nil
   374  }
   375  
   376  // RunReplicaSet launches (and verifies correctness) of a ReplicaSet
   377  // and waits until all the pods it launches to reach the "Running" state.
   378  // It's the caller's responsibility to clean up externally (i.e. use the
   379  // namespace lifecycle for handling Cleanup).
   380  func RunReplicaSet(ctx context.Context, config ReplicaSetConfig) error {
   381  	err := config.create()
   382  	if err != nil {
   383  		return err
   384  	}
   385  	return config.start(ctx)
   386  }
   387  
   388  func (config *ReplicaSetConfig) Run(ctx context.Context) error {
   389  	return RunReplicaSet(ctx, *config)
   390  }
   391  
   392  func (config *ReplicaSetConfig) GetKind() schema.GroupKind {
   393  	return extensionsinternal.Kind("ReplicaSet")
   394  }
   395  
   396  func (config *ReplicaSetConfig) GetGroupResource() schema.GroupResource {
   397  	return extensionsinternal.Resource("replicasets")
   398  }
   399  
   400  func (config *ReplicaSetConfig) GetGroupVersionResource() schema.GroupVersionResource {
   401  	return extensionsinternal.SchemeGroupVersion.WithResource("replicasets")
   402  }
   403  
   404  func (config *ReplicaSetConfig) create() error {
   405  	rs := &apps.ReplicaSet{
   406  		ObjectMeta: metav1.ObjectMeta{
   407  			Name: config.Name,
   408  		},
   409  		Spec: apps.ReplicaSetSpec{
   410  			Replicas: pointer.Int32(int32(config.Replicas)),
   411  			Selector: &metav1.LabelSelector{
   412  				MatchLabels: map[string]string{
   413  					"name": config.Name,
   414  				},
   415  			},
   416  			Template: v1.PodTemplateSpec{
   417  				ObjectMeta: metav1.ObjectMeta{
   418  					Labels:      map[string]string{"name": config.Name},
   419  					Annotations: config.Annotations,
   420  				},
   421  				Spec: v1.PodSpec{
   422  					Affinity:                      config.Affinity,
   423  					TerminationGracePeriodSeconds: config.getTerminationGracePeriodSeconds(nil),
   424  					Containers: []v1.Container{
   425  						{
   426  							Name:            config.Name,
   427  							Image:           config.Image,
   428  							Command:         config.Command,
   429  							Ports:           []v1.ContainerPort{{ContainerPort: 80}},
   430  							Lifecycle:       config.Lifecycle,
   431  							SecurityContext: config.SecurityContext,
   432  						},
   433  					},
   434  				},
   435  			},
   436  		},
   437  	}
   438  
   439  	if len(config.AdditionalContainers) > 0 {
   440  		rs.Spec.Template.Spec.Containers = append(rs.Spec.Template.Spec.Containers, config.AdditionalContainers...)
   441  	}
   442  
   443  	if len(config.SecretNames) > 0 {
   444  		attachSecrets(&rs.Spec.Template, config.SecretNames)
   445  	}
   446  	if len(config.ConfigMapNames) > 0 {
   447  		attachConfigMaps(&rs.Spec.Template, config.ConfigMapNames)
   448  	}
   449  
   450  	config.applyTo(&rs.Spec.Template)
   451  
   452  	if err := CreateReplicaSetWithRetries(config.Client, config.Namespace, rs); err != nil {
   453  		return fmt.Errorf("error creating replica set: %v", err)
   454  	}
   455  	config.RCConfigLog("Created replica set with name: %v, namespace: %v, replica count: %v", rs.Name, config.Namespace, removePtr(rs.Spec.Replicas))
   456  	return nil
   457  }
   458  
   459  // RunRC Launches (and verifies correctness) of a Replication Controller
   460  // and will wait for all pods it spawns to become "Running".
   461  // It's the caller's responsibility to clean up externally (i.e. use the
   462  // namespace lifecycle for handling Cleanup).
   463  func RunRC(ctx context.Context, config RCConfig) error {
   464  	err := config.create()
   465  	if err != nil {
   466  		return err
   467  	}
   468  	return config.start(ctx)
   469  }
   470  
   471  func (config *RCConfig) Run(ctx context.Context) error {
   472  	return RunRC(ctx, *config)
   473  }
   474  
   475  func (config *RCConfig) GetName() string {
   476  	return config.Name
   477  }
   478  
   479  func (config *RCConfig) GetNamespace() string {
   480  	return config.Namespace
   481  }
   482  
   483  func (config *RCConfig) GetKind() schema.GroupKind {
   484  	return api.Kind("ReplicationController")
   485  }
   486  
   487  func (config *RCConfig) GetGroupResource() schema.GroupResource {
   488  	return api.Resource("replicationcontrollers")
   489  }
   490  
   491  func (config *RCConfig) GetGroupVersionResource() schema.GroupVersionResource {
   492  	return api.SchemeGroupVersion.WithResource("replicationcontrollers")
   493  }
   494  
   495  func (config *RCConfig) GetClient() clientset.Interface {
   496  	return config.Client
   497  }
   498  
   499  func (config *RCConfig) GetScalesGetter() scaleclient.ScalesGetter {
   500  	return config.ScalesGetter
   501  }
   502  
   503  func (config *RCConfig) SetClient(c clientset.Interface) {
   504  	config.Client = c
   505  }
   506  
   507  func (config *RCConfig) SetScalesClient(getter scaleclient.ScalesGetter) {
   508  	config.ScalesGetter = getter
   509  }
   510  
   511  func (config *RCConfig) GetReplicas() int {
   512  	return config.Replicas
   513  }
   514  
   515  func (config *RCConfig) GetLabelValue(key string) (string, bool) {
   516  	value, found := config.Labels[key]
   517  	return value, found
   518  }
   519  
   520  func (config *RCConfig) create() error {
   521  	dnsDefault := v1.DNSDefault
   522  	if config.DNSPolicy == nil {
   523  		config.DNSPolicy = &dnsDefault
   524  	}
   525  	one := int64(1)
   526  	rc := &v1.ReplicationController{
   527  		ObjectMeta: metav1.ObjectMeta{
   528  			Name: config.Name,
   529  		},
   530  		Spec: v1.ReplicationControllerSpec{
   531  			Replicas: pointer.Int32(int32(config.Replicas)),
   532  			Selector: map[string]string{
   533  				"name": config.Name,
   534  			},
   535  			Template: &v1.PodTemplateSpec{
   536  				ObjectMeta: metav1.ObjectMeta{
   537  					Labels:      map[string]string{"name": config.Name},
   538  					Annotations: config.Annotations,
   539  				},
   540  				Spec: v1.PodSpec{
   541  					SchedulerName: config.SchedulerName,
   542  					Affinity:      config.Affinity,
   543  					Containers: []v1.Container{
   544  						{
   545  							Name:            config.Name,
   546  							Image:           config.Image,
   547  							Command:         config.Command,
   548  							Ports:           []v1.ContainerPort{{ContainerPort: 80}},
   549  							ReadinessProbe:  config.ReadinessProbe,
   550  							Lifecycle:       config.Lifecycle,
   551  							SecurityContext: config.SecurityContext,
   552  						},
   553  					},
   554  					DNSPolicy:                     *config.DNSPolicy,
   555  					NodeSelector:                  config.NodeSelector,
   556  					Tolerations:                   config.Tolerations,
   557  					TerminationGracePeriodSeconds: config.getTerminationGracePeriodSeconds(&one),
   558  					PriorityClassName:             config.PriorityClassName,
   559  				},
   560  			},
   561  		},
   562  	}
   563  
   564  	if len(config.AdditionalContainers) > 0 {
   565  		rc.Spec.Template.Spec.Containers = append(rc.Spec.Template.Spec.Containers, config.AdditionalContainers...)
   566  	}
   567  
   568  	if len(config.SecretNames) > 0 {
   569  		attachSecrets(rc.Spec.Template, config.SecretNames)
   570  	}
   571  	if len(config.ConfigMapNames) > 0 {
   572  		attachConfigMaps(rc.Spec.Template, config.ConfigMapNames)
   573  	}
   574  
   575  	config.applyTo(rc.Spec.Template)
   576  
   577  	if err := CreateRCWithRetries(config.Client, config.Namespace, rc); err != nil {
   578  		return fmt.Errorf("error creating replication controller: %v", err)
   579  	}
   580  	config.RCConfigLog("Created replication controller with name: %v, namespace: %v, replica count: %v", rc.Name, config.Namespace, removePtr(rc.Spec.Replicas))
   581  	return nil
   582  }
   583  
   584  func (config *RCConfig) applyTo(template *v1.PodTemplateSpec) {
   585  	for k, v := range config.Env {
   586  		c := &template.Spec.Containers[0]
   587  		c.Env = append(c.Env, v1.EnvVar{Name: k, Value: v})
   588  	}
   589  	for k, v := range config.Labels {
   590  		template.ObjectMeta.Labels[k] = v
   591  	}
   592  	template.Spec.NodeSelector = make(map[string]string)
   593  	for k, v := range config.NodeSelector {
   594  		template.Spec.NodeSelector[k] = v
   595  	}
   596  	if config.Tolerations != nil {
   597  		template.Spec.Tolerations = append([]v1.Toleration{}, config.Tolerations...)
   598  	}
   599  	for k, v := range config.Ports {
   600  		c := &template.Spec.Containers[0]
   601  		c.Ports = append(c.Ports, v1.ContainerPort{Name: k, ContainerPort: int32(v)})
   602  	}
   603  	for k, v := range config.HostPorts {
   604  		c := &template.Spec.Containers[0]
   605  		c.Ports = append(c.Ports, v1.ContainerPort{Name: k, ContainerPort: int32(v), HostPort: int32(v)})
   606  	}
   607  	if config.CpuLimit > 0 || config.MemLimit > 0 || config.GpuLimit > 0 {
   608  		template.Spec.Containers[0].Resources.Limits = v1.ResourceList{}
   609  	}
   610  	if config.CpuLimit > 0 {
   611  		template.Spec.Containers[0].Resources.Limits[v1.ResourceCPU] = *resource.NewMilliQuantity(config.CpuLimit, resource.DecimalSI)
   612  	}
   613  	if config.MemLimit > 0 {
   614  		template.Spec.Containers[0].Resources.Limits[v1.ResourceMemory] = *resource.NewQuantity(config.MemLimit, resource.DecimalSI)
   615  	}
   616  	if config.CpuRequest > 0 || config.MemRequest > 0 {
   617  		template.Spec.Containers[0].Resources.Requests = v1.ResourceList{}
   618  	}
   619  	if config.CpuRequest > 0 {
   620  		template.Spec.Containers[0].Resources.Requests[v1.ResourceCPU] = *resource.NewMilliQuantity(config.CpuRequest, resource.DecimalSI)
   621  	}
   622  	if config.MemRequest > 0 {
   623  		template.Spec.Containers[0].Resources.Requests[v1.ResourceMemory] = *resource.NewQuantity(config.MemRequest, resource.DecimalSI)
   624  	}
   625  	if config.GpuLimit > 0 {
   626  		template.Spec.Containers[0].Resources.Limits["nvidia.com/gpu"] = *resource.NewQuantity(config.GpuLimit, resource.DecimalSI)
   627  	}
   628  	if config.Lifecycle != nil {
   629  		template.Spec.Containers[0].Lifecycle = config.Lifecycle
   630  	}
   631  	if len(config.Volumes) > 0 {
   632  		template.Spec.Volumes = config.Volumes
   633  	}
   634  	if len(config.VolumeMounts) > 0 {
   635  		template.Spec.Containers[0].VolumeMounts = config.VolumeMounts
   636  	}
   637  	if config.PriorityClassName != "" {
   638  		template.Spec.PriorityClassName = config.PriorityClassName
   639  	}
   640  }
   641  
   642  type RCStartupStatus struct {
   643  	Expected              int
   644  	Terminating           int
   645  	Running               int
   646  	RunningButNotReady    int
   647  	Waiting               int
   648  	Pending               int
   649  	Scheduled             int
   650  	Unknown               int
   651  	Inactive              int
   652  	FailedContainers      int
   653  	Created               []*v1.Pod
   654  	ContainerRestartNodes sets.String
   655  }
   656  
   657  func (s *RCStartupStatus) String(name string) string {
   658  	return fmt.Sprintf("%v Pods: %d out of %d created, %d running, %d pending, %d waiting, %d inactive, %d terminating, %d unknown, %d runningButNotReady ",
   659  		name, len(s.Created), s.Expected, s.Running, s.Pending, s.Waiting, s.Inactive, s.Terminating, s.Unknown, s.RunningButNotReady)
   660  }
   661  
   662  func computeRCStartupStatus(pods []*v1.Pod, expected int) RCStartupStatus {
   663  	startupStatus := RCStartupStatus{
   664  		Expected:              expected,
   665  		Created:               make([]*v1.Pod, 0, expected),
   666  		ContainerRestartNodes: sets.NewString(),
   667  	}
   668  	for _, p := range pods {
   669  		if p.DeletionTimestamp != nil {
   670  			startupStatus.Terminating++
   671  			continue
   672  		}
   673  		startupStatus.Created = append(startupStatus.Created, p)
   674  		if p.Status.Phase == v1.PodRunning {
   675  			ready := false
   676  			for _, c := range p.Status.Conditions {
   677  				if c.Type == v1.PodReady && c.Status == v1.ConditionTrue {
   678  					ready = true
   679  					break
   680  				}
   681  			}
   682  			if ready {
   683  				// Only count a pod is running when it is also ready.
   684  				startupStatus.Running++
   685  			} else {
   686  				startupStatus.RunningButNotReady++
   687  			}
   688  			for _, v := range FailedContainers(p) {
   689  				startupStatus.FailedContainers = startupStatus.FailedContainers + v.Restarts
   690  				startupStatus.ContainerRestartNodes.Insert(p.Spec.NodeName)
   691  			}
   692  		} else if p.Status.Phase == v1.PodPending {
   693  			if p.Spec.NodeName == "" {
   694  				startupStatus.Waiting++
   695  			} else {
   696  				startupStatus.Pending++
   697  			}
   698  		} else if p.Status.Phase == v1.PodSucceeded || p.Status.Phase == v1.PodFailed {
   699  			startupStatus.Inactive++
   700  		} else if p.Status.Phase == v1.PodUnknown {
   701  			startupStatus.Unknown++
   702  		}
   703  		// Record count of scheduled pods (useful for computing scheduler throughput).
   704  		if p.Spec.NodeName != "" {
   705  			startupStatus.Scheduled++
   706  		}
   707  	}
   708  	return startupStatus
   709  }
   710  
   711  func (config *RCConfig) start(ctx context.Context) error {
   712  	// Don't force tests to fail if they don't care about containers restarting.
   713  	var maxContainerFailures int
   714  	if config.MaxContainerFailures == nil {
   715  		maxContainerFailures = int(math.Max(1.0, float64(config.Replicas)*.01))
   716  	} else {
   717  		maxContainerFailures = *config.MaxContainerFailures
   718  	}
   719  
   720  	label := labels.SelectorFromSet(labels.Set(map[string]string{"name": config.Name}))
   721  
   722  	ps, err := NewPodStore(config.Client, config.Namespace, label, fields.Everything())
   723  	if err != nil {
   724  		return err
   725  	}
   726  	defer ps.Stop()
   727  
   728  	interval := config.PollInterval
   729  	if interval <= 0 {
   730  		interval = 10 * time.Second
   731  	}
   732  	timeout := config.Timeout
   733  	if timeout <= 0 {
   734  		timeout = 5 * time.Minute
   735  	}
   736  	oldPods := make([]*v1.Pod, 0)
   737  	oldRunning := 0
   738  	lastChange := time.Now()
   739  	podDeletionsCount := 0
   740  	for oldRunning != config.Replicas {
   741  		time.Sleep(interval)
   742  
   743  		pods := ps.List()
   744  		startupStatus := computeRCStartupStatus(pods, config.Replicas)
   745  
   746  		if config.CreatedPods != nil {
   747  			*config.CreatedPods = startupStatus.Created
   748  		}
   749  		if !config.Silent {
   750  			config.RCConfigLog(startupStatus.String(config.Name))
   751  		}
   752  
   753  		if config.PodStatusFile != nil {
   754  			fmt.Fprintf(config.PodStatusFile, "%d, running, %d, pending, %d, waiting, %d, inactive, %d, unknown, %d, runningButNotReady\n", startupStatus.Running, startupStatus.Pending, startupStatus.Waiting, startupStatus.Inactive, startupStatus.Unknown, startupStatus.RunningButNotReady)
   755  		}
   756  
   757  		if startupStatus.FailedContainers > maxContainerFailures {
   758  			if config.NodeDumpFunc != nil {
   759  				config.NodeDumpFunc(ctx, config.Client, startupStatus.ContainerRestartNodes.List(), config.RCConfigLog)
   760  			}
   761  			if config.ContainerDumpFunc != nil {
   762  				// Get the logs from the failed containers to help diagnose what caused them to fail
   763  				config.ContainerDumpFunc(ctx, config.Client, config.Namespace, config.RCConfigLog)
   764  			}
   765  			return fmt.Errorf("%d containers failed which is more than allowed %d", startupStatus.FailedContainers, maxContainerFailures)
   766  		}
   767  
   768  		diff := diff(oldPods, pods)
   769  		deletedPods := diff.DeletedPods()
   770  		podDeletionsCount += len(deletedPods)
   771  		if podDeletionsCount > config.MaxAllowedPodDeletions {
   772  			// Number of pods which disappeared is over threshold
   773  			err := fmt.Errorf("%d pods disappeared for %s: %v", podDeletionsCount, config.Name, strings.Join(deletedPods, ", "))
   774  			config.RCConfigLog(err.Error())
   775  			config.RCConfigLog(diff.String(sets.NewString()))
   776  			return err
   777  		}
   778  
   779  		if len(pods) > len(oldPods) || startupStatus.Running > oldRunning {
   780  			lastChange = time.Now()
   781  		}
   782  		oldPods = pods
   783  		oldRunning = startupStatus.Running
   784  
   785  		if time.Since(lastChange) > timeout {
   786  			break
   787  		}
   788  	}
   789  
   790  	if oldRunning != config.Replicas {
   791  		// List only pods from a given replication controller.
   792  		options := metav1.ListOptions{LabelSelector: label.String()}
   793  		if pods, err := config.Client.CoreV1().Pods(config.Namespace).List(ctx, options); err == nil {
   794  			for _, pod := range pods.Items {
   795  				config.RCConfigLog("Pod %s\t%s\t%s\t%s", pod.Name, pod.Spec.NodeName, pod.Status.Phase, pod.DeletionTimestamp)
   796  			}
   797  		} else {
   798  			config.RCConfigLog("Can't list pod debug info: %v", err)
   799  		}
   800  		return fmt.Errorf("only %d pods started out of %d", oldRunning, config.Replicas)
   801  	}
   802  	return nil
   803  }
   804  
   805  // Simplified version of RunRC, that does not create RC, but creates plain Pods.
   806  // Optionally waits for pods to start running (if waitForRunning == true).
   807  // The number of replicas must be non-zero.
   808  func StartPods(c clientset.Interface, replicas int, namespace string, podNamePrefix string,
   809  	pod v1.Pod, waitForRunning bool, logFunc func(fmt string, args ...interface{})) error {
   810  	// no pod to start
   811  	if replicas < 1 {
   812  		panic("StartPods: number of replicas must be non-zero")
   813  	}
   814  	startPodsID := string(uuid.NewUUID()) // So that we can label and find them
   815  	for i := 0; i < replicas; i++ {
   816  		podName := fmt.Sprintf("%v-%v", podNamePrefix, i)
   817  		pod.ObjectMeta.Name = podName
   818  		pod.ObjectMeta.Labels["name"] = podName
   819  		pod.ObjectMeta.Labels["startPodsID"] = startPodsID
   820  		pod.Spec.Containers[0].Name = podName
   821  		if err := CreatePodWithRetries(c, namespace, &pod); err != nil {
   822  			return err
   823  		}
   824  	}
   825  	logFunc("Waiting for running...")
   826  	if waitForRunning {
   827  		label := labels.SelectorFromSet(labels.Set(map[string]string{"startPodsID": startPodsID}))
   828  		err := WaitForPodsWithLabelRunning(c, namespace, label)
   829  		if err != nil {
   830  			return fmt.Errorf("error waiting for %d pods to be running - probably a timeout: %v", replicas, err)
   831  		}
   832  	}
   833  	return nil
   834  }
   835  
   836  // Wait up to 10 minutes for all matching pods to become Running and at least one
   837  // matching pod exists.
   838  func WaitForPodsWithLabelRunning(c clientset.Interface, ns string, label labels.Selector) error {
   839  	return WaitForEnoughPodsWithLabelRunning(c, ns, label, -1)
   840  }
   841  
   842  // Wait up to 10 minutes for at least 'replicas' many pods to be Running and at least
   843  // one matching pod exists. If 'replicas' is < 0, wait for all matching pods running.
   844  func WaitForEnoughPodsWithLabelRunning(c clientset.Interface, ns string, label labels.Selector, replicas int) error {
   845  	running := false
   846  	ps, err := NewPodStore(c, ns, label, fields.Everything())
   847  	if err != nil {
   848  		return err
   849  	}
   850  	defer ps.Stop()
   851  
   852  	for start := time.Now(); time.Since(start) < 10*time.Minute; time.Sleep(5 * time.Second) {
   853  		pods := ps.List()
   854  		if len(pods) == 0 {
   855  			continue
   856  		}
   857  		runningPodsCount := 0
   858  		for _, p := range pods {
   859  			if p.Status.Phase == v1.PodRunning {
   860  				runningPodsCount++
   861  			}
   862  		}
   863  		if (replicas < 0 && runningPodsCount < len(pods)) || (runningPodsCount < replicas) {
   864  			continue
   865  		}
   866  		running = true
   867  		break
   868  	}
   869  	if !running {
   870  		return fmt.Errorf("timeout while waiting for pods with labels %q to be running", label.String())
   871  	}
   872  	return nil
   873  }
   874  
   875  type PrepareNodeStrategy interface {
   876  	// Modify pre-created Node objects before the test starts.
   877  	PreparePatch(node *v1.Node) []byte
   878  	// Create or modify any objects that depend on the node before the test starts.
   879  	// Caller will re-try when http.StatusConflict error is returned.
   880  	PrepareDependentObjects(ctx context.Context, node *v1.Node, client clientset.Interface) error
   881  	// Clean up any node modifications after the test finishes.
   882  	CleanupNode(ctx context.Context, node *v1.Node) *v1.Node
   883  	// Clean up any objects that depend on the node after the test finishes.
   884  	// Caller will re-try when http.StatusConflict error is returned.
   885  	CleanupDependentObjects(ctx context.Context, nodeName string, client clientset.Interface) error
   886  }
   887  
   888  type TrivialNodePrepareStrategy struct{}
   889  
   890  var _ PrepareNodeStrategy = &TrivialNodePrepareStrategy{}
   891  
   892  func (*TrivialNodePrepareStrategy) PreparePatch(*v1.Node) []byte {
   893  	return []byte{}
   894  }
   895  
   896  func (*TrivialNodePrepareStrategy) CleanupNode(ctx context.Context, node *v1.Node) *v1.Node {
   897  	nodeCopy := *node
   898  	return &nodeCopy
   899  }
   900  
   901  func (*TrivialNodePrepareStrategy) PrepareDependentObjects(ctx context.Context, node *v1.Node, client clientset.Interface) error {
   902  	return nil
   903  }
   904  
   905  func (*TrivialNodePrepareStrategy) CleanupDependentObjects(ctx context.Context, nodeName string, client clientset.Interface) error {
   906  	return nil
   907  }
   908  
   909  type LabelNodePrepareStrategy struct {
   910  	LabelKey      string
   911  	LabelValues   []string
   912  	roundRobinIdx int
   913  }
   914  
   915  var _ PrepareNodeStrategy = &LabelNodePrepareStrategy{}
   916  
   917  func NewLabelNodePrepareStrategy(labelKey string, labelValues ...string) *LabelNodePrepareStrategy {
   918  	return &LabelNodePrepareStrategy{
   919  		LabelKey:    labelKey,
   920  		LabelValues: labelValues,
   921  	}
   922  }
   923  
   924  func (s *LabelNodePrepareStrategy) PreparePatch(*v1.Node) []byte {
   925  	labelString := fmt.Sprintf("{\"%v\":\"%v\"}", s.LabelKey, s.LabelValues[s.roundRobinIdx])
   926  	patch := fmt.Sprintf(`{"metadata":{"labels":%v}}`, labelString)
   927  	s.roundRobinIdx++
   928  	if s.roundRobinIdx == len(s.LabelValues) {
   929  		s.roundRobinIdx = 0
   930  	}
   931  	return []byte(patch)
   932  }
   933  
   934  func (s *LabelNodePrepareStrategy) CleanupNode(ctx context.Context, node *v1.Node) *v1.Node {
   935  	nodeCopy := node.DeepCopy()
   936  	if node.Labels != nil && len(node.Labels[s.LabelKey]) != 0 {
   937  		delete(nodeCopy.Labels, s.LabelKey)
   938  	}
   939  	return nodeCopy
   940  }
   941  
   942  func (*LabelNodePrepareStrategy) PrepareDependentObjects(ctx context.Context, node *v1.Node, client clientset.Interface) error {
   943  	return nil
   944  }
   945  
   946  func (*LabelNodePrepareStrategy) CleanupDependentObjects(ctx context.Context, nodeName string, client clientset.Interface) error {
   947  	return nil
   948  }
   949  
   950  // NodeAllocatableStrategy fills node.status.allocatable and csiNode.spec.drivers[*].allocatable.
   951  // csiNode is created if it does not exist. On cleanup, any csiNode.spec.drivers[*].allocatable is
   952  // set to nil.
   953  type NodeAllocatableStrategy struct {
   954  	// Node.status.allocatable to fill to all nodes.
   955  	NodeAllocatable map[v1.ResourceName]string
   956  	// Map <driver_name> -> VolumeNodeResources to fill into csiNode.spec.drivers[<driver_name>].
   957  	CsiNodeAllocatable map[string]*storagev1.VolumeNodeResources
   958  	// List of in-tree volume plugins migrated to CSI.
   959  	MigratedPlugins []string
   960  }
   961  
   962  var _ PrepareNodeStrategy = &NodeAllocatableStrategy{}
   963  
   964  func NewNodeAllocatableStrategy(nodeAllocatable map[v1.ResourceName]string, csiNodeAllocatable map[string]*storagev1.VolumeNodeResources, migratedPlugins []string) *NodeAllocatableStrategy {
   965  	return &NodeAllocatableStrategy{
   966  		NodeAllocatable:    nodeAllocatable,
   967  		CsiNodeAllocatable: csiNodeAllocatable,
   968  		MigratedPlugins:    migratedPlugins,
   969  	}
   970  }
   971  
   972  func (s *NodeAllocatableStrategy) PreparePatch(node *v1.Node) []byte {
   973  	newNode := node.DeepCopy()
   974  	for name, value := range s.NodeAllocatable {
   975  		newNode.Status.Allocatable[name] = resource.MustParse(value)
   976  	}
   977  
   978  	oldJSON, err := json.Marshal(node)
   979  	if err != nil {
   980  		panic(err)
   981  	}
   982  	newJSON, err := json.Marshal(newNode)
   983  	if err != nil {
   984  		panic(err)
   985  	}
   986  
   987  	patch, err := strategicpatch.CreateTwoWayMergePatch(oldJSON, newJSON, v1.Node{})
   988  	if err != nil {
   989  		panic(err)
   990  	}
   991  	return patch
   992  }
   993  
   994  func (s *NodeAllocatableStrategy) CleanupNode(ctx context.Context, node *v1.Node) *v1.Node {
   995  	nodeCopy := node.DeepCopy()
   996  	for name := range s.NodeAllocatable {
   997  		delete(nodeCopy.Status.Allocatable, name)
   998  	}
   999  	return nodeCopy
  1000  }
  1001  
  1002  func (s *NodeAllocatableStrategy) createCSINode(ctx context.Context, nodeName string, client clientset.Interface) error {
  1003  	csiNode := &storagev1.CSINode{
  1004  		ObjectMeta: metav1.ObjectMeta{
  1005  			Name: nodeName,
  1006  			Annotations: map[string]string{
  1007  				v1.MigratedPluginsAnnotationKey: strings.Join(s.MigratedPlugins, ","),
  1008  			},
  1009  		},
  1010  		Spec: storagev1.CSINodeSpec{
  1011  			Drivers: []storagev1.CSINodeDriver{},
  1012  		},
  1013  	}
  1014  
  1015  	for driver, allocatable := range s.CsiNodeAllocatable {
  1016  		d := storagev1.CSINodeDriver{
  1017  			Name:        driver,
  1018  			Allocatable: allocatable,
  1019  			NodeID:      nodeName,
  1020  		}
  1021  		csiNode.Spec.Drivers = append(csiNode.Spec.Drivers, d)
  1022  	}
  1023  
  1024  	_, err := client.StorageV1().CSINodes().Create(ctx, csiNode, metav1.CreateOptions{})
  1025  	if apierrors.IsAlreadyExists(err) {
  1026  		// Something created CSINode instance after we checked it did not exist.
  1027  		// Make the caller to re-try PrepareDependentObjects by returning Conflict error
  1028  		err = apierrors.NewConflict(storagev1beta1.Resource("csinodes"), nodeName, err)
  1029  	}
  1030  	return err
  1031  }
  1032  
  1033  func (s *NodeAllocatableStrategy) updateCSINode(ctx context.Context, csiNode *storagev1.CSINode, client clientset.Interface) error {
  1034  	for driverName, allocatable := range s.CsiNodeAllocatable {
  1035  		found := false
  1036  		for i, driver := range csiNode.Spec.Drivers {
  1037  			if driver.Name == driverName {
  1038  				found = true
  1039  				csiNode.Spec.Drivers[i].Allocatable = allocatable
  1040  				break
  1041  			}
  1042  		}
  1043  		if !found {
  1044  			d := storagev1.CSINodeDriver{
  1045  				Name:        driverName,
  1046  				Allocatable: allocatable,
  1047  			}
  1048  
  1049  			csiNode.Spec.Drivers = append(csiNode.Spec.Drivers, d)
  1050  		}
  1051  	}
  1052  	csiNode.Annotations[v1.MigratedPluginsAnnotationKey] = strings.Join(s.MigratedPlugins, ",")
  1053  
  1054  	_, err := client.StorageV1().CSINodes().Update(ctx, csiNode, metav1.UpdateOptions{})
  1055  	return err
  1056  }
  1057  
  1058  func (s *NodeAllocatableStrategy) PrepareDependentObjects(ctx context.Context, node *v1.Node, client clientset.Interface) error {
  1059  	csiNode, err := client.StorageV1().CSINodes().Get(ctx, node.Name, metav1.GetOptions{})
  1060  	if err != nil {
  1061  		if apierrors.IsNotFound(err) {
  1062  			return s.createCSINode(ctx, node.Name, client)
  1063  		}
  1064  		return err
  1065  	}
  1066  	return s.updateCSINode(ctx, csiNode, client)
  1067  }
  1068  
  1069  func (s *NodeAllocatableStrategy) CleanupDependentObjects(ctx context.Context, nodeName string, client clientset.Interface) error {
  1070  	csiNode, err := client.StorageV1().CSINodes().Get(ctx, nodeName, metav1.GetOptions{})
  1071  	if err != nil {
  1072  		if apierrors.IsNotFound(err) {
  1073  			return nil
  1074  		}
  1075  		return err
  1076  	}
  1077  
  1078  	for driverName := range s.CsiNodeAllocatable {
  1079  		for i, driver := range csiNode.Spec.Drivers {
  1080  			if driver.Name == driverName {
  1081  				csiNode.Spec.Drivers[i].Allocatable = nil
  1082  			}
  1083  		}
  1084  	}
  1085  	return s.updateCSINode(ctx, csiNode, client)
  1086  }
  1087  
  1088  // UniqueNodeLabelStrategy sets a unique label for each node.
  1089  type UniqueNodeLabelStrategy struct {
  1090  	LabelKey string
  1091  }
  1092  
  1093  var _ PrepareNodeStrategy = &UniqueNodeLabelStrategy{}
  1094  
  1095  func NewUniqueNodeLabelStrategy(labelKey string) *UniqueNodeLabelStrategy {
  1096  	return &UniqueNodeLabelStrategy{
  1097  		LabelKey: labelKey,
  1098  	}
  1099  }
  1100  
  1101  func (s *UniqueNodeLabelStrategy) PreparePatch(*v1.Node) []byte {
  1102  	labelString := fmt.Sprintf("{\"%v\":\"%v\"}", s.LabelKey, string(uuid.NewUUID()))
  1103  	patch := fmt.Sprintf(`{"metadata":{"labels":%v}}`, labelString)
  1104  	return []byte(patch)
  1105  }
  1106  
  1107  func (s *UniqueNodeLabelStrategy) CleanupNode(ctx context.Context, node *v1.Node) *v1.Node {
  1108  	nodeCopy := node.DeepCopy()
  1109  	if node.Labels != nil && len(node.Labels[s.LabelKey]) != 0 {
  1110  		delete(nodeCopy.Labels, s.LabelKey)
  1111  	}
  1112  	return nodeCopy
  1113  }
  1114  
  1115  func (*UniqueNodeLabelStrategy) PrepareDependentObjects(ctx context.Context, node *v1.Node, client clientset.Interface) error {
  1116  	return nil
  1117  }
  1118  
  1119  func (*UniqueNodeLabelStrategy) CleanupDependentObjects(ctx context.Context, nodeName string, client clientset.Interface) error {
  1120  	return nil
  1121  }
  1122  
  1123  func DoPrepareNode(ctx context.Context, client clientset.Interface, node *v1.Node, strategy PrepareNodeStrategy) error {
  1124  	var err error
  1125  	patch := strategy.PreparePatch(node)
  1126  	if len(patch) == 0 {
  1127  		return nil
  1128  	}
  1129  	for attempt := 0; attempt < retries; attempt++ {
  1130  		if _, err = client.CoreV1().Nodes().Patch(ctx, node.Name, types.MergePatchType, []byte(patch), metav1.PatchOptions{}); err == nil {
  1131  			break
  1132  		}
  1133  		if !apierrors.IsConflict(err) {
  1134  			return fmt.Errorf("error while applying patch %v to Node %v: %v", string(patch), node.Name, err)
  1135  		}
  1136  		time.Sleep(100 * time.Millisecond)
  1137  	}
  1138  	if err != nil {
  1139  		return fmt.Errorf("too many conflicts when applying patch %v to Node %v: %s", string(patch), node.Name, err)
  1140  	}
  1141  
  1142  	for attempt := 0; attempt < retries; attempt++ {
  1143  		if err = strategy.PrepareDependentObjects(ctx, node, client); err == nil {
  1144  			break
  1145  		}
  1146  		if !apierrors.IsConflict(err) {
  1147  			return fmt.Errorf("error while preparing objects for node %s: %s", node.Name, err)
  1148  		}
  1149  		time.Sleep(100 * time.Millisecond)
  1150  	}
  1151  	if err != nil {
  1152  		return fmt.Errorf("too many conflicts when creating objects for node %s: %s", node.Name, err)
  1153  	}
  1154  	return nil
  1155  }
  1156  
  1157  type TestPodCreateStrategy func(ctx context.Context, client clientset.Interface, namespace string, podCount int) error
  1158  
  1159  type CountToPodStrategy struct {
  1160  	Count    int
  1161  	Strategy TestPodCreateStrategy
  1162  }
  1163  
  1164  type TestPodCreatorConfig map[string][]CountToPodStrategy
  1165  
  1166  func NewTestPodCreatorConfig() *TestPodCreatorConfig {
  1167  	config := make(TestPodCreatorConfig)
  1168  	return &config
  1169  }
  1170  
  1171  type CountToStrategy struct {
  1172  	Count    int
  1173  	Strategy PrepareNodeStrategy
  1174  }
  1175  
  1176  type TestNodePreparer interface {
  1177  	PrepareNodes(ctx context.Context, nextNodeIndex int) error
  1178  	CleanupNodes(ctx context.Context) error
  1179  }
  1180  
  1181  func (c *TestPodCreatorConfig) AddStrategy(
  1182  	namespace string, podCount int, strategy TestPodCreateStrategy) {
  1183  	(*c)[namespace] = append((*c)[namespace], CountToPodStrategy{Count: podCount, Strategy: strategy})
  1184  }
  1185  
  1186  type TestPodCreator struct {
  1187  	Client clientset.Interface
  1188  	// namespace -> count -> strategy
  1189  	Config *TestPodCreatorConfig
  1190  }
  1191  
  1192  func NewTestPodCreator(client clientset.Interface, config *TestPodCreatorConfig) *TestPodCreator {
  1193  	return &TestPodCreator{
  1194  		Client: client,
  1195  		Config: config,
  1196  	}
  1197  }
  1198  
  1199  func (c *TestPodCreator) CreatePods(ctx context.Context) error {
  1200  	for ns, v := range *(c.Config) {
  1201  		for _, countToStrategy := range v {
  1202  			if err := countToStrategy.Strategy(ctx, c.Client, ns, countToStrategy.Count); err != nil {
  1203  				return err
  1204  			}
  1205  		}
  1206  	}
  1207  	return nil
  1208  }
  1209  
  1210  func MakePodSpec() v1.PodSpec {
  1211  	return v1.PodSpec{
  1212  		Containers: []v1.Container{{
  1213  			Name:  "pause",
  1214  			Image: "registry.k8s.io/pause:3.9",
  1215  			Ports: []v1.ContainerPort{{ContainerPort: 80}},
  1216  			Resources: v1.ResourceRequirements{
  1217  				Limits: v1.ResourceList{
  1218  					v1.ResourceCPU:    resource.MustParse("100m"),
  1219  					v1.ResourceMemory: resource.MustParse("500Mi"),
  1220  				},
  1221  				Requests: v1.ResourceList{
  1222  					v1.ResourceCPU:    resource.MustParse("100m"),
  1223  					v1.ResourceMemory: resource.MustParse("500Mi"),
  1224  				},
  1225  			},
  1226  		}},
  1227  	}
  1228  }
  1229  
  1230  func makeCreatePod(client clientset.Interface, namespace string, podTemplate *v1.Pod) error {
  1231  	if err := CreatePodWithRetries(client, namespace, podTemplate); err != nil {
  1232  		return fmt.Errorf("error creating pod: %v", err)
  1233  	}
  1234  	return nil
  1235  }
  1236  
  1237  func CreatePod(ctx context.Context, client clientset.Interface, namespace string, podCount int, podTemplate *v1.Pod) error {
  1238  	var createError error
  1239  	lock := sync.Mutex{}
  1240  	createPodFunc := func(i int) {
  1241  		// client-go writes into the object that is passed to Create,
  1242  		// causing a data race unless we create a new copy for each
  1243  		// parallel call.
  1244  		if err := makeCreatePod(client, namespace, podTemplate.DeepCopy()); err != nil {
  1245  			lock.Lock()
  1246  			defer lock.Unlock()
  1247  			createError = err
  1248  		}
  1249  	}
  1250  
  1251  	if podCount < 30 {
  1252  		workqueue.ParallelizeUntil(ctx, podCount, podCount, createPodFunc)
  1253  	} else {
  1254  		workqueue.ParallelizeUntil(ctx, 30, podCount, createPodFunc)
  1255  	}
  1256  	return createError
  1257  }
  1258  
  1259  func CreatePodWithPersistentVolume(ctx context.Context, client clientset.Interface, namespace string, claimTemplate *v1.PersistentVolumeClaim, factory volumeFactory, podTemplate *v1.Pod, count int, bindVolume bool) error {
  1260  	var createError error
  1261  	lock := sync.Mutex{}
  1262  	createPodFunc := func(i int) {
  1263  		pvcName := fmt.Sprintf("pvc-%d", i)
  1264  		// pvc
  1265  		pvc := claimTemplate.DeepCopy()
  1266  		pvc.Name = pvcName
  1267  		// pv
  1268  		pv := factory(i)
  1269  		// PVs are cluster-wide resources.
  1270  		// Prepend a namespace to make the name globally unique.
  1271  		pv.Name = fmt.Sprintf("%s-%s", namespace, pv.Name)
  1272  		if bindVolume {
  1273  			// bind pv to "pvc-$i"
  1274  			pv.Spec.ClaimRef = &v1.ObjectReference{
  1275  				Kind:       "PersistentVolumeClaim",
  1276  				Namespace:  namespace,
  1277  				Name:       pvcName,
  1278  				APIVersion: "v1",
  1279  			}
  1280  			pv.Status.Phase = v1.VolumeBound
  1281  
  1282  			// bind pvc to "pv-$i"
  1283  			pvc.Spec.VolumeName = pv.Name
  1284  			pvc.Status.Phase = v1.ClaimBound
  1285  		} else {
  1286  			pv.Status.Phase = v1.VolumeAvailable
  1287  		}
  1288  
  1289  		// Create PVC first as it's referenced by the PV when the `bindVolume` is true.
  1290  		if err := CreatePersistentVolumeClaimWithRetries(client, namespace, pvc); err != nil {
  1291  			lock.Lock()
  1292  			defer lock.Unlock()
  1293  			createError = fmt.Errorf("error creating PVC: %s", err)
  1294  			return
  1295  		}
  1296  
  1297  		// We need to update statuses separately, as creating pv/pvc resets status to the default one.
  1298  		if _, err := client.CoreV1().PersistentVolumeClaims(namespace).UpdateStatus(ctx, pvc, metav1.UpdateOptions{}); err != nil {
  1299  			lock.Lock()
  1300  			defer lock.Unlock()
  1301  			createError = fmt.Errorf("error updating PVC status: %s", err)
  1302  			return
  1303  		}
  1304  
  1305  		if err := CreatePersistentVolumeWithRetries(client, pv); err != nil {
  1306  			lock.Lock()
  1307  			defer lock.Unlock()
  1308  			createError = fmt.Errorf("error creating PV: %s", err)
  1309  			return
  1310  		}
  1311  		// We need to update statuses separately, as creating pv/pvc resets status to the default one.
  1312  		if _, err := client.CoreV1().PersistentVolumes().UpdateStatus(ctx, pv, metav1.UpdateOptions{}); err != nil {
  1313  			lock.Lock()
  1314  			defer lock.Unlock()
  1315  			createError = fmt.Errorf("error updating PV status: %s", err)
  1316  			return
  1317  		}
  1318  
  1319  		// pod
  1320  		pod := podTemplate.DeepCopy()
  1321  		pod.Spec.Volumes = []v1.Volume{
  1322  			{
  1323  				Name: "vol",
  1324  				VolumeSource: v1.VolumeSource{
  1325  					PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  1326  						ClaimName: pvcName,
  1327  					},
  1328  				},
  1329  			},
  1330  		}
  1331  		if err := makeCreatePod(client, namespace, pod); err != nil {
  1332  			lock.Lock()
  1333  			defer lock.Unlock()
  1334  			createError = err
  1335  			return
  1336  		}
  1337  	}
  1338  
  1339  	if count < 30 {
  1340  		workqueue.ParallelizeUntil(ctx, count, count, createPodFunc)
  1341  	} else {
  1342  		workqueue.ParallelizeUntil(ctx, 30, count, createPodFunc)
  1343  	}
  1344  	return createError
  1345  }
  1346  
  1347  func NewCustomCreatePodStrategy(podTemplate *v1.Pod) TestPodCreateStrategy {
  1348  	return func(ctx context.Context, client clientset.Interface, namespace string, podCount int) error {
  1349  		return CreatePod(ctx, client, namespace, podCount, podTemplate)
  1350  	}
  1351  }
  1352  
  1353  // volumeFactory creates an unique PersistentVolume for given integer.
  1354  type volumeFactory func(uniqueID int) *v1.PersistentVolume
  1355  
  1356  func NewCreatePodWithPersistentVolumeStrategy(claimTemplate *v1.PersistentVolumeClaim, factory volumeFactory, podTemplate *v1.Pod) TestPodCreateStrategy {
  1357  	return func(ctx context.Context, client clientset.Interface, namespace string, podCount int) error {
  1358  		return CreatePodWithPersistentVolume(ctx, client, namespace, claimTemplate, factory, podTemplate, podCount, true /* bindVolume */)
  1359  	}
  1360  }
  1361  
  1362  // TODO: attach secrets using different possibilities: env vars, image pull secrets.
  1363  func attachSecrets(template *v1.PodTemplateSpec, secretNames []string) {
  1364  	volumes := make([]v1.Volume, 0, len(secretNames))
  1365  	mounts := make([]v1.VolumeMount, 0, len(secretNames))
  1366  	for _, name := range secretNames {
  1367  		volumes = append(volumes, v1.Volume{
  1368  			Name: name,
  1369  			VolumeSource: v1.VolumeSource{
  1370  				Secret: &v1.SecretVolumeSource{
  1371  					SecretName: name,
  1372  				},
  1373  			},
  1374  		})
  1375  		mounts = append(mounts, v1.VolumeMount{
  1376  			Name:      name,
  1377  			MountPath: fmt.Sprintf("/%v", name),
  1378  		})
  1379  	}
  1380  
  1381  	template.Spec.Volumes = volumes
  1382  	template.Spec.Containers[0].VolumeMounts = mounts
  1383  }
  1384  
  1385  // TODO: attach configmaps using different possibilities: env vars.
  1386  func attachConfigMaps(template *v1.PodTemplateSpec, configMapNames []string) {
  1387  	volumes := make([]v1.Volume, 0, len(configMapNames))
  1388  	mounts := make([]v1.VolumeMount, 0, len(configMapNames))
  1389  	for _, name := range configMapNames {
  1390  		volumes = append(volumes, v1.Volume{
  1391  			Name: name,
  1392  			VolumeSource: v1.VolumeSource{
  1393  				ConfigMap: &v1.ConfigMapVolumeSource{
  1394  					LocalObjectReference: v1.LocalObjectReference{
  1395  						Name: name,
  1396  					},
  1397  				},
  1398  			},
  1399  		})
  1400  		mounts = append(mounts, v1.VolumeMount{
  1401  			Name:      name,
  1402  			MountPath: fmt.Sprintf("/%v", name),
  1403  		})
  1404  	}
  1405  
  1406  	template.Spec.Volumes = volumes
  1407  	template.Spec.Containers[0].VolumeMounts = mounts
  1408  }
  1409  
  1410  func (config *RCConfig) getTerminationGracePeriodSeconds(defaultGrace *int64) *int64 {
  1411  	if config.TerminationGracePeriodSeconds == nil || *config.TerminationGracePeriodSeconds < 0 {
  1412  		return defaultGrace
  1413  	}
  1414  	return config.TerminationGracePeriodSeconds
  1415  }
  1416  
  1417  func attachServiceAccountTokenProjection(template *v1.PodTemplateSpec, name string) {
  1418  	template.Spec.Containers[0].VolumeMounts = append(template.Spec.Containers[0].VolumeMounts,
  1419  		v1.VolumeMount{
  1420  			Name:      name,
  1421  			MountPath: "/var/service-account-tokens/" + name,
  1422  		})
  1423  
  1424  	template.Spec.Volumes = append(template.Spec.Volumes,
  1425  		v1.Volume{
  1426  			Name: name,
  1427  			VolumeSource: v1.VolumeSource{
  1428  				Projected: &v1.ProjectedVolumeSource{
  1429  					Sources: []v1.VolumeProjection{
  1430  						{
  1431  							ServiceAccountToken: &v1.ServiceAccountTokenProjection{
  1432  								Path:     "token",
  1433  								Audience: name,
  1434  							},
  1435  						},
  1436  						{
  1437  							ConfigMap: &v1.ConfigMapProjection{
  1438  								LocalObjectReference: v1.LocalObjectReference{
  1439  									Name: "kube-root-ca-crt",
  1440  								},
  1441  								Items: []v1.KeyToPath{
  1442  									{
  1443  										Key:  "ca.crt",
  1444  										Path: "ca.crt",
  1445  									},
  1446  								},
  1447  							},
  1448  						},
  1449  						{
  1450  							DownwardAPI: &v1.DownwardAPIProjection{
  1451  								Items: []v1.DownwardAPIVolumeFile{
  1452  									{
  1453  										Path: "namespace",
  1454  										FieldRef: &v1.ObjectFieldSelector{
  1455  											APIVersion: "v1",
  1456  											FieldPath:  "metadata.namespace",
  1457  										},
  1458  									},
  1459  								},
  1460  							},
  1461  						},
  1462  					},
  1463  				},
  1464  			},
  1465  		})
  1466  }
  1467  
  1468  type DaemonConfig struct {
  1469  	Client    clientset.Interface
  1470  	Name      string
  1471  	Namespace string
  1472  	Image     string
  1473  	// If set this function will be used to print log lines instead of klog.
  1474  	LogFunc func(fmt string, args ...interface{})
  1475  	// How long we wait for DaemonSet to become running.
  1476  	Timeout time.Duration
  1477  }
  1478  
  1479  func (config *DaemonConfig) Run(ctx context.Context) error {
  1480  	if config.Image == "" {
  1481  		config.Image = "registry.k8s.io/pause:3.9"
  1482  	}
  1483  	nameLabel := map[string]string{
  1484  		"name": config.Name + "-daemon",
  1485  	}
  1486  	daemon := &apps.DaemonSet{
  1487  		ObjectMeta: metav1.ObjectMeta{
  1488  			Name: config.Name,
  1489  		},
  1490  		Spec: apps.DaemonSetSpec{
  1491  			Template: v1.PodTemplateSpec{
  1492  				ObjectMeta: metav1.ObjectMeta{
  1493  					Labels: nameLabel,
  1494  				},
  1495  				Spec: v1.PodSpec{
  1496  					Containers: []v1.Container{
  1497  						{
  1498  							Name:  config.Name,
  1499  							Image: config.Image,
  1500  						},
  1501  					},
  1502  				},
  1503  			},
  1504  		},
  1505  	}
  1506  
  1507  	if err := CreateDaemonSetWithRetries(config.Client, config.Namespace, daemon); err != nil {
  1508  		return fmt.Errorf("error creating daemonset: %v", err)
  1509  	}
  1510  
  1511  	var nodes *v1.NodeList
  1512  	var err error
  1513  	for i := 0; i < retries; i++ {
  1514  		// Wait for all daemons to be running
  1515  		nodes, err = config.Client.CoreV1().Nodes().List(ctx, metav1.ListOptions{ResourceVersion: "0"})
  1516  		if err == nil {
  1517  			break
  1518  		} else if i+1 == retries {
  1519  			return fmt.Errorf("error listing Nodes while waiting for DaemonSet %v: %v", config.Name, err)
  1520  		}
  1521  	}
  1522  
  1523  	timeout := config.Timeout
  1524  	if timeout <= 0 {
  1525  		timeout = 5 * time.Minute
  1526  	}
  1527  
  1528  	ps, err := NewPodStore(config.Client, config.Namespace, labels.SelectorFromSet(nameLabel), fields.Everything())
  1529  	if err != nil {
  1530  		return err
  1531  	}
  1532  	defer ps.Stop()
  1533  
  1534  	err = wait.Poll(time.Second, timeout, func() (bool, error) {
  1535  		pods := ps.List()
  1536  
  1537  		nodeHasDaemon := sets.NewString()
  1538  		for _, pod := range pods {
  1539  			podReady, _ := PodRunningReady(pod)
  1540  			if pod.Spec.NodeName != "" && podReady {
  1541  				nodeHasDaemon.Insert(pod.Spec.NodeName)
  1542  			}
  1543  		}
  1544  
  1545  		running := len(nodeHasDaemon)
  1546  		config.LogFunc("Found %v/%v Daemons %v running", running, config.Name, len(nodes.Items))
  1547  		return running == len(nodes.Items), nil
  1548  	})
  1549  	if err != nil {
  1550  		config.LogFunc("Timed out while waiting for DaemonSet %v/%v to be running.", config.Namespace, config.Name)
  1551  	} else {
  1552  		config.LogFunc("Created Daemon %v/%v", config.Namespace, config.Name)
  1553  	}
  1554  
  1555  	return err
  1556  }