Merge pull request #19380 from brendandburns/apiresource

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2016-02-03 00:49:47 -08:00
commit 5914deeac8
6 changed files with 39 additions and 8 deletions

View File

@ -55,6 +55,14 @@ type Storage interface {
New() runtime.Object New() runtime.Object
} }
// KindProvider specifies a different kind for its API than for its internal storage. This is necessary for external
// objects that are not compiled into the api server. For such objects, there is no in-memory representation for
// the object, so they must be represented as generic objects (e.g. RawJSON), but when we present the object as part of
// API discovery we want to present the specific kind, not the generic internal representation.
type KindProvider interface {
Kind() string
}
// Lister is an object that can retrieve resources that match the provided field and label criteria. // Lister is an object that can retrieve resources that match the provided field and label criteria.
type Lister interface { type Lister interface {
// NewList returns an empty object that can be used with the List call. // NewList returns an empty object that can be used with the List call.

View File

@ -339,6 +339,8 @@ type APIResource struct {
Name string `json:"name"` Name string `json:"name"`
// namespaced indicates if a resource is namespaced or not. // namespaced indicates if a resource is namespaced or not.
Namespaced bool `json:"namespaced"` Namespaced bool `json:"namespaced"`
// kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')
Kind string `json:"kind"`
} }
// APIResourceList is a list of APIResource, it is used to expose the name of the // APIResourceList is a list of APIResource, it is used to expose the name of the

View File

@ -51,6 +51,7 @@ var map_APIResource = map[string]string{
"": "APIResource specifies the name of a resource and whether it is namespaced.", "": "APIResource specifies the name of a resource and whether it is namespaced.",
"name": "name is the name of the resource.", "name": "name is the name of the resource.",
"namespaced": "namespaced indicates if a resource is namespaced or not.", "namespaced": "namespaced indicates if a resource is namespaced or not.",
"kind": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')",
} }
func (APIResource) SwaggerDoc() map[string]string { func (APIResource) SwaggerDoc() map[string]string {

View File

@ -152,10 +152,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
} }
} }
kind := fqKindToRegister.Kind
if fqKindToRegister.IsEmpty() { if fqKindToRegister.IsEmpty() {
return nil, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion) return nil, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion)
} }
kind := fqKindToRegister.Kind
versionedPtr, err := a.group.Creater.New(a.group.GroupVersion.WithKind(kind)) versionedPtr, err := a.group.Creater.New(a.group.GroupVersion.WithKind(kind))
if err != nil { if err != nil {
@ -322,6 +323,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
params := []*restful.Parameter{} params := []*restful.Parameter{}
actions := []action{} actions := []action{}
var resourceKind string
kindProvider, ok := storage.(rest.KindProvider)
if ok {
resourceKind = kindProvider.Kind()
} else {
resourceKind = fqKindToRegister.Kind
}
var apiResource unversioned.APIResource var apiResource unversioned.APIResource
// Get the list of actions for the given scope. // Get the list of actions for the given scope.
switch scope.Name() { switch scope.Name() {
@ -339,6 +348,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
} }
apiResource.Name = path apiResource.Name = path
apiResource.Namespaced = false apiResource.Namespaced = false
apiResource.Kind = resourceKind
namer := rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath)} namer := rootScopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath)}
// Handler for standard REST verbs (GET, PUT, POST and DELETE). // Handler for standard REST verbs (GET, PUT, POST and DELETE).
@ -381,6 +391,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
} }
apiResource.Name = path apiResource.Name = path
apiResource.Namespaced = true apiResource.Namespaced = true
apiResource.Kind = resourceKind
namer := scopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath), false} namer := scopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath), false}
actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister) actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister)

View File

@ -137,17 +137,17 @@ func TestGetServerResources(t *testing.T) {
stable := unversioned.APIResourceList{ stable := unversioned.APIResourceList{
GroupVersion: "v1", GroupVersion: "v1",
APIResources: []unversioned.APIResource{ APIResources: []unversioned.APIResource{
{"pods", true}, {"pods", true, "Pod"},
{"services", true}, {"services", true, "Service"},
{"namespaces", false}, {"namespaces", false, "Namespace"},
}, },
} }
beta := unversioned.APIResourceList{ beta := unversioned.APIResourceList{
GroupVersion: "extensions/v1", GroupVersion: "extensions/v1",
APIResources: []unversioned.APIResource{ APIResources: []unversioned.APIResource{
{"deployments", true}, {"deployments", true, "Deployment"},
{"ingresses", true}, {"ingresses", true, "Ingress"},
{"jobs", true}, {"jobs", true, "Job"},
}, },
} }
tests := []struct { tests := []struct {

View File

@ -33,6 +33,7 @@ import (
// REST implements a RESTStorage for ThirdPartyResourceDatas against etcd // REST implements a RESTStorage for ThirdPartyResourceDatas against etcd
type REST struct { type REST struct {
*etcdgeneric.Etcd *etcdgeneric.Etcd
kind string
} }
// NewREST returns a registry which will store ThirdPartyResourceData in the given helper // NewREST returns a registry which will store ThirdPartyResourceData in the given helper
@ -64,5 +65,13 @@ func NewREST(s storage.Interface, storageDecorator generic.StorageDecorator, gro
Storage: storageInterface, Storage: storageInterface,
} }
return &REST{store} return &REST{
Etcd: store,
kind: kind,
}
}
// Implements the rest.KindProvider interface
func (r *REST) Kind() string {
return r.kind
} }