Repository: kubernetes/kubernetes (language: go)

Issue:
JobTemplate validation bug: OnExitCodes should be an optional field for PodFailurePolicyRule
### What happened?

[OnExitCodes field should be optional](https://github.com/kubernetes/kubernetes/blob/60862c98a4c0bcbc351b076714108d364883b5f7/staging/src/k8s.io/api/batch/v1/types.go#L238) for a PodFailurePolicyRule. 

However, when trying to create a JobSet programmatically with a JobTemplate that defines a PodFailurePolicy rule for failing a job immediately if the failed pod had condition DisruptionTarget, I get a validation error indicating that `OnExitCodes` cannot be null. I've tried using both `k8s.io/api v0.29.4` and `k8s.io/api v0.30.2` (with matching versions for k8s.io/apimachinery package as well).

Error:
```
$ k apply -f examples/simple/failure-policy.yaml 
The JobSet "failurepolicy" is invalid: 
* spec.replicatedJobs[0].template.spec.podFailurePolicy.rules[0].onExitCodes: Invalid value: "null": spec.replicatedJobs[0].template.spec.podFailurePolicy.rules[0].onExitCodes in body must be of type object: "null"
* <nil>: Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation
```

JobSet spec:

```yaml
apiVersion: jobset.x-k8s.io/v1alpha2
kind: JobSet
metadata:
  name: failurepolicy
spec:
  replicatedJobs:
  - name: leader
    replicas: 1
    template:
      spec:
        # Set backoff limit to 0 so job will immediately fail if any pod fails.
        backoffLimit: 0
        completions: 2
        parallelism: 2
        template:
          spec:
            restartPolicy: Never
            containers:
            - name: leader
              image: bash:latest
              command:
              - bash
              - -xc
              - |
                for i in $(seq 1 1000)
                do
                  echo "$i"
                  sleep 1
                done
        podFailurePolicy:
          rules:
          - action: FailJob
            onPodConditions:
            - type: DisruptionTarget
```

### What did you expect to happen?

JobTemplate passes validation.

### How can we reproduce it (as minimally and precisely as possible)?

### Repro steps using k8s.io/api v0.29.4

1. Install JobSet:

```VERSION=v0.5.2
kubectl apply --server-side -f https://github.com/kubernetes-sigs/jobset/releases/download/$VERSION/manifests.yaml
```

2. Try applying example JobSet in the description of this issue.

```kubectl apply -f jobset-from-github-issue.yaml```


### Repro steps using k8s.io/api v0.30.2

1. Clone JobSet: `git clone https://github.com/kubernetes-sigs/jobset`
2. Upgrade k8s.io/api and k8s.io/apimachinery

```
go get -u k8s.io/api
go get -u k8s.io/apimachinery
go mod tidy
```

3. Try applying example JobSet in the description of this issue.

```kubectl apply -f jobset-from-github-issue.yaml```


### Anything else we need to know?

_No response_

### Kubernetes version

1.30 (tests on a kind cluster using kindest/node:v1.30.0)

### Cloud provider

<details>

</details>


### OS version

<details>

```console
# On Linux:
$ cat /etc/os-release
# paste output here
$ uname -a
# paste output here

# On Windows:
C:\> wmic os get Caption, Version, BuildNumber, OSArchitecture
# paste output here
```

</details>


### Install tools

<details>

</details>


### Container runtime (CRI) and version (if applicable)

<details>

</details>


### Related plugins (CNI, CSI, ...) and versions (if applicable)

<details>

</details>



Discussion / hints:
There are no sig labels on this issue. Please add an appropriate label by using one of the following commands:
- `/sig <group-name>`
- `/wg <group-name>`
- `/committee <group-name>`

Please see the [group list](https://git.k8s.io/community/sig-list.md) for a listing of the SIGs, working groups, and committees available.


<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</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 `triage/accepted` label and provide further guidance.

The `triage/accepted` label can be added by org members by writing `/triage accepted` in a comment.


<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>
cc @mimowo @alculquicondor 
/assign
I confirmed the issue, moreover, I see that when the JobSet webhook is disabled then it works ok, I think it must be the JobSet webhook which adds the "onExitCodes" field during deserializion /serialization step.

I can also see that when I rebuild JobSet using kubernetes field "onExitCodes" with "omitempty", then it the JobSet creates ok. I will open a PR.

Fix patch:
diff --git a/staging/src/k8s.io/api/batch/v1/types.go b/staging/src/k8s.io/api/batch/v1/types.go
index b29c29670403c..77080b0eac4c1 100644
--- a/staging/src/k8s.io/api/batch/v1/types.go
+++ b/staging/src/k8s.io/api/batch/v1/types.go
@@ -176,7 +176,7 @@ type PodFailurePolicyOnExitCodesRequirement struct {
 	// When specified, it should match one the container or initContainer
 	// names in the pod template.
 	// +optional
-	ContainerName *string `json:"containerName" protobuf:"bytes,1,opt,name=containerName"`
+	ContainerName *string `json:"containerName,omitempty" protobuf:"bytes,1,opt,name=containerName"`
 
 	// Represents the relationship between the container exit code(s) and the
 	// specified values. Containers completed with success (exit code 0) are
@@ -236,14 +236,14 @@ type PodFailurePolicyRule struct {
 
 	// Represents the requirement on the container exit codes.
 	// +optional
-	OnExitCodes *PodFailurePolicyOnExitCodesRequirement `json:"onExitCodes" protobuf:"bytes,2,opt,name=onExitCodes"`
+	OnExitCodes *PodFailurePolicyOnExitCodesRequirement `json:"onExitCodes,omitempty" protobuf:"bytes,2,opt,name=onExitCodes"`
 
 	// Represents the requirement on the pod conditions. The requirement is represented
 	// as a list of pod condition patterns. The requirement is satisfied if at
 	// least one pattern matches an actual pod condition. At most 20 elements are allowed.
 	// +listType=atomic
 	// +optional
-	OnPodConditions []PodFailurePolicyOnPodConditionsPattern `json:"onPodConditions" protobuf:"bytes,3,opt,name=onPodConditions"`
+	OnPodConditions []PodFailurePolicyOnPodConditionsPattern `json:"onPodConditions,omitempty" protobuf:"bytes,3,opt,name=onPodConditions"`
 }
 
 // PodFailurePolicy describes how failed pods influence the backoffLimit.


Test patch: