feat: update taint nodes by condition to GA

This commit is contained in:
draveness 2019-09-13 11:38:02 +08:00
parent 54a30700a3
commit 1163a1d51e
27 changed files with 89 additions and 300 deletions

View File

@ -179,7 +179,6 @@ func startNodeLifecycleController(ctx ControllerContext) (http.Handler, bool, er
ctx.ComponentConfig.NodeLifecycleController.UnhealthyZoneThreshold, ctx.ComponentConfig.NodeLifecycleController.UnhealthyZoneThreshold,
ctx.ComponentConfig.NodeLifecycleController.EnableTaintManager, ctx.ComponentConfig.NodeLifecycleController.EnableTaintManager,
utilfeature.DefaultFeatureGate.Enabled(features.TaintBasedEvictions), utilfeature.DefaultFeatureGate.Enabled(features.TaintBasedEvictions),
utilfeature.DefaultFeatureGate.Enabled(features.TaintNodesByCondition),
) )
if err != nil { if err != nil {
return nil, true, err return nil, true, err

View File

@ -26,7 +26,7 @@ import (
"time" "time"
apps "k8s.io/api/apps/v1" apps "k8s.io/api/apps/v1"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"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/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -1804,8 +1804,6 @@ func TestTaintPressureNodeDaemonLaunchesPod(t *testing.T) {
} }
manager.nodeStore.Add(node) manager.nodeStore.Add(node)
// Enabling critical pod and taint nodes by condition feature gate should create critical pod
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TaintNodesByCondition, true)()
manager.dsStore.Add(ds) manager.dsStore.Add(ds)
syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0, 0) syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0, 0)
} }

View File

@ -296,10 +296,6 @@ type Controller struct {
// taints instead of evicting Pods itself. // taints instead of evicting Pods itself.
useTaintBasedEvictions bool useTaintBasedEvictions bool
// if set to true, NodeController will taint Nodes based on its condition for 'NetworkUnavailable',
// 'MemoryPressure', 'PIDPressure' and 'DiskPressure'.
taintNodeByCondition bool
nodeUpdateQueue workqueue.Interface nodeUpdateQueue workqueue.Interface
} }
@ -320,7 +316,7 @@ func NewNodeLifecycleController(
unhealthyZoneThreshold float32, unhealthyZoneThreshold float32,
runTaintManager bool, runTaintManager bool,
useTaintBasedEvictions bool, useTaintBasedEvictions bool,
taintNodeByCondition bool) (*Controller, error) { ) (*Controller, error) {
if kubeClient == nil { if kubeClient == nil {
klog.Fatalf("kubeClient is nil when starting Controller") klog.Fatalf("kubeClient is nil when starting Controller")
@ -359,7 +355,6 @@ func NewNodeLifecycleController(
unhealthyZoneThreshold: unhealthyZoneThreshold, unhealthyZoneThreshold: unhealthyZoneThreshold,
runTaintManager: runTaintManager, runTaintManager: runTaintManager,
useTaintBasedEvictions: useTaintBasedEvictions && runTaintManager, useTaintBasedEvictions: useTaintBasedEvictions && runTaintManager,
taintNodeByCondition: taintNodeByCondition,
nodeUpdateQueue: workqueue.NewNamed("node_lifecycle_controller"), nodeUpdateQueue: workqueue.NewNamed("node_lifecycle_controller"),
} }
if useTaintBasedEvictions { if useTaintBasedEvictions {
@ -469,10 +464,6 @@ func NewNodeLifecycleController(
}), }),
}) })
if nc.taintNodeByCondition {
klog.Infof("Controller will taint node by condition.")
}
nc.leaseLister = leaseInformer.Lister() nc.leaseLister = leaseInformer.Lister()
if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) { if utilfeature.DefaultFeatureGate.Enabled(features.NodeLease) {
nc.leaseInformerSynced = leaseInformer.Informer().HasSynced nc.leaseInformerSynced = leaseInformer.Informer().HasSynced
@ -547,11 +538,9 @@ func (nc *Controller) doNodeProcessingPassWorker() {
return return
} }
nodeName := obj.(string) nodeName := obj.(string)
if nc.taintNodeByCondition { if err := nc.doNoScheduleTaintingPass(nodeName); err != nil {
if err := nc.doNoScheduleTaintingPass(nodeName); err != nil { klog.Errorf("Failed to taint NoSchedule on node <%s>, requeue it: %v", nodeName, err)
klog.Errorf("Failed to taint NoSchedule on node <%s>, requeue it: %v", nodeName, err) // TODO(k82cn): Add nodeName back to the queue
// TODO(k82cn): Add nodeName back to the queue
}
} }
// TODO: re-evaluate whether there are any labels that need to be // TODO: re-evaluate whether there are any labels that need to be
// reconcile in 1.19. Remove this function if it's no longer necessary. // reconcile in 1.19. Remove this function if it's no longer necessary.

View File

@ -182,7 +182,6 @@ func newNodeLifecycleControllerFromClient(
unhealthyZoneThreshold, unhealthyZoneThreshold,
useTaints, useTaints,
useTaints, useTaints,
useTaints,
) )
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -127,6 +127,7 @@ const (
// owner: @k82cn // owner: @k82cn
// beta: v1.12 // beta: v1.12
// GA: v1.17
// //
// Taint nodes based on their condition status for 'NetworkUnavailable', // Taint nodes based on their condition status for 'NetworkUnavailable',
// 'MemoryPressure', 'PIDPressure' and 'DiskPressure'. // 'MemoryPressure', 'PIDPressure' and 'DiskPressure'.
@ -512,7 +513,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
EphemeralContainers: {Default: false, PreRelease: featuregate.Alpha}, EphemeralContainers: {Default: false, PreRelease: featuregate.Alpha},
PodShareProcessNamespace: {Default: true, PreRelease: featuregate.Beta}, PodShareProcessNamespace: {Default: true, PreRelease: featuregate.Beta},
PodPriority: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.18 PodPriority: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.18
TaintNodesByCondition: {Default: true, PreRelease: featuregate.Beta}, TaintNodesByCondition: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.18
QOSReserved: {Default: false, PreRelease: featuregate.Alpha}, QOSReserved: {Default: false, PreRelease: featuregate.Alpha},
ExpandPersistentVolumes: {Default: true, PreRelease: featuregate.Beta}, ExpandPersistentVolumes: {Default: true, PreRelease: featuregate.Beta},
ExpandInUsePersistentVolumes: {Default: true, PreRelease: featuregate.Beta}, ExpandInUsePersistentVolumes: {Default: true, PreRelease: featuregate.Beta},

View File

@ -142,12 +142,9 @@ func DefaultOffAdmissionPlugins() sets.String {
resourcequota.PluginName, //ResourceQuota resourcequota.PluginName, //ResourceQuota
storageobjectinuseprotection.PluginName, //StorageObjectInUseProtection storageobjectinuseprotection.PluginName, //StorageObjectInUseProtection
podpriority.PluginName, //PodPriority podpriority.PluginName, //PodPriority
nodetaint.PluginName, //TaintNodesByCondition
) )
if utilfeature.DefaultFeatureGate.Enabled(features.TaintNodesByCondition) {
defaultOnPlugins.Insert(nodetaint.PluginName) //TaintNodesByCondition
}
if utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) { if utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) {
defaultOnPlugins.Insert(runtimeclass.PluginName) //RuntimeClass defaultOnPlugins.Insert(runtimeclass.PluginName) //RuntimeClass
} }

