2015-07-27 19:49:06 +00:00
|
|
|
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->
|
|
|
|
|
2016-06-10 23:46:46 +00:00
|
|
|
<!-- 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
|
2016-06-13 19:24:34 +00:00
|
|
|
[here](http://releases.k8s.io/release-1.3/docs/design/extending-api.md).
|
2016-06-10 23:46:46 +00:00
|
|
|
|
|
|
|
Documentation for other releases can be found at
|
|
|
|
[releases.k8s.io](http://releases.k8s.io).
|
|
|
|
</strong>
|
|
|
|
--
|
|
|
|
|
|
|
|
<!-- END STRIP_FOR_RELEASE -->
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
<!-- END MUNGE: UNVERSIONED_WARNING -->
|
|
|
|
|
|
|
|
# Adding custom resources to the Kubernetes API server
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
This document describes the design for implementing the storage of custom API
|
|
|
|
types in the Kubernetes API Server.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
## Resource Model
|
|
|
|
|
|
|
|
### The ThirdPartyResource
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
The `ThirdPartyResource` resource describes the multiple versions of a custom
|
|
|
|
resource that the user wants to add to the Kubernetes API. `ThirdPartyResource`
|
|
|
|
is a non-namespaced resource; attempting to place it in a namespace will return
|
|
|
|
an error.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
Each `ThirdPartyResource` resource has the following:
|
|
|
|
* Standard Kubernetes object metadata.
|
2016-04-14 00:55:22 +00:00
|
|
|
* ResourceKind - The kind of the resources described by this third party
|
|
|
|
resource.
|
2015-07-27 19:49:06 +00:00
|
|
|
* Description - A free text description of the resource.
|
|
|
|
* APIGroup - An API group that this resource should be placed into.
|
|
|
|
* Versions - One or more `Version` objects.
|
|
|
|
|
|
|
|
### The `Version` Object
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
The `Version` object describes a single concrete version of a custom resource.
|
|
|
|
The `Version` object currently only specifies:
|
2015-07-27 19:49:06 +00:00
|
|
|
* The `Name` of the version.
|
|
|
|
* The `APIGroup` this version should belong to.
|
|
|
|
|
|
|
|
## Expectations about third party objects
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
Every object that is added to a third-party Kubernetes object store is expected
|
|
|
|
to contain Kubernetes compatible [object metadata](../devel/api-conventions.md#metadata).
|
|
|
|
This requirement enables the Kubernetes API server to provide the following
|
|
|
|
features:
|
|
|
|
* Filtering lists of objects via label queries.
|
|
|
|
* `resourceVersion`-based optimistic concurrency via compare-and-swap.
|
|
|
|
* Versioned storage.
|
|
|
|
* Event recording.
|
|
|
|
* Integration with basic `kubectl` command line tooling.
|
|
|
|
* Watch for resource changes.
|
|
|
|
|
|
|
|
The `Kind` for an instance of a third-party object (e.g. CronTab) below is
|
|
|
|
expected to be programmatically convertible to the name of the resource using
|
|
|
|
the following conversion. Kinds are expected to be of the form
|
|
|
|
`<CamelCaseKind>`, and the `APIVersion` for the object is expected to be
|
|
|
|
`<api-group>/<api-version>`. To prevent collisions, it's expected that you'll
|
|
|
|
use a fully qualified domain name for the API group, e.g. `example.com`.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
2015-09-24 21:00:27 +00:00
|
|
|
For example `stable.example.com/v1`
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
'CamelCaseKind' is the specific type name.
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
To convert this into the `metadata.name` for the `ThirdPartyResource` resource
|
|
|
|
instance, the `<domain-name>` is copied verbatim, the `CamelCaseKind` is then
|
|
|
|
converted using '-' instead of capitalization ('camel-case'), with the first
|
|
|
|
character being assumed to be capitalized. In pseudo code:
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
```go
|
|
|
|
var result string
|
|
|
|
for ix := range kindName {
|
|
|
|
if isCapital(kindName[ix]) {
|
|
|
|
result = append(result, '-')
|
|
|
|
}
|
|
|
|
result = append(result, toLowerCase(kindName[ix])
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
As a concrete example, the resource named `camel-case-kind.example.com` defines
|
|
|
|
resources of Kind `CamelCaseKind`, in the APIGroup with the prefix
|
|
|
|
`example.com/...`.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
The reason for this is to enable rapid lookup of a `ThirdPartyResource` object
|
|
|
|
given the kind information. This is also the reason why `ThirdPartyResource` is
|
|
|
|
not namespaced.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
When a user creates a new `ThirdPartyResource`, the Kubernetes API Server reacts
|
|
|
|
by creating a new, namespaced RESTful resource path. For now, non-namespaced
|
|
|
|
objects are not supported. As with existing built-in objects, deleting a
|
|
|
|
namespace deletes all third party resources in that namespace.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
For example, if a user creates:
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
metadata:
|
2015-09-24 21:00:27 +00:00
|
|
|
name: cron-tab.stable.example.com
|
2015-10-13 00:47:16 +00:00
|
|
|
apiVersion: extensions/v1beta1
|
2015-07-27 19:49:06 +00:00
|
|
|
kind: ThirdPartyResource
|
|
|
|
description: "A specification of a Pod to run on a cron style schedule"
|
|
|
|
versions:
|
2015-09-24 21:00:27 +00:00
|
|
|
- name: v1
|
|
|
|
- name: v2
|
2015-07-27 19:49:06 +00:00
|
|
|
```
|
|
|
|
|
2015-09-24 21:00:27 +00:00
|
|
|
Then the API server will program in the new RESTful resource path:
|
|
|
|
* `/apis/stable.example.com/v1/namespaces/<namespace>/crontabs/...`
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
Now that this schema has been created, a user can `POST`:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"metadata": {
|
|
|
|
"name": "my-new-cron-object"
|
|
|
|
},
|
2015-09-24 21:00:27 +00:00
|
|
|
"apiVersion": "stable.example.com/v1",
|
2015-07-27 19:49:06 +00:00
|
|
|
"kind": "CronTab",
|
|
|
|
"cronSpec": "* * * * /5",
|
2015-12-02 06:24:58 +00:00
|
|
|
"image": "my-awesome-cron-image"
|
2015-07-27 19:49:06 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2015-09-24 21:00:27 +00:00
|
|
|
to: `/apis/stable.example.com/v1/namespaces/default/crontabs`
|
2015-07-27 19:49:06 +00:00
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
and the corresponding data will be stored into etcd by the APIServer, so that
|
|
|
|
when the user issues:
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
```
|
2015-09-24 21:00:27 +00:00
|
|
|
GET /apis/stable.example.com/v1/namespaces/default/crontabs/my-new-cron-object`
|
2015-07-27 19:49:06 +00:00
|
|
|
```
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
And when they do that, they will get back the same data, but with additional
|
|
|
|
Kubernetes metadata (e.g. `resourceVersion`, `createdTimestamp`) filled in.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
Likewise, to list all resources, a user can issue:
|
|
|
|
|
|
|
|
```
|
2015-09-24 21:00:27 +00:00
|
|
|
GET /apis/stable.example.com/v1/namespaces/default/crontabs
|
2015-07-27 19:49:06 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
and get back:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
2015-09-24 21:00:27 +00:00
|
|
|
"apiVersion": "stable.example.com/v1",
|
2015-07-27 19:49:06 +00:00
|
|
|
"kind": "CronTabList",
|
|
|
|
"items": [
|
|
|
|
{
|
|
|
|
"metadata": {
|
|
|
|
"name": "my-new-cron-object"
|
|
|
|
},
|
2015-09-24 21:00:27 +00:00
|
|
|
"apiVersion": "stable.example.com/v1",
|
2015-07-27 19:49:06 +00:00
|
|
|
"kind": "CronTab",
|
|
|
|
"cronSpec": "* * * * /5",
|
2015-12-02 06:24:58 +00:00
|
|
|
"image": "my-awesome-cron-image"
|
2015-07-27 19:49:06 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
Because all objects are expected to contain standard Kubernetes metadata fields,
|
|
|
|
these list operations can also use label queries to filter requests down to
|
|
|
|
specific subsets.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
Likewise, clients can use watch endpoints to watch for changes to stored
|
|
|
|
objects.
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
## Storage
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
In order to store custom user data in a versioned fashion inside of etcd, we
|
|
|
|
need to also introduce a `Codec`-compatible object for persistent storage in
|
|
|
|
etcd. This object is `ThirdPartyResourceData` and it contains:
|
|
|
|
* Standard API Metadata.
|
2015-07-27 19:49:06 +00:00
|
|
|
* `Data`: The raw JSON data for this custom object.
|
|
|
|
|
|
|
|
### Storage key specification
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
Each custom object stored by the API server needs a custom key in storage, this
|
|
|
|
is described below:
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
#### Definitions
|
|
|
|
|
2016-04-14 00:55:22 +00:00
|
|
|
* `resource-namespace`: the namespace of the particular resource that is
|
|
|
|
being stored
|
2015-07-27 19:49:06 +00:00
|
|
|
* `resource-name`: the name of the particular resource being stored
|
2016-04-14 00:55:22 +00:00
|
|
|
* `third-party-resource-namespace`: the namespace of the `ThirdPartyResource`
|
|
|
|
resource that represents the type for the specific instance being stored
|
|
|
|
* `third-party-resource-name`: the name of the `ThirdPartyResource` resource
|
|
|
|
that represents the type for the specific instance being stored
|
2015-07-27 19:49:06 +00:00
|
|
|
|
|
|
|
#### Key
|
|
|
|
|
|
|
|
Given the definitions above, the key for a specific third-party object is:
|
|
|
|
|
|
|
|
```
|
|
|
|
${standard-k8s-prefix}/third-party-resources/${third-party-resource-namespace}/${third-party-resource-name}/${resource-namespace}/${resource-name}
|
|
|
|
```
|
|
|
|
|
|
|
|
Thus, listing a third-party resource can be achieved by listing the directory:
|
|
|
|
|
|
|
|
```
|
|
|
|
${standard-k8s-prefix}/third-party-resources/${third-party-resource-namespace}/${third-party-resource-name}/${resource-namespace}/
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
|
|
|
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/design/extending-api.md?pixel)]()
|
|
|
|
<!-- END MUNGE: GENERATED_ANALYTICS -->
|