tomplus / kubernetes_asyncio

Python asynchronous client library for Kubernetes http://kubernetes.io/
Apache License 2.0
353 stars 70 forks source link

Since 30.1.0, receive 400 error patching a secret #332

Closed rad-pat closed 4 weeks ago

rad-pat commented 1 month ago

Since upgrading from 29.0.0 to 30.1.0 or later, we receive a 400 Bad Request when trying to patch a secret.

import asyncio

from kubernetes_asyncio import client
from kubernetes_asyncio.config import load_kube_config

async def patch_secret(secret_name: str):
    await load_kube_config()
    async with client.ApiClient() as api_client:
        corev1 = client.CoreV1Api(api_client)

        secret = client.V1Secret(
            kind='Secret',
            string_data={
                'some': 'data'
            },
            metadata=client.V1ObjectMeta(
                name=secret_name
            ),
            type='Opaque'
        )

        await corev1.create_namespaced_secret(
            namespace="pw-bug-fixes",
            body=secret
        )
        try:
            await corev1.patch_namespaced_secret(
                namespace="pw-bug-fixes",
                name=secret_name,
                body=secret
            )
        finally:
            await corev1.delete_namespaced_secret(
                namespace="pw-bug-fixes",
                name=secret_name,
            )

if __name__ == "__main__":
    asyncio.run(patch_secret('some-secret'))
Traceback (most recent call last):
  File "/home/plaid/patch_secret_bug.py", line 29, in patch_secret
    await corev1.patch_namespaced_secret(
  File "/home/plaid/py311/lib/python3.11/site-packages/kubernetes_asyncio/client/api_client.py", line 192, in __call_api
    raise e
  File "/home/plaid/py311/lib/python3.11/site-packages/kubernetes_asyncio/client/api_client.py", line 185, in __call_api
    response_data = await self.request(
                    ^^^^^^^^^^^^^^^^^^^
  File "/home/plaid/py311/lib/python3.11/site-packages/kubernetes_asyncio/client/rest.py", line 256, in PATCH
    return (await self.request("PATCH", url,
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/plaid/py311/lib/python3.11/site-packages/kubernetes_asyncio/client/rest.py", line 192, in request
    raise ApiException(http_resp=r)
kubernetes_asyncio.client.exceptions.ApiException: (400)
Reason: Bad Request
HTTP response headers: <CIMultiDictProxy('Audit-Id': '5ea7bf85-2b49-491e-98f1-323f102e72fc', 'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'X-Kubernetes-Pf-Flowschema-Uid': '684e80ca-63dd-4bbf-86a8-050948d97fd3', 'X-Kubernetes-Pf-Prioritylevel-Uid': 'aed42fd9-bb8a-4011-b997-c97e950c24f8', 'Date': 'Fri, 23 Aug 2024 10:38:19 GMT', 'Content-Length': '211')>
HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"error decoding patch: json: cannot unmarshal object into Go value of type []handlers.jsonPatchOp","reason":"BadRequest","code":400}
tomplus commented 1 month ago

Thanks for reporting this issue.

It's been introduced in 31.0.0 (by PR #303).

I'll fix it but for now you can use workarounds:

  1. converting a body to a dict
await corev1.patch_namespaced_secret(
                namespace="pw-bug-fixes",
                name=secret_name,
                body=secret.to_dict()
            )
  1. forcing a valid content-type:
await corev1.patch_namespaced_secret(
                namespace="pw-bug-fixes",
                name=secret_name,
                body=secret,
                _content_type='application/strategic-merge-patch+json'
            )