Skip to content

Commit d3e6dc0

Browse files
xyz-libadouralix
authored andcommitted
[fix] fix ds controller deletes pod when not match RequiredDuringSchedulingIgnoredDuringExecution
Signed-off-by: xyz-li <[email protected]> datadog:patch
1 parent 4c48d6e commit d3e6dc0

File tree

2 files changed

+109
-6
lines changed

2 files changed

+109
-6
lines changed

pkg/controller/daemon/daemon_controller.go

+31-4
Original file line numberDiff line numberDiff line change
@@ -1289,8 +1289,14 @@ func NodeShouldRunDaemonPod(node *v1.Node, ds *apps.DaemonSet) (bool, bool) {
12891289
}
12901290

12911291
taints := node.Spec.Taints
1292-
fitsNodeName, fitsNodeAffinity, fitsTaints := predicates(pod, node, taints)
1293-
if !fitsNodeName || !fitsNodeAffinity {
1292+
fitsNodeName := len(pod.Spec.NodeName) == 0 || pod.Spec.NodeName == node.Name
1293+
if !fitsNodeName {
1294+
return false, false
1295+
}
1296+
1297+
fitsNodeName, fitsNodeSelector, fitsNodeAffinity, fitsTaints := predicates(pod, node, taints)
1298+
1299+
if !fitsNodeName || !fitsNodeSelector {
12941300
return false, false
12951301
}
12961302

@@ -1302,14 +1308,35 @@ func NodeShouldRunDaemonPod(node *v1.Node, ds *apps.DaemonSet) (bool, bool) {
13021308
return false, !hasUntoleratedTaint
13031309
}
13041310

1311+
if !fitsNodeAffinity {
1312+
// IgnoredDuringExecution means that if the node labels change after Kubernetes schedules the Pod, the Pod continues to run.
1313+
return false, true
1314+
}
1315+
13051316
return true, true
13061317
}
13071318

