-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: check only controller ref to decide if a pod is replicated #5507
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,13 +78,13 @@ func GetPodsForDeletionOnNodeDrain( | |
pdbs []*policyv1.PodDisruptionBudget, | ||
skipNodesWithSystemPods bool, | ||
skipNodesWithLocalStorage bool, | ||
skipNodesWithCustomControllerPods bool, | ||
listers kube_util.ListerRegistry, | ||
minReplica int32, | ||
currentTime time.Time) (pods []*apiv1.Pod, daemonSetPods []*apiv1.Pod, blockingPod *BlockingPod, err error) { | ||
|
||
pods = []*apiv1.Pod{} | ||
daemonSetPods = []*apiv1.Pod{} | ||
checkReferences := listers != nil | ||
// filter kube-system PDBs to avoid doing it for every kube-system pod | ||
kubeSystemPDBs := make([]*policyv1.PodDisruptionBudget, 0) | ||
for _, pdb := range pdbs { | ||
|
@@ -93,6 +93,7 @@ func GetPodsForDeletionOnNodeDrain( | |
} | ||
} | ||
|
||
isDaemonSetPod := false | ||
for _, pod := range podList { | ||
if pod_util.IsMirrorPod(pod) { | ||
continue | ||
|
@@ -106,101 +107,25 @@ func GetPodsForDeletionOnNodeDrain( | |
continue | ||
} | ||
|
||
isDaemonSetPod := false | ||
replicated := false | ||
safeToEvict := hasSafeToEvictAnnotation(pod) | ||
terminal := isPodTerminal(pod) | ||
|
||
controllerRef := ControllerRef(pod) | ||
refKind := "" | ||
if controllerRef != nil { | ||
refKind = controllerRef.Kind | ||
} | ||
|
||
// For now, owner controller must be in the same namespace as the pod | ||
// so OwnerReference doesn't have its own Namespace field | ||
controllerNamespace := pod.Namespace | ||
|
||
if refKind == "ReplicationController" { | ||
if checkReferences { | ||
rc, err := listers.ReplicationControllerLister().ReplicationControllers(controllerNamespace).Get(controllerRef.Name) | ||
// Assume a reason for an error is because the RC is either | ||
// gone/missing or that the rc has too few replicas configured. | ||
// TODO: replace the minReplica check with pod disruption budget. | ||
if err == nil && rc != nil { | ||
if rc.Spec.Replicas != nil && *rc.Spec.Replicas < minReplica { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, &BlockingPod{Pod: pod, Reason: MinReplicasReached}, fmt.Errorf("replication controller for %s/%s has too few replicas spec: %d min: %d", | ||
pod.Namespace, pod.Name, rc.Spec.Replicas, minReplica) | ||
} | ||
replicated = true | ||
} else { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("replication controller for %s/%s is not available, err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} else { | ||
replicated = true | ||
if skipNodesWithCustomControllerPods { | ||
// TODO(vadasambar): remove this when we get rid of skipNodesWithCustomControllerPods | ||
replicated, isDaemonSetPod, blockingPod, err = legacyCheckForReplicatedPods(listers, pod, minReplica) | ||
if err != nil { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, blockingPod, err | ||
} | ||
} else if pod_util.IsDaemonSetPod(pod) { | ||
isDaemonSetPod = true | ||
// don't have listener for other DaemonSet kind | ||
// TODO: we should use a generic client for checking the reference. | ||
if checkReferences && refKind == "DaemonSet" { | ||
_, err := listers.DaemonSetLister().DaemonSets(controllerNamespace).Get(controllerRef.Name) | ||
if apierrors.IsNotFound(err) { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("daemonset for %s/%s is not present, err: %v", pod.Namespace, pod.Name, err) | ||
} else if err != nil { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, &BlockingPod{Pod: pod, Reason: UnexpectedError}, fmt.Errorf("error when trying to get daemonset for %s/%s , err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} | ||
} else if refKind == "Job" { | ||
if checkReferences { | ||
job, err := listers.JobLister().Jobs(controllerNamespace).Get(controllerRef.Name) | ||
|
||
// Assume the only reason for an error is because the Job is | ||
// gone/missing, not for any other cause. TODO(mml): something more | ||
// sophisticated than this | ||
if err == nil && job != nil { | ||
replicated = true | ||
} else { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("job for %s/%s is not available: err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} else { | ||
} else { | ||
if ControllerRef(pod) != nil { | ||
replicated = true | ||
} | ||
} else if refKind == "ReplicaSet" { | ||
if checkReferences { | ||
rs, err := listers.ReplicaSetLister().ReplicaSets(controllerNamespace).Get(controllerRef.Name) | ||
|
||
// Assume the only reason for an error is because the RS is | ||
// gone/missing, not for any other cause. TODO(mml): something more | ||
// sophisticated than this | ||
if err == nil && rs != nil { | ||
if rs.Spec.Replicas != nil && *rs.Spec.Replicas < minReplica { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, &BlockingPod{Pod: pod, Reason: MinReplicasReached}, fmt.Errorf("replication controller for %s/%s has too few replicas spec: %d min: %d", | ||
pod.Namespace, pod.Name, rs.Spec.Replicas, minReplica) | ||
} | ||
replicated = true | ||
} else { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("replication controller for %s/%s is not available, err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} else { | ||
replicated = true | ||
} | ||
} else if refKind == "StatefulSet" { | ||
if checkReferences { | ||
ss, err := listers.StatefulSetLister().StatefulSets(controllerNamespace).Get(controllerRef.Name) | ||
|
||
// Assume the only reason for an error is because the StatefulSet is | ||
// gone/missing, not for any other cause. TODO(mml): something more | ||
// sophisticated than this | ||
if err == nil && ss != nil { | ||
replicated = true | ||
} else { | ||
return []*apiv1.Pod{}, []*apiv1.Pod{}, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("statefulset for %s/%s is not available: err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} else { | ||
replicated = true | ||
if pod_util.IsDaemonSetPod(pod) { | ||
isDaemonSetPod = true | ||
} | ||
} | ||
|
||
if isDaemonSetPod { | ||
daemonSetPods = append(daemonSetPods, pod) | ||
continue | ||
|
@@ -231,6 +156,104 @@ func GetPodsForDeletionOnNodeDrain( | |
return pods, daemonSetPods, nil, nil | ||
} | ||
|
||
func legacyCheckForReplicatedPods(listers kube_util.ListerRegistry, pod *apiv1.Pod, minReplica int32) (replicated bool, isDaemonSetPod bool, blockingPod *BlockingPod, err error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that the return type This might not be the best approach since it can confuse someone when they look at line 162 and 165 below (because the variables are already defined due to them being named return types). Open to other ideas here. |
||
replicated = false | ||
refKind := "" | ||
checkReferences := listers != nil | ||
isDaemonSetPod = false | ||
|
||
controllerRef := ControllerRef(pod) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same code as our current code here abstracted into a function. I have done some minor changes like:
|
||
if controllerRef != nil { | ||
refKind = controllerRef.Kind | ||
} | ||
|
||
// For now, owner controller must be in the same namespace as the pod | ||
// so OwnerReference doesn't have its own Namespace field | ||
controllerNamespace := pod.Namespace | ||
if refKind == "ReplicationController" { | ||
if checkReferences { | ||
rc, err := listers.ReplicationControllerLister().ReplicationControllers(controllerNamespace).Get(controllerRef.Name) | ||
// Assume a reason for an error is because the RC is either | ||
// gone/missing or that the rc has too few replicas configured. | ||
// TODO: replace the minReplica check with pod disruption budget. | ||
if err == nil && rc != nil { | ||
if rc.Spec.Replicas != nil && *rc.Spec.Replicas < minReplica { | ||
return replicated, isDaemonSetPod, &BlockingPod{Pod: pod, Reason: MinReplicasReached}, fmt.Errorf("replication controller for %s/%s has too few replicas spec: %d min: %d", | ||
pod.Namespace, pod.Name, rc.Spec.Replicas, minReplica) | ||
} | ||
replicated = true | ||
} else { | ||
return replicated, isDaemonSetPod, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("replication controller for %s/%s is not available, err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} else { | ||
replicated = true | ||
} | ||
} else if pod_util.IsDaemonSetPod(pod) { | ||
isDaemonSetPod = true | ||
// don't have listener for other DaemonSet kind | ||
// TODO: we should use a generic client for checking the reference. | ||
if checkReferences && refKind == "DaemonSet" { | ||
_, err := listers.DaemonSetLister().DaemonSets(controllerNamespace).Get(controllerRef.Name) | ||
if apierrors.IsNotFound(err) { | ||
return replicated, isDaemonSetPod, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("daemonset for %s/%s is not present, err: %v", pod.Namespace, pod.Name, err) | ||
} else if err != nil { | ||
return replicated, isDaemonSetPod, &BlockingPod{Pod: pod, Reason: UnexpectedError}, fmt.Errorf("error when trying to get daemonset for %s/%s , err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} | ||
} else if refKind == "Job" { | ||
if checkReferences { | ||
job, err := listers.JobLister().Jobs(controllerNamespace).Get(controllerRef.Name) | ||
|
||
// Assume the only reason for an error is because the Job is | ||
// gone/missing, not for any other cause. TODO(mml): something more | ||
// sophisticated than this | ||
if err == nil && job != nil { | ||
replicated = true | ||
} else { | ||
return replicated, isDaemonSetPod, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("job for %s/%s is not available: err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} else { | ||
replicated = true | ||
} | ||
} else if refKind == "ReplicaSet" { | ||
if checkReferences { | ||
rs, err := listers.ReplicaSetLister().ReplicaSets(controllerNamespace).Get(controllerRef.Name) | ||
|
||
// Assume the only reason for an error is because the RS is | ||
// gone/missing, not for any other cause. TODO(mml): something more | ||
// sophisticated than this | ||
if err == nil && rs != nil { | ||
if rs.Spec.Replicas != nil && *rs.Spec.Replicas < minReplica { | ||
return replicated, isDaemonSetPod, &BlockingPod{Pod: pod, Reason: MinReplicasReached}, fmt.Errorf("replication controller for %s/%s has too few replicas spec: %d min: %d", | ||
pod.Namespace, pod.Name, rs.Spec.Replicas, minReplica) | ||
} | ||
replicated = true | ||
} else { | ||
return replicated, isDaemonSetPod, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("replication controller for %s/%s is not available, err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} else { | ||
replicated = true | ||
} | ||
} else if refKind == "StatefulSet" { | ||
if checkReferences { | ||
ss, err := listers.StatefulSetLister().StatefulSets(controllerNamespace).Get(controllerRef.Name) | ||
|
||
// Assume the only reason for an error is because the StatefulSet is | ||
// gone/missing, not for any other cause. TODO(mml): something more | ||
// sophisticated than this | ||
if err == nil && ss != nil { | ||
replicated = true | ||
} else { | ||
return replicated, isDaemonSetPod, &BlockingPod{Pod: pod, Reason: ControllerNotFound}, fmt.Errorf("statefulset for %s/%s is not available: err: %v", pod.Namespace, pod.Name, err) | ||
} | ||
} else { | ||
replicated = true | ||
} | ||
} | ||
|
||
return replicated, isDaemonSetPod, &BlockingPod{}, nil | ||
} | ||
|
||
// ControllerRef returns the OwnerReference to pod's controller. | ||
func ControllerRef(pod *apiv1.Pod) *metav1.OwnerReference { | ||
return metav1.GetControllerOf(pod) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have set
SkipNodesWithCustomControllerPods: true
to preserve current behavior of these test.