View File

@ -245,7 +245,6 @@ go_test(
"//staging/src/k8s.io/client-go/tools/record:go_default_library", "//staging/src/k8s.io/client-go/tools/record:go_default_library",
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
"//staging/src/k8s.io/client-go/util/testing:go_default_library", "//staging/src/k8s.io/client-go/util/testing:go_default_library",
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library", "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//staging/src/k8s.io/component-base/version:go_default_library", "//staging/src/k8s.io/component-base/version:go_default_library",
"//vendor/github.com/google/cadvisor/info/v1:go_default_library", "//vendor/github.com/google/cadvisor/info/v1:go_default_library",

View File

@ -144,13 +144,12 @@ func (m *managerImpl) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAd
return lifecycle.PodAdmitResult{Admit: true} return lifecycle.PodAdmitResult{Admit: true}
} }
// When node has memory pressure and TaintNodesByCondition is enabled, check BestEffort Pod's toleration: // When node has memory pressure, check BestEffort Pod's toleration:
// admit it if tolerates memory pressure taint, fail for other tolerations, e.g. DiskPressure. // admit it if tolerates memory pressure taint, fail for other tolerations, e.g. DiskPressure.
if utilfeature.DefaultFeatureGate.Enabled(features.TaintNodesByCondition) && if v1helper.TolerationsTolerateTaint(attrs.Pod.Spec.Tolerations, &v1.Taint{
v1helper.TolerationsTolerateTaint(attrs.Pod.Spec.Tolerations, &v1.Taint{ Key: schedulerapi.TaintNodeMemoryPressure,
Key: schedulerapi.TaintNodeMemoryPressure, Effect: v1.TaintEffectNoSchedule,
Effect: v1.TaintEffectNoSchedule, }) {
}) {
return lifecycle.PodAdmitResult{Admit: true} return lifecycle.PodAdmitResult{Admit: true}
} }
} }

View File

