kubeclient icon indicating copy to clipboard operation
kubeclient copied to clipboard

HTTP requests to arbitrary path

Open cben opened this issue 6 years ago • 5 comments

STATUS: exists on master, undocumented

A few use cases have come up over time for making arbitrary HTTP requests to the server, that probably don't justify builtin methods in kubeclient. Generally, users wish the library covered 100% of their needs, but it's not always reasonable to inflate API surface (especially for k8s derivative-specific stuff like openshift), nor to force users to wait until their use case is added to the library.

Use cases

  • #170 /healthz (k8s), /healthz/ping, /healthz/ready (possibly openshift specific?).
  • #309 /version (k8s), /version/openshift (openshift v3, replaced by CRDs in v4).
  • #245 unclear what exactly desired but compared to kubectl cluster-info which hits many URLs.
  • #388 querying /apis and /apis/SOMEGROUP ? Maybe deserves native support.
  • #428 querying /apis/SOMEGROUP/SOMEVERSION? Maybe deserves native support.

Non-goals, until proven otherwise

  • Arbitrary headers? Only case I remember is patch formats #268 but that's for an existing method and we went with native support in #357.
  • Arbitrary http methods?
    • #353 needs POST but also WebSocket and lots of low-level stuff, not "simple escape hatch" material!

~~Shouldn't expose underlying client~~

I think adding a generic "custom request to this path" is a good escape hatch, IFF we can cover it with a small API.

Technically, you can already do arbitrary requests via kclient.create_rest_client(path).get.body but that's deliberately undocumented, we'd like to switch away from RestClient (#237, more pressing as RestClient is presently unmaintained). Even if we switch, by same logic, kubeclient shouldn't tie its API to specific client.

UPDATE: I've changed my opinion since; after master moved to RestClient -> Faraday, we decided that Faraday actually provides reasonably abstracted API how to make http requests, and it's better to use an "aspiring standard one" than invent our own.

Proposed minimal viable API

kclient.http_get(path, as: :ros)

Performs GET request.
path is relative to api server "root" (cf. #318), not this Client's apis/group/version.
Does kubeclient-typical error handling and parsing (use as: :raw to get unparsed string).

cben avatar Jan 23 '19 10:01 cben

Naming is hard

Shouldn't call it .get because that'd collide with the #332 family of .get, .create etc.

My next idea was .get_path but should also avoid any name starting with .get_... to avoid confusion with .get_pod etc.

=> Provisionally calling it .http_get but open to better ideas...

cben avatar Jan 31 '19 13:01 cben

#428 has a use case for requesting same discovery info kubeclient does, which would be a wasteful request. And generally, many use cases here like /version are safe to cache if user makes the call several time. But some like /healthz must not be cached.

Extended proposal (not atomic, just the first is already valuable):

  • .http_get(path, as: :ros) makes a request every time.
  • .http_get_once(path, as: :ros) makes a request at most once per path, then remembers it for other calls with same path (on this client object, no sharing). Discovery would switch to use this. This would become more useful if/when we get to multi-group discovery (#348 or alternatives)...
  • [ ] do we need a way to clear cache? If yes, this design is misguided :wink:

Could/should .http_get automagically honor response HTTP headers? I think explicit intent is better here. Much kubeclient usage involves calls like .get_pods with the expectation you're getting fresh info so let's stick to that?

cben avatar Dec 29 '19 12:12 cben

@cben did you make any progress here? I want to use the client to consume https://github.com/kubernetes-sigs/metrics-server/ but the api lives in /apis/metrics.k8s.io/v1beta1 therefore I would need this.

How exactly can I help here? I would be happy to get my hands dirty if you give me some insight on what design you were thinking of following.

pedro-stanaka avatar May 04 '21 06:05 pedro-stanaka

Yes we did! Since #466 on master branch Client objects now have .faraday_client method you can use for arbitrary requests. It's not documented yet, and not in any release yet :-( Gonna be in 5.0 release, which I'm afraid will take some more time (#435).

But /apis/metrics.k8s.io/v1beta1 sounds like a regular k8s API group. You shouldn't need this "escape hatch" for regular resources, just a Client object configured for this group. Try this:

client = Kubeclient::Client.new('http://SERVER:6443/apis/metrics.k8s.io', 'v1beta1')

cben avatar May 05 '21 19:05 cben

This worked flawlessly. Thanks a bunch. I am using master with ruby 3.

pedro-stanaka avatar May 08 '21 07:05 pedro-stanaka