mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Add initial storage types to the Kubernetes API
This commit is contained in:
		
							
								
								
									
										222
									
								
								docs/design/extending-api.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								docs/design/extending-api.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
			
		||||
<!-- 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.
 | 
			
		||||
 | 
			
		||||
<strong>
 | 
			
		||||
The latest 1.0.x release of this document can be found
 | 
			
		||||
[here](http://releases.k8s.io/release-1.0/docs/design/extending-api.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 -->
 | 
			
		||||
 | 
			
		||||
# Adding custom resources to the Kubernetes API server
 | 
			
		||||
 | 
			
		||||
This document describes the design for implementing the storage of custom API types in the Kubernetes API Server.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Resource Model
 | 
			
		||||
 | 
			
		||||
### The ThirdPartyResource
 | 
			
		||||
 | 
			
		||||
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 resource
 | 
			
		||||
will return an error.
 | 
			
		||||
 | 
			
		||||
Each `ThirdPartyResource` resource has the following:
 | 
			
		||||
   * Standard Kubernetes object metadata.
 | 
			
		||||
   * ResourceKind - The kind of the resources described by this third party resource.
 | 
			
		||||
   * 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
 | 
			
		||||
 | 
			
		||||
The `Version` object describes a single concrete version of a custom resource.  The `Version` object currently
 | 
			
		||||
only specifies:
 | 
			
		||||
   * The `Name` of the version.
 | 
			
		||||
   * The `APIGroup` this version should belong to.
 | 
			
		||||
 | 
			
		||||
## Expectations about third party objects
 | 
			
		||||
 | 
			
		||||
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 LabelQueries
 | 
			
		||||
   * `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
 | 
			
		||||
programnatically convertible to the name of the resource using
 | 
			
		||||
the following conversion.  Kinds are expected to be of the form `<CamelCaseKind>`, the
 | 
			
		||||
`APIVersion` for the object is expected to be `<domain-name>/<api-group>/<api-version>`.
 | 
			
		||||
 | 
			
		||||
For example `example.com/stable/v1`
 | 
			
		||||
 | 
			
		||||
`domain-name` is expected to be a fully qualified domain name.
 | 
			
		||||
 | 
			
		||||
'CamelCaseKind' is the specific type name.
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
var result string
 | 
			
		||||
for ix := range kindName {
 | 
			
		||||
  if isCapital(kindName[ix]) {
 | 
			
		||||
    result = append(result, '-')
 | 
			
		||||
  }
 | 
			
		||||
  result = append(result, toLowerCase(kindName[ix])
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
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/...`.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
For example, if a user creates:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
metadata:
 | 
			
		||||
  name: cron-tab.example.com
 | 
			
		||||
apiVersion: experimental/v1
 | 
			
		||||
kind: ThirdPartyResource
 | 
			
		||||
description: "A specification of a Pod to run on a cron style schedule"
 | 
			
		||||
versions:
 | 
			
		||||
  - name: stable/v1
 | 
			
		||||
  - name: experimental/v2
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then the API server will program in two new RESTful resource paths:
 | 
			
		||||
   * `/thirdparty/example.com/stable/v1/namespaces/<namespace>/crontabs/...`
 | 
			
		||||
   * `/thirdparty/example.com/experimental/v2/namespaces/<namespace>/crontabs/...`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Now that this schema has been created, a user can `POST`:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
   "metadata": {
 | 
			
		||||
     "name": "my-new-cron-object"
 | 
			
		||||
   },
 | 
			
		||||
   "apiVersion": "example.com/stable/v1",
 | 
			
		||||
   "kind": "CronTab",
 | 
			
		||||
   "cronSpec": "* * * * /5",
 | 
			
		||||
   "image":     "my-awesome-chron-image"
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
to: `/third-party/example.com/stable/v1/namespaces/default/crontabs/my-new-cron-object`
 | 
			
		||||
 | 
			
		||||
and the corresponding data will be stored into etcd by the APIServer, so that when the user issues:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
GET /third-party/example.com/stable/v1/namespaces/default/crontabs/my-new-cron-object`
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And when they do that, they will get back the same data, but with additional Kubernetes metadata
 | 
			
		||||
(e.g. `resourceVersion`, `createdTimestamp`) filled in.
 | 
			
		||||
 | 
			
		||||
Likewise, to list all resources, a user can issue:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
GET /third-party/example.com/stable/v1/namespaces/default/crontabs
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
and get back:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
   "apiVersion": "example.com/stable/v1",
 | 
			
		||||
   "kind": "CronTabList",
 | 
			
		||||
   "items": [
 | 
			
		||||
     {
 | 
			
		||||
       "metadata": {
 | 
			
		||||
         "name": "my-new-cron-object"
 | 
			
		||||
       },
 | 
			
		||||
       "apiVersion": "example.com/stable/v1",
 | 
			
		||||
       "kind": "CronTab",
 | 
			
		||||
       "cronSpec": "* * * * /5",
 | 
			
		||||
       "image":     "my-awesome-chron-image"
 | 
			
		||||
    }
 | 
			
		||||
   ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Because all objects are expected to contain standard Kubernetes metdata fileds, these
 | 
			
		||||
list operations can also use `Label` queries to filter requests down to specific subsets.
 | 
			
		||||
 | 
			
		||||
Likewise, clients can use watch endpoints to watch for changes to stored objects.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Storage
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
   * `Data`: The raw JSON data for this custom object.
 | 
			
		||||
 | 
			
		||||
### Storage key specification
 | 
			
		||||
 | 
			
		||||
Each custom object stored by the API server needs a custom key in storage, this is described below:
 | 
			
		||||
 | 
			
		||||
#### Definitions
 | 
			
		||||
 | 
			
		||||
   * `resource-namespace` : the namespace of the particular resource that is being stored
 | 
			
		||||
   * `resource-name`: the name of the particular resource being stored
 | 
			
		||||
   * `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.
 | 
			
		||||
 | 
			
		||||
#### 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 -->
 | 
			
		||||
[]()
 | 
			
		||||
<!-- END MUNGE: GENERATED_ANALYTICS -->
 | 
			
		||||
@@ -29,6 +29,12 @@ import (
 | 
			
		||||
	inf "speter.net/go/exp/math/dec/inf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func deepCopy_api_APIVersion(in APIVersion, out *APIVersion, c *conversion.Cloner) error {
 | 
			
		||||
	out.Name = in.Name
 | 
			
		||||
	out.APIGroup = in.APIGroup
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_api_AWSElasticBlockStoreVolumeSource(in AWSElasticBlockStoreVolumeSource, out *AWSElasticBlockStoreVolumeSource, c *conversion.Cloner) error {
 | 
			
		||||
	out.VolumeID = in.VolumeID
 | 
			
		||||
	out.FSType = in.FSType
 | 
			
		||||
@@ -2014,6 +2020,65 @@ func deepCopy_api_TCPSocketAction(in TCPSocketAction, out *TCPSocketAction, c *c
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_api_ThirdPartyResource(in ThirdPartyResource, out *ThirdPartyResource, c *conversion.Cloner) error {
 | 
			
		||||
	if err := deepCopy_api_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	out.Description = in.Description
 | 
			
		||||
	if in.Versions != nil {
 | 
			
		||||
		out.Versions = make([]APIVersion, len(in.Versions))
 | 
			
		||||
		for i := range in.Versions {
 | 
			
		||||
			if err := deepCopy_api_APIVersion(in.Versions[i], &out.Versions[i], c); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Versions = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_api_ThirdPartyResourceData(in ThirdPartyResourceData, out *ThirdPartyResourceData, c *conversion.Cloner) error {
 | 
			
		||||
	if err := deepCopy_api_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if in.Data != nil {
 | 
			
		||||
		out.Data = make([]uint8, len(in.Data))
 | 
			
		||||
		for i := range in.Data {
 | 
			
		||||
			out.Data[i] = in.Data[i]
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Data = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_api_ThirdPartyResourceList(in ThirdPartyResourceList, out *ThirdPartyResourceList, c *conversion.Cloner) error {
 | 
			
		||||
	if err := deepCopy_api_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := deepCopy_api_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if in.Items != nil {
 | 
			
		||||
		out.Items = make([]ThirdPartyResource, len(in.Items))
 | 
			
		||||
		for i := range in.Items {
 | 
			
		||||
			if err := deepCopy_api_ThirdPartyResource(in.Items[i], &out.Items[i], c); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Items = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_api_TypeMeta(in TypeMeta, out *TypeMeta, c *conversion.Cloner) error {
 | 
			
		||||
	out.Kind = in.Kind
 | 
			
		||||
	out.APIVersion = in.APIVersion
 | 
			
		||||
@@ -2161,6 +2226,7 @@ func deepCopy_util_Time(in util.Time, out *util.Time, c *conversion.Cloner) erro
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	err := Scheme.AddGeneratedDeepCopyFuncs(
 | 
			
		||||
		deepCopy_api_APIVersion,
 | 
			
		||||
		deepCopy_api_AWSElasticBlockStoreVolumeSource,
 | 
			
		||||
		deepCopy_api_Binding,
 | 
			
		||||
		deepCopy_api_Capabilities,
 | 
			
		||||
@@ -2276,6 +2342,9 @@ func init() {
 | 
			
		||||
		deepCopy_api_StatusCause,
 | 
			
		||||
		deepCopy_api_StatusDetails,
 | 
			
		||||
		deepCopy_api_TCPSocketAction,
 | 
			
		||||
		deepCopy_api_ThirdPartyResource,
 | 
			
		||||
		deepCopy_api_ThirdPartyResourceData,
 | 
			
		||||
		deepCopy_api_ThirdPartyResourceList,
 | 
			
		||||
		deepCopy_api_TypeMeta,
 | 
			
		||||
		deepCopy_api_Volume,
 | 
			
		||||
		deepCopy_api_VolumeMount,
 | 
			
		||||
 
 | 
			
		||||
@@ -95,7 +95,10 @@ func init() {
 | 
			
		||||
		"PodExecOptions",
 | 
			
		||||
		"PodAttachOptions",
 | 
			
		||||
		"PodProxyOptions",
 | 
			
		||||
		"Daemon")
 | 
			
		||||
		"Daemon",
 | 
			
		||||
		"ThirdPartyResource",
 | 
			
		||||
		"ThirdPartyResourceData",
 | 
			
		||||
		"ThirdPartyResourceList")
 | 
			
		||||
 | 
			
		||||
	mapper := api.NewDefaultRESTMapper("api", versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
 | 
			
		||||
	// setup aliases for groups of resources
 | 
			
		||||
 
 | 
			
		||||
@@ -69,6 +69,9 @@ func init() {
 | 
			
		||||
		&ComponentStatusList{},
 | 
			
		||||
		&SerializedReference{},
 | 
			
		||||
		&RangeAllocation{},
 | 
			
		||||
		&ThirdPartyResource{},
 | 
			
		||||
		&ThirdPartyResourceList{},
 | 
			
		||||
		&ThirdPartyResourceData{},
 | 
			
		||||
	)
 | 
			
		||||
	// Legacy names are supported
 | 
			
		||||
	Scheme.AddKnownTypeWithName("", "Minion", &Node{})
 | 
			
		||||
@@ -119,3 +122,6 @@ func (*ComponentStatus) IsAnAPIObject()           {}
 | 
			
		||||
func (*ComponentStatusList) IsAnAPIObject()       {}
 | 
			
		||||
func (*SerializedReference) IsAnAPIObject()       {}
 | 
			
		||||
func (*RangeAllocation) IsAnAPIObject()           {}
 | 
			
		||||
func (*ThirdPartyResource) IsAnAPIObject()        {}
 | 
			
		||||
func (*ThirdPartyResourceList) IsAnAPIObject()    {}
 | 
			
		||||
func (*ThirdPartyResourceData) IsAnAPIObject()    {}
 | 
			
		||||
 
 | 
			
		||||
@@ -2208,3 +2208,35 @@ type RangeAllocation struct {
 | 
			
		||||
	// a single allocated address (the fifth bit on CIDR 10.0.0.0/8 is 10.0.0.4).
 | 
			
		||||
	Data []byte `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A ThirdPartyResource is a generic representation of a resource, it is used by add-ons and plugins to add new resource
 | 
			
		||||
// types to the API.  It consists of one or more Versions of the api.
 | 
			
		||||
type ThirdPartyResource struct {
 | 
			
		||||
	TypeMeta   `json:",inline"`
 | 
			
		||||
	ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"`
 | 
			
		||||
 | 
			
		||||
	Description string `json:"description,omitempty" description:"The description of this object"`
 | 
			
		||||
 | 
			
		||||
	Versions []APIVersion `json:"versions,omitempty" description:"The versions for this third party object"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ThirdPartyResourceList struct {
 | 
			
		||||
	TypeMeta `json:",inline"`
 | 
			
		||||
	ListMeta `json:"metadata,omitempty" description:"standard list metadata; see http://docs.k8s.io/api-conventions.md#metadata"`
 | 
			
		||||
 | 
			
		||||
	Items []ThirdPartyResource `json:"items" description:"items is a list of schema objects"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An APIVersion represents a single concrete version of an object model.
 | 
			
		||||
type APIVersion struct {
 | 
			
		||||
	Name     string `json:"name,omitempty" description:"name of this version (e.g. 'v1')"`
 | 
			
		||||
	APIGroup string `json:"apiGroup,omitempty" description:"The API group to add this object into, default 'experimental'"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An internal object, used for versioned storage in etcd.  Not exposed to the end user.
 | 
			
		||||
type ThirdPartyResourceData struct {
 | 
			
		||||
	TypeMeta   `json:",inline"`
 | 
			
		||||
	ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"`
 | 
			
		||||
 | 
			
		||||
	Data []byte `json:"name,omitempty" description:"the raw JSON data for this data"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,15 @@ import (
 | 
			
		||||
	conversion "k8s.io/kubernetes/pkg/conversion"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func convert_api_APIVersion_To_v1_APIVersion(in *api.APIVersion, out *APIVersion, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*api.APIVersion))(in)
 | 
			
		||||
	}
 | 
			
		||||
	out.Name = in.Name
 | 
			
		||||
	out.APIGroup = in.APIGroup
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in *api.AWSElasticBlockStoreVolumeSource, out *AWSElasticBlockStoreVolumeSource, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*api.AWSElasticBlockStoreVolumeSource))(in)
 | 
			
		||||
@@ -2243,6 +2252,69 @@ func convert_api_TCPSocketAction_To_v1_TCPSocketAction(in *api.TCPSocketAction,
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_api_ThirdPartyResource_To_v1_ThirdPartyResource(in *api.ThirdPartyResource, out *ThirdPartyResource, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*api.ThirdPartyResource))(in)
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	out.Description = in.Description
 | 
			
		||||
	if in.Versions != nil {
 | 
			
		||||
		out.Versions = make([]APIVersion, len(in.Versions))
 | 
			
		||||
		for i := range in.Versions {
 | 
			
		||||
			if err := convert_api_APIVersion_To_v1_APIVersion(&in.Versions[i], &out.Versions[i], s); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Versions = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_api_ThirdPartyResourceData_To_v1_ThirdPartyResourceData(in *api.ThirdPartyResourceData, out *ThirdPartyResourceData, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*api.ThirdPartyResourceData))(in)
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.Convert(&in.Data, &out.Data, 0); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_api_ThirdPartyResourceList_To_v1_ThirdPartyResourceList(in *api.ThirdPartyResourceList, out *ThirdPartyResourceList, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*api.ThirdPartyResourceList))(in)
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_api_ListMeta_To_v1_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if in.Items != nil {
 | 
			
		||||
		out.Items = make([]ThirdPartyResource, len(in.Items))
 | 
			
		||||
		for i := range in.Items {
 | 
			
		||||
			if err := convert_api_ThirdPartyResource_To_v1_ThirdPartyResource(&in.Items[i], &out.Items[i], s); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Items = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_api_TypeMeta_To_v1_TypeMeta(in *api.TypeMeta, out *TypeMeta, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*api.TypeMeta))(in)
 | 
			
		||||
@@ -2368,6 +2440,15 @@ func convert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *Volu
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_v1_APIVersion_To_api_APIVersion(in *APIVersion, out *api.APIVersion, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*APIVersion))(in)
 | 
			
		||||
	}
 | 
			
		||||
	out.Name = in.Name
 | 
			
		||||
	out.APIGroup = in.APIGroup
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource(in *AWSElasticBlockStoreVolumeSource, out *api.AWSElasticBlockStoreVolumeSource, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*AWSElasticBlockStoreVolumeSource))(in)
 | 
			
		||||
@@ -4586,6 +4667,69 @@ func convert_v1_TCPSocketAction_To_api_TCPSocketAction(in *TCPSocketAction, out
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_v1_ThirdPartyResource_To_api_ThirdPartyResource(in *ThirdPartyResource, out *api.ThirdPartyResource, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*ThirdPartyResource))(in)
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	out.Description = in.Description
 | 
			
		||||
	if in.Versions != nil {
 | 
			
		||||
		out.Versions = make([]api.APIVersion, len(in.Versions))
 | 
			
		||||
		for i := range in.Versions {
 | 
			
		||||
			if err := convert_v1_APIVersion_To_api_APIVersion(&in.Versions[i], &out.Versions[i], s); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Versions = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_v1_ThirdPartyResourceData_To_api_ThirdPartyResourceData(in *ThirdPartyResourceData, out *api.ThirdPartyResourceData, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*ThirdPartyResourceData))(in)
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.Convert(&in.Data, &out.Data, 0); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_v1_ThirdPartyResourceList_To_api_ThirdPartyResourceList(in *ThirdPartyResourceList, out *api.ThirdPartyResourceList, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*ThirdPartyResourceList))(in)
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := convert_v1_ListMeta_To_api_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if in.Items != nil {
 | 
			
		||||
		out.Items = make([]api.ThirdPartyResource, len(in.Items))
 | 
			
		||||
		for i := range in.Items {
 | 
			
		||||
			if err := convert_v1_ThirdPartyResource_To_api_ThirdPartyResource(&in.Items[i], &out.Items[i], s); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Items = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convert_v1_TypeMeta_To_api_TypeMeta(in *TypeMeta, out *api.TypeMeta, s conversion.Scope) error {
 | 
			
		||||
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
 | 
			
		||||
		defaulting.(func(*TypeMeta))(in)
 | 
			
		||||
@@ -4713,6 +4857,7 @@ func convert_v1_VolumeSource_To_api_VolumeSource(in *VolumeSource, out *api.Volu
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	err := api.Scheme.AddGeneratedConversionFuncs(
 | 
			
		||||
		convert_api_APIVersion_To_v1_APIVersion,
 | 
			
		||||
		convert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource,
 | 
			
		||||
		convert_api_Binding_To_v1_Binding,
 | 
			
		||||
		convert_api_Capabilities_To_v1_Capabilities,
 | 
			
		||||
@@ -4826,10 +4971,14 @@ func init() {
 | 
			
		||||
		convert_api_StatusDetails_To_v1_StatusDetails,
 | 
			
		||||
		convert_api_Status_To_v1_Status,
 | 
			
		||||
		convert_api_TCPSocketAction_To_v1_TCPSocketAction,
 | 
			
		||||
		convert_api_ThirdPartyResourceData_To_v1_ThirdPartyResourceData,
 | 
			
		||||
		convert_api_ThirdPartyResourceList_To_v1_ThirdPartyResourceList,
 | 
			
		||||
		convert_api_ThirdPartyResource_To_v1_ThirdPartyResource,
 | 
			
		||||
		convert_api_TypeMeta_To_v1_TypeMeta,
 | 
			
		||||
		convert_api_VolumeMount_To_v1_VolumeMount,
 | 
			
		||||
		convert_api_VolumeSource_To_v1_VolumeSource,
 | 
			
		||||
		convert_api_Volume_To_v1_Volume,
 | 
			
		||||
		convert_v1_APIVersion_To_api_APIVersion,
 | 
			
		||||
		convert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource,
 | 
			
		||||
		convert_v1_Binding_To_api_Binding,
 | 
			
		||||
		convert_v1_Capabilities_To_api_Capabilities,
 | 
			
		||||
@@ -4943,6 +5092,9 @@ func init() {
 | 
			
		||||
		convert_v1_StatusDetails_To_api_StatusDetails,
 | 
			
		||||
		convert_v1_Status_To_api_Status,
 | 
			
		||||
		convert_v1_TCPSocketAction_To_api_TCPSocketAction,
 | 
			
		||||
		convert_v1_ThirdPartyResourceData_To_api_ThirdPartyResourceData,
 | 
			
		||||
		convert_v1_ThirdPartyResourceList_To_api_ThirdPartyResourceList,
 | 
			
		||||
		convert_v1_ThirdPartyResource_To_api_ThirdPartyResource,
 | 
			
		||||
		convert_v1_TypeMeta_To_api_TypeMeta,
 | 
			
		||||
		convert_v1_VolumeMount_To_api_VolumeMount,
 | 
			
		||||
		convert_v1_VolumeSource_To_api_VolumeSource,
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,12 @@ func deepCopy_resource_Quantity(in resource.Quantity, out *resource.Quantity, c
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_v1_APIVersion(in APIVersion, out *APIVersion, c *conversion.Cloner) error {
 | 
			
		||||
	out.Name = in.Name
 | 
			
		||||
	out.APIGroup = in.APIGroup
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_v1_AWSElasticBlockStoreVolumeSource(in AWSElasticBlockStoreVolumeSource, out *AWSElasticBlockStoreVolumeSource, c *conversion.Cloner) error {
 | 
			
		||||
	out.VolumeID = in.VolumeID
 | 
			
		||||
	out.FSType = in.FSType
 | 
			
		||||
@@ -2019,6 +2025,65 @@ func deepCopy_v1_TCPSocketAction(in TCPSocketAction, out *TCPSocketAction, c *co
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_v1_ThirdPartyResource(in ThirdPartyResource, out *ThirdPartyResource, c *conversion.Cloner) error {
 | 
			
		||||
	if err := deepCopy_v1_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	out.Description = in.Description
 | 
			
		||||
	if in.Versions != nil {
 | 
			
		||||
		out.Versions = make([]APIVersion, len(in.Versions))
 | 
			
		||||
		for i := range in.Versions {
 | 
			
		||||
			if err := deepCopy_v1_APIVersion(in.Versions[i], &out.Versions[i], c); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Versions = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_v1_ThirdPartyResourceData(in ThirdPartyResourceData, out *ThirdPartyResourceData, c *conversion.Cloner) error {
 | 
			
		||||
	if err := deepCopy_v1_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if in.Data != nil {
 | 
			
		||||
		out.Data = make([]uint8, len(in.Data))
 | 
			
		||||
		for i := range in.Data {
 | 
			
		||||
			out.Data[i] = in.Data[i]
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Data = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_v1_ThirdPartyResourceList(in ThirdPartyResourceList, out *ThirdPartyResourceList, c *conversion.Cloner) error {
 | 
			
		||||
	if err := deepCopy_v1_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := deepCopy_v1_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if in.Items != nil {
 | 
			
		||||
		out.Items = make([]ThirdPartyResource, len(in.Items))
 | 
			
		||||
		for i := range in.Items {
 | 
			
		||||
			if err := deepCopy_v1_ThirdPartyResource(in.Items[i], &out.Items[i], c); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Items = nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deepCopy_v1_TypeMeta(in TypeMeta, out *TypeMeta, c *conversion.Cloner) error {
 | 
			
		||||
	out.Kind = in.Kind
 | 
			
		||||
	out.APIVersion = in.APIVersion
 | 
			
		||||
@@ -2163,6 +2228,7 @@ func deepCopy_util_Time(in util.Time, out *util.Time, c *conversion.Cloner) erro
 | 
			
		||||
func init() {
 | 
			
		||||
	err := api.Scheme.AddGeneratedDeepCopyFuncs(
 | 
			
		||||
		deepCopy_resource_Quantity,
 | 
			
		||||
		deepCopy_v1_APIVersion,
 | 
			
		||||
		deepCopy_v1_AWSElasticBlockStoreVolumeSource,
 | 
			
		||||
		deepCopy_v1_Binding,
 | 
			
		||||
		deepCopy_v1_Capabilities,
 | 
			
		||||
@@ -2278,6 +2344,9 @@ func init() {
 | 
			
		||||
		deepCopy_v1_StatusCause,
 | 
			
		||||
		deepCopy_v1_StatusDetails,
 | 
			
		||||
		deepCopy_v1_TCPSocketAction,
 | 
			
		||||
		deepCopy_v1_ThirdPartyResource,
 | 
			
		||||
		deepCopy_v1_ThirdPartyResourceData,
 | 
			
		||||
		deepCopy_v1_ThirdPartyResourceList,
 | 
			
		||||
		deepCopy_v1_TypeMeta,
 | 
			
		||||
		deepCopy_v1_Volume,
 | 
			
		||||
		deepCopy_v1_VolumeMount,
 | 
			
		||||
 
 | 
			
		||||
@@ -84,6 +84,9 @@ func addKnownTypes() {
 | 
			
		||||
		&ComponentStatusList{},
 | 
			
		||||
		&SerializedReference{},
 | 
			
		||||
		&RangeAllocation{},
 | 
			
		||||
		&ThirdPartyResource{},
 | 
			
		||||
		&ThirdPartyResourceList{},
 | 
			
		||||
		&ThirdPartyResourceData{},
 | 
			
		||||
	)
 | 
			
		||||
	// Legacy names are supported
 | 
			
		||||
	api.Scheme.AddKnownTypeWithName("v1", "Minion", &Node{})
 | 
			
		||||
@@ -134,3 +137,6 @@ func (*ComponentStatus) IsAnAPIObject()           {}
 | 
			
		||||
func (*ComponentStatusList) IsAnAPIObject()       {}
 | 
			
		||||
func (*SerializedReference) IsAnAPIObject()       {}
 | 
			
		||||
func (*RangeAllocation) IsAnAPIObject()           {}
 | 
			
		||||
func (*ThirdPartyResource) IsAnAPIObject()        {}
 | 
			
		||||
func (*ThirdPartyResourceList) IsAnAPIObject()    {}
 | 
			
		||||
func (*ThirdPartyResourceData) IsAnAPIObject()    {}
 | 
			
		||||
 
 | 
			
		||||
@@ -2060,3 +2060,35 @@ type RangeAllocation struct {
 | 
			
		||||
	Range string `json:"range" description:"a range string that identifies the range represented by 'data'; required"`
 | 
			
		||||
	Data  []byte `json:"data" description:"a bit array containing all allocated addresses in the previous segment"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A ThirdPartyResource is a generic representation of a resource, it is used by add-ons and plugins to add new resource
 | 
			
		||||
// types to the API.  It consists of one or more Versions of the api.
 | 
			
		||||
type ThirdPartyResource struct {
 | 
			
		||||
	TypeMeta   `json:",inline"`
 | 
			
		||||
	ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"`
 | 
			
		||||
 | 
			
		||||
	Description string `json:"description,omitempty" description:"The description of this object"`
 | 
			
		||||
 | 
			
		||||
	Versions []APIVersion `json:"versions,omitempty" description:"The versions for this third party object"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ThirdPartyResourceList struct {
 | 
			
		||||
	TypeMeta `json:",inline"`
 | 
			
		||||
	ListMeta `json:"metadata,omitempty" description:"standard list metadata; see http://docs.k8s.io/api-conventions.md#metadata"`
 | 
			
		||||
 | 
			
		||||
	Items []ThirdPartyResource `json:"items" description:"items is a list of schema objects"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An APIVersion represents a single concrete version of an object model.
 | 
			
		||||
type APIVersion struct {
 | 
			
		||||
	Name     string `json:"name,omitempty" description:"name of this version (e.g. 'v1')"`
 | 
			
		||||
	APIGroup string `json:"apiGroup,omitempty" description:"The API group to add this object into, default 'experimental'"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An internal object, used for versioned storage in etcd.  Not exposed to the end user.
 | 
			
		||||
type ThirdPartyResourceData struct {
 | 
			
		||||
	TypeMeta   `json:",inline"`
 | 
			
		||||
	ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"`
 | 
			
		||||
 | 
			
		||||
	Data []byte `json:"name,omitempty" description:"the raw JSON data for this data"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1802,3 +1802,26 @@ func ValidateSecurityContext(sc *api.SecurityContext) errs.ValidationErrorList {
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ValidateThirdPartyResource(obj *api.ThirdPartyResource) errs.ValidationErrorList {
 | 
			
		||||
	allErrs := errs.ValidationErrorList{}
 | 
			
		||||
	if len(obj.Name) == 0 {
 | 
			
		||||
		allErrs = append(allErrs, errs.NewFieldInvalid("name", obj.Name, "name must be non-empty"))
 | 
			
		||||
	}
 | 
			
		||||
	versions := util.StringSet{}
 | 
			
		||||
	for ix := range obj.Versions {
 | 
			
		||||
		version := &obj.Versions[ix]
 | 
			
		||||
		if len(version.Name) == 0 {
 | 
			
		||||
			allErrs = append(allErrs, errs.NewFieldInvalid("name", version, "name can not be empty"))
 | 
			
		||||
		}
 | 
			
		||||
		if versions.Has(version.Name) {
 | 
			
		||||
			allErrs = append(allErrs, errs.NewFieldDuplicate("version", version))
 | 
			
		||||
		}
 | 
			
		||||
		versions.Insert(version.Name)
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ValidateSchemaUpdate(oldResource, newResource *api.ThirdPartyResource) errs.ValidationErrorList {
 | 
			
		||||
	return errs.ValidationErrorList{fmt.Errorf("Schema update is not supported.")}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user