Skip to content

Commit 4c63bd5

Browse files
nammnclaude
andcommitted
Fix SSA BadRequestError by stripping server-managed fields
When objects are loaded from the Kubernetes API via load(), they include server-managed fields (managedFields, creationTimestamp, generation, resourceVersion, uid, selfLink, status). Server-side apply (SSA) rejects requests that include these fields. Added _strip_server_managed_fields() helper that removes these fields before making SSA calls. Applied to both customobject.py:patch() and kubeobject.py:update() methods. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ce44855 commit 4c63bd5

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

docker/mongodb-kubernetes-tests/kubeobject/customobject.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import copy
34
from datetime import datetime, timedelta
45
from typing import Dict, Optional
56

@@ -8,6 +9,35 @@
89
from kubernetes.dynamic import DynamicClient
910

1011

12+
def _strip_server_managed_fields(obj: Dict) -> Dict:
13+
"""Strip server-managed fields from object before server-side apply.
14+
15+
Server-side apply (SSA) will reject requests that include certain
16+
server-managed fields. These fields are automatically added by the
17+
server when an object is created/updated and should not be sent
18+
back in SSA requests.
19+
"""
20+
result = copy.deepcopy(obj)
21+
22+
# Remove status - server-managed
23+
result.pop("status", None)
24+
25+
# Remove server-managed metadata fields
26+
if "metadata" in result:
27+
metadata = result["metadata"]
28+
for field in [
29+
"managedFields",
30+
"creationTimestamp",
31+
"generation",
32+
"resourceVersion",
33+
"uid",
34+
"selfLink",
35+
]:
36+
metadata.pop(field, None)
37+
38+
return result
39+
40+
1141
class CustomObject:
1242
"""CustomObject is an object mapping to a Custom Resource in Kubernetes. It
1343
includes simple facilities to update the Custom Resource, save it and
@@ -132,10 +162,13 @@ def patch(self, field_manager: str = "e2e-tests") -> CustomObject:
132162
api_version = self.backing_obj.get("apiVersion", f"{self.group}/{self.version}")
133163
kind = self.backing_obj.get("kind", self.kind)
134164

165+
# Strip server-managed fields before SSA to avoid 400 Bad Request
166+
body = _strip_server_managed_fields(self.backing_obj)
167+
135168
resource_api = self._dynamic_client.resources.get(api_version=api_version, kind=kind)
136169
result = resource_api.server_side_apply(
137170
namespace=self.namespace,
138-
body=self.backing_obj,
171+
body=body,
139172
field_manager=field_manager,
140173
)
141174
self.backing_obj = result.to_dict()

docker/mongodb-kubernetes-tests/kubeobject/kubeobject.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import yaml
1010
from box import Box
11+
from kubeobject.customobject import _strip_server_managed_fields
1112
from kubeobject.exceptions import ObjectNotBoundException
1213
from kubernetes import client
1314
from kubernetes.client.api import ApiextensionsV1Api, CustomObjectsApi
@@ -104,9 +105,12 @@ def update(self, field_manager: str = "e2e-tests"):
104105
# there's no corresponding object in the Kubernetes cluster
105106
raise ObjectNotBoundException
106107

107-
body = self.__dict__[KubeObject.BACKING_OBJ].to_dict()
108-
api_version = body.get("apiVersion", f"{self.crd['group']}/{self.crd['version']}")
109-
kind = body.get("kind")
108+
raw_body = self.__dict__[KubeObject.BACKING_OBJ].to_dict()
109+
api_version = raw_body.get("apiVersion", f"{self.crd['group']}/{self.crd['version']}")
110+
kind = raw_body.get("kind")
111+
112+
# Strip server-managed fields before SSA to avoid 400 Bad Request
113+
body = _strip_server_managed_fields(raw_body)
110114

111115
resource_api = self.__dict__["_dynamic_client"].resources.get(api_version=api_version, kind=kind)
112116
result = resource_api.server_side_apply(

0 commit comments

Comments
 (0)