@ -25,7 +25,7 @@ import (
"strings" "strings"
"time" "time"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
@ -246,13 +246,11 @@ func (kl *Kubelet) initialNode() (*v1.Node, error) {
Effect: v1.TaintEffectNoSchedule, Effect: v1.TaintEffectNoSchedule,
} }
// If TaintNodesByCondition enabled, taint node with TaintNodeUnschedulable when initializing // Taint node with TaintNodeUnschedulable when initializing
// node to avoid race condition; refer to #63897 for more detail. // node to avoid race condition; refer to #63897 for more detail.
if utilfeature.DefaultFeatureGate.Enabled(features.TaintNodesByCondition) { if node.Spec.Unschedulable &&
if node.Spec.Unschedulable && !taintutil.TaintExists(nodeTaints, &unschedulableTaint) {
!taintutil.TaintExists(nodeTaints, &unschedulableTaint) { nodeTaints = append(nodeTaints, unschedulableTaint)
nodeTaints = append(nodeTaints, unschedulableTaint)
}
} }
if kl.externalCloudProvider { if kl.externalCloudProvider {

View File

@ -48,7 +48,6 @@ import (
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
"k8s.io/component-base/featuregate"
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/component-base/version" "k8s.io/component-base/version"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
@ -1983,25 +1982,23 @@ func TestRegisterWithApiServerWithTaint(t *testing.T) {
// Make node to be unschedulable. // Make node to be unschedulable.
kubelet.registerSchedulable = false kubelet.registerSchedulable = false
forEachFeatureGate(t, []featuregate.Feature{features.TaintNodesByCondition}, func(t *testing.T) { // Reset kubelet status for each test.
// Reset kubelet status for each test. kubelet.registrationCompleted = false
kubelet.registrationCompleted = false
// Register node to apiserver. // Register node to apiserver.
kubelet.registerWithAPIServer() kubelet.registerWithAPIServer()
// Check the unschedulable taint. // Check the unschedulable taint.
got := gotNode.(*v1.Node) got := gotNode.(*v1.Node)
unschedulableTaint := &v1.Taint{ unschedulableTaint := &v1.Taint{
Key: schedulerapi.TaintNodeUnschedulable, Key: schedulerapi.TaintNodeUnschedulable,
Effect: v1.TaintEffectNoSchedule, Effect: v1.TaintEffectNoSchedule,
} }
require.Equal(t, require.Equal(t,
utilfeature.DefaultFeatureGate.Enabled(features.TaintNodesByCondition), true,
taintutil.TaintExists(got.Spec.Taints, unschedulableTaint), taintutil.TaintExists(got.Spec.Taints, unschedulableTaint),
"test unschedulable taint for TaintNodesByCondition") "test unschedulable taint for TaintNodesByCondition")
})
} }
func TestNodeStatusHasChanged(t *testing.T) { func TestNodeStatusHasChanged(t *testing.T) {

View File

@ -35,12 +35,9 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
"k8s.io/client-go/util/flowcontrol" "k8s.io/client-go/util/flowcontrol"
"k8s.io/component-base/featuregate"
featuregatetesting "k8s.io/component-base/featuregate/testing"
cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing" cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
"k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/cm"
"k8s.io/kubernetes/pkg/kubelet/config" "k8s.io/kubernetes/pkg/kubelet/config"
@ -2031,17 +2028,6 @@ func runVolumeManager(kubelet *Kubelet) chan struct{} {
return stopCh return stopCh
} }
func forEachFeatureGate(t *testing.T, fs []featuregate.Feature, tf func(t *testing.T)) {
for _, fg := range fs {
for _, f := range []bool{true, false} {
func() {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, fg, f)()
t.Run(fmt.Sprintf("%v(%t)", fg, f), tf)
}()
}
}
}
// Sort pods by UID. // Sort pods by UID.
type podsByUID []*v1.Pod type podsByUID []*v1.Pod

View File

@ -17,12 +17,7 @@ go_test(
name = "go_default_test", name = "go_default_test",
srcs = ["plugins_test.go"], srcs = ["plugins_test.go"],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = ["//pkg/scheduler:go_default_library"],
"//pkg/features:go_default_library",
"//pkg/scheduler:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
],
) )
filegroup( filegroup(

View File

@ -47,12 +47,9 @@ func defaultPredicates() sets.String {
predicates.MatchInterPodAffinityPred, predicates.MatchInterPodAffinityPred,
predicates.NoDiskConflictPred, predicates.NoDiskConflictPred,
predicates.GeneralPred, predicates.GeneralPred,
predicates.CheckNodeMemoryPressurePred,
predicates.CheckNodeDiskPressurePred,
predicates.CheckNodePIDPressurePred,
predicates.CheckNodeConditionPred,
predicates.PodToleratesNodeTaintsPred, predicates.PodToleratesNodeTaintsPred,
predicates.CheckVolumeBindingPred, predicates.CheckVolumeBindingPred,
predicates.CheckNodeUnschedulablePred,
) )
} }
@ -62,34 +59,6 @@ func defaultPredicates() sets.String {
// of a feature gate temporarily. // of a feature gate temporarily.
func ApplyFeatureGates() (restore func()) { func ApplyFeatureGates() (restore func()) {
snapshot := scheduler.RegisteredPredicatesAndPrioritiesSnapshot() snapshot := scheduler.RegisteredPredicatesAndPrioritiesSnapshot()
if utilfeature.DefaultFeatureGate.Enabled(features.TaintNodesByCondition) {
// Remove "CheckNodeCondition", "CheckNodeMemoryPressure", "CheckNodePIDPressure"
// and "CheckNodeDiskPressure" predicates
scheduler.RemoveFitPredicate(predicates.CheckNodeConditionPred)
scheduler.RemoveFitPredicate(predicates.CheckNodeMemoryPressurePred)
scheduler.RemoveFitPredicate(predicates.CheckNodeDiskPressurePred)
scheduler.RemoveFitPredicate(predicates.CheckNodePIDPressurePred)
// Remove key "CheckNodeCondition", "CheckNodeMemoryPressure", "CheckNodePIDPressure" and "CheckNodeDiskPressure"
// from ALL algorithm provider
// The key will be removed from all providers which in algorithmProviderMap[]
// if you just want remove specific provider, call func RemovePredicateKeyFromAlgoProvider()
scheduler.RemovePredicateKeyFromAlgorithmProviderMap(predicates.CheckNodeConditionPred)
scheduler.RemovePredicateKeyFromAlgorithmProviderMap(predicates.CheckNodeMemoryPressurePred)
scheduler.RemovePredicateKeyFromAlgorithmProviderMap(predicates.CheckNodeDiskPressurePred)
scheduler.RemovePredicateKeyFromAlgorithmProviderMap(predicates.CheckNodePIDPressurePred)
// Fit is determined based on whether a pod can tolerate all of the node's taints
scheduler.RegisterMandatoryFitPredicate(predicates.PodToleratesNodeTaintsPred, predicates.PodToleratesNodeTaints)
// Fit is determined based on whether a pod can tolerate unschedulable of node
scheduler.RegisterMandatoryFitPredicate(predicates.CheckNodeUnschedulablePred, predicates.CheckNodeUnschedulablePredicate)
// Insert Key "PodToleratesNodeTaints" and "CheckNodeUnschedulable" To All Algorithm Provider
// The key will insert to all providers which in algorithmProviderMap[]
// if you just want insert to specific provider, call func InsertPredicateKeyToAlgoProvider()
scheduler.InsertPredicateKeyToAlgorithmProviderMap(predicates.PodToleratesNodeTaintsPred)
scheduler.InsertPredicateKeyToAlgorithmProviderMap(predicates.CheckNodeUnschedulablePred)
klog.Infof("TaintNodesByCondition is enabled, PodToleratesNodeTaints predicate is mandatory")
}
// Only register EvenPodsSpread predicate & priority if the feature is enabled // Only register EvenPodsSpread predicate & priority if the feature is enabled
if utilfeature.DefaultFeatureGate.Enabled(features.EvenPodsSpread) { if utilfeature.DefaultFeatureGate.Enabled(features.EvenPodsSpread) {

View File

@ -78,12 +78,9 @@ func TestDefaultPredicates(t *testing.T) {
predicates.MatchInterPodAffinityPred, predicates.MatchInterPodAffinityPred,
predicates.NoDiskConflictPred, predicates.NoDiskConflictPred,
predicates.GeneralPred, predicates.GeneralPred,
predicates.CheckNodeMemoryPressurePred,
predicates.CheckNodeDiskPressurePred,
predicates.CheckNodePIDPressurePred,
predicates.CheckNodeConditionPred,
predicates.PodToleratesNodeTaintsPred, predicates.PodToleratesNodeTaintsPred,
predicates.CheckVolumeBindingPred, predicates.CheckVolumeBindingPred,
predicates.CheckNodeUnschedulablePred,
) )
if expected := defaultPredicates(); !result.Equal(expected) { if expected := defaultPredicates(); !result.Equal(expected) {

View File

@ -113,10 +113,13 @@ func init() {
scheduler.RegisterFitPredicate(predicates.CheckNodePIDPressurePred, predicates.CheckNodePIDPressurePredicate) scheduler.RegisterFitPredicate(predicates.CheckNodePIDPressurePred, predicates.CheckNodePIDPressurePredicate)
// Fit is determined by node conditions: not ready, network unavailable or out of disk. // Fit is determined by node conditions: not ready, network unavailable or out of disk.
scheduler.RegisterMandatoryFitPredicate(predicates.CheckNodeConditionPred, predicates.CheckNodeConditionPredicate) scheduler.RegisterFitPredicate(predicates.CheckNodeConditionPred, predicates.CheckNodeConditionPredicate)
// Fit is determined based on whether a pod can tolerate all of the node's taints // Fit is determined based on whether a pod can tolerate all of the node's taints
scheduler.RegisterFitPredicate(predicates.PodToleratesNodeTaintsPred, predicates.PodToleratesNodeTaints) scheduler.RegisterMandatoryFitPredicate(predicates.PodToleratesNodeTaintsPred, predicates.PodToleratesNodeTaints)
// Fit is determined based on whether a pod can tolerate unschedulable of node
scheduler.RegisterMandatoryFitPredicate(predicates.CheckNodeUnschedulablePred, predicates.CheckNodeUnschedulablePredicate)
// Fit is determined by volume topology requirements. // Fit is determined by volume topology requirements.
scheduler.RegisterFitPredicateFactory( scheduler.RegisterFitPredicateFactory(

View File

@ -20,9 +20,6 @@ import (
"fmt" "fmt"
"testing" "testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/scheduler" "k8s.io/kubernetes/pkg/scheduler"
) )
@ -81,8 +78,8 @@ func TestApplyFeatureGates(t *testing.T) {
t.Fatalf("Error retrieving provider: %v", err) t.Fatalf("Error retrieving provider: %v", err)
} }
if !p.FitPredicateKeys.Has("CheckNodeCondition") { if p.FitPredicateKeys.Has("CheckNodeCondition") {
t.Fatalf("Failed to find predicate: 'CheckNodeCondition'") t.Fatalf("Unexpected predicate: 'CheckNodeCondition'")
} }
if !p.FitPredicateKeys.Has("PodToleratesNodeTaints") { if !p.FitPredicateKeys.Has("PodToleratesNodeTaints") {
@ -91,9 +88,6 @@ func TestApplyFeatureGates(t *testing.T) {
}) })
} }
// Apply features for algorithm providers.
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TaintNodesByCondition, true)()
defer ApplyFeatureGates()() defer ApplyFeatureGates()()
for _, pn := range algorithmProviderNames { for _, pn := range algorithmProviderNames {

View File

@ -65,6 +65,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
{Name: "NodeName"}, {Name: "NodeName"},
{Name: "NodePorts"}, {Name: "NodePorts"},
{Name: "NodeAffinity"}, {Name: "NodeAffinity"},
{Name: "TaintToleration"},
}, },
}, },
}, },
@ -104,6 +105,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
{Name: "NodeAffinity"}, {Name: "NodeAffinity"},
{Name: "NodeResources"}, {Name: "NodeResources"},
{Name: "VolumeRestrictions"}, {Name: "VolumeRestrictions"},
{Name: "TaintToleration"},
}, },
}, },
}, },
@ -150,6 +152,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
{Name: "NodeAffinity"}, {Name: "NodeAffinity"},
{Name: "NodeResources"}, {Name: "NodeResources"},
{Name: "VolumeRestrictions"}, {Name: "VolumeRestrictions"},
{Name: "TaintToleration"},
}, },
}, },
}, },
@ -205,6 +208,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
{Name: "NodeAffinity"}, {Name: "NodeAffinity"},
{Name: "NodeResources"}, {Name: "NodeResources"},
{Name: "VolumeRestrictions"}, {Name: "VolumeRestrictions"},
{Name: "TaintToleration"},
{Name: "VolumeZone"}, {Name: "VolumeZone"},
}, },
"ScorePlugin": { "ScorePlugin": {
@ -1207,7 +1211,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
registeredPriorities := sets.NewString(scheduler.ListRegisteredPriorityFunctions()...) registeredPriorities := sets.NewString(scheduler.ListRegisteredPriorityFunctions()...)
seenPredicates := sets.NewString() seenPredicates := sets.NewString()
seenPriorities := sets.NewString() seenPriorities := sets.NewString()
mandatoryPredicates := sets.NewString("CheckNodeCondition") mandatoryPredicates := sets.NewString("CheckNodeUnschedulable")
generalPredicateFilters := []string{"NodeResources", "NodeName", "NodePorts", "NodeAffinity"} generalPredicateFilters := []string{"NodeResources", "NodeName", "NodePorts", "NodeAffinity"}
filterToPredicateMap := map[string]string{ filterToPredicateMap := map[string]string{
"TaintToleration": "PodToleratesNodeTaints", "TaintToleration": "PodToleratesNodeTaints",

View File

@ -7,10 +7,7 @@ go_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
], ],
) )
@ -20,12 +17,10 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
], ],
) )

