mirror of https://github.com/k3s-io/k3s
Enable PodTolerateNodeTaints predicate in DaemonSet controller
parent
95badd95ce
commit
b593427105
|
@ -27,6 +27,7 @@ go_library(
|
||||||
"//pkg/client/listers/extensions/v1beta1:go_default_library",
|
"//pkg/client/listers/extensions/v1beta1:go_default_library",
|
||||||
"//pkg/controller:go_default_library",
|
"//pkg/controller:go_default_library",
|
||||||
"//pkg/util/metrics:go_default_library",
|
"//pkg/util/metrics:go_default_library",
|
||||||
|
"//plugin/pkg/scheduler/algorithm:go_default_library",
|
||||||
"//plugin/pkg/scheduler/algorithm/predicates:go_default_library",
|
"//plugin/pkg/scheduler/algorithm/predicates:go_default_library",
|
||||||
"//plugin/pkg/scheduler/schedulercache:go_default_library",
|
"//plugin/pkg/scheduler/schedulercache:go_default_library",
|
||||||
"//vendor:github.com/golang/glog",
|
"//vendor:github.com/golang/glog",
|
||||||
|
|
|
@ -45,6 +45,7 @@ import (
|
||||||
extensionslisters "k8s.io/kubernetes/pkg/client/listers/extensions/v1beta1"
|
extensionslisters "k8s.io/kubernetes/pkg/client/listers/extensions/v1beta1"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
"k8s.io/kubernetes/pkg/util/metrics"
|
"k8s.io/kubernetes/pkg/util/metrics"
|
||||||
|
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
|
||||||
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates"
|
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates"
|
||||||
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
|
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
|
||||||
|
|
||||||
|
@ -779,13 +780,14 @@ func (dsc *DaemonSetsController) nodeShouldRunDaemonPod(node *v1.Node, ds *exten
|
||||||
|
|
||||||
nodeInfo := schedulercache.NewNodeInfo(pods...)
|
nodeInfo := schedulercache.NewNodeInfo(pods...)
|
||||||
nodeInfo.SetNode(node)
|
nodeInfo.SetNode(node)
|
||||||
_, reasons, err := predicates.GeneralPredicates(newPod, nil, nodeInfo)
|
_, reasons, err := daemonSetPredicates(newPod, nodeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Warningf("GeneralPredicates failed on ds '%s/%s' due to unexpected error: %v", ds.ObjectMeta.Namespace, ds.ObjectMeta.Name, err)
|
glog.Warningf("daemonSetPredicates failed on ds '%s/%s' due to unexpected error: %v", ds.ObjectMeta.Namespace, ds.ObjectMeta.Name, err)
|
||||||
return false, false, false, err
|
return false, false, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range reasons {
|
for _, r := range reasons {
|
||||||
glog.V(4).Infof("GeneralPredicates failed on ds '%s/%s' for reason: %v", ds.ObjectMeta.Namespace, ds.ObjectMeta.Name, r.GetReason())
|
glog.V(4).Infof("daemonSetPredicates failed on ds '%s/%s' for reason: %v", ds.ObjectMeta.Namespace, ds.ObjectMeta.Name, r.GetReason())
|
||||||
switch reason := r.(type) {
|
switch reason := r.(type) {
|
||||||
case *predicates.InsufficientResourceError:
|
case *predicates.InsufficientResourceError:
|
||||||
dsc.eventRecorder.Eventf(ds, v1.EventTypeNormal, FailedPlacementReason, "failed to place pod on %q: %s", node.ObjectMeta.Name, reason.Error())
|
dsc.eventRecorder.Eventf(ds, v1.EventTypeNormal, FailedPlacementReason, "failed to place pod on %q: %s", node.ObjectMeta.Name, reason.Error())
|
||||||
|
@ -801,7 +803,9 @@ func (dsc *DaemonSetsController) nodeShouldRunDaemonPod(node *v1.Node, ds *exten
|
||||||
predicates.ErrNodeLabelPresenceViolated,
|
predicates.ErrNodeLabelPresenceViolated,
|
||||||
// this one is probably intentional since it's a workaround for not having
|
// this one is probably intentional since it's a workaround for not having
|
||||||
// pod hard anti affinity.
|
// pod hard anti affinity.
|
||||||
predicates.ErrPodNotFitsHostPorts:
|
predicates.ErrPodNotFitsHostPorts,
|
||||||
|
// DaemonSet is expected to respect taints and tolerations
|
||||||
|
predicates.ErrTaintsTolerationsNotMatch:
|
||||||
wantToRun, shouldSchedule, shouldContinueRunning = false, false, false
|
wantToRun, shouldSchedule, shouldContinueRunning = false, false, false
|
||||||
// unintentional
|
// unintentional
|
||||||
case
|
case
|
||||||
|
@ -818,9 +822,9 @@ func (dsc *DaemonSetsController) nodeShouldRunDaemonPod(node *v1.Node, ds *exten
|
||||||
// unexpected
|
// unexpected
|
||||||
case
|
case
|
||||||
predicates.ErrPodAffinityNotMatch,
|
predicates.ErrPodAffinityNotMatch,
|
||||||
predicates.ErrServiceAffinityViolated,
|
predicates.ErrServiceAffinityViolated:
|
||||||
predicates.ErrTaintsTolerationsNotMatch:
|
glog.Warningf("unexpected predicate failure reason: %s", reason.GetReason())
|
||||||
return false, false, false, fmt.Errorf("unexpected reason: GeneralPredicates should not return reason %s", reason.GetReason())
|
return false, false, false, fmt.Errorf("unexpected reason: daemonSetPredicates should not return reason %s", reason.GetReason())
|
||||||
default:
|
default:
|
||||||
glog.V(4).Infof("unknown predicate failure reason: %s", reason.GetReason())
|
glog.V(4).Infof("unknown predicate failure reason: %s", reason.GetReason())
|
||||||
wantToRun, shouldSchedule, shouldContinueRunning = false, false, false
|
wantToRun, shouldSchedule, shouldContinueRunning = false, false, false
|
||||||
|
@ -834,6 +838,30 @@ func (dsc *DaemonSetsController) nodeShouldRunDaemonPod(node *v1.Node, ds *exten
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// daemonSetPredicates checks if a DaemonSet's pod can be scheduled on a node using GeneralPredicates
|
||||||
|
// and PodToleratesNodeTaints predicate
|
||||||
|
func daemonSetPredicates(pod *v1.Pod, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
|
||||||
|
var predicateFails []algorithm.PredicateFailureReason
|
||||||
|
|
||||||
|
fit, reasons, err := predicates.GeneralPredicates(pod, nil, nodeInfo)
|
||||||
|
if err != nil {
|
||||||
|
return false, predicateFails, err
|
||||||
|
}
|
||||||
|
if !fit {
|
||||||
|
predicateFails = append(predicateFails, reasons...)
|
||||||
|
}
|
||||||
|
|
||||||
|
fit, reasons, err = predicates.PodToleratesNodeTaints(pod, nil, nodeInfo)
|
||||||
|
if err != nil {
|
||||||
|
return false, predicateFails, err
|
||||||
|
}
|
||||||
|
if !fit {
|
||||||
|
predicateFails = append(predicateFails, reasons...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(predicateFails) == 0, predicateFails, nil
|
||||||
|
}
|
||||||
|
|
||||||
// byCreationTimestamp sorts a list by creation timestamp, using their names as a tie breaker.
|
// byCreationTimestamp sorts a list by creation timestamp, using their names as a tie breaker.
|
||||||
type byCreationTimestamp []*extensions.DaemonSet
|
type byCreationTimestamp []*extensions.DaemonSet
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,15 @@ var (
|
||||||
alwaysReady = func() bool { return true }
|
alwaysReady = func() bool { return true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
noSchedule = `
|
||||||
|
[{
|
||||||
|
"key": "dedicated",
|
||||||
|
"value": "user1",
|
||||||
|
"effect": "NoSchedule"
|
||||||
|
}]`
|
||||||
|
)
|
||||||
|
|
||||||
func getKey(ds *extensions.DaemonSet, t *testing.T) string {
|
func getKey(ds *extensions.DaemonSet, t *testing.T) string {
|
||||||
if key, err := controller.KeyFunc(ds); err != nil {
|
if key, err := controller.KeyFunc(ds); err != nil {
|
||||||
t.Errorf("Unexpected error getting key for ds %v: %v", ds.Name, err)
|
t.Errorf("Unexpected error getting key for ds %v: %v", ds.Name, err)
|
||||||
|
@ -708,6 +717,63 @@ func TestDaemonKillFailedPods(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DaemonSet should not launch a pod on a tainted node when the pod doesn't tolerate that taint.
|
||||||
|
func TestTaintedNodeDaemonDoesNotLaunchUntoleratePod(t *testing.T) {
|
||||||
|
manager, podControl, _ := newTestController()
|
||||||
|
|
||||||
|
node := newNode("tainted", nil)
|
||||||
|
setNodeTaint(node, noSchedule)
|
||||||
|
manager.nodeStore.Add(node)
|
||||||
|
|
||||||
|
ds := newDaemonSet("untolerate")
|
||||||
|
manager.dsStore.Add(ds)
|
||||||
|
|
||||||
|
syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DaemonSet should launch a pod on a tainted node when the pod can tolerate that taint.
|
||||||
|
func TestTaintedNodeDaemonLaunchesToleratePod(t *testing.T) {
|
||||||
|
manager, podControl, _ := newTestController()
|
||||||
|
|
||||||
|
node := newNode("tainted", nil)
|
||||||
|
setNodeTaint(node, noSchedule)
|
||||||
|
manager.nodeStore.Add(node)
|
||||||
|
|
||||||
|
ds := newDaemonSet("tolerate")
|
||||||
|
setDaemonSetToleration(ds, noSchedule)
|
||||||
|
manager.dsStore.Add(ds)
|
||||||
|
|
||||||
|
syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DaemonSet should launch a pod on an untainted node when the pod has tolerations.
|
||||||
|
func TestNodeDaemonLaunchesToleratePod(t *testing.T) {
|
||||||
|
manager, podControl, _ := newTestController()
|
||||||
|
|
||||||
|
node := newNode("untainted", nil)
|
||||||
|
manager.nodeStore.Add(node)
|
||||||
|
|
||||||
|
ds := newDaemonSet("tolerate")
|
||||||
|
setDaemonSetToleration(ds, noSchedule)
|
||||||
|
manager.dsStore.Add(ds)
|
||||||
|
|
||||||
|
syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNodeTaint(node *v1.Node, taint string) {
|
||||||
|
if node.ObjectMeta.Annotations == nil {
|
||||||
|
node.ObjectMeta.Annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
node.ObjectMeta.Annotations[v1.TaintsAnnotationKey] = taint
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDaemonSetToleration(ds *extensions.DaemonSet, toleration string) {
|
||||||
|
if ds.Spec.Template.ObjectMeta.Annotations == nil {
|
||||||
|
ds.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
ds.Spec.Template.ObjectMeta.Annotations[v1.TolerationsAnnotationKey] = toleration
|
||||||
|
}
|
||||||
|
|
||||||
func TestNodeShouldRunDaemonPod(t *testing.T) {
|
func TestNodeShouldRunDaemonPod(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
podsOnNode []*v1.Pod
|
podsOnNode []*v1.Pod
|
||||||
|
|
Loading…
Reference in New Issue