Change:
now is separately organized as top-level SIGDescribe. However, it's better folded into the existing top-level SIGDescribe, and it can be refactored as a . /sig scheduling /kind cleanup /good-first-issue
I'm new to Kubernetes and I'd like to work on this issue :)
Sure, feel free to take it.
/assign
Seems like the doesn't follow this issue. I will take it.
are you still working on it?
/assign
I will be working on this during the weekend because I'm on call this week.
As this is a good-first-issue issue. Let's leave it to new contributor.
Sure.
created a PR for this.
It seems was raised slightly earlier than your PR, sorry that we have to follow the first-come-first-serve principle. /remove-good-first-issue

Discussion:
} }) }) <del> }) // construct a fakecpu so as to set it to status of Node object // otherwise if we update CPU/Memory/etc, those values will be corrected back by kubelet var fakecpu v1.ResourceName = "example.com/fakecpu" </del> <ins> ginkgo.Context("PreemptionExecutionPath", func() { // construct a fakecpu so as to set it to status of Node object // otherwise if we update CPU/Memory/etc, those values will be corrected back by kubelet var fakecpu v1.ResourceName = "example.com/fakecpu" var cs clientset.Interface var node *v1.Node var ns, nodeHostNameLabel string f := framework.NewDefaultFramework("sched-preemption-path") </ins> <del> var _ = SIGDescribe("PreemptionExecutionPath", func() { var cs clientset.Interface var node *v1.Node var ns, nodeHostNameLabel string f := framework.NewDefaultFramework("sched-preemption-path") </del> <ins> priorityPairs := make([]priorityPair, 0) </ins> <del> priorityPairs := make([]priorityPair, 0) </del> <ins> ginkgo.AfterEach(func() { // print out additional info if tests failed if ginkgo.CurrentGinkgoTestDescription().Failed { // list existing priorities priorityList, err := cs.SchedulingV1().PriorityClasses().List(context.TODO(), metav1.ListOptions{}) if err != nil { framework.Logf("Unable to list priorities: %v", err) } else { framework.Logf("List existing priorities:") for _, p := range priorityList.Items { framework.Logf("%v/%v created at %v", p.Name, p.Value, p.CreationTimestamp) } } } </ins> <del> ginkgo.AfterEach(func() { // print out additional info if tests failed if ginkgo.CurrentGinkgoTestDescription().Failed { // list existing priorities priorityList, err := cs.SchedulingV1().PriorityClasses().List(context.TODO(), metav1.ListOptions{}) </del> <ins> if node != nil { nodeCopy := node.DeepCopy() // force it to update nodeCopy.ResourceVersion = "0" delete(nodeCopy.Status.Capacity, fakecpu) _, err := cs.CoreV1().Nodes().UpdateStatus(context.TODO(), nodeCopy, metav1.UpdateOptions{}) framework.ExpectNoError(err) } for _, pair := range priorityPairs { cs.SchedulingV1().PriorityClasses().Delete(context.TODO(), pair.name, metav1.NewDeleteOptions(0)) } }) ginkgo.BeforeEach(func() { cs = f.ClientSet ns = f.Namespace.Name // find an available node ginkgo.By("Finding an available node") nodeName := GetNodeThatCanRunPod(f) framework.Logf("found a healthy node: %s", nodeName) // get the node API object var err error node, err = cs.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{}) </ins> if err != nil { <del> framework.Logf("Unable to list priorities: %v", err) } else { framework.Logf("List existing priorities:") for _, p := range priorityList.Items { framework.Logf("%v/%v created at %v", p.Name, p.Value, p.CreationTimestamp) } </del> <ins> framework.Failf("error getting node %q: %v", nodeName, err) } var ok bool nodeHostNameLabel, ok = node.GetObjectMeta().GetLabels()["kubernetes.io/hostname"] if !ok { framework.Failf("error getting kubernetes.io/hostname label on node %s", nodeName) </ins> } <del> } if node != nil { </del> <ins> // update Node API object with a fake resource </ins> nodeCopy := node.DeepCopy() // force it to update nodeCopy.ResourceVersion = "0" <del> delete(nodeCopy.Status.Capacity, fakecpu) _, err := cs.CoreV1().Nodes().UpdateStatus(context.TODO(), nodeCopy, metav1.UpdateOptions{}) </del> <ins> nodeCopy.Status.Capacity[fakecpu] = resource.MustParse("1000") node, err = cs.CoreV1().Nodes().UpdateStatus(context.TODO(), nodeCopy, metav1.UpdateOptions{}) </ins> framework.ExpectNoError(err) <del> } for _, pair := range priorityPairs { cs.SchedulingV1().PriorityClasses().Delete(context.TODO(), pair.name, metav1.NewDeleteOptions(0)) } }) ginkgo.BeforeEach(func() { cs = f.ClientSet ns = f.Namespace.Name // find an available node ginkgo.By("Finding an available node") nodeName := GetNodeThatCanRunPod(f) framework.Logf("found a healthy node: %s", nodeName) // get the node API object var err error node, err = cs.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{}) if err != nil { framework.Failf("error getting node %q: %v", nodeName, err) } var ok bool nodeHostNameLabel, ok = node.GetObjectMeta().GetLabels()["kubernetes.io/hostname"] if !ok { framework.Failf("error getting kubernetes.io/hostname label on node %s", nodeName) } // update Node API object with a fake resource nodeCopy := node.DeepCopy() // force it to update nodeCopy.ResourceVersion = "0" nodeCopy.Status.Capacity[fakecpu] = resource.MustParse("1000") node, err = cs.CoreV1().Nodes().UpdateStatus(context.TODO(), nodeCopy, metav1.UpdateOptions{}) framework.ExpectNoError(err) // create four PriorityClass: p1, p2, p3, p4 for i := 1; i <= 4; i++ { priorityName := fmt.Sprintf("p%d", i) priorityVal := int32(i) priorityPairs = append(priorityPairs, priorityPair{name: priorityName, value: priorityVal}) _, err := cs.SchedulingV1().PriorityClasses().Create(context.TODO(), &schedulingv1.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: priorityName}, Value: priorityVal}, metav1.CreateOptions{}) if err != nil { framework.Logf("Failed to create priority '%v/%v': %v", priorityName, priorityVal, err) framework.Logf("Reason: %v. Msg: %v", apierrors.ReasonForError(err), err) </del> <ins> // create four PriorityClass: p1, p2, p3, p4 for i := 1; i <= 4; i++ { priorityName := fmt.Sprintf("p%d", i) priorityVal := int32(i) priorityPairs = append(priorityPairs, priorityPair{name: priorityName, value: priorityVal}) _, err := cs.SchedulingV1().PriorityClasses().Create(context.TODO(), &schedulingv1.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: priorityName}, Value: priorityVal}, metav1.CreateOptions{}) if err != nil { framework.Logf("Failed to create priority '%v/%v': %v", priorityName, priorityVal, err) framework.Logf("Reason: %v. Msg: %v", apierrors.ReasonForError(err), err) } framework.ExpectEqual(err == nil || apierrors.IsAlreadyExists(err), true) </ins> } <del> framework.ExpectEqual(err == nil || apierrors.IsAlreadyExists(err), true) } }) </del> <ins> }) </ins> <del> ginkgo.It("runs ReplicaSets to verify preemption running path", func() { podNamesSeen := []int32{0, 0, 0} stopCh := make(chan struct{}) </del> <ins> ginkgo.It("runs ReplicaSets to verify preemption running path", func() { podNamesSeen := []int32{0, 0, 0} stopCh := make(chan struct{}) </ins> <del> // create a pod controller to list/watch pod events from the test framework namespace _, podController := cache.NewInformer( &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { obj, err := f.ClientSet.CoreV1().Pods(ns).List(context.TODO(), options) return runtime.Object(obj), err }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { return f.ClientSet.CoreV1().Pods(ns).Watch(context.TODO(), options) </del> <ins> // create a pod controller to list/watch pod events from the test framework namespace _, podController := cache.NewInformer( &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { obj, err := f.ClientSet.CoreV1().Pods(ns).List(context.TODO(), options) return runtime.Object(obj), err }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { return f.ClientSet.CoreV1().Pods(ns).Watch(context.TODO(), options) }, </ins> }, <del> }, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { if pod, ok := obj.(*v1.Pod); ok { if strings.HasPrefix(pod.Name, "rs-pod1") { atomic.AddInt32(&podNamesSeen[0], 1) } else if strings.HasPrefix(pod.Name, "rs-pod2") { atomic.AddInt32(&podNamesSeen[1], 1) } else if strings.HasPrefix(pod.Name, "rs-pod3") { atomic.AddInt32(&podNamesSeen[2], 1) </del> <ins> &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { if pod, ok := obj.(*v1.Pod); ok { if strings.HasPrefix(pod.Name, "rs-pod1") { atomic.AddInt32(&podNamesSeen[0], 1) } else if strings.HasPrefix(pod.Name, "rs-pod2") { atomic.AddInt32(&podNamesSeen[1], 1) } else if strings.HasPrefix(pod.Name, "rs-pod3") { atomic.AddInt32(&podNamesSeen[2], 1) } </ins> } <del> } </del> <ins> }, </ins> }, <del> }, ) go podController.Run(stopCh) defer close(stopCh) // prepare three ReplicaSet rsConfs := []pauseRSConfig{ { Replicas: int32(1), PodConfig: pausePodConfig{ Name:              "pod1", Namespace:         ns, Labels:            map[string]string{"name": "pod1"}, PriorityClassName: "p1", NodeSelector:      map[string]string{"kubernetes.io/hostname": nodeHostNameLabel}, Resources: &v1.ResourceRequirements{ Requests: v1.ResourceList{fakecpu: resource.MustParse("200")}, Limits:   v1.ResourceList{fakecpu: resource.MustParse("200")}, </del> <ins> ) go podController.Run(stopCh) defer close(stopCh) // prepare three ReplicaSet rsConfs := []pauseRSConfig{ { Replicas: int32(1), PodConfig: pausePodConfig{ Name:              "pod1", Namespace:         ns, Labels:            map[string]string{"name": "pod1"}, PriorityClassName: "p1", NodeSelector:      map[string]string{"kubernetes.io/hostname": nodeHostNameLabel}, Resources: &v1.ResourceRequirements{ Requests: v1.ResourceList{fakecpu: resource.MustParse("200")}, Limits:   v1.ResourceList{fakecpu: resource.MustParse("200")}, }, </ins> }, }, <del> }, { Replicas: int32(1), PodConfig: pausePodConfig{ Name:              "pod2", Namespace:         ns, Labels:            map[string]string{"name": "pod2"}, PriorityClassName: "p2", NodeSelector:      map[string]string{"kubernetes.io/hostname": nodeHostNameLabel}, Resources: &v1.ResourceRequirements{ Requests: v1.ResourceList{fakecpu: resource.MustParse("300")}, Limits:   v1.ResourceList{fakecpu: resource.MustParse("300")}, </del> <ins> { Replicas: int32(1), PodConfig: pausePodConfig{ Name:              "pod2", Namespace:         ns, Labels:            map[string]string{"name": "pod2"}, PriorityClassName: "p2", NodeSelector:      map[string]string{"kubernetes.io/hostname": nodeHostNameLabel}, Resources: &v1.ResourceRequirements{ Requests: v1.ResourceList{fakecpu: resource.MustParse("300")}, Limits:   v1.ResourceList{fakecpu: resource.MustParse("300")}, }, </ins> }, }, <del> }, { Replicas: int32(1), PodConfig: pausePodConfig{ Name:              "pod3", Namespace:         ns, Labels:            map[string]string{"name": "pod3"}, PriorityClassName: "p3", NodeSelector:      map[string]string{"kubernetes.io/hostname": nodeHostNameLabel}, Resources: &v1.ResourceRequirements{ Requests: v1.ResourceList{fakecpu: resource.MustParse("450")}, Limits:   v1.ResourceList{fakecpu: resource.MustParse("450")}, </del> <ins> { Replicas: int32(1), PodConfig: pausePodConfig{ Name:              "pod3", Namespace:         ns, Labels:            map[string]string{"name": "pod3"}, PriorityClassName: "p3", NodeSelector:      map[string]string{"kubernetes.io/hostname": nodeHostNameLabel}, Resources: &v1.ResourceRequirements{ Requests: v1.ResourceList{fakecpu: resource.MustParse("450")}, Limits:   v1.ResourceList{fakecpu: resource.MustParse("450")}, }, </ins> }, }, <del> }, } // create ReplicaSet{1,2,3} so as to occupy 950/1000 fake resource for i := range rsConfs { runPauseRS(f, rsConfs[i]) } </del> <ins> } // create ReplicaSet{1,2,3} so as to occupy 950/1000 fake resource for i := range rsConfs { runPauseRS(f, rsConfs[i]) } framework.Logf("pods created so far: %v", podNamesSeen) framework.Logf("length of pods created so far: %v", len(podNamesSeen)) </ins> <del> framework.Logf("pods created so far: %v", podNamesSeen) framework.Logf("length of pods created so far: %v", len(podNamesSeen)) </del> <ins> // create a Preemptor Pod preemptorPodConf := pausePodConfig{ Name:              "pod4", Namespace:         ns, Labels:            map[string]string{"name": "pod4"}, PriorityClassName: "p4", NodeSelector:      map[string]string{"kubernetes.io/hostname": nodeHostNameLabel}, Resources: &v1.ResourceRequirements{ Requests: v1.ResourceList{fakecpu: resource.MustParse("500")}, Limits:   v1.ResourceList{fakecpu: resource.MustParse("500")}, }, } preemptorPod := createPod(f, preemptorPodConf) waitForPreemptingWithTimeout(f, preemptorPod, framework.PodGetTimeout) </ins> <del> // create a Preemptor Pod preemptorPodConf := pausePodConfig{ Name:              "pod4", Namespace:         ns, Labels:            map[string]string{"name": "pod4"}, PriorityClassName: "p4", NodeSelector:      map[string]string{"kubernetes.io/hostname": nodeHostNameLabel}, Resources: &v1.ResourceRequirements{ Requests: v1.ResourceList{fakecpu: resource.MustParse("500")}, Limits:   v1.ResourceList{fakecpu: resource.MustParse("500")}, }, } preemptorPod := createPod(f, preemptorPodConf) waitForPreemptingWithTimeout(f, preemptorPod, framework.PodGetTimeout) </del> <ins> framework.Logf("pods created so far: %v", podNamesSeen) </ins> <del> framework.Logf("pods created so far: %v", podNamesSeen) </del> <ins> // count pods number of ReplicaSet{1,2,3}: // - if it's more than expected replicas, it denotes its pods have been over-preempted // - if it's less than expected replicas, it denotes its pods are under-preempted // "*2" means pods of ReplicaSet{1,2} are expected to be only preempted once. expectedRSPods := []int32{1 * 2, 1 * 2, 1} err := wait.Poll(framework.Poll, framework.PollShortTimeout, func() (bool, error) { for i := 0; i < len(podNamesSeen); i++ { got := atomic.LoadInt32(&podNamesSeen[i]) if got < expectedRSPods[i] { framework.Logf("waiting for rs%d to observe %d pod creations, got %d", i+1, expectedRSPods[i], got) return false, nil } else if got > expectedRSPods[i] { return false, fmt.Errorf("rs%d had more than %d pods created: %d", i+1, expectedRSPods[i], got) } } return true, nil }) if err != nil { framework.Logf("pods created so far: %v", podNamesSeen) framework.Failf("failed pod observation expectations: %v", err) } </ins> <del> // count pods number of ReplicaSet{1,2,3}: // - if it's more than expected replicas, it denotes its pods have been over-preempted // - if it's less than expected replicas, it denotes its pods are under-preempted // "*2" means pods of ReplicaSet{1,2} are expected to be only preempted once. expectedRSPods := []int32{1 * 2, 1 * 2, 1} err := wait.Poll(framework.Poll, framework.PollShortTimeout, func() (bool, error) { </del> <ins> // If logic continues to here, we should do a final check to ensure within a time period, // the state is stable; otherwise, pods may be over-preempted. time.Sleep(5 * time.Second) </ins> for i := 0; i < len(podNamesSeen); i++ { got := atomic.LoadInt32(&podNamesSeen[i]) if got < expectedRSPods[i] { <del> framework.Logf("waiting for rs%d to observe %d pod creations, got %d", i+1, expectedRSPods[i], got) return false, nil </del> <ins> framework.Failf("pods of ReplicaSet%d have been under-preempted: expect %v pod names, but got %d", i+1, expectedRSPods[i], got) </ins> } else if got > expectedRSPods[i] { <del> return false, fmt.Errorf("rs%d had more than %d pods created: %d", i+1, expectedRSPods[i], got) </del> <ins> framework.Failf("pods of ReplicaSet%d have been over-preempted: expect %v pod names, but got %d", i+1, expectedRSPods[i], got) </ins> } } <del> return true, nil </del> }) <del> if err != nil { framework.Logf("pods created so far: %v", podNamesSeen) framework.Failf("failed pod observation expectations: %v", err) } // If logic continues to here, we should do a final check to ensure within a time period, // the state is stable; otherwise, pods may be over-preempted. time.Sleep(5 * time.Second) for i := 0; i < len(podNamesSeen); i++ { got := atomic.LoadInt32(&podNamesSeen[i]) if got < expectedRSPods[i] { framework.Failf("pods of ReplicaSet%d have been under-preempted: expect %v pod names, but got %d", i+1, expectedRSPods[i], got) } else if got > expectedRSPods[i] { framework.Failf("pods of ReplicaSet%d have been over-preempted: expect %v pod names, but got %d", i+1, expectedRSPods[i], got) } } </del> }) })