View File

@ -22,10 +22,7 @@ import (
"io" "io"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/component-base/featuregate"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
) )
const ( const (
@ -46,16 +43,13 @@ func Register(plugins *admission.Plugins) {
// This plugin identifies requests from nodes // This plugin identifies requests from nodes
func NewPlugin() *Plugin { func NewPlugin() *Plugin {
return &Plugin{ return &Plugin{
Handler: admission.NewHandler(admission.Create), Handler: admission.NewHandler(admission.Create),
features: utilfeature.DefaultFeatureGate,
} }
} }
// Plugin holds state for and implements the admission plugin. // Plugin holds state for and implements the admission plugin.
type Plugin struct { type Plugin struct {
*admission.Handler *admission.Handler
// allows overriding for testing
features featuregate.FeatureGate
} }
var ( var (
@ -68,11 +62,6 @@ var (
// Admit is the main function that checks node identity and adds taints as needed. // Admit is the main function that checks node identity and adds taints as needed.
func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error { func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
// If TaintNodesByCondition is not enabled, we don't need to do anything.
if !p.features.Enabled(features.TaintNodesByCondition) {
return nil
}
// Our job is just to taint nodes. // Our job is just to taint nodes.
if a.GetResource().GroupResource() != nodeResource || a.GetSubresource() != "" { if a.GetResource().GroupResource() != nodeResource || a.GetSubresource() != "" {
return nil return nil
@ -83,11 +72,11 @@ func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject())) return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
} }
// Taint node with NotReady taint at creation if TaintNodesByCondition is // Taint node with NotReady taint at creation. This is needed to make sure
// enabled. This is needed to make sure that nodes are added to the cluster // that nodes are added to the cluster with the NotReady taint. Otherwise,
// with the NotReady taint. Otherwise, a new node may receive the taint with // a new node may receive the taint with some delay causing pods to be
// some delay causing pods to be scheduled on a not-ready node. // scheduled on a not-ready node. Node controller will remove the taint
// Node controller will remove the taint when the node becomes ready. // when the node becomes ready.
addNotReadyTaint(node) addNotReadyTaint(node)
return nil return nil
} }

View File

@ -26,25 +26,9 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/component-base/featuregate"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
) )
var (
enableTaintNodesByCondition = featuregate.NewFeatureGate()
disableTaintNodesByCondition = featuregate.NewFeatureGate()
)
func init() {
if err := enableTaintNodesByCondition.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.TaintNodesByCondition: {Default: true}}); err != nil {
panic(err)
}
if err := disableTaintNodesByCondition.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.TaintNodesByCondition: {Default: false}}); err != nil {
panic(err)
}
}
func Test_nodeTaints(t *testing.T) { func Test_nodeTaints(t *testing.T) {
var ( var (
mynode = &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}} mynode = &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}}
@ -63,7 +47,6 @@ func Test_nodeTaints(t *testing.T) {
name string name string
node api.Node node api.Node
oldNode api.Node oldNode api.Node
features featuregate.FeatureGate
operation admission.Operation operation admission.Operation
options runtime.Object options runtime.Object
expectedTaints []api.Taint expectedTaints []api.Taint
@ -71,23 +54,13 @@ func Test_nodeTaints(t *testing.T) {
{ {
name: "notReady taint is added on creation", name: "notReady taint is added on creation",
node: myNodeObj, node: myNodeObj,
features: enableTaintNodesByCondition,
operation: admission.Create, operation: admission.Create,
options: &metav1.CreateOptions{}, options: &metav1.CreateOptions{},
expectedTaints: []api.Taint{notReadyTaint}, expectedTaints: []api.Taint{notReadyTaint},
}, },
{
name: "NotReady taint is not added when TaintNodesByCondition is disabled",
node: myNodeObj,
features: disableTaintNodesByCondition,
operation: admission.Create,
options: &metav1.CreateOptions{},
expectedTaints: nil,
},
{ {
name: "already tainted node is not tainted again", name: "already tainted node is not tainted again",
node: myTaintedNodeObj, node: myTaintedNodeObj,
features: enableTaintNodesByCondition,
operation: admission.Create, operation: admission.Create,
options: &metav1.CreateOptions{}, options: &metav1.CreateOptions{},
expectedTaints: []api.Taint{notReadyTaint}, expectedTaints: []api.Taint{notReadyTaint},
@ -95,7 +68,6 @@ func Test_nodeTaints(t *testing.T) {
{ {
name: "NotReady taint is added to an unready node as well", name: "NotReady taint is added to an unready node as well",
node: myUnreadyNodeObj, node: myUnreadyNodeObj,
features: enableTaintNodesByCondition,
operation: admission.Create, operation: admission.Create,
options: &metav1.CreateOptions{}, options: &metav1.CreateOptions{},
expectedTaints: []api.Taint{notReadyTaint}, expectedTaints: []api.Taint{notReadyTaint},
@ -105,9 +77,6 @@ func Test_nodeTaints(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
attributes := admission.NewAttributesRecord(&tt.node, &tt.oldNode, nodeKind, myNodeObj.Namespace, myNodeObj.Name, resource, "", tt.operation, tt.options, false, mynode) attributes := admission.NewAttributesRecord(&tt.node, &tt.oldNode, nodeKind, myNodeObj.Namespace, myNodeObj.Name, resource, "", tt.operation, tt.options, false, mynode)
c := NewPlugin() c := NewPlugin()
if tt.features != nil {
c.features = tt.features
}
err := c.Admit(context.TODO(), attributes, nil) err := c.Admit(context.TODO(), attributes, nil)
if err != nil { if err != nil {
t.Errorf("nodePlugin.Admit() error = %v", err) t.Errorf("nodePlugin.Admit() error = %v", err)

View File

@ -12,7 +12,6 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//pkg/scheduler/api:go_default_library", "//pkg/scheduler/api:go_default_library",
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library", "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
@ -21,11 +20,9 @@ go_test(
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library",
], ],
) )

View File

@ -29,13 +29,10 @@ import (
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
admissiontesting "k8s.io/apiserver/pkg/admission/testing" admissiontesting "k8s.io/apiserver/pkg/admission/testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
featuregatetesting "k8s.io/component-base/featuregate/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api" schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
) )
@ -87,8 +84,6 @@ func TestPodAdmission(t *testing.T) {
}, },
} }
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TaintNodesByCondition, true)()
tests := []struct { tests := []struct {
pod *api.Pod pod *api.Pod
defaultClusterTolerations []api.Toleration defaultClusterTolerations []api.Toleration

View File

@ -966,10 +966,8 @@ func TestTaintedNode(t *testing.T) {
} }
// TestUnschedulableNodeDaemonDoesLaunchPod tests that the DaemonSet Pods can still be scheduled // TestUnschedulableNodeDaemonDoesLaunchPod tests that the DaemonSet Pods can still be scheduled
// to the Unschedulable nodes when TaintNodesByCondition are enabled. // to the Unschedulable nodes.
func TestUnschedulableNodeDaemonDoesLaunchPod(t *testing.T) { func TestUnschedulableNodeDaemonDoesLaunchPod(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TaintNodesByCondition, true)()
forEachFeatureGate(t, func(t *testing.T) { forEachFeatureGate(t, func(t *testing.T) {
forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) { forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
server, closeFn, dc, informers, clientset := setup(t) server, closeFn, dc, informers, clientset := setup(t)

View File

@ -21,7 +21,7 @@ import (
"testing" "testing"
"time" "time"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@ -877,56 +877,6 @@ func TestInterPodAffinity(t *testing.T) {
} }
} }
// TestNodePIDPressure verifies that scheduler's CheckNodePIDPressurePredicate predicate
// functions works correctly.
func TestNodePIDPressure(t *testing.T) {
context := initTest(t, "node-pid-pressure")
defer cleanupTest(t, context)
// Add a node.
node, err := createNode(context.clientSet, "testnode", nil)
if err != nil {
t.Fatalf("Cannot create node: %v", err)
}
cs := context.clientSet
// Adds PID pressure condition to the node.
node.Status.Conditions = []v1.NodeCondition{
{
Type: v1.NodePIDPressure,
Status: v1.ConditionTrue,
},
}
// Update node condition.
err = updateNodeStatus(context.clientSet, node)
if err != nil {
t.Fatalf("Cannot update node: %v", err)
}
// Create test pod.
testPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pidpressure-fake-name"},
Spec: v1.PodSpec{
Containers: []v1.Container{
{Name: "container", Image: imageutils.GetPauseImageName()},
},
},
}
testPod, err = cs.CoreV1().Pods(context.ns.Name).Create(testPod)
if err != nil {
t.Fatalf("Test Failed: error: %v, while creating pod", err)
}
err = waitForPodUnschedulable(cs, testPod)
if err != nil {
t.Errorf("Test Failed: error, %v, while waiting for scheduled", err)
}
cleanupPods(cs, t, []*v1.Pod{testPod})
}
// TestEvenPodsSpreadPredicate verifies that EvenPodsSpread predicate functions well. // TestEvenPodsSpreadPredicate verifies that EvenPodsSpread predicate functions well.
func TestEvenPodsSpreadPredicate(t *testing.T) { func TestEvenPodsSpreadPredicate(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EvenPodsSpread, true)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EvenPodsSpread, true)()

View File

@ -110,7 +110,7 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) {
] ]
}`, }`,
expectedPredicates: sets.NewString( expectedPredicates: sets.NewString(
"CheckNodeCondition", // mandatory predicate "CheckNodeUnschedulable", // mandatory predicate
"PredicateOne", "PredicateOne",
"PredicateTwo", "PredicateTwo",
), ),
@ -118,6 +118,11 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) {
"PriorityOne", "PriorityOne",
"PriorityTwo", "PriorityTwo",
), ),
expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
"FilterPlugin": {
{Name: "TaintToleration"},
},
},
}, },
{ {
policy: `{ policy: `{
@ -125,10 +130,7 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) {
"apiVersion" : "v1" "apiVersion" : "v1"
}`, }`,
expectedPredicates: sets.NewString( expectedPredicates: sets.NewString(
"CheckNodeCondition", // mandatory predicate "CheckNodeUnschedulable", // mandatory predicate
"CheckNodeDiskPressure",
"CheckNodeMemoryPressure",
"CheckNodePIDPressure",
"MaxAzureDiskVolumeCount", "MaxAzureDiskVolumeCount",
"MaxEBSVolumeCount", "MaxEBSVolumeCount",
"MaxGCEPDVolumeCount", "MaxGCEPDVolumeCount",
@ -168,9 +170,14 @@ func TestSchedulerCreationFromConfigMap(t *testing.T) {
"priorities" : [] "priorities" : []
}`, }`,
expectedPredicates: sets.NewString( expectedPredicates: sets.NewString(
"CheckNodeCondition", // mandatory predicate "CheckNodeUnschedulable", // mandatory predicate
), ),
expectedPrioritizers: sets.NewString(), expectedPrioritizers: sets.NewString(),
expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
"FilterPlugin": {
{Name: "TaintToleration"},
},
},
}, },
{ {
policy: `apiVersion: v1 policy: `apiVersion: v1
@ -185,7 +192,7 @@ priorities:
weight: 5 weight: 5
`, `,
expectedPredicates: sets.NewString( expectedPredicates: sets.NewString(
"CheckNodeCondition", // mandatory predicate "CheckNodeUnschedulable", // mandatory predicate
"PredicateOne", "PredicateOne",
"PredicateTwo", "PredicateTwo",
), ),
@ -193,16 +200,18 @@ priorities:
"PriorityOne", "PriorityOne",
"PriorityTwo", "PriorityTwo",
), ),
expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
"FilterPlugin": {
{Name: "TaintToleration"},
},
},
}, },
{ {
policy: `apiVersion: v1 policy: `apiVersion: v1
kind: Policy kind: Policy
`, `,
expectedPredicates: sets.NewString( expectedPredicates: sets.NewString(
"CheckNodeCondition", // mandatory predicate "CheckNodeUnschedulable", // mandatory predicate
"CheckNodeDiskPressure",
"CheckNodeMemoryPressure",
"CheckNodePIDPressure",
"MaxAzureDiskVolumeCount", "MaxAzureDiskVolumeCount",
"MaxEBSVolumeCount", "MaxEBSVolumeCount",
"MaxGCEPDVolumeCount", "MaxGCEPDVolumeCount",
@ -241,9 +250,14 @@ predicates: []
priorities: [] priorities: []
`, `,
expectedPredicates: sets.NewString( expectedPredicates: sets.NewString(
"CheckNodeCondition", // mandatory predicate "CheckNodeUnschedulable", // mandatory predicate
), ),
expectedPrioritizers: sets.NewString(), expectedPrioritizers: sets.NewString(),
expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
"FilterPlugin": {
{Name: "TaintToleration"},
},
},
}, },
} { } {
// Add a ConfigMap object. // Add a ConfigMap object.
@ -362,12 +376,6 @@ func TestUnschedulableNodes(t *testing.T) {
Reason: fmt.Sprintf("schedulable condition"), Reason: fmt.Sprintf("schedulable condition"),
LastHeartbeatTime: metav1.Time{Time: time.Now()}, LastHeartbeatTime: metav1.Time{Time: time.Now()},
} }
badCondition := v1.NodeCondition{
Type: v1.NodeReady,
Status: v1.ConditionUnknown,
Reason: fmt.Sprintf("unschedulable condition"),
LastHeartbeatTime: metav1.Time{Time: time.Now()},
}
// Create a new schedulable node, since we're first going to apply // Create a new schedulable node, since we're first going to apply
// the unschedulable condition and verify that pods aren't scheduled. // the unschedulable condition and verify that pods aren't scheduled.
node := &v1.Node{ node := &v1.Node{
@ -426,43 +434,6 @@ func TestUnschedulableNodes(t *testing.T) {
} }
}, },
}, },
// Test node.Status.Conditions=ConditionTrue/Unknown
{
makeUnSchedulable: func(t *testing.T, n *v1.Node, nodeLister corelisters.NodeLister, c clientset.Interface) {
n.Status = v1.NodeStatus{
Capacity: v1.ResourceList{
v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
},
Conditions: []v1.NodeCondition{badCondition},
}
if _, err = c.CoreV1().Nodes().UpdateStatus(n); err != nil {
t.Fatalf("Failed to update node with bad status condition: %v", err)
}
err = waitForReflection(t, nodeLister, nodeKey, func(node interface{}) bool {
return node != nil && node.(*v1.Node).Status.Conditions[0].Status == v1.ConditionUnknown
})
if err != nil {
t.Fatalf("Failed to observe reflected update for status condition update: %v", err)
}
},
makeSchedulable: func(t *testing.T, n *v1.Node, nodeLister corelisters.NodeLister, c clientset.Interface) {
n.Status = v1.NodeStatus{
Capacity: v1.ResourceList{
v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
},
Conditions: []v1.NodeCondition{goodCondition},
}
if _, err = c.CoreV1().Nodes().UpdateStatus(n); err != nil {
t.Fatalf("Failed to update node with healthy status condition: %v", err)
}
err = waitForReflection(t, nodeLister, nodeKey, func(node interface{}) bool {
return node != nil && node.(*v1.Node).Status.Conditions[0].Status == v1.ConditionTrue
})
if err != nil {
t.Fatalf("Failed to observe reflected update for status condition update: %v", err)
}
},
},
} }
for i, mod := range nodeModifications { for i, mod := range nodeModifications {
@ -484,7 +455,7 @@ func TestUnschedulableNodes(t *testing.T) {
// There are no schedulable nodes - the pod shouldn't be scheduled. // There are no schedulable nodes - the pod shouldn't be scheduled.
err = waitForPodToScheduleWithTimeout(context.clientSet, myPod, 2*time.Second) err = waitForPodToScheduleWithTimeout(context.clientSet, myPod, 2*time.Second)
if err == nil { if err == nil {
t.Errorf("Pod scheduled successfully on unschedulable nodes") t.Errorf("Test %d: Pod scheduled successfully on unschedulable nodes", i)
} }
if err != wait.ErrWaitTimeout { if err != wait.ErrWaitTimeout {
t.Errorf("Test %d: failed while trying to confirm the pod does not get scheduled on the node: %v", i, err) t.Errorf("Test %d: failed while trying to confirm the pod does not get scheduled on the node: %v", i, err)

View File

@ -67,9 +67,6 @@ func newPod(nsName, name string, req, limit v1.ResourceList) *v1.Pod {
// TestTaintNodeByCondition tests related cases for TaintNodeByCondition feature. // TestTaintNodeByCondition tests related cases for TaintNodeByCondition feature.
func TestTaintNodeByCondition(t *testing.T) { func TestTaintNodeByCondition(t *testing.T) {
// Enable TaintNodeByCondition
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TaintNodesByCondition, true)()
// Build PodToleration Admission. // Build PodToleration Admission.
admission := podtolerationrestriction.NewPodTolerationsPlugin(&pluginapi.Configuration{}) admission := podtolerationrestriction.NewPodTolerationsPlugin(&pluginapi.Configuration{})
@ -110,7 +107,6 @@ func TestTaintNodeByCondition(t *testing.T) {
100, // Unhealthy zone threshold 100, // Unhealthy zone threshold
true, // Run taint manager true, // Run taint manager
true, // Use taint based evictions true, // Use taint based evictions
true, // Enabled TaintNodeByCondition feature
) )
if err != nil { if err != nil {
t.Errorf("Failed to create node controller: %v", err) t.Errorf("Failed to create node controller: %v", err)
@ -539,7 +535,12 @@ func TestTaintNodeByCondition(t *testing.T) {
t.Errorf("Failed to create node, err: %v", err) t.Errorf("Failed to create node, err: %v", err)
} }
if err := waitForNodeTaints(cs, node, test.expectedTaints); err != nil { if err := waitForNodeTaints(cs, node, test.expectedTaints); err != nil {
t.Errorf("Failed to taint node <%s>, err: %v", node.Name, err) node, err = cs.CoreV1().Nodes().Get(node.Name, metav1.GetOptions{})
if err != nil {
t.Errorf("Failed to get node <%s>", node.Name)
}
t.Errorf("Failed to taint node <%s>, expected: %v, got: %v, err: %v", node.Name, test.expectedTaints, node.Spec.Taints, err)
} }
var pods []*v1.Pod var pods []*v1.Pod
@ -689,7 +690,6 @@ func TestTaintBasedEvictions(t *testing.T) {
0.55, // Unhealthy zone threshold 0.55, // Unhealthy zone threshold
true, // Run taint manager true, // Run taint manager
true, // Use taint based evictions true, // Use taint based evictions
true, // Enabled TaintNodeByCondition feature
) )
if err != nil { if err != nil {
t.Errorf("Failed to create node controller: %v", err) t.Errorf("Failed to create node controller: %v", err)

View File

@ -212,6 +212,7 @@ func initTestSchedulerWithOptions(
context.informerFactory.WaitForCacheSync(context.scheduler.StopEverything) context.informerFactory.WaitForCacheSync(context.scheduler.StopEverything)
go context.scheduler.Run(context.ctx) go context.scheduler.Run(context.ctx)
return context return context
} }