mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	e2e-scheduling: add basic PodOverhead test
Add a test to verify that Overhead is being considered during predication. Signed-off-by: Eric Ernst <eric.ernst@intel.com>
This commit is contained in:
		@@ -22,6 +22,7 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						nodev1beta1 "k8s.io/api/node/v1beta1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/sets"
 | 
						"k8s.io/apimachinery/pkg/util/sets"
 | 
				
			||||||
@@ -61,6 +62,7 @@ type pausePodConfig struct {
 | 
				
			|||||||
	Affinity                          *v1.Affinity
 | 
						Affinity                          *v1.Affinity
 | 
				
			||||||
	Annotations, Labels, NodeSelector map[string]string
 | 
						Annotations, Labels, NodeSelector map[string]string
 | 
				
			||||||
	Resources                         *v1.ResourceRequirements
 | 
						Resources                         *v1.ResourceRequirements
 | 
				
			||||||
 | 
						RuntimeClassHandler               *string
 | 
				
			||||||
	Tolerations                       []v1.Toleration
 | 
						Tolerations                       []v1.Toleration
 | 
				
			||||||
	NodeName                          string
 | 
						NodeName                          string
 | 
				
			||||||
	Ports                             []v1.ContainerPort
 | 
						Ports                             []v1.ContainerPort
 | 
				
			||||||
@@ -196,6 +198,116 @@ var _ = SIGDescribe("SchedulerPredicates [Serial]", func() {
 | 
				
			|||||||
		verifyResult(cs, podsNeededForSaturation, 1, ns)
 | 
							verifyResult(cs, podsNeededForSaturation, 1, ns)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This test verifies we don't allow scheduling of pods in a way that sum of limits +
 | 
				
			||||||
 | 
						// associated overhead is greater than machine's capacity.
 | 
				
			||||||
 | 
						// It assumes that cluster add-on pods stay stable and cannot be run in parallel
 | 
				
			||||||
 | 
						// with any other test that touches Nodes or Pods.
 | 
				
			||||||
 | 
						// Because of this we need to have precise control on what's running in the cluster.
 | 
				
			||||||
 | 
						// Test scenario:
 | 
				
			||||||
 | 
						// 1. Find the first ready node on the system, and add a fake resource for test
 | 
				
			||||||
 | 
						// 2. Create one with affinity to the particular node that uses 70% of the fake resource.
 | 
				
			||||||
 | 
						// 3. Wait for the pod to be scheduled.
 | 
				
			||||||
 | 
						// 4. Create another pod with affinity to the particular node that needs 20% of the fake resource and
 | 
				
			||||||
 | 
						//    an overhead set as 25% of the fake resource.
 | 
				
			||||||
 | 
						// 5. Make sure this additional pod is not scheduled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ginkgo.Context("validates pod overhead is considered along with resource limits of pods that are allowed to run", func() {
 | 
				
			||||||
 | 
							var testNodeName string
 | 
				
			||||||
 | 
							var handler string
 | 
				
			||||||
 | 
							var beardsecond v1.ResourceName = "example.com/beardsecond"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ginkgo.BeforeEach(func() {
 | 
				
			||||||
 | 
								WaitForStableCluster(cs, masterNodes)
 | 
				
			||||||
 | 
								ginkgo.By("Add RuntimeClass and fake resource")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// find a node which can run a pod:
 | 
				
			||||||
 | 
								testNodeName = GetNodeThatCanRunPod(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Get node object:
 | 
				
			||||||
 | 
								node, err := cs.CoreV1().Nodes().Get(context.TODO(), testNodeName, metav1.GetOptions{})
 | 
				
			||||||
 | 
								framework.ExpectNoError(err, "unable to get node object for node %v", testNodeName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// update Node API object with a fake resource
 | 
				
			||||||
 | 
								nodeCopy := node.DeepCopy()
 | 
				
			||||||
 | 
								nodeCopy.ResourceVersion = "0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								nodeCopy.Status.Capacity[beardsecond] = resource.MustParse("1000")
 | 
				
			||||||
 | 
								_, err = cs.CoreV1().Nodes().UpdateStatus(context.TODO(), nodeCopy, metav1.UpdateOptions{})
 | 
				
			||||||
 | 
								framework.ExpectNoError(err, "unable to apply fake resource to %v", testNodeName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Register a runtimeClass with overhead set as 25% of the available beard-seconds
 | 
				
			||||||
 | 
								handler = e2enode.PreconfiguredRuntimeClassHandler(framework.TestContext.ContainerRuntime)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								rc := &nodev1beta1.RuntimeClass{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: handler},
 | 
				
			||||||
 | 
									Handler:    handler,
 | 
				
			||||||
 | 
									Overhead: &nodev1beta1.Overhead{
 | 
				
			||||||
 | 
										PodFixed: v1.ResourceList{
 | 
				
			||||||
 | 
											beardsecond: resource.MustParse("250"),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								_, err = cs.NodeV1beta1().RuntimeClasses().Create(context.TODO(), rc, metav1.CreateOptions{})
 | 
				
			||||||
 | 
								framework.ExpectNoError(err, "failed to create RuntimeClass resource")
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ginkgo.AfterEach(func() {
 | 
				
			||||||
 | 
								ginkgo.By("Remove fake resource and RuntimeClass")
 | 
				
			||||||
 | 
								// remove fake resource:
 | 
				
			||||||
 | 
								if testNodeName != "" {
 | 
				
			||||||
 | 
									// Get node object:
 | 
				
			||||||
 | 
									node, err := cs.CoreV1().Nodes().Get(context.TODO(), testNodeName, metav1.GetOptions{})
 | 
				
			||||||
 | 
									framework.ExpectNoError(err, "unable to get node object for node %v", testNodeName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									nodeCopy := node.DeepCopy()
 | 
				
			||||||
 | 
									// force it to update
 | 
				
			||||||
 | 
									nodeCopy.ResourceVersion = "0"
 | 
				
			||||||
 | 
									delete(nodeCopy.Status.Capacity, beardsecond)
 | 
				
			||||||
 | 
									_, err = cs.CoreV1().Nodes().UpdateStatus(context.TODO(), nodeCopy, metav1.UpdateOptions{})
 | 
				
			||||||
 | 
									framework.ExpectNoError(err, "unable to update node %v", testNodeName)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// remove RuntimeClass
 | 
				
			||||||
 | 
								cs.NodeV1beta1().RuntimeClasses().Delete(context.TODO(), e2enode.PreconfiguredRuntimeClassHandler(framework.TestContext.ContainerRuntime), nil)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ginkgo.It("verify pod overhead is accounted for", func() {
 | 
				
			||||||
 | 
								framework.ExpectEqual(testNodeName != "", true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ginkgo.By("Starting Pod to consume most of the node's resource.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Create pod which requires 70% of the available beard-seconds.
 | 
				
			||||||
 | 
								fillerPod := createPausePod(f, pausePodConfig{
 | 
				
			||||||
 | 
									Name: "filler-pod-" + string(uuid.NewUUID()),
 | 
				
			||||||
 | 
									Resources: &v1.ResourceRequirements{
 | 
				
			||||||
 | 
										Requests: v1.ResourceList{beardsecond: resource.MustParse("700")},
 | 
				
			||||||
 | 
										Limits:   v1.ResourceList{beardsecond: resource.MustParse("700")},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Wait for filler pod to schedule.
 | 
				
			||||||
 | 
								framework.ExpectNoError(e2epod.WaitForPodRunningInNamespace(cs, fillerPod))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ginkgo.By("Creating another pod that requires unavailable amount of resources.")
 | 
				
			||||||
 | 
								// Create another pod that requires 20% of available beard-seconds, but utilizes the RuntimeClass
 | 
				
			||||||
 | 
								// which defines a pod overhead that requires an additional 25%.
 | 
				
			||||||
 | 
								// This pod should remain pending as at least 70% of beard-second in
 | 
				
			||||||
 | 
								// the node are already consumed.
 | 
				
			||||||
 | 
								podName := "additional-pod" + string(uuid.NewUUID())
 | 
				
			||||||
 | 
								conf := pausePodConfig{
 | 
				
			||||||
 | 
									RuntimeClassHandler: &handler,
 | 
				
			||||||
 | 
									Name:                podName,
 | 
				
			||||||
 | 
									Labels:              map[string]string{"name": "additional"},
 | 
				
			||||||
 | 
									Resources: &v1.ResourceRequirements{
 | 
				
			||||||
 | 
										Limits: v1.ResourceList{beardsecond: resource.MustParse("200")},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								WaitForSchedulerAfterAction(f, createPausePodAction(f, conf), ns, podName, false)
 | 
				
			||||||
 | 
								verifyResult(cs, 1, 1, ns)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// This test verifies we don't allow scheduling of pods in a way that sum of
 | 
						// This test verifies we don't allow scheduling of pods in a way that sum of
 | 
				
			||||||
	// resource requests of pods is greater than machines capacity.
 | 
						// resource requests of pods is greater than machines capacity.
 | 
				
			||||||
	// It assumes that cluster add-on pods stay stable and cannot be run in parallel
 | 
						// It assumes that cluster add-on pods stay stable and cannot be run in parallel
 | 
				
			||||||
@@ -715,6 +827,7 @@ func initPausePod(f *framework.Framework, conf pausePodConfig) *v1.Pod {
 | 
				
			|||||||
			NodeSelector:              conf.NodeSelector,
 | 
								NodeSelector:              conf.NodeSelector,
 | 
				
			||||||
			Affinity:                  conf.Affinity,
 | 
								Affinity:                  conf.Affinity,
 | 
				
			||||||
			TopologySpreadConstraints: conf.TopologySpreadConstraints,
 | 
								TopologySpreadConstraints: conf.TopologySpreadConstraints,
 | 
				
			||||||
 | 
								RuntimeClassName:          conf.RuntimeClassHandler,
 | 
				
			||||||
			Containers: []v1.Container{
 | 
								Containers: []v1.Container{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					Name:  conf.Name,
 | 
										Name:  conf.Name,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user