Change:
Pod backoff calculation excludes pods that match an rule in the failure policy: This could lead to fast retries, overwhelming the control plane, if most failures match an rule. The semantics of is not to count towards the limit, but the backoff should still be respected. /sig apps Recreations to be throttled. a Job with failure policy that matches every kind of failure. a container that always fails You would observe immediate recreation. No response_ 27+ <detailsN/A </details<details</details<details</details<details</details<details</details
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
cc

Discussion:
active := int32(len(jobCtx.activePods)) newSucceededPods, newFailedPods := getNewFinishedPods(jobCtx) jobCtx.succeeded = job.Status.Succeeded + int32(len(newSucceededPods)) + int32(len(jobCtx.uncounted.succeeded)) <del> failed := job.Status.Failed + int32(len(newFailedPods)) + int32(len(jobCtx.uncounted.failed)) </del> <ins> failed := job.Status.Failed + int32(nonIgnoredFailedPodsCount(jobCtx, newFailedPods)) + int32(len(jobCtx.uncounted.failed)) </ins> var ready *int32 if feature.DefaultFeatureGate.Enabled(features.JobReadyPods) { ready = pointer.Int32(countReadyPods(jobCtx.activePods))

return successfulDeletes, errorFromChannel(errCh) } <ins> func nonIgnoredFailedPodsCount(jobCtx *syncJobCtx, failedPods []*v1.Pod) int { result := len(failedPods) if feature.DefaultFeatureGate.Enabled(features.JobPodFailurePolicy) && jobCtx.job.Spec.PodFailurePolicy != nil { for _, p := range failedPods { _, countFailed, _ := matchPodFailurePolicy(jobCtx.job.Spec.PodFailurePolicy, p) if !countFailed { result-- } } } return result } </ins> // deleteJobPods deletes the pods, returns the number of successful removals // and any error. func (jm *Controller) deleteJobPods(ctx context.Context, job *batch.Job, jobKey string, pods []*v1.Pod) (int32, error) {

return p.Status.Phase == v1.PodSucceeded }) failedPods = getValidPodsWithFilter(jobCtx, jobCtx.uncounted.Failed(), func(p *v1.Pod) bool { <del> if feature.DefaultFeatureGate.Enabled(features.JobPodFailurePolicy) && jobCtx.job.Spec.PodFailurePolicy != nil { if !isPodFailed(p, jobCtx.job) { return false } _, countFailed, _ := matchPodFailurePolicy(jobCtx.job.Spec.PodFailurePolicy, p) return countFailed } else { return isPodFailed(p, jobCtx.job) } </del> <ins> return isPodFailed(p, jobCtx.job) </ins> }) return succeededPods, failedPods }

wantStatusFailed:    0, wantStatusSucceeded: 0, }, <ins> "ignore pod failure based on OnPodConditions, ignored failures delays pod recreation": { enableJobPodFailurePolicy: true, job: batch.Job{ TypeMeta:   metav1.TypeMeta{Kind: "Job"}, ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector:     validSelector, Template:     validTemplate, Parallelism:  pointer.Int32(1), Completions:  pointer.Int32(1), BackoffLimit: pointer.Int32(0), PodFailurePolicy: &batch.PodFailurePolicy{ Rules: []batch.PodFailurePolicyRule{ { Action: batch.PodFailurePolicyActionIgnore, OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{ { Type:   v1.DisruptionTarget, Status: v1.ConditionTrue, }, }, }, }, }, }, }, pods: []v1.Pod{ { ObjectMeta: metav1.ObjectMeta{ DeletionTimestamp: &now, }, Status: v1.PodStatus{ Phase: v1.PodFailed, Conditions: []v1.PodCondition{ { Type:   v1.DisruptionTarget, Status: v1.ConditionTrue, }, }, }, }, }, wantConditions:      nil, wantStatusActive:    0, wantStatusFailed:    0, wantStatusSucceeded: 0, }, </ins> "fail job based on OnPodConditions": { enableJobPodFailurePolicy: true, job: batch.Job{