Repository: kubernetes
Issue: Controllers writing different API versions breaks SSA diffing
Labels: kind/bug,sig/api-machinery,triage/accepted

### What happened?

I have two controllers:
* One does a SSA of the resource with version "v1"
* One does a PUT of the resource `/status` with version v1beta1

Each time the SSA operation does a full write, incrementing the resourceVersion, causing an infinite loop as they continually apply.

If both use the same version, this does NOT occur

### What did you expect to happen?

The SSA call, which modifies nothing, is a NOP.

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

Sorry its not a _minimal_ reproducer, but should be easy to do

`spec.yaml`

<details>

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: hyper
  namespace: default
  annotations:
    networking.istio.io/service-type: "ClusterIP"
  ownerReferences:
    - apiVersion: networking.example.io/v1
      kind: SuperService
      name: hyper
      uid: "c5725128-104a-4c64-968b-4565a090d520"
spec:
  gatewayClassName: istio
  infrastructure:
    annotations:
      proxy.istio.io/config: |-
        proxyMetadata:
          ENABLE_HBONE: "false"
  listeners:
    - name: "http"
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: Same
    - name: "hyper-https"
      protocol: HTTPS
      allowedRoutes:
        namespaces:
          from: Same
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
        - name: hyper-tls
          group: ""
          kind: Secret
```
</details>

status.yaml
<details>

```yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  annotations:
    gateway.istio.io/controller-version: '5'
    networking.istio.io/service-type: ClusterIP
  creationTimestamp: null
  name: hyper
  namespace: default
  ownerReferences:
  - apiVersion: networking.example.io/v1
    kind: SuperService
    name: hyper
    uid: c5725128-104a-4c64-968b-4565a090d520
  resourceVersion: '384735'
  uid: 5dc737e6-2a53-4c48-9c66-555f8c534dd9
spec:
  gatewayClassName: ''
  listeners: null
status:
  addresses:
  - type: Hostname
    value: hyper-istio.default.svc.cluster.local
  - type: IPAddress
    value: 10.96.212.50
  conditions:
  - lastTransitionTime: '2023-11-30T21:18:10Z'
    message: Resource accepted
    observedGeneration: 1
    reason: Accepted
    status: 'True'
    type: Accepted
  - lastTransitionTime: '2023-11-30T21:18:10Z'
    message: Resource programmed, assigned to service(s) hyper-istio.default.svc.cluster.local:443
      and hyper-istio.default.svc.cluster.local:80
    observedGeneration: 1
    reason: Programmed
    status: 'True'
    type: Programmed
  listeners:
  - attachedRoutes: 1
    conditions:
    - lastTransitionTime: '2023-11-30T21:18:10Z'
      message: No errors found
      observedGeneration: 1
      reason: Accepted
      status: 'True'
      type: Accepted
    - lastTransitionTime: '2023-11-30T21:18:10Z'
      message: No errors found
      observedGeneration: 1
      reason: NoConflicts
      status: 'False'
      type: Conflicted
    - lastTransitionTime: '2023-11-30T21:18:10Z'
      message: No errors found
      observedGeneration: 1
      reason: Programmed
      status: 'True'
      type: Programmed
    - lastTransitionTime: '2023-11-30T21:18:10Z'
      message: No errors found
      observedGeneration: 1
      reason: ResolvedRefs
      status: 'True'
      type: ResolvedRefs
    name: http
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
    - group: gateway.networking.k8s.io
      kind: GRPCRoute
  - attachedRoutes: 1
    conditions:
    - lastTransitionTime: '2023-11-30T21:18:10Z'
      message: No errors found
      observedGeneration: 1
      reason: Accepted
      status: 'True'
      type: Accepted
    - lastTransitionTime: '2023-11-30T21:18:10Z'
      message: No errors found
      observedGeneration: 1
      reason: NoConflicts
      status: 'False'
      type: Conflicted
    - lastTransitionTime: '2023-11-30T21:18:10Z'
      message: Bad TLS configuration
      observedGeneration: 1
      reason: Invalid
      status: 'False'
      type: Programmed
    - lastTransitionTime: '2023-11-30T21:18:10Z'
      message: invalid certificate reference /Secret/hyper-tls., secret default/hyper-tls
        not found
      observedGeneration: 1
      reason: InvalidCertificateRef
      status: 'False'
      type: ResolvedRefs
    name: hyper-https
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
    - group: gateway.networking.k8s.io
      kind: GRPCRoute
```
</details>

Then, alternate between:
* `kubectl apply --server-side spec.yaml`
* `curl /apis/gateway.networking.k8s.io/v1beta1/namespaces/default/gateways/hyper/status -X PUT -d @status.yaml -H "Content-type:  application/yaml"`

(update resourceVersion in status.yaml in between).

Each time you will see the object change. Before and after below:

After apply
<details>

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  annotations:
    gateway.istio.io/controller-version: "5"
    networking.istio.io/service-type: ClusterIP
  creationTimestamp: "2023-11-30T21:18:10Z"
  generation: 1
  managedFields:
  - apiVersion: gateway.networking.k8s.io/v1beta1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          f:gateway.istio.io/controller-version: {}
    manager: istio.io/gateway-controller
    operation: Apply
    time: "2023-11-30T21:18:10Z"
  - apiVersion: gateway.networking.k8s.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          f:networking.istio.io/service-type: {}
        f:ownerReferences:
          k:{"uid":"c5725128-104a-4c64-968b-4565a090d520"}: {}
      f:spec:
        f:gatewayClassName: {}
        f:infrastructure:
          f:annotations:
            f:proxy.istio.io/config: {}
        f:listeners:
          k:{"name":"http"}:
            .: {}
            f:allowedRoutes:
              f:namespaces:
                f:from: {}
            f:name: {}
            f:port: {}
            f:protocol: {}
          k:{"name":"hyper-https"}:
            .: {}
            f:allowedRoutes:
              f:namespaces:
                f:from: {}
            f:name: {}
            f:port: {}
            f:protocol: {}
            f:tls:
              f:certificateRefs: {}
              f:mode: {}
    manager: kubectl
    operation: Apply
    time: "2023-11-30T21:18:10Z"
  - apiVersion: gateway.networking.k8s.io/v1beta1
    fieldsType: FieldsV1
    fieldsV1:
      f:status:
        f:addresses: {}
        f:conditions:
          k:{"type":"Accepted"}:
            f:lastTransitionTime: {}
            f:message: {}
            f:observedGeneration: {}
            f:reason: {}
            f:status: {}
          k:{"type":"Programmed"}:
            f:lastTransitionTime: {}
            f:message: {}
            f:observedGeneration: {}
            f:reason: {}
            f:status: {}
        f:listeners:
          .: {}
          k:{"name":"http"}:
            .: {}
            f:attachedRoutes: {}
            f:conditions:
              .: {}
              k:{"type":"Accepted"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"Conflicted"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"Programmed"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"ResolvedRefs"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
            f:name: {}
            f:supportedKinds: {}
          k:{"name":"hyper-https"}:
            .: {}
            f:attachedRoutes: {}
            f:conditions:
              .: {}
              k:{"type":"Accepted"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"Conflicted"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"Programmed"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"ResolvedRefs"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
            f:name: {}
            f:supportedKinds: {}
    manager: pilot-discovery
    operation: Update
    subresource: status
    time: "2023-11-30T21:18:10Z"
  name: hyper
  namespace: default
  ownerReferences:
  - apiVersion: networking.example.io/v1
    kind: SuperService
    name: hyper
    uid: c5725128-104a-4c64-968b-4565a090d520
  resourceVersion: "384735"
  uid: 5dc737e6-2a53-4c48-9c66-555f8c534dd9
spec:
  gatewayClassName: istio
  infrastructure:
    annotations:
      proxy.istio.io/config: |-
        proxyMetadata:
          ENABLE_HBONE: "false"
  listeners:
  - allowedRoutes:
      namespaces:
        from: Same
    name: http
    port: 80
    protocol: HTTP
  - allowedRoutes:
      namespaces:
        from: Same
    name: hyper-https
    port: 443
    protocol: HTTPS
    tls:
      certificateRefs:
      - group: ""
        kind: Secret
        name: hyper-tls
      mode: Terminate
status:
  addresses:
  - type: Hostname
    value: hyper-istio.default.svc.cluster.local
  - type: IPAddress
    value: 10.96.212.50
  conditions:
  - lastTransitionTime: "2023-11-30T21:18:10Z"
    message: Resource accepted
    observedGeneration: 1
    reason: Accepted
    status: "True"
    type: Accepted
  - lastTransitionTime: "2023-11-30T21:18:10Z"
    message: Resource programmed, assigned to service(s) hyper-istio.default.svc.cluster.local:443
      and hyper-istio.default.svc.cluster.local:80
    observedGeneration: 1
    reason: Programmed
    status: "True"
    type: Programmed
  listeners:
  - attachedRoutes: 1
    conditions:
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: Accepted
      status: "True"
      type: Accepted
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: NoConflicts
      status: "False"
      type: Conflicted
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: Programmed
      status: "True"
      type: Programmed
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: ResolvedRefs
      status: "True"
      type: ResolvedRefs
    name: http
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
    - group: gateway.networking.k8s.io
      kind: GRPCRoute
  - attachedRoutes: 1
    conditions:
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: Accepted
      status: "True"
      type: Accepted
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: NoConflicts
      status: "False"
      type: Conflicted
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: Bad TLS configuration
      observedGeneration: 1
      reason: Invalid
      status: "False"
      type: Programmed
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: invalid certificate reference /Secret/hyper-tls., secret default/hyper-tls
        not found
      observedGeneration: 1
      reason: InvalidCertificateRef
      status: "False"
      type: ResolvedRefs
    name: hyper-https
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
    - group: gateway.networking.k8s.io
      kind: GRPCRoute
```

