remove rootscopedkinds from groupmeta

Kubernetes-commit: 8ae62517da5eff6d6bad21badfd39ee88463ad42
This commit is contained in:
David Eads 2018-04-30 13:27:01 -04:00 committed by Kubernetes Publisher
parent bd62696dfb
commit b7f90743d0
8 changed files with 91 additions and 66 deletions

View File

@ -20,7 +20,6 @@ import (
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/apis/apiserver"
"k8s.io/apiserver/pkg/apis/apiserver/v1alpha1"
)
@ -30,7 +29,6 @@ func Install(registry *registered.APIRegistrationManager, scheme *runtime.Scheme
if err := announced.NewGroupMetaFactory(
&announced.GroupMetaFactoryArgs{
GroupName: apiserver.GroupName,
RootScopedKinds: sets.NewString("AdmissionConfiguration"),
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
AddInternalObjectsToScheme: apiserver.AddToScheme,
},

View File

@ -22,7 +22,6 @@ import (
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/apis/audit/v1alpha1"
"k8s.io/apiserver/pkg/apis/audit/v1beta1"
@ -32,10 +31,8 @@ import (
func Install(registry *registered.APIRegistrationManager, scheme *runtime.Scheme) {
if err := announced.NewGroupMetaFactory(
&announced.GroupMetaFactoryArgs{
GroupName: audit.GroupName,
VersionPreferenceOrder: []string{v1beta1.SchemeGroupVersion.Version, v1alpha1.SchemeGroupVersion.Version},
// Any Kind that is not namespaced must be cluster scoped.
RootScopedKinds: sets.NewString("Event", "Policy"),
GroupName: audit.GroupName,
VersionPreferenceOrder: []string{v1beta1.SchemeGroupVersion.Version, v1alpha1.SchemeGroupVersion.Version},
AddInternalObjectsToScheme: audit.AddToScheme,
},
announced.VersionToSchemeFunc{

View File

@ -347,6 +347,10 @@ type SimpleRESTStorage struct {
injectedFunction func(obj runtime.Object) (returnObj runtime.Object, err error)
}
func (storage *SimpleRESTStorage) NamespaceScoped() bool {
return true
}
func (storage *SimpleRESTStorage) Export(ctx context.Context, name string, opts metav1.ExportOptions) (runtime.Object, error) {
obj, err := storage.Get(ctx, name, &metav1.GetOptions{})
if err != nil {
@ -615,6 +619,10 @@ type GetWithOptionsRootRESTStorage struct {
takesPath string
}
func (r *GetWithOptionsRootRESTStorage) NamespaceScoped() bool {
return false
}
func (r *GetWithOptionsRootRESTStorage) Get(ctx context.Context, name string, options runtime.Object) (runtime.Object, error) {
if _, ok := options.(*genericapitesting.SimpleGetOptions); !ok {
return nil, fmt.Errorf("Unexpected options object: %#v", options)
@ -801,6 +809,10 @@ func TestNotFound(t *testing.T) {
type UnimplementedRESTStorage struct{}
func (UnimplementedRESTStorage) NamespaceScoped() bool {
return true
}
func (UnimplementedRESTStorage) New() runtime.Object {
return &genericapitesting.Simple{}
}
@ -3774,10 +3786,6 @@ func (storage *SimpleXGSubresourceRESTStorage) GroupVersionKind(containingGV sch
return storage.itemGVK
}
func (*SimpleXGSubresourceRESTStorage) ClusterScoped() bool {
return false
}
func TestXGSubresource(t *testing.T) {
container := restful.NewContainer()
container.Router(restful.CurlyRouter{})

View File

@ -132,18 +132,18 @@ func (a *APIInstaller) newWebService() *restful.WebService {
}
// getResourceKind returns the external group version kind registered for the given storage
// object and whether or not the kind is cluster scoped. If the storage object is a subresource and has an override supplied for it, it returns
// object. If the storage object is a subresource and has an override supplied for it, it returns
// the group version kind supplied in the override.
func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (schema.GroupVersionKind, bool, error) {
func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (schema.GroupVersionKind, error) {
// Let the storage tell us exactly what GVK it has
if gvkProvider, ok := storage.(rest.GroupVersionKindProvider); ok {
return gvkProvider.GroupVersionKind(a.group.GroupVersion), gvkProvider.ClusterScoped(), nil
return gvkProvider.GroupVersionKind(a.group.GroupVersion), nil
}
object := storage.New()
fqKinds, _, err := a.group.Typer.ObjectKinds(object)
if err != nil {
return schema.GroupVersionKind{}, false, err
return schema.GroupVersionKind{}, err
}
// a given go type can have multiple potential fully qualified kinds. Find the one that corresponds with the group
@ -156,11 +156,11 @@ func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (schem
}
}
if fqKindToRegister.Empty() {
return schema.GroupVersionKind{}, false, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion)
return schema.GroupVersionKind{}, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion)
}
// group is guaranteed to match based on the check above
return fqKindToRegister, a.group.RootScopedKinds.Has(fqKindToRegister.Kind), nil
return fqKindToRegister, nil
}
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService) (*metav1.APIResource, error) {
@ -176,7 +176,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
return nil, err
}
fqKindToRegister, clusterScoped, err := a.getResourceKind(path, storage)
fqKindToRegister, err := a.getResourceKind(path, storage)
if err != nil {
return nil, err
}
@ -187,7 +187,28 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
}
defaultVersionedObject := indirectArbitraryPointer(versionedPtr)
kind := fqKindToRegister.Kind
hasSubresource := len(subresource) > 0
isSubresource := len(subresource) > 0
// If there is a subresource, namespace scoping is defined by the parent resource
namespaceScoped := true
if isSubresource {
parentStorage, ok := a.group.Storage[resource]
if !ok {
return nil, fmt.Errorf("missing parent storage: %q", resource)
}
scoper, ok := parentStorage.(rest.Scoper)
if !ok {
return nil, fmt.Errorf("%q must implement scoper", resource)
}
namespaceScoped = scoper.NamespaceScoped()
} else {
scoper, ok := storage.(rest.Scoper)
if !ok {
return nil, fmt.Errorf("%q must implement scoper", resource)
}
namespaceScoped = scoper.NamespaceScoped()
}
// what verbs are supported by the storage, used to know what verbs we support per path
creater, isCreater := storage.(rest.Creater)
@ -330,7 +351,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
var apiResource metav1.APIResource
// Get the list of actions for the given scope.
switch {
case clusterScoped:
case !namespaceScoped:
// Handle non-namespace scoped resources like nodes.
resourcePath := resource
resourceParams := params
@ -338,7 +359,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
nameParams := append(params, nameParam)
proxyParams := append(nameParams, pathParam)
suffix := ""
if hasSubresource {
if isSubresource {
suffix = "/" + subresource
itemPath = itemPath + suffix
resourcePath = itemPath
@ -387,7 +408,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
nameParams := append(namespaceParams, nameParam)
proxyParams := append(nameParams, pathParam)
itemPathSuffix := ""
if hasSubresource {
if isSubresource {
itemPathSuffix = "/" + subresource
itemPath = itemPath + itemPathSuffix
resourcePath = itemPath
@ -423,7 +444,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
// list or post across namespace.
// For ex: LIST all pods in all namespaces by sending a LIST request at /api/apiVersion/pods.
// TODO: more strongly type whether a resource allows these actions on "all namespaces" (bulk delete)
if !hasSubresource {
if !isSubresource {
actions = appendIf(actions, action{"LIST", resource, params, namer, true}, isLister)
actions = appendIf(actions, action{"WATCHLIST", "watch/" + resource, params, namer, true}, allowWatchList)
}
@ -509,12 +530,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
routes := []*restful.RouteBuilder{}
// If there is a subresource, kind should be the parent's kind.
if hasSubresource {
if isSubresource {
parentStorage, ok := a.group.Storage[resource]
if !ok {
return nil, fmt.Errorf("missing parent storage: %q", resource)
}
fqParentKind, _, err := a.getResourceKind(resource, parentStorage)
fqParentKind, err := a.getResourceKind(resource, parentStorage)
if err != nil {
return nil, err
}
@ -527,7 +549,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
case "GET": // Get a resource.
var handler restful.RouteFunction
if isGetterWithOptions {
handler = restfulGetResourceWithOptions(getterWithOptions, reqScope, hasSubresource)
handler = restfulGetResourceWithOptions(getterWithOptions, reqScope, isSubresource)
} else {
handler = restfulGetResource(getter, exporter, reqScope)
}
@ -543,7 +565,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
handler = genericfilters.RestfulWithCompression(handler)
}
doc := "read the specified " + kind
if hasSubresource {
if isSubresource {
doc = "read " + subresource + " of the specified " + kind
}
route := ws.GET(action.Path).To(handler).
@ -567,7 +589,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
routes = append(routes, route)
case "LIST": // List all resources of a kind.
doc := "list objects of kind " + kind
if hasSubresource {
if isSubresource {
doc = "list " + subresource + " of objects of kind " + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulListResource(lister, watcher, reqScope, false, a.minRequestTimeout))
@ -587,13 +609,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
switch {
case isLister && isWatcher:
doc := "list or watch objects of kind " + kind
if hasSubresource {
if isSubresource {
doc = "list or watch " + subresource + " of objects of kind " + kind
}
route.Doc(doc)
case isWatcher:
doc := "watch objects of kind " + kind
if hasSubresource {
if isSubresource {
doc = "watch " + subresource + "of objects of kind " + kind
}
route.Doc(doc)
@ -602,7 +624,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
routes = append(routes, route)
case "PUT": // Update a resource.
doc := "replace the specified " + kind
if hasSubresource {
if isSubresource {
doc = "replace " + subresource + " of the specified " + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulUpdateResource(updater, reqScope, admit))
@ -621,7 +643,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
routes = append(routes, route)
case "PATCH": // Partially update a resource
doc := "partially update the specified " + kind
if hasSubresource {
if isSubresource {
doc = "partially update " + subresource + " of the specified " + kind
}
supportedTypes := []string{
@ -651,7 +673,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
handler = metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, handler)
article := getArticleForNoun(kind, " ")
doc := "create" + article + kind
if hasSubresource {
if isSubresource {
doc = "create " + subresource + " of" + article + kind
}
route := ws.POST(action.Path).To(handler).
@ -671,7 +693,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
case "DELETE": // Delete a resource.
article := getArticleForNoun(kind, " ")
doc := "delete" + article + kind
if hasSubresource {
if isSubresource {
doc = "delete " + subresource + " of" + article + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulDeleteResource(gracefulDeleter, isGracefulDeleter, reqScope, admit))
@ -692,7 +714,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
routes = append(routes, route)
case "DELETECOLLECTION":
doc := "delete collection of " + kind
if hasSubresource {
if isSubresource {
doc = "delete collection of " + subresource + " of a " + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulDeleteCollection(collectionDeleter, isCollectionDeleter, reqScope, admit))
@ -711,7 +733,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
// TODO: deprecated
case "WATCH": // Watch a resource.
doc := "watch changes to an object of kind " + kind
if hasSubresource {
if isSubresource {
doc = "watch changes to " + subresource + " of an object of kind " + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
@ -730,7 +752,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
// TODO: deprecated
case "WATCHLIST": // Watch all resources of a kind.
doc := "watch individual changes to a list of " + kind
if hasSubresource {
if isSubresource {
doc = "watch individual changes to a list of " + subresource + " of " + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
@ -753,10 +775,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
connectProducedObject = "string"
}
doc := "connect " + method + " requests to " + kind
if hasSubresource {
if isSubresource {
doc = "connect " + method + " requests to " + subresource + " of " + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulConnectResource(connecter, reqScope, admit, path, hasSubresource))
handler := metrics.InstrumentRouteFunc(action.Verb, resource, subresource, requestScope, restfulConnectResource(connecter, reqScope, admit, path, isSubresource))
route := ws.Method(method).Path(action.Path).
To(handler).
Doc(doc).

View File

@ -242,6 +242,18 @@ func (e *Store) NewList() runtime.Object {
return e.NewListFunc()
}
// NamespaceScoped indicates whether the resource is namespaced
func (e *Store) NamespaceScoped() bool {
if e.CreateStrategy != nil {
return e.CreateStrategy.NamespaceScoped()
}
if e.UpdateStrategy != nil {
return e.UpdateStrategy.NamespaceScoped()
}
panic("programmer error: no CRUD for resource, you're crazy, override NamespaceScoped too")
}
// GetCreateStrategy implements GenericStore.
func (e *Store) GetCreateStrategy() rest.RESTCreateStrategy {
return e.CreateStrategy

View File

@ -58,6 +58,13 @@ type Storage interface {
New() runtime.Object
}
// Scoper indicates what scope the resource is at. It must be specified.
// It is usually provided automatically based on your strategy.
type Scoper interface {
// NamespaceScoped returns true if the storage is namespaced
NamespaceScoped() bool
}
// 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. runtime.Unknown), but when we present the object as part of
@ -83,7 +90,6 @@ type CategoriesProvider interface {
// TODO KindProvider (only used by federation) should be removed and replaced with this, but that presents greater risk late in 1.8.
type GroupVersionKindProvider interface {
GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind
ClusterScoped() bool
}
// Lister is an object that can retrieve resources that match the provided field and label criteria.

View File

@ -424,7 +424,6 @@ func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
Defaulter: apiGroupInfo.Scheme,
Typer: apiGroupInfo.Scheme,
Linker: runtime.SelfLinker(meta.NewAccessor()),
RootScopedKinds: apiGroupInfo.GroupMeta.RootScopedKinds,
Admit: s.admissionControl,
MinRequestTimeout: s.minRequestTimeout,

View File

@ -43,7 +43,6 @@ import (
"k8s.io/apimachinery/pkg/version"
"k8s.io/apiserver/pkg/apis/example"
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/discovery"
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
@ -416,34 +415,14 @@ func (authz *mockAuthorizer) Authorize(a authorizer.Attributes) (authorized auth
return authorizer.DecisionAllow, "", nil
}
type mockAuthenticator struct {
lastURI string
}
func (authn *mockAuthenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
authn.lastURI = req.RequestURI
return &user.DefaultInfo{
Name: "foo",
}, true, nil
}
func decodeResponse(resp *http.Response, obj interface{}) error {
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if err := json.Unmarshal(data, obj); err != nil {
return err
}
return nil
}
type testGetterStorage struct {
Version string
}
func (p *testGetterStorage) NamespaceScoped() bool {
return true
}
func (p *testGetterStorage) New() runtime.Object {
return &metav1.APIGroup{
TypeMeta: metav1.TypeMeta{
@ -461,6 +440,10 @@ type testNoVerbsStorage struct {
Version string
}
func (p *testNoVerbsStorage) NamespaceScoped() bool {
return true
}
func (p *testNoVerbsStorage) New() runtime.Object {
return &metav1.APIGroup{
TypeMeta: metav1.TypeMeta{