13081319
// predicates checks if a DaemonSet's pod can run on a node.
1309-
func predicates(pod *v1.Pod, node *v1.Node, taints []v1.Taint) (fitsNodeName, fitsNodeAffinity, fitsTaints bool) {
1320+
func predicates(pod *v1.Pod, node *v1.Node, taints []v1.Taint) (fitsNodeName, fitsNodeSelector, fitsNodeAffinity, fitsTaints bool) {
13101321
fitsNodeName = len(pod.Spec.NodeName) == 0 || pod.Spec.NodeName == node.Name
1322+
1323+
if len(pod.Spec.NodeSelector) > 0 {
1324+
selector := labels.SelectorFromSet(pod.Spec.NodeSelector)
1325+
fitsNodeSelector = selector.Matches(labels.Set(node.Labels))
1326+
} else {
1327+
fitsNodeSelector = true
1328+
}
1329+
1330+
if pod.Spec.Affinity != nil &&
1331+
pod.Spec.Affinity.NodeAffinity != nil &&
1332+
pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
1333+
affinity := nodeaffinity.NewLazyErrorNodeSelector(pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
1334+
fitsNodeAffinity, _ = affinity.Match(node)
1335+
} else {
1336+
fitsNodeAffinity = true
1337+
}
1338+
13111339
// Ignore parsing errors for backwards compatibility.
1312-
fitsNodeAffinity, _ = nodeaffinity.GetRequiredNodeAffinity(pod).Match(node)
13131340
_, hasUntoleratedTaint := v1helper.FindMatchingUntoleratedTaint(taints, pod.Spec.Tolerations, func(t *v1.Taint) bool {
13141341
return t.Effect == v1.TaintEffectNoExecute || t.Effect == v1.TaintEffectNoSchedule
13151342
})

pkg/controller/daemon/daemon_controller_test.go

+78-2
Original file line numberDiff line numberDiff line change
@@ -1640,6 +1640,52 @@ func TestNodeAffinityDaemonLaunchesPods(t *testing.T) {
16401640
}
16411641
}
16421642

1643+
// RequiredDuringSchedulingIgnoredDuringExecution means that if the node labels change after Kubernetes schedules the Pod, the Pod continues to run.
1644+
func TestNodeAffinityAndChangeNodeLabels(t *testing.T) {
1645+
logger, _ := ktesting.NewTestContext(t)
1646+
for _, strategy := range updateStrategies() {
1647+
daemon := newDaemonSet("foo")
1648+
daemon.Spec.UpdateStrategy = *strategy
1649+
daemon.Spec.Template.Spec.Affinity = &v1.Affinity{
1650+
NodeAffinity: &v1.NodeAffinity{
1651+
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
1652+
NodeSelectorTerms: []v1.NodeSelectorTerm{
1653+
{
1654+
MatchExpressions: []v1.NodeSelectorRequirement{
1655+
{
1656+
Key: "color",
1657+
Operator: v1.NodeSelectorOpIn,
1658+
Values: []string{simpleNodeLabel["color"]},
1659+
},
1660+
},
1661+
},
1662+
},
1663+
},
1664+
},
1665+
}
1666+
_, ctx := ktesting.NewTestContext(t)
1667+
1668+
manager, podControl, _, err := newTestController(ctx, daemon)
1669+
if err != nil {
1670+
t.Fatalf("error creating DaemonSetsController: %v", err)
1671+
}
1672+
node1 := newNode("node-1", simpleNodeLabel)
1673+
node2 := newNode("node-2", simpleNodeLabel)
1674+
manager.nodeStore.Add(node1)
1675+
manager.nodeStore.Add(node2)
1676+
err = manager.dsStore.Add(daemon)
1677+
if err != nil {
1678+
t.Fatal(err)
1679+
}
1680+
expectSyncDaemonSets(t, manager, daemon, podControl, 2, 0, 0)
1681+
oldNode := node1.DeepCopy()
1682+
node1.Labels = nil
1683+
manager.updateNode(logger, oldNode, node1)
1684+
manager.nodeStore.Add(newNode("node-3", nil))
1685+
expectSyncDaemonSets(t, manager, daemon, podControl, 2, 0, 0)
1686+
}
1687+
}
1688+
16431689
func TestNumberReadyStatus(t *testing.T) {
16441690
for _, strategy := range updateStrategies() {
16451691
ds := newDaemonSet("foo")
@@ -2284,7 +2330,7 @@ func TestNodeShouldRunDaemonPod(t *testing.T) {
22842330
shouldContinueRunning: true,
22852331
},
22862332
{
2287-
predicateName: "ErrPodAffinityNotMatch",
2333+
predicateName: "PodAffinityNotMatchDuringExecution",
22882334
ds: &apps.DaemonSet{
22892335
Spec: apps.DaemonSetSpec{
22902336
Selector: &metav1.LabelSelector{MatchLabels: simpleDaemonSetLabel},
@@ -2315,7 +2361,7 @@ func TestNodeShouldRunDaemonPod(t *testing.T) {
23152361
},
23162362
},
23172363
shouldRun: false,
2318-
shouldContinueRunning: false,
2364+
shouldContinueRunning: true,
23192365
},
23202366
{
23212367
predicateName: "ShouldRunDaemonPod",
@@ -2476,6 +2522,36 @@ func TestUpdateNode(t *testing.T) {
24762522
return 1
24772523
},
24782524
},
2525+
{
2526+
test: "Node labels changed, ds with NodeAffinity ",
2527+
oldNode: newNode("node1", simpleNodeLabel),
2528+
newNode: newNode("node1", simpleNodeLabel2),
2529+
ds: func() *apps.DaemonSet {
2530+
ds := newDaemonSet("ds")
2531+
ds.Spec.Template.Spec.Affinity = &v1.Affinity{
2532+
NodeAffinity: &v1.NodeAffinity{
2533+
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
2534+
NodeSelectorTerms: []v1.NodeSelectorTerm{
2535+
{
2536+
MatchExpressions: []v1.NodeSelectorRequirement{
2537+
{
2538+
Key: "color",
2539+
Operator: v1.NodeSelectorOpIn,
2540+
Values: []string{"blue"},
2541+
},
2542+
},
2543+
},
2544+
},
2545+
},
2546+
},
2547+
}
2548+
return ds
2549+
}(),
2550+
shouldEnqueue: true,
2551+
expectedCreates: func() int {
2552+
return 1
2553+
},
2554+
},
24792555
}
24802556
for _, c := range cases {
24812557
for _, strategy := range updateStrategies() {

0 commit comments

Comments
 (0)