</details>

After status update
<details>

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  annotations:
    gateway.istio.io/controller-version: "5"
    networking.istio.io/service-type: ClusterIP
  creationTimestamp: "2023-11-30T21:18:10Z"
  generation: 1
  managedFields:
  - apiVersion: gateway.networking.k8s.io/v1beta1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          f:gateway.istio.io/controller-version: {}
    manager: istio.io/gateway-controller
    operation: Apply
    time: "2023-11-30T21:18:10Z"
  - apiVersion: gateway.networking.k8s.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          f:networking.istio.io/service-type: {}
        f:ownerReferences:
          k:{"uid":"c5725128-104a-4c64-968b-4565a090d520"}: {}
    manager: kubectl
    operation: Apply
    time: "2023-11-30T21:18:10Z"
  - apiVersion: gateway.networking.k8s.io/v1beta1
    fieldsType: FieldsV1
    fieldsV1:
      f:status:
        f:addresses: {}
        f:conditions:
          k:{"type":"Accepted"}:
            f:lastTransitionTime: {}
            f:message: {}
            f:observedGeneration: {}
            f:reason: {}
            f:status: {}
          k:{"type":"Programmed"}:
            f:lastTransitionTime: {}
            f:message: {}
            f:observedGeneration: {}
            f:reason: {}
            f:status: {}
        f:listeners:
          .: {}
          k:{"name":"http"}:
            .: {}
            f:attachedRoutes: {}
            f:conditions:
              .: {}
              k:{"type":"Accepted"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"Conflicted"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"Programmed"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"ResolvedRefs"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
            f:name: {}
            f:supportedKinds: {}
          k:{"name":"hyper-https"}:
            .: {}
            f:attachedRoutes: {}
            f:conditions:
              .: {}
              k:{"type":"Accepted"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"Conflicted"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"Programmed"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
              k:{"type":"ResolvedRefs"}:
                .: {}
                f:lastTransitionTime: {}
                f:message: {}
                f:observedGeneration: {}
                f:reason: {}
                f:status: {}
                f:type: {}
            f:name: {}
            f:supportedKinds: {}
    manager: pilot-discovery
    operation: Update
    subresource: status
    time: "2023-11-30T21:18:10Z"
  name: hyper
  namespace: default
  ownerReferences:
  - apiVersion: networking.example.io/v1
    kind: SuperService
    name: hyper
    uid: c5725128-104a-4c64-968b-4565a090d520
  resourceVersion: "384769"
  uid: 5dc737e6-2a53-4c48-9c66-555f8c534dd9
spec:
  gatewayClassName: istio
  infrastructure:
    annotations:
      proxy.istio.io/config: |-
        proxyMetadata:
          ENABLE_HBONE: "false"
  listeners:
  - allowedRoutes:
      namespaces:
        from: Same
    name: http
    port: 80
    protocol: HTTP
  - allowedRoutes:
      namespaces:
        from: Same
    name: hyper-https
    port: 443
    protocol: HTTPS
    tls:
      certificateRefs:
      - group: ""
        kind: Secret
        name: hyper-tls
      mode: Terminate
status:
  addresses:
  - type: Hostname
    value: hyper-istio.default.svc.cluster.local
  - type: IPAddress
    value: 10.96.212.50
  conditions:
  - lastTransitionTime: "2023-11-30T21:18:10Z"
    message: Resource accepted
    observedGeneration: 1
    reason: Accepted
    status: "True"
    type: Accepted
  - lastTransitionTime: "2023-11-30T21:18:10Z"
    message: Resource programmed, assigned to service(s) hyper-istio.default.svc.cluster.local:443
      and hyper-istio.default.svc.cluster.local:80
    observedGeneration: 1
    reason: Programmed
    status: "True"
    type: Programmed
  listeners:
  - attachedRoutes: 1
    conditions:
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: Accepted
      status: "True"
      type: Accepted
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: NoConflicts
      status: "False"
      type: Conflicted
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: Programmed
      status: "True"
      type: Programmed
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: ResolvedRefs
      status: "True"
      type: ResolvedRefs
    name: http
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
    - group: gateway.networking.k8s.io
      kind: GRPCRoute
  - attachedRoutes: 1
    conditions:
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: Accepted
      status: "True"
      type: Accepted
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: No errors found
      observedGeneration: 1
      reason: NoConflicts
      status: "False"
      type: Conflicted
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: Bad TLS configuration
      observedGeneration: 1
      reason: Invalid
      status: "False"
      type: Programmed
    - lastTransitionTime: "2023-11-30T21:18:10Z"
      message: invalid certificate reference /Secret/hyper-tls., secret default/hyper-tls
        not found
      observedGeneration: 1
      reason: InvalidCertificateRef
      status: "False"
      type: ResolvedRefs
    name: hyper-https
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
    - group: gateway.networking.k8s.io
      kind: GRPCRoute
```


Diff:

```diff
27,51d26
<       f:spec:
<         f:gatewayClassName: {}
<         f:infrastructure:
<           f:annotations:
<             f:proxy.istio.io/config: {}
<         f:listeners:
<           k:{"name":"http"}:
<             .: {}
<             f:allowedRoutes:
<               f:namespaces:
<                 f:from: {}
<             f:name: {}
<             f:port: {}
<             f:protocol: {}
<           k:{"name":"hyper-https"}:
<             .: {}
<             f:allowedRoutes:
<               f:namespaces:
<                 f:from: {}
<             f:name: {}
<             f:port: {}
<             f:protocol: {}
<             f:tls:
<               f:certificateRefs: {}
<               f:mode: {}
164c139
<   resourceVersion: "384735"
---
>   resourceVersion: "384769"
```

Basically the `spec` is removed when we do the status update
</details>

### Anything else we need to know?

_No response_

### Kubernetes version

<details>

```console
$ kubectl version
v1.28.0
```

</details>


### Cloud provider

<details>
kind
</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>