mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #117073 from mimowo/fix-daemonset-pod-restarts
Fix: Recreate DaemonSet pods completed with Succeeded phase
This commit is contained in:
		@@ -75,6 +75,8 @@ const (
 | 
				
			|||||||
	FailedPlacementReason = "FailedPlacement"
 | 
						FailedPlacementReason = "FailedPlacement"
 | 
				
			||||||
	// FailedDaemonPodReason is added to an event when the status of a Pod of a DaemonSet is 'Failed'.
 | 
						// FailedDaemonPodReason is added to an event when the status of a Pod of a DaemonSet is 'Failed'.
 | 
				
			||||||
	FailedDaemonPodReason = "FailedDaemonPod"
 | 
						FailedDaemonPodReason = "FailedDaemonPod"
 | 
				
			||||||
 | 
						// SucceededDaemonPodReason is added to an event when the status of a Pod of a DaemonSet is 'Succeeded'.
 | 
				
			||||||
 | 
						SucceededDaemonPodReason = "SucceededDaemonPod"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// controllerKind contains the schema.GroupVersionKind for this controller type.
 | 
					// controllerKind contains the schema.GroupVersionKind for this controller type.
 | 
				
			||||||
@@ -842,6 +844,12 @@ func (dsc *DaemonSetsController) podsShouldBeOnNode(
 | 
				
			|||||||
				// Emit an event so that it's discoverable to users.
 | 
									// Emit an event so that it's discoverable to users.
 | 
				
			||||||
				dsc.eventRecorder.Eventf(ds, v1.EventTypeWarning, FailedDaemonPodReason, msg)
 | 
									dsc.eventRecorder.Eventf(ds, v1.EventTypeWarning, FailedDaemonPodReason, msg)
 | 
				
			||||||
				podsToDelete = append(podsToDelete, pod.Name)
 | 
									podsToDelete = append(podsToDelete, pod.Name)
 | 
				
			||||||
 | 
								} else if pod.Status.Phase == v1.PodSucceeded {
 | 
				
			||||||
 | 
									msg := fmt.Sprintf("Found succeeded daemon pod %s/%s on node %s, will try to delete it", pod.Namespace, pod.Name, node.Name)
 | 
				
			||||||
 | 
									logger.V(2).Info("Found succeeded daemon pod on node, will try to delete it", "pod", klog.KObj(pod), "node", klog.KObj(node))
 | 
				
			||||||
 | 
									// Emit an event so that it's discoverable to users.
 | 
				
			||||||
 | 
									dsc.eventRecorder.Eventf(ds, v1.EventTypeNormal, SucceededDaemonPodReason, msg)
 | 
				
			||||||
 | 
									podsToDelete = append(podsToDelete, pod.Name)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				daemonPodsRunning = append(daemonPodsRunning, pod)
 | 
									daemonPodsRunning = append(daemonPodsRunning, pod)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -312,6 +312,41 @@ func validateDaemonSetPodsAndMarkReady(
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func validateDaemonSetPodsActive(
 | 
				
			||||||
 | 
						podClient corev1client.PodInterface,
 | 
				
			||||||
 | 
						podInformer cache.SharedIndexInformer,
 | 
				
			||||||
 | 
						numberPods int,
 | 
				
			||||||
 | 
						t *testing.T,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						if err := wait.Poll(time.Second, 60*time.Second, func() (bool, error) {
 | 
				
			||||||
 | 
							objects := podInformer.GetIndexer().List()
 | 
				
			||||||
 | 
							if len(objects) < numberPods {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							podsActiveCount := 0
 | 
				
			||||||
 | 
							for _, object := range objects {
 | 
				
			||||||
 | 
								pod := object.(*v1.Pod)
 | 
				
			||||||
 | 
								ownerReferences := pod.ObjectMeta.OwnerReferences
 | 
				
			||||||
 | 
								if len(ownerReferences) != 1 {
 | 
				
			||||||
 | 
									return false, fmt.Errorf("Pod %s has %d OwnerReferences, expected only 1", pod.Name, len(ownerReferences))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								controllerRef := ownerReferences[0]
 | 
				
			||||||
 | 
								if got, want := controllerRef.Kind, "DaemonSet"; got != want {
 | 
				
			||||||
 | 
									t.Errorf("controllerRef.Kind = %q, want %q", got, want)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if controllerRef.Controller == nil || *controllerRef.Controller != true {
 | 
				
			||||||
 | 
									t.Errorf("controllerRef.Controller is not set to true")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if pod.Status.Phase == v1.PodRunning || pod.Status.Phase == v1.PodPending {
 | 
				
			||||||
 | 
									podsActiveCount += 1
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return podsActiveCount == numberPods, nil
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// podUnschedulable returns a condition function that returns true if the given pod
 | 
					// podUnschedulable returns a condition function that returns true if the given pod
 | 
				
			||||||
// gets unschedulable status.
 | 
					// gets unschedulable status.
 | 
				
			||||||
func podUnschedulable(c clientset.Interface, podNamespace, podName string) wait.ConditionFunc {
 | 
					func podUnschedulable(c clientset.Interface, podNamespace, podName string) wait.ConditionFunc {
 | 
				
			||||||
@@ -498,6 +533,50 @@ func TestSimpleDaemonSetLaunchesPods(t *testing.T) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSimpleDaemonSetRestartsPodsOnTerminalPhase(t *testing.T) {
 | 
				
			||||||
 | 
						for _, podPhase := range []v1.PodPhase{v1.PodSucceeded, v1.PodFailed} {
 | 
				
			||||||
 | 
							t.Run(string(podPhase), func(tt *testing.T) {
 | 
				
			||||||
 | 
								forEachStrategy(tt, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
 | 
				
			||||||
 | 
									ctx, closeFn, dc, informers, clientset := setup(t)
 | 
				
			||||||
 | 
									defer closeFn()
 | 
				
			||||||
 | 
									ns := framework.CreateNamespaceOrDie(clientset, "daemonset-restart-terminal-pod-test", t)
 | 
				
			||||||
 | 
									defer framework.DeleteNamespaceOrDie(clientset, ns, t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									dsClient := clientset.AppsV1().DaemonSets(ns.Name)
 | 
				
			||||||
 | 
									podClient := clientset.CoreV1().Pods(ns.Name)
 | 
				
			||||||
 | 
									nodeClient := clientset.CoreV1().Nodes()
 | 
				
			||||||
 | 
									podInformer := informers.Core().V1().Pods().Informer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									informers.Start(ctx.Done())
 | 
				
			||||||
 | 
									go dc.Run(ctx, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ds := newDaemonSet("restart-terminal-pod", ns.Name)
 | 
				
			||||||
 | 
									ds.Spec.UpdateStrategy = *strategy
 | 
				
			||||||
 | 
									if _, err := dsClient.Create(ctx, ds, metav1.CreateOptions{}); err != nil {
 | 
				
			||||||
 | 
										t.Fatalf("Failed to create DaemonSet: %v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer cleanupDaemonSets(t, clientset, ds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									numNodes := 3
 | 
				
			||||||
 | 
									addNodes(nodeClient, 0, numNodes, nil, t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									validateDaemonSetPodsAndMarkReady(podClient, podInformer, numNodes, t)
 | 
				
			||||||
 | 
									validateDaemonSetStatus(dsClient, ds.Name, int32(numNodes), t)
 | 
				
			||||||
 | 
									podToMarkAsTerminal := podInformer.GetIndexer().List()[0].(*v1.Pod)
 | 
				
			||||||
 | 
									podCopy := podToMarkAsTerminal.DeepCopy()
 | 
				
			||||||
 | 
									podCopy.Status.Phase = podPhase
 | 
				
			||||||
 | 
									if _, err := podClient.UpdateStatus(ctx, podCopy, metav1.UpdateOptions{}); err != nil {
 | 
				
			||||||
 | 
										t.Fatalf("Failed to mark the pod as terminal with phase: %v. Error: %v", podPhase, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									// verify all pods are active. They either continue Running or are Pending after restart
 | 
				
			||||||
 | 
									validateDaemonSetPodsActive(podClient, podInformer, numNodes, t)
 | 
				
			||||||
 | 
									validateDaemonSetPodsAndMarkReady(podClient, podInformer, numNodes, t)
 | 
				
			||||||
 | 
									validateDaemonSetStatus(dsClient, ds.Name, int32(numNodes), t)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDaemonSetWithNodeSelectorLaunchesPods(t *testing.T) {
 | 
					func TestDaemonSetWithNodeSelectorLaunchesPods(t *testing.T) {
 | 
				
			||||||
	forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
 | 
						forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
 | 
				
			||||||
		ctx, closeFn, dc, informers, clientset := setup(t)
 | 
							ctx, closeFn, dc, informers, clientset := setup(t)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user