mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			351 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->
 | 
						|
 | 
						|
<!-- BEGIN STRIP_FOR_RELEASE -->
 | 
						|
 | 
						|
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
 | 
						|
     width="25" height="25">
 | 
						|
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
 | 
						|
     width="25" height="25">
 | 
						|
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
 | 
						|
     width="25" height="25">
 | 
						|
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
 | 
						|
     width="25" height="25">
 | 
						|
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
 | 
						|
     width="25" height="25">
 | 
						|
 | 
						|
<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2>
 | 
						|
 | 
						|
If you are using a released version of Kubernetes, you should
 | 
						|
refer to the docs that go with that version.
 | 
						|
 | 
						|
<!-- TAG RELEASE_LINK, added by the munger automatically -->
 | 
						|
<strong>
 | 
						|
The latest release of this document can be found
 | 
						|
[here](http://releases.k8s.io/release-1.2/docs/proposals/client-package-structure.md).
 | 
						|
 | 
						|
Documentation for other releases can be found at
 | 
						|
[releases.k8s.io](http://releases.k8s.io).
 | 
						|
</strong>
 | 
						|
--
 | 
						|
 | 
						|
<!-- END STRIP_FOR_RELEASE -->
 | 
						|
 | 
						|
<!-- END MUNGE: UNVERSIONED_WARNING -->
 | 
						|
 | 
						|
<!-- BEGIN MUNGE: GENERATED_TOC -->
 | 
						|
 | 
						|
- [Client: layering and package structure](#client-layering-and-package-structure)
 | 
						|
  - [Desired layers](#desired-layers)
 | 
						|
    - [Transport](#transport)
 | 
						|
    - [RESTClient/request.go](#restclientrequestgo)
 | 
						|
    - [Mux layer](#mux-layer)
 | 
						|
    - [High-level: Individual typed](#high-level-individual-typed)
 | 
						|
      - [High-level, typed: Discovery](#high-level-typed-discovery)
 | 
						|
    - [High-level: Dynamic](#high-level-dynamic)
 | 
						|
    - [High-level: Client Sets](#high-level-client-sets)
 | 
						|
  - [Package Structure](#package-structure)
 | 
						|
  - [Client Guarantees (and testing)](#client-guarantees-and-testing)
 | 
						|
 | 
						|
<!-- END MUNGE: GENERATED_TOC -->
 | 
						|
 | 
						|
# Client: layering and package structure
 | 
						|
 | 
						|
## Desired layers
 | 
						|
 | 
						|
### Transport
 | 
						|
 | 
						|
The transport layer is concerned with round-tripping requests to an apiserver
 | 
						|
somewhere. It consumes a Config object with options appropriate for this.
 | 
						|
(That's most of the current client.Config structure.)
 | 
						|
 | 
						|
Transport delivers an object that implements http's RoundTripper interface
 | 
						|
and/or can be used in place of http.DefaultTransport to route requests.
 | 
						|
 | 
						|
Transport objects are safe for concurrent use, and are cached and reused by
 | 
						|
subsequent layers.
 | 
						|
 | 
						|
Tentative name: "Transport".
 | 
						|
 | 
						|
It's expected that the transport config will be general enough that third
 | 
						|
parties (e.g., OpenShift) will not need their own implementation, rather they
 | 
						|
can change the certs, token, etc., to be appropriate for their own servers,
 | 
						|
etc..
 | 
						|
 | 
						|
Action items:
 | 
						|
* Split out of current client package into a new package. (@krousey)
 | 
						|
 | 
						|
### RESTClient/request.go
 | 
						|
 | 
						|
RESTClient consumes a Transport and a Codec (and optionally a group/version),
 | 
						|
and produces something that implements the interface currently in request.go.
 | 
						|
That is, with a RESTClient, you can write chains of calls like:
 | 
						|
 | 
						|
`c.Get().Path(p).Param("name", "value").Do()`
 | 
						|
 | 
						|
RESTClient is generically usable by any client for servers exposing REST-like
 | 
						|
semantics. It provides helpers that benefit those following api-conventions.md,
 | 
						|
but does not mandate them. It provides a higher level http interface that
 | 
						|
abstracts transport, wire serialization, retry logic, and error handling.
 | 
						|
Kubernetes-like constructs that deviate from standard HTTP should be bypassable.
 | 
						|
Every non-trivial call made to a remote restful API from Kubernetes code should
 | 
						|
go through a rest client.
 | 
						|
 | 
						|
The group and version may be empty when constructing a RESTClient. This is valid
 | 
						|
for executing discovery commands. The group and version may be overridable with
 | 
						|
a chained function call.
 | 
						|
 | 
						|
Ideally, no semantic behavior is built into RESTClient, and RESTClient will use
 | 
						|
the Codec it was constructed with for all semantic operations, including turning
 | 
						|
options objects into URL query parameters. Unfortunately, that is not true of
 | 
						|
today's RESTClient, which may have some semantic information built in. We will
 | 
						|
remove this.
 | 
						|
 | 
						|
RESTClient should not make assumptions about the format of data produced or
 | 
						|
consumed by the Codec. Currently, it is JSON, but we want to support binary
 | 
						|
protocols in the future.
 | 
						|
 | 
						|
The Codec would look something like this:
 | 
						|
 | 
						|
```go
 | 
						|
type Codec interface {
 | 
						|
  Encode(runtime.Object) ([]byte, error)
 | 
						|
  Decode([]byte]) (runtime.Object, error)
 | 
						|
 | 
						|
  // Used to version-control query parameters
 | 
						|
  EncodeParameters(optionsObject runtime.Object) (url.Values, error)
 | 
						|
 | 
						|
  // Not included here since the client doesn't need it, but a corresponding
 | 
						|
  // DecodeParametersInto method would be available on the server.
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
There should be one codec per version. RESTClient is *not* responsible for
 | 
						|
converting between versions; if a client wishes, they can supply a Codec that
 | 
						|
does that. But RESTClient will make the assumption that it's talking to a single
 | 
						|
group/version, and will not contain any conversion logic. (This is a slight
 | 
						|
change from the current state.)
 | 
						|
 | 
						|
As with Transport, it is expected that 3rd party providers following the api
 | 
						|
conventions should be able to use RESTClient, and will not need to implement
 | 
						|
their own.
 | 
						|
 | 
						|
Action items:
 | 
						|
* Split out of the current client package. (@krousey)
 | 
						|
* Possibly, convert to an interface (currently, it's a struct). This will allow
 | 
						|
  extending the error-checking monad that's currently in request.go up an
 | 
						|
  additional layer.
 | 
						|
* Switch from ParamX("x") functions to using types representing the collection
 | 
						|
  of parameters and the Codec for query parameter serialization.
 | 
						|
* Any other Kubernetes group specific behavior should also be removed from
 | 
						|
  RESTClient.
 | 
						|
 | 
						|
### Mux layer
 | 
						|
 | 
						|
(See TODO at end; this can probably be merged with the "client set" concept.)
 | 
						|
 | 
						|
The client muxer layer has a map of group/version to cached RESTClient, and
 | 
						|
knows how to construct a new RESTClient in case of a cache miss (using the
 | 
						|
discovery client mentioned below). The ClientMux may need to deal with multiple
 | 
						|
transports pointing at differing destinations (e.g. OpenShift or other 3rd party
 | 
						|
provider API may be at a different location).
 | 
						|
 | 
						|
When constructing a RESTClient generically, the muxer will just use the Codec
 | 
						|
the high-level dynamic client would use. Alternatively, the user should be able
 | 
						|
to pass in a Codec-- for the case where the correct types are compiled in.
 | 
						|
 | 
						|
Tentative name: ClientMux
 | 
						|
 | 
						|
Action items:
 | 
						|
* Move client cache out of kubectl libraries into a more general home.
 | 
						|
* TODO: a mux layer may not be necessary, depending on what needs to be cached.
 | 
						|
  If transports are cached already, and RESTClients are extremely light-weight,
 | 
						|
  there may not need to be much code at all in this layer.
 | 
						|
 | 
						|
### High-level: Individual typed
 | 
						|
 | 
						|
Our current high-level client allows you to write things like
 | 
						|
`c.Pods("namespace").Create(p)`; we will insert a level for the group.
 | 
						|
 | 
						|
That is, the system will be:
 | 
						|
 | 
						|
`clientset.GroupName().NamespaceSpecifier().Action()`
 | 
						|
 | 
						|
Where:
 | 
						|
* `clientset` is a thing that holds multiple individually typed clients (see
 | 
						|
  below).
 | 
						|
* `GroupName()` returns the generated client that this section is about.
 | 
						|
* `NamespaceSpecifier()` may take a namespace parameter or nothing.
 | 
						|
* `Action` is one of Create/Get/Update/Delete/Watch, or appropriate actions
 | 
						|
  from the type's subresources.
 | 
						|
* It is TBD how we'll represent subresources and their actions. This is
 | 
						|
  inconsistent in the current clients, so we'll need to define a consistent
 | 
						|
  format. Possible choices:
 | 
						|
 * Insert a `.Subresource()` before the `.Action()`
 | 
						|
 * Flatten subresources, such that they become special Actions on the parent
 | 
						|
   resource.
 | 
						|
 | 
						|
The types returned/consumed by such functions will be e.g. api/v1, NOT the
 | 
						|
current version inspecific types. The current internal-versioned client is
 | 
						|
inconvenient for users, as it does not protect them from having to recompile
 | 
						|
their code with every minor update. (We may continue to generate an
 | 
						|
internal-versioned client for our own use for a while, but even for our own
 | 
						|
components it probably makes sense to switch to specifically versioned clients.)
 | 
						|
 | 
						|
We will provide this structure for each version of each group. It is infeasible
 | 
						|
to do this manually, so we will generate this. The generator will accept both
 | 
						|
swagger and the ordinary go types. The generator should operate on out-of-tree
 | 
						|
sources AND out-of-tree destinations, so it will be useful for consuming
 | 
						|
out-of-tree APIs and for others to build custom clients into their own
 | 
						|
repositories.
 | 
						|
 | 
						|
Typed clients will be constructabale given a ClientMux; the typed constructor will use
 | 
						|
the ClientMux to find or construct an appropriate RESTClient. Alternatively, a
 | 
						|
typed client should be constructable individually given a config, from which it
 | 
						|
will be able to construct the appropriate RESTClient.
 | 
						|
 | 
						|
Typed clients do not require any version negotiation. The server either supports
 | 
						|
the client's group/version, or it does not. However, there are ways around this:
 | 
						|
* If you want to use a typed client against a server's API endpoint and the
 | 
						|
  server's API version doesn't match the client's API version, you can construct
 | 
						|
  the client with a RESTClient using a Codec that does the conversion (this is
 | 
						|
  basically what our client does now).
 | 
						|
* Alternatively, you could use the dynamic client.
 | 
						|
 | 
						|
Action items:
 | 
						|
* Move current typed clients into new directory structure (described below)
 | 
						|
* Finish client generation logic. (@caesarxuchao, @lavalamp)
 | 
						|
 | 
						|
#### High-level, typed: Discovery
 | 
						|
 | 
						|
A `DiscoveryClient` is necessary to discover the api groups, versions, and
 | 
						|
resources a server supports. It's constructable given a RESTClient. It is
 | 
						|
consumed by both the ClientMux and users who want to iterate over groups,
 | 
						|
versions, or resources. (Example: namespace controller.)
 | 
						|
 | 
						|
The DiscoveryClient is *not* required if you already know the group/version of
 | 
						|
the resource you want to use: you can simply try the operation without checking
 | 
						|
first, which is lower-latency anyway as it avoids an extra round-trip.
 | 
						|
 | 
						|
Action items:
 | 
						|
* Refactor existing functions to present a sane interface, as close to that
 | 
						|
  offered by the other typed clients as possible. (@caeserxuchao)
 | 
						|
* Use a RESTClient to make the necessary API calls.
 | 
						|
* Make sure that no discovery happens unless it is explicitly requested. (Make
 | 
						|
  sure SetKubeDefaults doesn't call it, for example.)
 | 
						|
 | 
						|
### High-level: Dynamic
 | 
						|
 | 
						|
The dynamic client lets users consume apis which are not compiled into their
 | 
						|
binary. It will provide the same interface as the typed client, but will take
 | 
						|
and return `runtime.Object`s instead of typed objects. There is only one dynamic
 | 
						|
client, so it's not necessary to generate it, although optionally we may do so
 | 
						|
depending on whether the typed client generator makes it easy.
 | 
						|
 | 
						|
A dynamic client is constructable given a config, group, and version. It will
 | 
						|
use this to construct a RESTClient with a Codec which encodes/decodes to
 | 
						|
'Unstructured' `runtime.Object`s. The group and version may be from a previous
 | 
						|
invocation of a DiscoveryClient, or they may be known by other means.
 | 
						|
 | 
						|
For now, the dynamic client will assume that a JSON encoding is allowed. In the
 | 
						|
future, if we have binary-only APIs (unlikely?), we can add that to the
 | 
						|
discovery information and construct an appropriate dynamic Codec.
 | 
						|
 | 
						|
Action items:
 | 
						|
* A rudimentary version of this exists in kubectl's builder. It needs to be
 | 
						|
  moved to a more general place.
 | 
						|
* Produce a useful 'Unstructured' runtime.Object, which allows for easy
 | 
						|
  Object/ListMeta introspection.
 | 
						|
 | 
						|
### High-level: Client Sets
 | 
						|
 | 
						|
Because there will be multiple groups with multiple versions, we will provide an
 | 
						|
aggregation layer that combines multiple typed clients in a single object.
 | 
						|
 | 
						|
We do this to:
 | 
						|
* Deliver a concrete thing for users to consume, construct, and pass around. We
 | 
						|
  don't want people making 10 typed clients and making a random system to keep
 | 
						|
  track of them.
 | 
						|
* Constrain the testing matrix. Users can generate a client set at their whim
 | 
						|
  against their cluster, but we need to make guarantees that the clients we
 | 
						|
  shipped with v1.X.0 will work with v1.X+1.0, and vice versa. That's not
 | 
						|
  practical unless we "bless" a particular version of each API group and ship an
 | 
						|
  official client set with earch release. (If the server supports 15 groups with
 | 
						|
  2 versions each, that's 2^15 different possible client sets. We don't want to
 | 
						|
  test all of them.)
 | 
						|
 | 
						|
A client set is generated into its own package. The generator will take the list
 | 
						|
of group/versions to be included. Only one version from each group will be in
 | 
						|
the client set.
 | 
						|
 | 
						|
A client set is constructable at runtime from either a ClientMux or a transport
 | 
						|
config (for easy one-stop-shopping).
 | 
						|
 | 
						|
An example:
 | 
						|
 | 
						|
```go
 | 
						|
import (
 | 
						|
  api_v1 "k8s.io/kubernetes/pkg/client/typed/generated/v1"
 | 
						|
  ext_v1beta1 "k8s.io/kubernetes/pkg/client/typed/generated/extensions/v1beta1"
 | 
						|
  net_v1beta1 "k8s.io/kubernetes/pkg/client/typed/generated/net/v1beta1"
 | 
						|
  "k8s.io/kubernetes/pkg/client/typed/dynamic"
 | 
						|
)
 | 
						|
 | 
						|
type Client interface {
 | 
						|
  API() api_v1.Client
 | 
						|
  Extensions() ext_v1beta1.Client
 | 
						|
  Net() net_v1beta1.Client
 | 
						|
  // ... other typed clients here.
 | 
						|
 | 
						|
  // Included in every set
 | 
						|
  Discovery() discovery.Client
 | 
						|
  GroupVersion(group, version string) dynamic.Client
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
Note that a particular version is chosen for each group. It is a general rule
 | 
						|
for our API structure that no client need care about more than one version of
 | 
						|
each group at a time.
 | 
						|
 | 
						|
This is the primary deliverable that people would consume. It is also generated.
 | 
						|
 | 
						|
Action items:
 | 
						|
* This needs to be built. It will replace the ClientInterface that everyone
 | 
						|
  passes around right now.
 | 
						|
 | 
						|
## Package Structure
 | 
						|
 | 
						|
```
 | 
						|
pkg/client/
 | 
						|
----------/transport/     # transport & associated config
 | 
						|
----------/restclient/
 | 
						|
----------/clientmux/
 | 
						|
----------/typed/
 | 
						|
----------------/discovery/
 | 
						|
----------------/generated/
 | 
						|
--------------------------/<group>/
 | 
						|
----------------------------------/<version>/
 | 
						|
--------------------------------------------/<resource>.go
 | 
						|
----------------/dynamic/
 | 
						|
----------/clientsets/
 | 
						|
---------------------/release-1.1/
 | 
						|
---------------------/release-1.2/
 | 
						|
---------------------/the-test-set-you-just-generated/
 | 
						|
```
 | 
						|
 | 
						|
`/clientsets/` will retain their contents until they reach their expire date.
 | 
						|
e.g., when we release v1.N, we'll remove clientset v1.(N-3). Clients from old
 | 
						|
releases live on and continue to work (i.e., are tested) without any interface
 | 
						|
changes for multiple releases, to give users time to transition.
 | 
						|
 | 
						|
## Client Guarantees (and testing)
 | 
						|
 | 
						|
Once we release a clientset, we will not make interface changes to it. Users of
 | 
						|
that client will not have to change their code until they are deliberately
 | 
						|
upgrading their import. We probably will want to generate some sort of stub test
 | 
						|
with a clienset, to ensure that we don't change the interface.
 | 
						|
 | 
						|
 | 
						|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
 | 
						|
[]()
 | 
						|
<!-- END MUNGE: GENERATED_ANALYTICS -->
 |