Repository: kubernetes
Issue: Empty `nodeSelectorTerms` should be valid values according to the document, but considered as invalid value when creating pod and at Python Client Library
Labels: kind/bug,sig/api-machinery,sig/apps,triage/accepted

### What happened?

Kubernetes servers and its Python client use different validation rules as described in the documentation against the `nodeSelectorTerms` field. Specifying empty values for the field `nodeSelectorTerms` results in invalid rejected pod spec and causes exception on the Python Client Library

### What did you expect to happen?

Specifying `nodeSelectorTerms` is one of many ways to constrain pods to nodes. According to [[Kubernetes API doc,](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#nodeselectorterm-v1-core)](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#nodeselectorterm-v1-core) this field has the following document:

```
A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.
```

According to kubernetes' documentation and validation, the following values for `nodeSelectorTerms` are valid:

- null
- an empty array -- []
- a non-empty array with `nodeSelectorTerm`

**However**, the K8S Python client does not allow `nodeSelectorTerms` being null as can been seen from [[python/v1_node_selector.py at master · kubernetes-client/python (github.com)](https://github.com/kubernetes-client/python/blob/master/kubernetes/client/models/v1_node_selector.py#L74-L75)](https://github.com/kubernetes-client/python/blob/master/kubernetes/client/models/v1_node_selector.py#L74-L75)

```python
if self.local_vars_configuration.client_side_validation and node_selector_terms is None:  # noqa: E501
    raise ValueError("Invalid value for `node_selector_terms`, must not be `None`")  # noqa: E501
```

The generated pod spec is also rejected by the Kubernetes later based on the events:
   ```shell
   Warning  FailedCreate      0s (x10 over 3s)  statefulset-controller  create Pod redis-cluster-leader-0 in StatefulSet redis-cluster-leader failed error: Pod "redis-cluster-leader-0" is invalid: spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Required value: must have at least one node selector term
   ```

### How can we reproduce it (as minimally and precisely as possible)?

1. Deploy the redis-operator: [[OT-CONTAINER-KIT/redis-operator](https://github.com/OT-CONTAINER-KIT/redis-operator)](https://github.com/OT-CONTAINER-KIT/redis-operator)

2. Submit this YAML file to K8S

   ```yaml
   apiVersion: redis.redis.opstreelabs.in/v1beta1
   kind: RedisCluster
   metadata:
    name: test-cluster
   spec:
    clusterSize: 3
    kubernetesConfig:
      image: quay.io/opstree/redis:v6.2.5
    redisLeader:
    affinity:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms: []
   ```

3. Execute `kubectl get statefulset.apps/test-cluster-leader -o yaml` and observe that **the `nodeSelectorTerms` field is null** (due to Golang's JSON marshalling behavior).

4. Use k8s python client's `list_namespaced_stateful_set` function to list the object. Notice that an exception is thrown. 

   ```shell
   File "/home/matt/.local/lib/python3.8/site-packages/kubernetes/client/models/v1_node_selector.py", line 75, in node_selector_terms
      raise ValueError("Invalid value for `node_selector_terms`, must not be `None`")  # noqa: E501
   ```

### Anything else we need to know?

1. W think the problem lies on the K8S server side. The validation rule it used should reject `nodeSelectorTerms` being `null` or `[]` because such value does not pass the statefulset controller's validation rules. The problem is harmful because the erroneous input does not get rejected explicitly and it is applied partially.

   ```shell
   Warning  FailedCreate      0s (x10 over 3s)  statefulset-controller  create Pod redis-cluster-leader-0 in StatefulSet redis-cluster-leader failed error: Pod "redis-cluster-leader-0" is invalid: spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Required value: must have at least one node selector term
   ```

2. As can be seen from the step 3 above, when we submit `nodeSelectorTerms: []` to K8S, the observed statefulset state has `nodeSelectorTerms: null`. I doubt this has to with the marshalling & unmarshalling process but since no special json tag is given here, I am not sure why this happens.

   ```go
   //https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/api/core/v1/types.go#L2732-L2739
   
   type NodeSelector struct {
   	//Required. A list of node selector terms. The terms are ORed.
    	NodeSelectorTerms []NodeSelectorTerm `json:"nodeSelectorTerms" protobuf:"bytes,1,rep,name=nodeSelectorTerms"`
   }
   ```
3. Empty array bypasses the validation generated by the controller-gen. If a field is required, by specifying an empty array it can bypass the CRD validation, but later get marshaled to `null`.

### Kubernetes version

<details>

```console
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"24", GitVersion:"v1.24.0", GitCommit:"4ce5a8954017644c5420bae81d72b09b735c21f0", GitTreeState:"clean", BuildDate:"2022-05-03T13:46:05Z", GoVersion:"go1.18.1", Compiler:"gc", Platform:"darwin/amd64"}
Kustomize Version: v4.5.4
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.9", GitCommit:"6df4433e288edc9c40c2e344eb336f63fad45cd2", GitTreeState:"clean", BuildDate:"2022-05-19T19:53:08Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}
```

</details>


### Cloud provider

<details>

</details>


### OS version

<details>

```console
# On Linux:
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.5 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.5 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

$ uname -a
Linux riverrun 5.15.0-46-generic #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

# 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>