Skip to content

Commit a1fb647

Browse files
xyz-linyodas
authored andcommitted
[fix] fix ds controller deletes pod when not match RequiredDuringSchedulingIgnoredDuringExecution
Signed-off-by: xyz-li <[email protected]> datadog:patch
1 parent 46962c8 commit a1fb647

File tree

2 files changed

+109
-6
lines changed

2 files changed

+109
-6
lines changed

pkg/controller/daemon/daemon_controller.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,8 +1313,14 @@ func NodeShouldRunDaemonPod(node *v1.Node, ds *apps.DaemonSet) (bool, bool) {
13131313
}
13141314

13151315
taints := node.Spec.Taints
1316-
fitsNodeName, fitsNodeAffinity, fitsTaints := predicates(pod, node, taints)
1317-
if !fitsNodeName || !fitsNodeAffinity {
1316+
fitsNodeName := len(pod.Spec.NodeName) == 0 || pod.Spec.NodeName == node.Name
1317+
if !fitsNodeName {
1318+
return false, false
1319+
}
1320+
1321+
fitsNodeName, fitsNodeSelector, fitsNodeAffinity, fitsTaints := predicates(pod, node, taints)
1322+
1323+
if !fitsNodeName || !fitsNodeSelector {
13181324
return false, false
13191325
}
13201326

@@ -1326,14 +1332,35 @@ func NodeShouldRunDaemonPod(node *v1.Node, ds *apps.DaemonSet) (bool, bool) {
13261332
return false, !hasUntoleratedTaint
13271333
}
13281334

1335+
if !fitsNodeAffinity {
1336+
// IgnoredDuringExecution means that if the node labels change after Kubernetes schedules the Pod, the Pod continues to run.
1337+
return false, true
1338+
}
1339+
13291340
return true, true
13301341
}
13311342

13321343
// predicates checks if a DaemonSet's pod can run on a node.
1333-
func predicates(pod *v1.Pod, node *v1.Node, taints []v1.Taint) (fitsNodeName, fitsNodeAffinity, fitsTaints bool) {
1344+
func predicates(pod *v1.Pod, node *v1.Node, taints []v1.Taint) (fitsNodeName, fitsNodeSelector, fitsNodeAffinity, fitsTaints bool) {
13341345
fitsNodeName = len(pod.Spec.NodeName) == 0 || pod.Spec.NodeName == node.Name
1346+
1347+
if len(pod.Spec.NodeSelector) > 0 {
1348+
selector := labels.SelectorFromSet(pod.Spec.NodeSelector)
1349+
fitsNodeSelector = selector.Matches(labels.Set(node.Labels))
1350+
} else {
1351+
fitsNodeSelector = true
1352+
}
1353+
1354+
if pod.Spec.Affinity != nil &&
1355+
pod.Spec.Affinity.NodeAffinity != nil &&
1356+
pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
1357+
affinity := nodeaffinity.NewLazyErrorNodeSelector(pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
1358+
fitsNodeAffinity, _ = affinity.Match(node)
1359+
} else {
1360+
fitsNodeAffinity = true
1361+
}
1362+
13351363
// Ignore parsing errors for backwards compatibility.
1336-
fitsNodeAffinity, _ = nodeaffinity.GetRequiredNodeAffinity(pod).Match(node)
13371364
_, hasUntoleratedTaint := v1helper.FindMatchingUntoleratedTaint(taints, pod.Spec.Tolerations, func(t *v1.Taint) bool {
13381365
return t.Effect == v1.TaintEffectNoExecute || t.Effect == v1.TaintEffectNoSchedule
13391366
})

pkg/controller/daemon/daemon_controller_test.go

Lines changed: 78 additions & 2 deletions
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)