kr8s-org / kr8s

A batteries-included Python client library for Kubernetes that feels familiar for folks who already know how to use kubectl
https://kr8s.org
BSD 3-Clause "New" or "Revised" License
799 stars 43 forks source link

Unknown objects ending with y cannot be fetched #460

Closed matthiasvegh closed 1 month ago

matthiasvegh commented 1 month ago

Which project are you reporting a bug for?

kr8s

What happened?

Resources whose singular name ends with y appear to be pluralized naively by adding an s. This makes it impossible to get them using kr8s. Whereas kubectl get works without issue:

kubectl get ingress.networking.k8s.io
kubectl get networkpolicy
kubectl get CSIStorageCapacity

In my program, I don't actually wish to interact much with the objects themselves, I merely wish to list all objects (of any kind) that matches a particular label-selector and delete them akin to:

kubectl api-resources --namespaced -o name --namespace ${NS} --verbs=list --no-headers \
  | xargs kubectl get -l foo=bar --namespace ${NS} --no-headers \
  | xargs kubectl delete --force

As such, registering the classes that may appear in my cluster is not really practical. As of #432, I thought I wouldn't need to, and could just take advantage of kr8s' excellent async functionality.

However, it appears that resource names that end in y cause kr8s to choke, unless it's a Kind already known by kr8s like NetworkPolicy. I'm using CSIStorageCapacity as an example below as I think that is universal enough to not just be on my cluster. In practice, I have a lot of resources of the form CertificateAuthority, InternalOAuth2Identity, HTTPProxy, etc, nor can I workaround the issue by supplying the plural name instead of the singular name, as kr8s internally looks it up again, and uses the Kind instead of the name I supplied.

Based on the error messages produced, it appears that the path computed simply appends an s to the singularName, ending in 404s, note csistoragecapacitys:

Traceback (most recent call last):
  File "/home/emtyvgh/.local/lib/python3.10/site-packages/kr8s/_api.py", line 172, in call_api
    response.raise_for_status()
  File "/home/emtyvgh/.local/lib/python3.10/site-packages/httpx/_models.py", line 761, in raise_for_status
    raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: Client error '404 Not Found' for url 'https://<cluster>:6443/apis/storage.k8s.io/v1/namespaces/default/csistoragecapacitys'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404

Digging a bit into the code, it appears that the logic to pluralize is in new_class which could accept a plural form (which appears to have been worked on in #403). Unfortunately, async_get_kind only passes kind, and namespaced but not plural. Perhaps async_lookup_kind could return both names?

Anything else?

kr8s version: 0.17.0 python version 3.10

my program:

#!/usr/bin/env python3.10

import asyncio
import kr8s
import kr8s.asyncio

async def main(loop):
    api = await kr8s.asyncio.api()

    ingresses = await api.get('ingress.networking.k8s.io')                      # so far, so good.
    networkpolicies = await api.get('networkpolicies.networking.k8s.io')        # still fine.
    csistoragecapacities = await api.get('csistoragecapacities.storage.k8s.io') # boom!
    csistoragecapacities = await api.get('CSIStorageCapacity')                  # no better

if __name__ == '__main__':
    print(kr8s.__version__)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(loop))
    loop.close()
jacobtomlinson commented 1 month ago

Thanks for the nicely written issue! I've fixed this up in #464.

I really appreciate the small reproducer you included, and digging into the code a little to highlight functions that are problematic. It made it super quick to add a small failing test that highlighted the bug, and then put in a quick fix to get the test passing.

https://github.com/kr8s-org/kr8s/blob/808a54a0e3145886527aa5bf64adde2cdf47bd42/kr8s/tests/test_api.py#L386-L397

jacobtomlinson commented 1 month ago

The fix for this is included in v0.17.1.