Merge pull request #12763 from brendandburns/schema-api

Add a storage implementation for ThirdPartyResources.
pull/6/head
Zach Loafman 2015-08-21 18:54:16 -07:00
commit 5e1d793a0c
22 changed files with 824 additions and 221 deletions

View File

@ -278,6 +278,7 @@ _kubectl_get()
must_have_one_noun+=("secret")
must_have_one_noun+=("service")
must_have_one_noun+=("serviceaccount")
must_have_one_noun+=("thirdpartyresource")
}
_kubectl_describe()
@ -449,6 +450,7 @@ _kubectl_delete()
must_have_one_noun+=("secret")
must_have_one_noun+=("service")
must_have_one_noun+=("serviceaccount")
must_have_one_noun+=("thirdpartyresource")
}
_kubectl_namespace()

View File

@ -30,12 +30,6 @@ 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
@ -2027,27 +2021,6 @@ 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
@ -2066,26 +2039,6 @@ func deepCopy_api_ThirdPartyResourceData(in ThirdPartyResourceData, out *ThirdPa
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
@ -2233,7 +2186,6 @@ 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,
@ -2349,9 +2301,7 @@ 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,

View File

@ -124,7 +124,7 @@ func TestList(t *testing.T) {
roundTripSame(t, item)
}
var nonRoundTrippableTypes = util.NewStringSet()
var nonRoundTrippableTypes = util.NewStringSet("ThirdPartyResource")
var nonInternalRoundTrippableTypes = util.NewStringSet("List", "ListOptions", "PodExecOptions", "PodAttachOptions")
var nonRoundTrippableTypesByVersion = map[string][]string{}

View File

@ -26,15 +26,6 @@ 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)
@ -2259,30 +2250,6 @@ 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)
@ -2299,29 +2266,6 @@ func convert_api_ThirdPartyResourceData_To_v1_ThirdPartyResourceData(in *api.Thi
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)
@ -2447,15 +2391,6 @@ 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)
@ -4680,30 +4615,6 @@ 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)
@ -4720,29 +4631,6 @@ func convert_v1_ThirdPartyResourceData_To_api_ThirdPartyResourceData(in *ThirdPa
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)
@ -4870,7 +4758,6 @@ 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,
@ -4985,13 +4872,10 @@ func init() {
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,
@ -5106,8 +4990,6 @@ func init() {
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,

View File

@ -45,12 +45,6 @@ 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
@ -2032,27 +2026,6 @@ 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
@ -2071,26 +2044,6 @@ func deepCopy_v1_ThirdPartyResourceData(in ThirdPartyResourceData, out *ThirdPar
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
@ -2235,7 +2188,6 @@ 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,
@ -2351,9 +2303,7 @@ 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,

View File

@ -1823,6 +1823,10 @@ func ValidateSecurityContext(sc *api.SecurityContext) errs.ValidationErrorList {
return allErrs
}
func ValidateThirdPartyResourceUpdate(old, update *api.ThirdPartyResource) errs.ValidationErrorList {
return ValidateThirdPartyResource(update)
}
func ValidateThirdPartyResource(obj *api.ThirdPartyResource) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}
if len(obj.Name) == 0 {

View File

@ -100,6 +100,12 @@ func deepCopy_resource_Quantity(in resource.Quantity, out *resource.Quantity, c
return nil
}
func deepCopy_expapi_APIVersion(in APIVersion, out *APIVersion, c *conversion.Cloner) error {
out.Name = in.Name
out.APIGroup = in.APIGroup
return nil
}
func deepCopy_expapi_HorizontalPodAutoscaler(in HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, c *conversion.Cloner) error {
if err := deepCopy_api_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
@ -228,6 +234,47 @@ func deepCopy_expapi_SubresourceReference(in SubresourceReference, out *Subresou
return nil
}
func deepCopy_expapi_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_expapi_APIVersion(in.Versions[i], &out.Versions[i], c); err != nil {
return err
}
}
} else {
out.Versions = nil
}
return nil
}
func deepCopy_expapi_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_expapi_ThirdPartyResource(in.Items[i], &out.Items[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func deepCopy_util_Time(in util.Time, out *util.Time, c *conversion.Cloner) error {
if newVal, err := c.DeepCopy(in.Time); err != nil {
return err
@ -243,6 +290,7 @@ func init() {
deepCopy_api_ObjectMeta,
deepCopy_api_TypeMeta,
deepCopy_resource_Quantity,
deepCopy_expapi_APIVersion,
deepCopy_expapi_HorizontalPodAutoscaler,
deepCopy_expapi_HorizontalPodAutoscalerList,
deepCopy_expapi_HorizontalPodAutoscalerSpec,
@ -253,6 +301,8 @@ func init() {
deepCopy_expapi_ScaleSpec,
deepCopy_expapi_ScaleStatus,
deepCopy_expapi_SubresourceReference,
deepCopy_expapi_ThirdPartyResource,
deepCopy_expapi_ThirdPartyResourceList,
deepCopy_util_Time,
)
if err != nil {

View File

@ -32,6 +32,8 @@ func addKnownTypes() {
&HorizontalPodAutoscalerList{},
&ReplicationControllerDummy{},
&Scale{},
&ThirdPartyResource{},
&ThirdPartyResourceList{},
)
}
@ -39,3 +41,5 @@ func (*HorizontalPodAutoscaler) IsAnAPIObject() {}
func (*HorizontalPodAutoscalerList) IsAnAPIObject() {}
func (*ReplicationControllerDummy) IsAnAPIObject() {}
func (*Scale) IsAnAPIObject() {}
func (*ThirdPartyResource) IsAnAPIObject() {}
func (*ThirdPartyResourceList) IsAnAPIObject() {}

View File

@ -134,3 +134,35 @@ type HorizontalPodAutoscalerList struct {
Items []HorizontalPodAutoscaler `json:"items"`
}
// 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 {
api.TypeMeta `json:",inline"`
api.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 {
api.TypeMeta `json:",inline"`
api.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 {
api.TypeMeta `json:",inline"`
api.ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"`
Data []byte `json:"name,omitempty" description:"the raw JSON data for this data"`
}

View File

@ -155,6 +155,15 @@ func convert_v1_TypeMeta_To_api_TypeMeta(in *v1.TypeMeta, out *api.TypeMeta, s c
return nil
}
func convert_expapi_APIVersion_To_v1_APIVersion(in *expapi.APIVersion, out *APIVersion, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.APIVersion))(in)
}
out.Name = in.Name
out.APIGroup = in.APIGroup
return nil
}
func convert_expapi_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(in *expapi.HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.HorizontalPodAutoscaler))(in)
@ -312,6 +321,62 @@ func convert_expapi_SubresourceReference_To_v1_SubresourceReference(in *expapi.S
return nil
}
func convert_expapi_ThirdPartyResource_To_v1_ThirdPartyResource(in *expapi.ThirdPartyResource, out *ThirdPartyResource, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.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_expapi_APIVersion_To_v1_APIVersion(&in.Versions[i], &out.Versions[i], s); err != nil {
return err
}
}
} else {
out.Versions = nil
}
return nil
}
func convert_expapi_ThirdPartyResourceList_To_v1_ThirdPartyResourceList(in *expapi.ThirdPartyResourceList, out *ThirdPartyResourceList, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.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_expapi_ThirdPartyResource_To_v1_ThirdPartyResource(&in.Items[i], &out.Items[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func convert_v1_APIVersion_To_expapi_APIVersion(in *APIVersion, out *expapi.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_HorizontalPodAutoscaler_To_expapi_HorizontalPodAutoscaler(in *HorizontalPodAutoscaler, out *expapi.HorizontalPodAutoscaler, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*HorizontalPodAutoscaler))(in)
@ -469,11 +534,59 @@ func convert_v1_SubresourceReference_To_expapi_SubresourceReference(in *Subresou
return nil
}
func convert_v1_ThirdPartyResource_To_expapi_ThirdPartyResource(in *ThirdPartyResource, out *expapi.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([]expapi.APIVersion, len(in.Versions))
for i := range in.Versions {
if err := convert_v1_APIVersion_To_expapi_APIVersion(&in.Versions[i], &out.Versions[i], s); err != nil {
return err
}
}
} else {
out.Versions = nil
}
return nil
}
func convert_v1_ThirdPartyResourceList_To_expapi_ThirdPartyResourceList(in *ThirdPartyResourceList, out *expapi.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([]expapi.ThirdPartyResource, len(in.Items))
for i := range in.Items {
if err := convert_v1_ThirdPartyResource_To_expapi_ThirdPartyResource(&in.Items[i], &out.Items[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func init() {
err := api.Scheme.AddGeneratedConversionFuncs(
convert_api_ListMeta_To_v1_ListMeta,
convert_api_ObjectMeta_To_v1_ObjectMeta,
convert_api_TypeMeta_To_v1_TypeMeta,
convert_expapi_APIVersion_To_v1_APIVersion,
convert_expapi_HorizontalPodAutoscalerList_To_v1_HorizontalPodAutoscalerList,
convert_expapi_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAutoscalerSpec,
convert_expapi_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerStatus,
@ -484,6 +597,9 @@ func init() {
convert_expapi_ScaleStatus_To_v1_ScaleStatus,
convert_expapi_Scale_To_v1_Scale,
convert_expapi_SubresourceReference_To_v1_SubresourceReference,
convert_expapi_ThirdPartyResourceList_To_v1_ThirdPartyResourceList,
convert_expapi_ThirdPartyResource_To_v1_ThirdPartyResource,
convert_v1_APIVersion_To_expapi_APIVersion,
convert_v1_HorizontalPodAutoscalerList_To_expapi_HorizontalPodAutoscalerList,
convert_v1_HorizontalPodAutoscalerSpec_To_expapi_HorizontalPodAutoscalerSpec,
convert_v1_HorizontalPodAutoscalerStatus_To_expapi_HorizontalPodAutoscalerStatus,
@ -496,6 +612,8 @@ func init() {
convert_v1_ScaleStatus_To_expapi_ScaleStatus,
convert_v1_Scale_To_expapi_Scale,
convert_v1_SubresourceReference_To_expapi_SubresourceReference,
convert_v1_ThirdPartyResourceList_To_expapi_ThirdPartyResourceList,
convert_v1_ThirdPartyResource_To_expapi_ThirdPartyResource,
convert_v1_TypeMeta_To_api_TypeMeta,
)
if err != nil {

View File

@ -101,6 +101,12 @@ func deepCopy_v1_TypeMeta(in v1.TypeMeta, out *v1.TypeMeta, c *conversion.Cloner
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_HorizontalPodAutoscaler(in HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, c *conversion.Cloner) error {
if err := deepCopy_v1_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
@ -229,6 +235,47 @@ func deepCopy_v1_SubresourceReference(in SubresourceReference, out *SubresourceR
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_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_util_Time(in util.Time, out *util.Time, c *conversion.Cloner) error {
if newVal, err := c.DeepCopy(in.Time); err != nil {
return err
@ -244,6 +291,7 @@ func init() {
deepCopy_v1_ListMeta,
deepCopy_v1_ObjectMeta,
deepCopy_v1_TypeMeta,
deepCopy_v1_APIVersion,
deepCopy_v1_HorizontalPodAutoscaler,
deepCopy_v1_HorizontalPodAutoscalerList,
deepCopy_v1_HorizontalPodAutoscalerSpec,
@ -254,6 +302,8 @@ func init() {
deepCopy_v1_ScaleSpec,
deepCopy_v1_ScaleStatus,
deepCopy_v1_SubresourceReference,
deepCopy_v1_ThirdPartyResource,
deepCopy_v1_ThirdPartyResourceList,
deepCopy_util_Time,
)
if err != nil {

View File

@ -16,5 +16,13 @@ limitations under the License.
package v1
import "k8s.io/kubernetes/pkg/api"
func addDefaultingFuncs() {
api.Scheme.AddDefaultingFuncs(
func(obj *APIVersion) {
if len(obj.APIGroup) == 0 {
obj.APIGroup = "experimental"
}
})
}

View File

@ -36,6 +36,8 @@ func addKnownTypes() {
&HorizontalPodAutoscalerList{},
&ReplicationControllerDummy{},
&Scale{},
&ThirdPartyResource{},
&ThirdPartyResourceList{},
)
}
@ -43,3 +45,5 @@ func (*HorizontalPodAutoscaler) IsAnAPIObject() {}
func (*HorizontalPodAutoscalerList) IsAnAPIObject() {}
func (*ReplicationControllerDummy) IsAnAPIObject() {}
func (*Scale) IsAnAPIObject() {}
func (*ThirdPartyResource) IsAnAPIObject() {}
func (*ThirdPartyResourceList) IsAnAPIObject() {}

View File

@ -120,3 +120,35 @@ type HorizontalPodAutoscalerList struct {
Items []HorizontalPodAutoscaler `json:"items" description:"list of horizontal pod autoscalers"`
}
// 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 {
v1.TypeMeta `json:",inline"`
v1.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 {
v1.TypeMeta `json:",inline"`
v1.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 {
v1.TypeMeta `json:",inline"`
v1.ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"`
Data []byte `json:"name,omitempty" description:"the raw JSON data for this data"`
}

View File

@ -20,6 +20,7 @@ import (
"k8s.io/kubernetes/pkg/api"
apivalidation "k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/util"
errs "k8s.io/kubernetes/pkg/util/fielderrors"
)
@ -65,3 +66,26 @@ func ValidateHorizontalPodAutoscalerUpdate(newAutoscler, oldAutoscaler *expapi.H
allErrs = append(allErrs, validateHorizontalPodAutoscalerSpec(newAutoscler.Spec)...)
return allErrs
}
func ValidateThirdPartyResourceUpdate(old, update *expapi.ThirdPartyResource) errs.ValidationErrorList {
return ValidateThirdPartyResource(update)
}
func ValidateThirdPartyResource(obj *expapi.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
}

View File

@ -33,6 +33,7 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/jsonpath"
@ -283,6 +284,7 @@ var serviceAccountColumns = []string{"NAME", "SECRETS", "AGE"}
var persistentVolumeColumns = []string{"NAME", "LABELS", "CAPACITY", "ACCESSMODES", "STATUS", "CLAIM", "REASON", "AGE"}
var persistentVolumeClaimColumns = []string{"NAME", "LABELS", "STATUS", "VOLUME", "CAPACITY", "ACCESSMODES", "AGE"}
var componentStatusColumns = []string{"NAME", "STATUS", "MESSAGE", "ERROR"}
var thirdPartyResourceColumns = []string{"NAME", "DESCRIPTION", "VERSION(S)"}
var withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print cluster name too.
// addDefaultHandlers adds print handlers for default Kubernetes types.
@ -317,6 +319,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(persistentVolumeColumns, printPersistentVolumeList)
h.Handler(componentStatusColumns, printComponentStatus)
h.Handler(componentStatusColumns, printComponentStatusList)
h.Handler(thirdPartyResourceColumns, printThirdPartyResource)
h.Handler(thirdPartyResourceColumns, printThirdPartyResourceList)
}
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
@ -1014,6 +1018,29 @@ func printComponentStatusList(list *api.ComponentStatusList, w io.Writer, withNa
return nil
}
func printThirdPartyResource(rsrc *expapi.ThirdPartyResource, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error {
versions := make([]string, len(rsrc.Versions))
for ix := range rsrc.Versions {
version := &rsrc.Versions[ix]
versions[ix] = fmt.Sprint("%s/%s", version.APIGroup, version.Name)
}
versionsString := strings.Join(versions, ",")
if _, err := fmt.Fprintf(w, "%s\t%s\t%s", rsrc.Name, rsrc.Description, versionsString); err != nil {
return err
}
return nil
}
func printThirdPartyResourceList(list *expapi.ThirdPartyResourceList, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error {
for _, item := range list.Items {
if err := printThirdPartyResource(&item, w, withNamespace, wide, showAll, columnLabels); err != nil {
return err
}
}
return nil
}
func appendLabels(itemLabels map[string]string, columnLabels []string) string {
var buffer bytes.Buffer

View File

@ -71,6 +71,7 @@ import (
serviceetcd "k8s.io/kubernetes/pkg/registry/service/etcd"
ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator"
serviceaccountetcd "k8s.io/kubernetes/pkg/registry/serviceaccount/etcd"
thirdpartyresourceetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresource/etcd"
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/tools"
@ -779,13 +780,15 @@ func (m *Master) api_v1() *apiserver.APIGroupVersion {
// expapi returns the resources and codec for the experimental api
func (m *Master) expapi(c *Config) *apiserver.APIGroupVersion {
controllerStorage := expcontrolleretcd.NewStorage(c.DatabaseStorage)
autoscalerStorage := horizontalpodautoscaleretcd.NewREST(c.DatabaseStorage)
controllerStorage := expcontrolleretcd.NewStorage(c.ExpDatabaseStorage)
autoscalerStorage := horizontalpodautoscaleretcd.NewREST(c.ExpDatabaseStorage)
thirdPartyResourceStorage := thirdpartyresourceetcd.NewREST(c.ExpDatabaseStorage)
storage := map[string]rest.Storage{
strings.ToLower("replicationControllers"): controllerStorage.ReplicationController,
strings.ToLower("replicationControllers/scale"): controllerStorage.Scale,
strings.ToLower("horizontalpodautoscalers"): autoscalerStorage,
"thirdpartyresources": thirdPartyResourceStorage,
}
return &apiserver.APIGroupVersion{

View File

@ -0,0 +1,19 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package thirdpartyresource provides Registry interface and its REST
// implementation for storing ThirdPartyResource api objects.
package thirdpartyresource

View File

@ -0,0 +1,63 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package etcd
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd"
"k8s.io/kubernetes/pkg/registry/thirdpartyresource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
)
// REST implements a RESTStorage for ThirdPartyResources against etcd
type REST struct {
*etcdgeneric.Etcd
}
// NewREST returns a registry which will store ThirdPartyResource in the given helper
func NewREST(s storage.Interface) *REST {
prefix := "/thirdpartyresources"
store := &etcdgeneric.Etcd{
NewFunc: func() runtime.Object { return &expapi.ThirdPartyResource{} },
NewListFunc: func() runtime.Object { return &expapi.ThirdPartyResourceList{} },
KeyRootFunc: func(ctx api.Context) string {
return etcdgeneric.NamespaceKeyRootFunc(ctx, prefix)
},
KeyFunc: func(ctx api.Context, id string) (string, error) {
return etcdgeneric.NamespaceKeyFunc(ctx, prefix, id)
},
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*expapi.ThirdPartyResource).Name, nil
},
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
return thirdpartyresource.Matcher(label, field)
},
EndpointName: "thirdPartyResources",
CreateStrategy: thirdpartyresource.Strategy,
UpdateStrategy: thirdpartyresource.Strategy,
Storage: s,
}
return &REST{store}
}

View File

@ -0,0 +1,205 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package etcd
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/expapi/v1"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
"github.com/coreos/go-etcd/etcd"
)
var scheme *runtime.Scheme
var codec runtime.Codec
func init() {
// Ensure that expapi/v1 packege is used, so that it will get initialized and register HorizontalPodAutoscaler object.
_ = v1.ThirdPartyResource{}
}
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient, storage.Interface) {
fakeEtcdClient := tools.NewFakeEtcdClient(t)
fakeEtcdClient.TestIndex = true
etcdStorage := etcdstorage.NewEtcdStorage(fakeEtcdClient, testapi.Codec(), etcdtest.PathPrefix())
storage := NewREST(etcdStorage)
return storage, fakeEtcdClient, etcdStorage
}
func validNewThirdPartyResource(name string) *expapi.ThirdPartyResource {
return &expapi.ThirdPartyResource{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: api.NamespaceDefault,
},
Versions: []expapi.APIVersion{
{
Name: "stable/v1",
},
},
}
}
func TestCreate(t *testing.T) {
storage, fakeEtcdClient, _ := newStorage(t)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
rsrc := validNewThirdPartyResource("foo")
rsrc.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
rsrc,
// invalid
&expapi.ThirdPartyResource{},
)
}
func TestUpdate(t *testing.T) {
storage, fakeEtcdClient, _ := newStorage(t)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
key, err := storage.KeyFunc(test.TestContext(), "foo")
if err != nil {
t.Fatal(err)
}
key = etcdtest.AddPrefix(key)
fakeEtcdClient.ExpectNotFoundGet(key)
fakeEtcdClient.ChangeIndex = 2
rsrc := validNewThirdPartyResource("foo")
existing := validNewThirdPartyResource("exists")
existing.Namespace = test.TestNamespace()
obj, err := storage.Create(test.TestContext(), existing)
if err != nil {
t.Fatalf("unable to create object: %v", err)
}
older := obj.(*expapi.ThirdPartyResource)
older.ResourceVersion = "1"
test.TestUpdate(
rsrc,
existing,
older,
)
}
func TestDelete(t *testing.T) {
ctx := api.NewDefaultContext()
storage, fakeEtcdClient, _ := newStorage(t)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
rsrc := validNewThirdPartyResource("foo2")
key, _ := storage.KeyFunc(ctx, "foo2")
key = etcdtest.AddPrefix(key)
createFn := func() runtime.Object {
fakeEtcdClient.Data[key] = tools.EtcdResponseWithError{
R: &etcd.Response{
Node: &etcd.Node{
Value: runtime.EncodeOrDie(testapi.Codec(), rsrc),
ModifiedIndex: 1,
},
},
}
return rsrc
}
gracefulSetFn := func() bool {
if fakeEtcdClient.Data[key].R.Node == nil {
return false
}
return fakeEtcdClient.Data[key].R.Node.TTL == 30
}
test.TestDeleteNoGraceful(createFn, gracefulSetFn)
}
func TestGet(t *testing.T) {
storage, fakeEtcdClient, _ := newStorage(t)
test := resttest.New(t, storage, fakeEtcdClient.SetError)
rsrc := validNewThirdPartyResource("foo")
test.TestGet(rsrc)
}
func TestEmptyList(t *testing.T) {
ctx := api.NewDefaultContext()
registry, fakeClient, _ := newStorage(t)
fakeClient.ChangeIndex = 1
key := registry.KeyRootFunc(ctx)
key = etcdtest.AddPrefix(key)
fakeClient.Data[key] = tools.EtcdResponseWithError{
R: &etcd.Response{},
E: fakeClient.NewError(tools.EtcdErrorCodeNotFound),
}
rsrcList, err := registry.List(ctx, labels.Everything(), fields.Everything())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(rsrcList.(*expapi.ThirdPartyResourceList).Items) != 0 {
t.Errorf("Unexpected non-zero autoscaler list: %#v", rsrcList)
}
if rsrcList.(*expapi.ThirdPartyResourceList).ResourceVersion != "1" {
t.Errorf("Unexpected resource version: %#v", rsrcList)
}
}
func TestList(t *testing.T) {
ctx := api.NewDefaultContext()
registry, fakeClient, _ := newStorage(t)
fakeClient.ChangeIndex = 1
key := registry.KeyRootFunc(ctx)
key = etcdtest.AddPrefix(key)
fakeClient.Data[key] = tools.EtcdResponseWithError{
R: &etcd.Response{
Node: &etcd.Node{
Nodes: []*etcd.Node{
{
Value: runtime.EncodeOrDie(testapi.Codec(), &expapi.ThirdPartyResource{
ObjectMeta: api.ObjectMeta{Name: "foo"},
}),
},
{
Value: runtime.EncodeOrDie(testapi.Codec(), &expapi.ThirdPartyResource{
ObjectMeta: api.ObjectMeta{Name: "bar"},
}),
},
},
},
},
}
obj, err := registry.List(ctx, labels.Everything(), fields.Everything())
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
rsrcList := obj.(*expapi.ThirdPartyResourceList)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(rsrcList.Items) != 2 {
t.Errorf("Unexpected ThirdPartyResource list: %#v", rsrcList)
}
if rsrcList.Items[0].Name != "foo" {
t.Errorf("Unexpected ThirdPartyResource: %#v", rsrcList.Items[0])
}
if rsrcList.Items[1].Name != "bar" {
t.Errorf("Unexpected ThirdPartyResource: %#v", rsrcList.Items[1])
}
}

View File

@ -0,0 +1,88 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package thirdpartyresource
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/watch"
)
// Registry is an interface implemented by things that know how to store ThirdPartyResource objects.
type Registry interface {
// ListThirdPartyResources obtains a list of ThirdPartyResources having labels which match selector.
ListThirdPartyResources(ctx api.Context, selector labels.Selector) (*expapi.ThirdPartyResourceList, error)
// Watch for new/changed/deleted ThirdPartyResources
WatchThirdPartyResources(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
// Get a specific ThirdPartyResource
GetThirdPartyResource(ctx api.Context, name string) (*expapi.ThirdPartyResource, error)
// Create a ThirdPartyResource based on a specification.
CreateThirdPartyResource(ctx api.Context, resource *expapi.ThirdPartyResource) (*expapi.ThirdPartyResource, error)
// Update an existing ThirdPartyResource
UpdateThirdPartyResource(ctx api.Context, resource *expapi.ThirdPartyResource) (*expapi.ThirdPartyResource, error)
// Delete an existing ThirdPartyResource
DeleteThirdPartyResource(ctx api.Context, name string) error
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListThirdPartyResources(ctx api.Context, label labels.Selector) (*expapi.ThirdPartyResourceList, error) {
obj, err := s.List(ctx, label, fields.Everything())
if err != nil {
return nil, err
}
return obj.(*expapi.ThirdPartyResourceList), nil
}
func (s *storage) WatchThirdPartyResources(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
return s.Watch(ctx, label, field, resourceVersion)
}
func (s *storage) GetThirdPartyResource(ctx api.Context, name string) (*expapi.ThirdPartyResource, error) {
obj, err := s.Get(ctx, name)
if err != nil {
return nil, err
}
return obj.(*expapi.ThirdPartyResource), nil
}
func (s *storage) CreateThirdPartyResource(ctx api.Context, ThirdPartyResource *expapi.ThirdPartyResource) (*expapi.ThirdPartyResource, error) {
obj, err := s.Create(ctx, ThirdPartyResource)
return obj.(*expapi.ThirdPartyResource), err
}
func (s *storage) UpdateThirdPartyResource(ctx api.Context, ThirdPartyResource *expapi.ThirdPartyResource) (*expapi.ThirdPartyResource, error) {
obj, _, err := s.Update(ctx, ThirdPartyResource)
return obj.(*expapi.ThirdPartyResource), err
}
func (s *storage) DeleteThirdPartyResource(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View File

@ -0,0 +1,88 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package thirdpartyresource
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/expapi/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/fielderrors"
)
// strategy implements behavior for ThirdPartyResource objects
type strategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// Strategy is the default logic that applies when creating and updating ThirdPartyResource
// objects via the REST API.
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
var _ = rest.RESTCreateStrategy(Strategy)
var _ = rest.RESTUpdateStrategy(Strategy)
func (strategy) NamespaceScoped() bool {
return true
}
func (strategy) PrepareForCreate(obj runtime.Object) {
}
func (strategy) Validate(ctx api.Context, obj runtime.Object) fielderrors.ValidationErrorList {
return validation.ValidateThirdPartyResource(obj.(*expapi.ThirdPartyResource))
}
func (strategy) AllowCreateOnUpdate() bool {
return false
}
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
}
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
return validation.ValidateThirdPartyResourceUpdate(old.(*expapi.ThirdPartyResource), obj.(*expapi.ThirdPartyResource))
}
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
sa, ok := obj.(*expapi.ThirdPartyResource)
if !ok {
return false, fmt.Errorf("not a ThirdPartyResource")
}
fields := SelectableFields(sa)
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
})
}
// SelectableFields returns a label set that can be used for filter selection
func SelectableFields(obj *expapi.ThirdPartyResource) labels.Set {
return labels.Set{}
}