From b1aa40b043e5da559d7771c72e6910ec4713f383 Mon Sep 17 00:00:00 2001 From: Paul O'Reilly Date: Sun, 3 May 2026 14:08:23 +1200 Subject: [PATCH] kubernetes: SSA via CustomObjectsApi needs apply-patch+yaml client Document the kubernetes-py v35 gotcha that bit M22 Phase 8: patch with force=True against the default merge-patch Content-Type returns 422 silently in mocks but breaks at the apiserver. Workaround is a dedicated ApiClient with the apply-patch header. Co-Authored-By: Claude Opus 4.7 (1M context) --- kubernetes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kubernetes.md b/kubernetes.md index 7444c5c..c5fb30f 100644 --- a/kubernetes.md +++ b/kubernetes.md @@ -177,3 +177,7 @@ A reconciliation controller should touch only resources explicitly declared in i ## Webhook-Triggered Reconciliation with Token Auth Pair periodic reconciliation with an authenticated POST `/reconcile` endpoint so push events can trigger immediate sync. Use a 32+ char Bearer token with constant-time comparison, fail-closed (return 501) if the token is not configured. Avoids worst-case polling latency when a human just committed. + +## kubernetes-py CustomObjectsApi: SSA Requires a Dedicated ApiClient + +`CustomObjectsApi.patch_namespaced_custom_object(force=True)` fails with HTTP 422 on kubernetes-py v35 (`PatchOptions.meta.k8s.io is invalid: force: Forbidden: may not be specified for non-apply patch`). The default Content-Type is `application/merge-patch+json`; `force` is only valid on real server-side applies (`application/apply-patch+yaml`). The `_content_type` kwarg that older docs reference is not exposed in v35. Workaround: build a dedicated `ApiClient` and `set_default_header("Content-Type", "application/apply-patch+yaml")` on it; pass that client to a separate `CustomObjectsApi` used only for SSA patches. Reads/deletes use the default client (no body, default Content-Type harmless). The bug is silent in tests because mocks accept any kwargs — only a real apiserver round-trip surfaces it.