k8s.io/kubernetes@v1.29.3/test/e2e/framework/daemonset/fixtures.go (about)

     1  /*
     2  Copyright 2021 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 daemonset
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	appsv1 "k8s.io/api/apps/v1"
    24  	v1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	clientset "k8s.io/client-go/kubernetes"
    27  	"k8s.io/kubectl/pkg/util/podutils"
    28  	"k8s.io/kubernetes/pkg/controller/daemon"
    29  	"k8s.io/kubernetes/test/e2e/framework"
    30  )
    31  
    32  func NewDaemonSet(dsName, image string, labels map[string]string, volumes []v1.Volume, mounts []v1.VolumeMount, ports []v1.ContainerPort, args ...string) *appsv1.DaemonSet {
    33  	return &appsv1.DaemonSet{
    34  		ObjectMeta: metav1.ObjectMeta{
    35  			Name:   dsName,
    36  			Labels: labels,
    37  		},
    38  		Spec: appsv1.DaemonSetSpec{
    39  			Selector: &metav1.LabelSelector{
    40  				MatchLabels: labels,
    41  			},
    42  			Template: v1.PodTemplateSpec{
    43  				ObjectMeta: metav1.ObjectMeta{
    44  					Labels: labels,
    45  				},
    46  				Spec: v1.PodSpec{
    47  					Containers: []v1.Container{
    48  						{
    49  							Name:            "app",
    50  							Image:           image,
    51  							Args:            args,
    52  							Ports:           ports,
    53  							VolumeMounts:    mounts,
    54  							SecurityContext: &v1.SecurityContext{},
    55  						},
    56  					},
    57  					SecurityContext: &v1.PodSecurityContext{},
    58  					Volumes:         volumes,
    59  				},
    60  			},
    61  		},
    62  	}
    63  }
    64  
    65  func CheckRunningOnAllNodes(ctx context.Context, f *framework.Framework, ds *appsv1.DaemonSet) (bool, error) {
    66  	nodeNames := SchedulableNodes(ctx, f.ClientSet, ds)
    67  	return CheckDaemonPodOnNodes(f, ds, nodeNames)(ctx)
    68  }
    69  
    70  // CheckPresentOnNodes will check that the daemonset will be present on at least the given number of
    71  // schedulable nodes.
    72  func CheckPresentOnNodes(ctx context.Context, c clientset.Interface, ds *appsv1.DaemonSet, ns string, numNodes int) (bool, error) {
    73  	nodeNames := SchedulableNodes(ctx, c, ds)
    74  	if len(nodeNames) < numNodes {
    75  		return false, nil
    76  	}
    77  	return checkDaemonPodStateOnNodes(ctx, c, ds, ns, nodeNames, func(pod *v1.Pod) bool {
    78  		return pod.Status.Phase != v1.PodPending
    79  	})
    80  }
    81  
    82  func SchedulableNodes(ctx context.Context, c clientset.Interface, ds *appsv1.DaemonSet) []string {
    83  	nodeList, err := c.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
    84  	framework.ExpectNoError(err)
    85  	nodeNames := make([]string, 0)
    86  	for _, node := range nodeList.Items {
    87  		shouldRun, _ := daemon.NodeShouldRunDaemonPod(&node, ds)
    88  		if !shouldRun {
    89  			framework.Logf("DaemonSet pods can't tolerate node %s with taints %+v, skip checking this node", node.Name, node.Spec.Taints)
    90  			continue
    91  		}
    92  		nodeNames = append(nodeNames, node.Name)
    93  	}
    94  	return nodeNames
    95  }
    96  
    97  func CheckDaemonPodOnNodes(f *framework.Framework, ds *appsv1.DaemonSet, nodeNames []string) func(ctx context.Context) (bool, error) {
    98  	return func(ctx context.Context) (bool, error) {
    99  		return checkDaemonPodStateOnNodes(ctx, f.ClientSet, ds, f.Namespace.Name, nodeNames, func(pod *v1.Pod) bool {
   100  			return podutils.IsPodAvailable(pod, ds.Spec.MinReadySeconds, metav1.Now())
   101  		})
   102  	}
   103  }
   104  
   105  func checkDaemonPodStateOnNodes(ctx context.Context, c clientset.Interface, ds *appsv1.DaemonSet, ns string, nodeNames []string, stateChecker func(*v1.Pod) bool) (bool, error) {
   106  	podList, err := c.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{})
   107  	if err != nil {
   108  		framework.Logf("could not get the pod list: %v", err)
   109  		return false, nil
   110  	}
   111  	pods := podList.Items
   112  
   113  	nodesToPodCount := make(map[string]int)
   114  	for _, pod := range pods {
   115  		if !metav1.IsControlledBy(&pod, ds) {
   116  			continue
   117  		}
   118  		if pod.DeletionTimestamp != nil {
   119  			continue
   120  		}
   121  		if stateChecker(&pod) {
   122  			nodesToPodCount[pod.Spec.NodeName]++
   123  		}
   124  	}
   125  	framework.Logf("Number of nodes with available pods controlled by daemonset %s: %d", ds.Name, len(nodesToPodCount))
   126  
   127  	// Ensure that exactly 1 pod is running on all nodes in nodeNames.
   128  	for _, nodeName := range nodeNames {
   129  		if nodesToPodCount[nodeName] != 1 {
   130  			framework.Logf("Node %s is running %d daemon pod, expected 1", nodeName, nodesToPodCount[nodeName])
   131  			return false, nil
   132  		}
   133  	}
   134  
   135  	framework.Logf("Number of running nodes: %d, number of available pods: %d in daemonset %s", len(nodeNames), len(nodesToPodCount), ds.Name)
   136  	// Ensure that sizes of the lists are the same. We've verified that every element of nodeNames is in
   137  	// nodesToPodCount, so verifying the lengths are equal ensures that there aren't pods running on any
   138  	// other nodes.
   139  	return len(nodesToPodCount) == len(nodeNames), nil
   140  }
   141  
   142  func CheckDaemonStatus(ctx context.Context, f *framework.Framework, dsName string) error {
   143  	ds, err := f.ClientSet.AppsV1().DaemonSets(f.Namespace.Name).Get(ctx, dsName, metav1.GetOptions{})
   144  	if err != nil {
   145  		return err
   146  	}
   147  	desired, scheduled, ready := ds.Status.DesiredNumberScheduled, ds.Status.CurrentNumberScheduled, ds.Status.NumberReady
   148  	if desired != scheduled && desired != ready {
   149  		return fmt.Errorf("error in daemon status. DesiredScheduled: %d, CurrentScheduled: %d, Ready: %d", desired, scheduled, ready)
   150  	}
   151  	return nil
   152  }