kubernetes-client/haskell

Support Watch

mbohlool opened this issue · 13 comments

Watch is a non-standard method that swagger-codegen does not support and we need to add support for it like other clients (e.g. python here)

I have the bones of a working watch client here https://github.com/wayofthepie/kubernetes-watch/blob/master/src/Kubernetes/WatchClient.hs. I was going to create it directly within this repo, but it currently relies on the streaming-bytestring library and I can't see a way of adding non-generated dependencies to the cabal file.

What were your thoughts on a watch client, is it preferable to have it contained within this repo? Or as a separate package? I will be updating the watch client library above soon anyway, adding some docs and putting it to use.

you can exclude the cabal file from generation and edit it. It is reasonable to manually edit dependency list as we did the same in other clients. The watch is better to be in this repo for now and when we get to a stable repo, it should be moved to a -base repo.

Can this ticket be closed now that the watch client was merged in #14 ?

Yeah this can be closed.

/close

@guoshimin: You can't close an active issue/PR unless you authored it or you are a collaborator.

In response to this:

/close

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Guess somebody else will have to close this then.

/close

@guoshimin: Closing this issue.

In response to this:

/close

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Is there a way to do watch in a type-safe way? such that the response isn't just a Stream of bytestrings?

There definitely is a way to make response not just be a stream of bytestrings. I wrote this program to demonstrate: https://gist.github.com/akshaymankar/8294788b6d9a9dd01a59573147f9afa1

However, there is a type which the dispatchTypedWatch function cannot type-check as of now. If you look at the signature, it expects a consumer function of type (Stream (Of (Either String (WatchEvent a))) IO () -> IO ()) where a is constrained as instance of FromJSON. Ideally the KubernetesRequest object should've been able to communicate the type which the watch should return, but it doesn't. This is either a limitation of the OpenAPI spec or the one generated by K/K. This is also a reason that (Stream (Of (Either String (WatchEvent a))) IO () -> IO ()) has Either String and doesn't look like (Stream (Of (WatchEvent a)) IO () -> IO ()).

But all is not lost, there is a (slightly twisted) way we could identify this, the openapi schema has x-kubernetes-group-version-kind field which specifies the type of object a given API deals with. The openapi-generator could parse it and add some information to the KubernetesRequest object. The generator as of now is very generic, there are a few benefits of making it specific for this project, but it sounds like a lot of work.

Also, until we can get to fix the openapi-generator, maybe we can add the dispatchTypedWatch function to the client. Maybe with a better name as it is not as typed as one would hope.

I was wondering if we could make some of the types in the generator polymorphic to facilitate this

So have data EndpointsList f = EndpointList (f Endpoints))

Now we can do:

EndpointsList (Compose Stream (Compose Of WatchEvent)) for streaming requests
And EndpointsList [] for normal requests

If we write a custom generator, we can. But in plain openapi-spec, there is no indication about the return type of watch.

Consider spec for listing namespaced pods:

    get:
      description: list or watch objects of kind Pod
      operationId: listNamespacedPod
      parameters:
      - description: object name and auth scope, such as for teams and projects
        in: path
        name: namespace
        required: true
        schema:
          type: string
      - description: If 'true', then the output is pretty printed.
        in: query
        name: pretty
        schema:
          type: string
      - description: ".."
        in: query
        name: continue
        schema:
          type: string
      - description: A selector to restrict the list of returned objects by their
          fields. Defaults to everything.
        in: query
        name: fieldSelector
        schema:
          type: string
      - description: A selector to restrict the list of returned objects by their
          labels. Defaults to everything.
        in: query
        name: labelSelector
        schema:
          type: string
      - description: ".."
        in: query
        name: limit
        schema:
          type: integer
      - description: ".."
        in: query
        name: resourceVersion
        schema:
          type: string
      - description: Timeout for the list/watch call. This limits the duration of
          the call, regardless of any activity or inactivity.
        in: query
        name: timeoutSeconds
        schema:
          type: integer
      - description: Watch for changes to the described resources and return them
          as a stream of add, update, and remove notifications. Specify resourceVersion.
        in: query
        name: watch
        schema:
          type: boolean
      responses:
        200:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/v1.PodList'
            application/yaml:
              schema:
                $ref: '#/components/schemas/v1.PodList'
            application/vnd.kubernetes.protobuf:
              schema:
                $ref: '#/components/schemas/v1.PodList'
            application/json;stream=watch:
              schema:
                $ref: '#/components/schemas/v1.PodList'
            application/vnd.kubernetes.protobuf;stream=watch:
              schema:
                $ref: '#/components/schemas/v1.PodList'
          description: OK
        401:
          content: {}
          description: Unauthorized
      tags:
      - core_v1
      x-kubernetes-action: list
      x-kubernetes-group-version-kind:
        group: ""
        kind: Pod
        version: v1

In case of 200, content can be one of these:

          content:
            application/json:
              schema:
                $ref: '#/components/schemas/v1.PodList'
            application/yaml:
              schema:
                $ref: '#/components/schemas/v1.PodList'
            application/vnd.kubernetes.protobuf:
              schema:
                $ref: '#/components/schemas/v1.PodList'
            application/json;stream=watch:
              schema:
                $ref: '#/components/schemas/v1.PodList'
            application/vnd.kubernetes.protobuf;stream=watch:
              schema:
                $ref: '#/components/schemas/v1.PodList'

Even when the content-type is application/json;stream=watch, it says the schema has to be #/components/schemas/v1.PodList, which looks wrong to me. I am not sure how the generator would deal with it if there were different $refs in the spec. Maybe then we could have it properly type checked.

I couldn't find any issues about this in K/K, it might be worth creating an issue there. But I am not very confident about my analysis.

Also, maybe it is worth creating a new issue for this in this repository.