mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 23:37:01 +00:00
cleanup: move NodeSchedulingPropertiesChange
This commit is contained in:
parent
c21aabfdc7
commit
9772ff2848
@ -24,7 +24,6 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
storagev1 "k8s.io/api/storage/v1"
|
storagev1 "k8s.io/api/storage/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
@ -94,7 +93,7 @@ func (sched *Scheduler) updateNodeInCache(oldObj, newObj interface{}) {
|
|||||||
logger.V(4).Info("Update event for node", "node", klog.KObj(newNode))
|
logger.V(4).Info("Update event for node", "node", klog.KObj(newNode))
|
||||||
nodeInfo := sched.Cache.UpdateNode(logger, oldNode, newNode)
|
nodeInfo := sched.Cache.UpdateNode(logger, oldNode, newNode)
|
||||||
// Only requeue unschedulable pods if the node became more schedulable.
|
// Only requeue unschedulable pods if the node became more schedulable.
|
||||||
for _, evt := range nodeSchedulingPropertiesChange(newNode, oldNode) {
|
for _, evt := range queue.NodeSchedulingPropertiesChange(newNode, oldNode) {
|
||||||
sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, evt, oldNode, newNode, preCheckForNode(nodeInfo))
|
sched.SchedulingQueue.MoveAllToActiveOrBackoffQueue(logger, evt, oldNode, newNode, preCheckForNode(nodeInfo))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -571,62 +570,6 @@ func addAllEventHandlers(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeSchedulingPropertiesChange(newNode *v1.Node, oldNode *v1.Node) []framework.ClusterEvent {
|
|
||||||
var events []framework.ClusterEvent
|
|
||||||
|
|
||||||
if nodeSpecUnschedulableChanged(newNode, oldNode) {
|
|
||||||
events = append(events, queue.NodeSpecUnschedulableChange)
|
|
||||||
}
|
|
||||||
if nodeAllocatableChanged(newNode, oldNode) {
|
|
||||||
events = append(events, queue.NodeAllocatableChange)
|
|
||||||
}
|
|
||||||
if nodeLabelsChanged(newNode, oldNode) {
|
|
||||||
events = append(events, queue.NodeLabelChange)
|
|
||||||
}
|
|
||||||
if nodeTaintsChanged(newNode, oldNode) {
|
|
||||||
events = append(events, queue.NodeTaintChange)
|
|
||||||
}
|
|
||||||
if nodeConditionsChanged(newNode, oldNode) {
|
|
||||||
events = append(events, queue.NodeConditionChange)
|
|
||||||
}
|
|
||||||
if nodeAnnotationsChanged(newNode, oldNode) {
|
|
||||||
events = append(events, queue.NodeAnnotationChange)
|
|
||||||
}
|
|
||||||
|
|
||||||
return events
|
|
||||||
}
|
|
||||||
|
|
||||||
func nodeAllocatableChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
|
||||||
return !equality.Semantic.DeepEqual(oldNode.Status.Allocatable, newNode.Status.Allocatable)
|
|
||||||
}
|
|
||||||
|
|
||||||
func nodeLabelsChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
|
||||||
return !equality.Semantic.DeepEqual(oldNode.GetLabels(), newNode.GetLabels())
|
|
||||||
}
|
|
||||||
|
|
||||||
func nodeTaintsChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
|
||||||
return !equality.Semantic.DeepEqual(newNode.Spec.Taints, oldNode.Spec.Taints)
|
|
||||||
}
|
|
||||||
|
|
||||||
func nodeConditionsChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
|
||||||
strip := func(conditions []v1.NodeCondition) map[v1.NodeConditionType]v1.ConditionStatus {
|
|
||||||
conditionStatuses := make(map[v1.NodeConditionType]v1.ConditionStatus, len(conditions))
|
|
||||||
for i := range conditions {
|
|
||||||
conditionStatuses[conditions[i].Type] = conditions[i].Status
|
|
||||||
}
|
|
||||||
return conditionStatuses
|
|
||||||
}
|
|
||||||
return !equality.Semantic.DeepEqual(strip(oldNode.Status.Conditions), strip(newNode.Status.Conditions))
|
|
||||||
}
|
|
||||||
|
|
||||||
func nodeSpecUnschedulableChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
|
||||||
return newNode.Spec.Unschedulable != oldNode.Spec.Unschedulable && !newNode.Spec.Unschedulable
|
|
||||||
}
|
|
||||||
|
|
||||||
func nodeAnnotationsChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
|
||||||
return !equality.Semantic.DeepEqual(oldNode.GetAnnotations(), newNode.GetAnnotations())
|
|
||||||
}
|
|
||||||
|
|
||||||
func preCheckForNode(nodeInfo *framework.NodeInfo) queue.PreEnqueueCheck {
|
func preCheckForNode(nodeInfo *framework.NodeInfo) queue.PreEnqueueCheck {
|
||||||
// Note: the following checks doesn't take preemption into considerations, in very rare
|
// Note: the following checks doesn't take preemption into considerations, in very rare
|
||||||
// cases (e.g., node resizing), "pod" may still fail a check but preemption helps. We deliberately
|
// cases (e.g., node resizing), "pod" may still fail a check but preemption helps. We deliberately
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
resourcev1alpha2 "k8s.io/api/resource/v1alpha2"
|
resourcev1alpha2 "k8s.io/api/resource/v1alpha2"
|
||||||
storagev1 "k8s.io/api/storage/v1"
|
storagev1 "k8s.io/api/storage/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
"k8s.io/klog/v2/ktesting"
|
"k8s.io/klog/v2/ktesting"
|
||||||
@ -53,157 +52,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/scheduler/util/assumecache"
|
"k8s.io/kubernetes/pkg/scheduler/util/assumecache"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNodeAllocatableChanged(t *testing.T) {
|
|
||||||
newQuantity := func(value int64) resource.Quantity {
|
|
||||||
return *resource.NewQuantity(value, resource.BinarySI)
|
|
||||||
}
|
|
||||||
for _, test := range []struct {
|
|
||||||
Name string
|
|
||||||
Changed bool
|
|
||||||
OldAllocatable v1.ResourceList
|
|
||||||
NewAllocatable v1.ResourceList
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "no allocatable resources changed",
|
|
||||||
Changed: false,
|
|
||||||
OldAllocatable: v1.ResourceList{v1.ResourceMemory: newQuantity(1024)},
|
|
||||||
NewAllocatable: v1.ResourceList{v1.ResourceMemory: newQuantity(1024)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "new node has more allocatable resources",
|
|
||||||
Changed: true,
|
|
||||||
OldAllocatable: v1.ResourceList{v1.ResourceMemory: newQuantity(1024)},
|
|
||||||
NewAllocatable: v1.ResourceList{v1.ResourceMemory: newQuantity(1024), v1.ResourceStorage: newQuantity(1024)},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
|
||||||
oldNode := &v1.Node{Status: v1.NodeStatus{Allocatable: test.OldAllocatable}}
|
|
||||||
newNode := &v1.Node{Status: v1.NodeStatus{Allocatable: test.NewAllocatable}}
|
|
||||||
changed := nodeAllocatableChanged(newNode, oldNode)
|
|
||||||
if changed != test.Changed {
|
|
||||||
t.Errorf("nodeAllocatableChanged should be %t, got %t", test.Changed, changed)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNodeLabelsChanged(t *testing.T) {
|
|
||||||
for _, test := range []struct {
|
|
||||||
Name string
|
|
||||||
Changed bool
|
|
||||||
OldLabels map[string]string
|
|
||||||
NewLabels map[string]string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "no labels changed",
|
|
||||||
Changed: false,
|
|
||||||
OldLabels: map[string]string{"foo": "bar"},
|
|
||||||
NewLabels: map[string]string{"foo": "bar"},
|
|
||||||
},
|
|
||||||
// Labels changed.
|
|
||||||
{
|
|
||||||
Name: "new node has more labels",
|
|
||||||
Changed: true,
|
|
||||||
OldLabels: map[string]string{"foo": "bar"},
|
|
||||||
NewLabels: map[string]string{"foo": "bar", "test": "value"},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
|
||||||
oldNode := &v1.Node{ObjectMeta: metav1.ObjectMeta{Labels: test.OldLabels}}
|
|
||||||
newNode := &v1.Node{ObjectMeta: metav1.ObjectMeta{Labels: test.NewLabels}}
|
|
||||||
changed := nodeLabelsChanged(newNode, oldNode)
|
|
||||||
if changed != test.Changed {
|
|
||||||
t.Errorf("Test case %q failed: should be %t, got %t", test.Name, test.Changed, changed)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNodeTaintsChanged(t *testing.T) {
|
|
||||||
for _, test := range []struct {
|
|
||||||
Name string
|
|
||||||
Changed bool
|
|
||||||
OldTaints []v1.Taint
|
|
||||||
NewTaints []v1.Taint
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "no taint changed",
|
|
||||||
Changed: false,
|
|
||||||
OldTaints: []v1.Taint{{Key: "key", Value: "value"}},
|
|
||||||
NewTaints: []v1.Taint{{Key: "key", Value: "value"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "taint value changed",
|
|
||||||
Changed: true,
|
|
||||||
OldTaints: []v1.Taint{{Key: "key", Value: "value1"}},
|
|
||||||
NewTaints: []v1.Taint{{Key: "key", Value: "value2"}},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
|
||||||
oldNode := &v1.Node{Spec: v1.NodeSpec{Taints: test.OldTaints}}
|
|
||||||
newNode := &v1.Node{Spec: v1.NodeSpec{Taints: test.NewTaints}}
|
|
||||||
changed := nodeTaintsChanged(newNode, oldNode)
|
|
||||||
if changed != test.Changed {
|
|
||||||
t.Errorf("Test case %q failed: should be %t, not %t", test.Name, test.Changed, changed)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNodeConditionsChanged(t *testing.T) {
|
|
||||||
nodeConditionType := reflect.TypeOf(v1.NodeCondition{})
|
|
||||||
if nodeConditionType.NumField() != 6 {
|
|
||||||
t.Errorf("NodeCondition type has changed. The nodeConditionsChanged() function must be reevaluated.")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
|
||||||
Name string
|
|
||||||
Changed bool
|
|
||||||
OldConditions []v1.NodeCondition
|
|
||||||
NewConditions []v1.NodeCondition
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "no condition changed",
|
|
||||||
Changed: false,
|
|
||||||
OldConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue}},
|
|
||||||
NewConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "only LastHeartbeatTime changed",
|
|
||||||
Changed: false,
|
|
||||||
OldConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue, LastHeartbeatTime: metav1.Unix(1, 0)}},
|
|
||||||
NewConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue, LastHeartbeatTime: metav1.Unix(2, 0)}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "new node has more healthy conditions",
|
|
||||||
Changed: true,
|
|
||||||
OldConditions: []v1.NodeCondition{},
|
|
||||||
NewConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "new node has less unhealthy conditions",
|
|
||||||
Changed: true,
|
|
||||||
OldConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue}},
|
|
||||||
NewConditions: []v1.NodeCondition{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "condition status changed",
|
|
||||||
Changed: true,
|
|
||||||
OldConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
|
|
||||||
NewConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
|
||||||
oldNode := &v1.Node{Status: v1.NodeStatus{Conditions: test.OldConditions}}
|
|
||||||
newNode := &v1.Node{Status: v1.NodeStatus{Conditions: test.NewConditions}}
|
|
||||||
changed := nodeConditionsChanged(newNode, oldNode)
|
|
||||||
if changed != test.Changed {
|
|
||||||
t.Errorf("Test case %q failed: should be %t, got %t", test.Name, test.Changed, changed)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePodInCache(t *testing.T) {
|
func TestUpdatePodInCache(t *testing.T) {
|
||||||
ttl := 10 * time.Second
|
ttl := 10 * time.Second
|
||||||
nodeName := "node"
|
nodeName := "node"
|
||||||
@ -574,91 +422,3 @@ func TestAdmissionCheck(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeSchedulingPropertiesChange(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
newNode *v1.Node
|
|
||||||
oldNode *v1.Node
|
|
||||||
wantEvents []framework.ClusterEvent
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "no specific changed applied",
|
|
||||||
newNode: st.MakeNode().Unschedulable(false).Obj(),
|
|
||||||
oldNode: st.MakeNode().Unschedulable(false).Obj(),
|
|
||||||
wantEvents: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "only node spec unavailable changed",
|
|
||||||
newNode: st.MakeNode().Unschedulable(false).Obj(),
|
|
||||||
oldNode: st.MakeNode().Unschedulable(true).Obj(),
|
|
||||||
wantEvents: []framework.ClusterEvent{queue.NodeSpecUnschedulableChange},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "only node allocatable changed",
|
|
||||||
newNode: st.MakeNode().Capacity(map[v1.ResourceName]string{
|
|
||||||
v1.ResourceCPU: "1000m",
|
|
||||||
v1.ResourceMemory: "100m",
|
|
||||||
v1.ResourceName("example.com/foo"): "1"},
|
|
||||||
).Obj(),
|
|
||||||
oldNode: st.MakeNode().Capacity(map[v1.ResourceName]string{
|
|
||||||
v1.ResourceCPU: "1000m",
|
|
||||||
v1.ResourceMemory: "100m",
|
|
||||||
v1.ResourceName("example.com/foo"): "2"},
|
|
||||||
).Obj(),
|
|
||||||
wantEvents: []framework.ClusterEvent{queue.NodeAllocatableChange},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "only node label changed",
|
|
||||||
newNode: st.MakeNode().Label("foo", "bar").Obj(),
|
|
||||||
oldNode: st.MakeNode().Label("foo", "fuz").Obj(),
|
|
||||||
wantEvents: []framework.ClusterEvent{queue.NodeLabelChange},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "only node taint changed",
|
|
||||||
newNode: st.MakeNode().Taints([]v1.Taint{
|
|
||||||
{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectNoSchedule},
|
|
||||||
}).Obj(),
|
|
||||||
oldNode: st.MakeNode().Taints([]v1.Taint{
|
|
||||||
{Key: v1.TaintNodeUnschedulable, Value: "foo", Effect: v1.TaintEffectNoSchedule},
|
|
||||||
}).Obj(),
|
|
||||||
wantEvents: []framework.ClusterEvent{queue.NodeTaintChange},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "only node annotation changed",
|
|
||||||
newNode: st.MakeNode().Annotation("foo", "bar").Obj(),
|
|
||||||
oldNode: st.MakeNode().Annotation("foo", "fuz").Obj(),
|
|
||||||
wantEvents: []framework.ClusterEvent{queue.NodeAnnotationChange},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "only node condition changed",
|
|
||||||
newNode: st.MakeNode().Obj(),
|
|
||||||
oldNode: st.MakeNode().Condition(
|
|
||||||
v1.NodeReady,
|
|
||||||
v1.ConditionTrue,
|
|
||||||
"Ready",
|
|
||||||
"Ready",
|
|
||||||
).Obj(),
|
|
||||||
wantEvents: []framework.ClusterEvent{queue.NodeConditionChange},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "both node label and node taint changed",
|
|
||||||
newNode: st.MakeNode().
|
|
||||||
Label("foo", "bar").
|
|
||||||
Taints([]v1.Taint{
|
|
||||||
{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectNoSchedule},
|
|
||||||
}).Obj(),
|
|
||||||
oldNode: st.MakeNode().Taints([]v1.Taint{
|
|
||||||
{Key: v1.TaintNodeUnschedulable, Value: "foo", Effect: v1.TaintEffectNoSchedule},
|
|
||||||
}).Obj(),
|
|
||||||
wantEvents: []framework.ClusterEvent{queue.NodeLabelChange, queue.NodeTaintChange},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
gotEvents := nodeSchedulingPropertiesChange(tc.newNode, tc.oldNode)
|
|
||||||
if diff := cmp.Diff(tc.wantEvents, gotEvents); diff != "" {
|
|
||||||
t.Errorf("unexpected event (-want, +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package queue
|
package queue
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework"
|
"k8s.io/kubernetes/pkg/scheduler/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,3 +91,59 @@ var (
|
|||||||
// UnschedulableTimeout is the event when a pod stays in unschedulable for longer than timeout.
|
// UnschedulableTimeout is the event when a pod stays in unschedulable for longer than timeout.
|
||||||
UnschedulableTimeout = framework.ClusterEvent{Resource: framework.WildCard, ActionType: framework.All, Label: "UnschedulableTimeout"}
|
UnschedulableTimeout = framework.ClusterEvent{Resource: framework.WildCard, ActionType: framework.All, Label: "UnschedulableTimeout"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func NodeSchedulingPropertiesChange(newNode *v1.Node, oldNode *v1.Node) []framework.ClusterEvent {
|
||||||
|
var events []framework.ClusterEvent
|
||||||
|
|
||||||
|
if nodeSpecUnschedulableChanged(newNode, oldNode) {
|
||||||
|
events = append(events, NodeSpecUnschedulableChange)
|
||||||
|
}
|
||||||
|
if nodeAllocatableChanged(newNode, oldNode) {
|
||||||
|
events = append(events, NodeAllocatableChange)
|
||||||
|
}
|
||||||
|
if nodeLabelsChanged(newNode, oldNode) {
|
||||||
|
events = append(events, NodeLabelChange)
|
||||||
|
}
|
||||||
|
if nodeTaintsChanged(newNode, oldNode) {
|
||||||
|
events = append(events, NodeTaintChange)
|
||||||
|
}
|
||||||
|
if nodeConditionsChanged(newNode, oldNode) {
|
||||||
|
events = append(events, NodeConditionChange)
|
||||||
|
}
|
||||||
|
if nodeAnnotationsChanged(newNode, oldNode) {
|
||||||
|
events = append(events, NodeAnnotationChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeAllocatableChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
||||||
|
return !equality.Semantic.DeepEqual(oldNode.Status.Allocatable, newNode.Status.Allocatable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeLabelsChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
||||||
|
return !equality.Semantic.DeepEqual(oldNode.GetLabels(), newNode.GetLabels())
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeTaintsChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
||||||
|
return !equality.Semantic.DeepEqual(newNode.Spec.Taints, oldNode.Spec.Taints)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeConditionsChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
||||||
|
strip := func(conditions []v1.NodeCondition) map[v1.NodeConditionType]v1.ConditionStatus {
|
||||||
|
conditionStatuses := make(map[v1.NodeConditionType]v1.ConditionStatus, len(conditions))
|
||||||
|
for i := range conditions {
|
||||||
|
conditionStatuses[conditions[i].Type] = conditions[i].Status
|
||||||
|
}
|
||||||
|
return conditionStatuses
|
||||||
|
}
|
||||||
|
return !equality.Semantic.DeepEqual(strip(oldNode.Status.Conditions), strip(newNode.Status.Conditions))
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeSpecUnschedulableChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
||||||
|
return newNode.Spec.Unschedulable != oldNode.Spec.Unschedulable && !newNode.Spec.Unschedulable
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeAnnotationsChanged(newNode *v1.Node, oldNode *v1.Node) bool {
|
||||||
|
return !equality.Semantic.DeepEqual(oldNode.GetAnnotations(), newNode.GetAnnotations())
|
||||||
|
}
|
||||||
|
265
pkg/scheduler/internal/queue/events_test.go
Normal file
265
pkg/scheduler/internal/queue/events_test.go
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 The Kubernetes Authors.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/scheduler/framework"
|
||||||
|
st "k8s.io/kubernetes/pkg/scheduler/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNodeAllocatableChanged(t *testing.T) {
|
||||||
|
newQuantity := func(value int64) resource.Quantity {
|
||||||
|
return *resource.NewQuantity(value, resource.BinarySI)
|
||||||
|
}
|
||||||
|
for _, test := range []struct {
|
||||||
|
Name string
|
||||||
|
Changed bool
|
||||||
|
OldAllocatable v1.ResourceList
|
||||||
|
NewAllocatable v1.ResourceList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "no allocatable resources changed",
|
||||||
|
Changed: false,
|
||||||
|
OldAllocatable: v1.ResourceList{v1.ResourceMemory: newQuantity(1024)},
|
||||||
|
NewAllocatable: v1.ResourceList{v1.ResourceMemory: newQuantity(1024)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "new node has more allocatable resources",
|
||||||
|
Changed: true,
|
||||||
|
OldAllocatable: v1.ResourceList{v1.ResourceMemory: newQuantity(1024)},
|
||||||
|
NewAllocatable: v1.ResourceList{v1.ResourceMemory: newQuantity(1024), v1.ResourceStorage: newQuantity(1024)},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
oldNode := &v1.Node{Status: v1.NodeStatus{Allocatable: test.OldAllocatable}}
|
||||||
|
newNode := &v1.Node{Status: v1.NodeStatus{Allocatable: test.NewAllocatable}}
|
||||||
|
changed := nodeAllocatableChanged(newNode, oldNode)
|
||||||
|
if changed != test.Changed {
|
||||||
|
t.Errorf("nodeAllocatableChanged should be %t, got %t", test.Changed, changed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeLabelsChanged(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
Name string
|
||||||
|
Changed bool
|
||||||
|
OldLabels map[string]string
|
||||||
|
NewLabels map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "no labels changed",
|
||||||
|
Changed: false,
|
||||||
|
OldLabels: map[string]string{"foo": "bar"},
|
||||||
|
NewLabels: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
// Labels changed.
|
||||||
|
{
|
||||||
|
Name: "new node has more labels",
|
||||||
|
Changed: true,
|
||||||
|
OldLabels: map[string]string{"foo": "bar"},
|
||||||
|
NewLabels: map[string]string{"foo": "bar", "test": "value"},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
oldNode := &v1.Node{ObjectMeta: metav1.ObjectMeta{Labels: test.OldLabels}}
|
||||||
|
newNode := &v1.Node{ObjectMeta: metav1.ObjectMeta{Labels: test.NewLabels}}
|
||||||
|
changed := nodeLabelsChanged(newNode, oldNode)
|
||||||
|
if changed != test.Changed {
|
||||||
|
t.Errorf("Test case %q failed: should be %t, got %t", test.Name, test.Changed, changed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeTaintsChanged(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
Name string
|
||||||
|
Changed bool
|
||||||
|
OldTaints []v1.Taint
|
||||||
|
NewTaints []v1.Taint
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "no taint changed",
|
||||||
|
Changed: false,
|
||||||
|
OldTaints: []v1.Taint{{Key: "key", Value: "value"}},
|
||||||
|
NewTaints: []v1.Taint{{Key: "key", Value: "value"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "taint value changed",
|
||||||
|
Changed: true,
|
||||||
|
OldTaints: []v1.Taint{{Key: "key", Value: "value1"}},
|
||||||
|
NewTaints: []v1.Taint{{Key: "key", Value: "value2"}},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
oldNode := &v1.Node{Spec: v1.NodeSpec{Taints: test.OldTaints}}
|
||||||
|
newNode := &v1.Node{Spec: v1.NodeSpec{Taints: test.NewTaints}}
|
||||||
|
changed := nodeTaintsChanged(newNode, oldNode)
|
||||||
|
if changed != test.Changed {
|
||||||
|
t.Errorf("Test case %q failed: should be %t, not %t", test.Name, test.Changed, changed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeConditionsChanged(t *testing.T) {
|
||||||
|
nodeConditionType := reflect.TypeOf(v1.NodeCondition{})
|
||||||
|
if nodeConditionType.NumField() != 6 {
|
||||||
|
t.Errorf("NodeCondition type has changed. The nodeConditionsChanged() function must be reevaluated.")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
Name string
|
||||||
|
Changed bool
|
||||||
|
OldConditions []v1.NodeCondition
|
||||||
|
NewConditions []v1.NodeCondition
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "no condition changed",
|
||||||
|
Changed: false,
|
||||||
|
OldConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue}},
|
||||||
|
NewConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "only LastHeartbeatTime changed",
|
||||||
|
Changed: false,
|
||||||
|
OldConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue, LastHeartbeatTime: metav1.Unix(1, 0)}},
|
||||||
|
NewConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue, LastHeartbeatTime: metav1.Unix(2, 0)}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "new node has more healthy conditions",
|
||||||
|
Changed: true,
|
||||||
|
OldConditions: []v1.NodeCondition{},
|
||||||
|
NewConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "new node has less unhealthy conditions",
|
||||||
|
Changed: true,
|
||||||
|
OldConditions: []v1.NodeCondition{{Type: v1.NodeDiskPressure, Status: v1.ConditionTrue}},
|
||||||
|
NewConditions: []v1.NodeCondition{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "condition status changed",
|
||||||
|
Changed: true,
|
||||||
|
OldConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionFalse}},
|
||||||
|
NewConditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
oldNode := &v1.Node{Status: v1.NodeStatus{Conditions: test.OldConditions}}
|
||||||
|
newNode := &v1.Node{Status: v1.NodeStatus{Conditions: test.NewConditions}}
|
||||||
|
changed := nodeConditionsChanged(newNode, oldNode)
|
||||||
|
if changed != test.Changed {
|
||||||
|
t.Errorf("Test case %q failed: should be %t, got %t", test.Name, test.Changed, changed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeSchedulingPropertiesChange(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
newNode *v1.Node
|
||||||
|
oldNode *v1.Node
|
||||||
|
wantEvents []framework.ClusterEvent
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no specific changed applied",
|
||||||
|
newNode: st.MakeNode().Unschedulable(false).Obj(),
|
||||||
|
oldNode: st.MakeNode().Unschedulable(false).Obj(),
|
||||||
|
wantEvents: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only node spec unavailable changed",
|
||||||
|
newNode: st.MakeNode().Unschedulable(false).Obj(),
|
||||||
|
oldNode: st.MakeNode().Unschedulable(true).Obj(),
|
||||||
|
wantEvents: []framework.ClusterEvent{NodeSpecUnschedulableChange},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only node allocatable changed",
|
||||||
|
newNode: st.MakeNode().Capacity(map[v1.ResourceName]string{
|
||||||
|
v1.ResourceCPU: "1000m",
|
||||||
|
v1.ResourceMemory: "100m",
|
||||||
|
v1.ResourceName("example.com/foo"): "1"},
|
||||||
|
).Obj(),
|
||||||
|
oldNode: st.MakeNode().Capacity(map[v1.ResourceName]string{
|
||||||
|
v1.ResourceCPU: "1000m",
|
||||||
|
v1.ResourceMemory: "100m",
|
||||||
|
v1.ResourceName("example.com/foo"): "2"},
|
||||||
|
).Obj(),
|
||||||
|
wantEvents: []framework.ClusterEvent{NodeAllocatableChange},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only node label changed",
|
||||||
|
newNode: st.MakeNode().Label("foo", "bar").Obj(),
|
||||||
|
oldNode: st.MakeNode().Label("foo", "fuz").Obj(),
|
||||||
|
wantEvents: []framework.ClusterEvent{NodeLabelChange},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only node taint changed",
|
||||||
|
newNode: st.MakeNode().Taints([]v1.Taint{
|
||||||
|
{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectNoSchedule},
|
||||||
|
}).Obj(),
|
||||||
|
oldNode: st.MakeNode().Taints([]v1.Taint{
|
||||||
|
{Key: v1.TaintNodeUnschedulable, Value: "foo", Effect: v1.TaintEffectNoSchedule},
|
||||||
|
}).Obj(),
|
||||||
|
wantEvents: []framework.ClusterEvent{NodeTaintChange},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only node annotation changed",
|
||||||
|
newNode: st.MakeNode().Annotation("foo", "bar").Obj(),
|
||||||
|
oldNode: st.MakeNode().Annotation("foo", "fuz").Obj(),
|
||||||
|
wantEvents: []framework.ClusterEvent{NodeAnnotationChange},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only node condition changed",
|
||||||
|
newNode: st.MakeNode().Obj(),
|
||||||
|
oldNode: st.MakeNode().Condition(
|
||||||
|
v1.NodeReady,
|
||||||
|
v1.ConditionTrue,
|
||||||
|
"Ready",
|
||||||
|
"Ready",
|
||||||
|
).Obj(),
|
||||||
|
wantEvents: []framework.ClusterEvent{NodeConditionChange},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both node label and node taint changed",
|
||||||
|
newNode: st.MakeNode().
|
||||||
|
Label("foo", "bar").
|
||||||
|
Taints([]v1.Taint{
|
||||||
|
{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectNoSchedule},
|
||||||
|
}).Obj(),
|
||||||
|
oldNode: st.MakeNode().Taints([]v1.Taint{
|
||||||
|
{Key: v1.TaintNodeUnschedulable, Value: "foo", Effect: v1.TaintEffectNoSchedule},
|
||||||
|
}).Obj(),
|
||||||
|
wantEvents: []framework.ClusterEvent{NodeLabelChange, NodeTaintChange},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
gotEvents := NodeSchedulingPropertiesChange(tc.newNode, tc.oldNode)
|
||||||
|
if diff := cmp.Diff(tc.wantEvents, gotEvents); diff != "" {
|
||||||
|
t.Errorf("unexpected event (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user