implement taints and tolerations

This commit is contained in:
Kevin
2016-03-31 11:42:57 +08:00
parent 952e8302fb
commit 52fb89ff73
34 changed files with 4623 additions and 69 deletions

View File

@@ -32,6 +32,7 @@ var (
ErrVolumeZoneConflict = newPredicateFailureError("NoVolumeZoneConflict")
ErrNodeSelectorNotMatch = newPredicateFailureError("MatchNodeSelector")
ErrPodAffinityNotMatch = newPredicateFailureError("MatchInterPodAffinity")
ErrTaintsTolerationsNotMatch = newPredicateFailureError("PodToleratesNodeTaints")
ErrPodNotMatchHostName = newPredicateFailureError("HostName")
ErrPodNotFitsHostPorts = newPredicateFailureError("PodFitsHostPorts")
ErrNodeLabelPresenceViolated = newPredicateFailureError("CheckNodeLabelPresence")

View File

@@ -944,3 +944,58 @@ func (checker *PodAffinityChecker) NodeMatchPodAffinityAntiAffinity(pod *api.Pod
}
return true
}
type TolerationMatch struct {
info NodeInfo
}
func NewTolerationMatchPredicate(info NodeInfo) algorithm.FitPredicate {
tolerationMatch := &TolerationMatch{
info: info,
}
return tolerationMatch.PodToleratesNodeTaints
}
func (t *TolerationMatch) PodToleratesNodeTaints(pod *api.Pod, nodeInfo *schedulercache.NodeInfo) (bool, error) {
node := nodeInfo.Node()
taints, err := api.GetTaintsFromNodeAnnotations(node.Annotations)
if err != nil {
return false, err
}
tolerations, err := api.GetTolerationsFromPodAnnotations(pod.Annotations)
if err != nil {
return false, err
}
if tolerationsToleratesTaints(tolerations, taints) {
return true, nil
}
return false, ErrTaintsTolerationsNotMatch
}
func tolerationsToleratesTaints(tolerations []api.Toleration, taints []api.Taint) bool {
// If the taint list is nil/empty, it is tolerated by all tolerations by default.
if len(taints) == 0 {
return true
}
// The taint list isn't nil/empty, a nil/empty toleration list can't tolerate them.
if len(tolerations) == 0 {
return false
}
for _, taint := range taints {
// skip taints that have effect PreferNoSchedule, since it is for priorities
if taint.Effect == api.TaintEffectPreferNoSchedule {
continue
}
if !api.TaintToleratedByTolerations(taint, tolerations) {
return false
}
}
return true
}

View File

@@ -2358,3 +2358,286 @@ func TestInterPodAffinityWithMultipleNodes(t *testing.T) {
}
}
}
func TestPodToleratesTaints(t *testing.T) {
podTolerateTaintsTests := []struct {
pod *api.Pod
node api.Node
fits bool
test string
}{
{
pod: &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod0",
},
},
node: api.Node{
ObjectMeta: api.ObjectMeta{
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "dedicated",
"value": "user1",
"effect": "NoSchedule"
}]`,
},
},
},
fits: false,
test: "a pod having no tolerations can't be scheduled onto a node with nonempty taints",
},
{
pod: &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod1",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "dedicated",
"value": "user1",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Image: "pod1:V1"}},
},
},
node: api.Node{
ObjectMeta: api.ObjectMeta{
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "dedicated",
"value": "user1",
"effect": "NoSchedule"
}]`,
},
},
},
fits: true,
test: "a pod which can be scheduled on a dedicated node assgined to user1 with effect NoSchedule",
},
{
pod: &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod2",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "dedicated",
"operator": "Equal",
"value": "user2",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Image: "pod2:V1"}},
},
},
node: api.Node{
ObjectMeta: api.ObjectMeta{
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "dedicated",
"value": "user1",
"effect": "NoSchedule"
}]`,
},
},
},
fits: false,
test: "a pod which can't be scheduled on a dedicated node assgined to user2 with effect NoSchedule",
},
{
pod: &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod2",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"operator": "Exists",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Image: "pod2:V1"}},
},
},
node: api.Node{
ObjectMeta: api.ObjectMeta{
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "foo",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
},
fits: true,
test: "a pod can be scheduled onto the node, with a toleration uses operator Exists that tolerates the taints on the node",
},
{
pod: &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod2",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "dedicated",
"operator": "Equal",
"value": "user2",
"effect": "NoSchedule"
}, {
"key": "foo",
"operator": "Exists",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Image: "pod2:V1"}},
},
},
node: api.Node{
ObjectMeta: api.ObjectMeta{
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "dedicated",
"value": "user2",
"effect": "NoSchedule"
}, {
"key": "foo",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
},
fits: true,
test: "a pod has multiple tolerations, node has multiple taints, all the taints are tolerated, pod can be scheduled onto the node",
},
{
pod: &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod2",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"operator": "Equal",
"value": "bar",
"effect": "PreferNoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Image: "pod2:V1"}},
},
},
node: api.Node{
ObjectMeta: api.ObjectMeta{
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "foo",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
},
fits: false,
test: "a pod has a toleration that keys and values match the taint on the node, but (non-empty) effect doesn't match, " +
"can't be scheduled onto the node",
},
{
pod: &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod2",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"operator": "Equal",
"value": "bar"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Image: "pod2:V1"}},
},
},
node: api.Node{
ObjectMeta: api.ObjectMeta{
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "foo",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
},
fits: true,
test: "The pod has a toleration that keys and values match the taint on the node, the effect of toleration is empty, " +
"and the effect of taint is NoSchedule. Pod can be scheduled onto the node",
},
{
pod: &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod2",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "dedicated",
"operator": "Equal",
"value": "user2",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Image: "pod2:V1"}},
},
},
node: api.Node{
ObjectMeta: api.ObjectMeta{
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "dedicated",
"value": "user1",
"effect": "PreferNoSchedule"
}]`,
},
},
},
fits: true,
test: "The pod has a toleration that key and value don't match the taint on the node, " +
"but the effect of taint on node is PreferNochedule. Pod can be shceduled onto the node",
},
}
for _, test := range podTolerateTaintsTests {
tolerationMatch := TolerationMatch{FakeNodeInfo(test.node)}
nodeInfo := schedulercache.NewNodeInfo()
nodeInfo.SetNode(&test.node)
fits, err := tolerationMatch.PodToleratesNodeTaints(test.pod, nodeInfo)
if fits == false && !reflect.DeepEqual(err, ErrTaintsTolerationsNotMatch) {
t.Errorf("%s, unexpected error: %v", test.test, err)
}
if fits != test.fits {
t.Errorf("%s, expected: %v got %v", test.test, test.fits, fits)
}
}
}