mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
feat: update taint nodes by condition to GA
This commit is contained in:
parent
54a30700a3
commit
1163a1d51e
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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},
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -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}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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(
|
||||||
|
@ -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 {
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)()
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user