Switch genericapiserver to use metainternalversion.ListOptions

Decouple ListOption parsing from the scheme - instead, it is a property
of the server (and clients should use metav1.ListOptions for now).
This commit is contained in:
Clayton Coleman 2017-01-21 17:04:29 -05:00
parent c12344b3b8
commit 3ba366fcf1
No known key found for this signature in database
GPG Key ID: 3D16906B4F1C5CB3
9 changed files with 51 additions and 47 deletions

View File

@ -36,6 +36,7 @@ import (
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
@ -117,20 +118,11 @@ func newMapper() *meta.DefaultRESTMapper {
}
func addGrouplessTypes() {
type ListOptions struct {
Object runtime.Object
metav1.TypeMeta `json:",inline"`
LabelSelector string `json:"labelSelector,omitempty"`
FieldSelector string `json:"fieldSelector,omitempty"`
Watch bool `json:"watch,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(grouplessGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &ListOptions{}, &metav1.ExportOptions{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ListOptions{}, &metav1.ExportOptions{},
&v1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
api.Scheme.AddKnownTypes(grouplessInternalGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &api.ListOptions{}, &metav1.ExportOptions{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
&genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
}
@ -145,12 +137,12 @@ func addTestTypes() {
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(testGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &ListOptions{}, &metav1.ExportOptions{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
&v1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
&SimpleXGSubresource{})
api.Scheme.AddKnownTypes(testGroupVersion, &v1.Pod{})
api.Scheme.AddKnownTypes(testInternalGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &api.ListOptions{}, &metav1.ExportOptions{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
&genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
&SimpleXGSubresource{})
api.Scheme.AddKnownTypes(testInternalGroupVersion, &api.Pod{})
@ -163,17 +155,8 @@ func addTestTypes() {
}
func addNewTestTypes() {
type ListOptions struct {
Object runtime.Object
metav1.TypeMeta `json:",inline"`
LabelSelector string `json:"labelSelector,omitempty"`
FieldSelector string `json:"fieldSelector,omitempty"`
Watch bool `json:"watch,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
}
api.Scheme.AddKnownTypes(newGroupVersion,
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &ListOptions{}, &metav1.ExportOptions{},
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
&api.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
&v1.Pod{},
)
@ -393,7 +376,7 @@ func (storage *SimpleRESTStorage) Export(ctx request.Context, name string, opts
return obj, storage.errors["export"]
}
func (storage *SimpleRESTStorage) List(ctx request.Context, options *api.ListOptions) (runtime.Object, error) {
func (storage *SimpleRESTStorage) List(ctx request.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
storage.checkContext(ctx)
result := &genericapitesting.SimpleList{
Items: storage.list,
@ -509,7 +492,7 @@ func (storage *SimpleRESTStorage) Update(ctx request.Context, name string, objIn
}
// Implement ResourceWatcher.
func (storage *SimpleRESTStorage) Watch(ctx request.Context, options *api.ListOptions) (watch.Interface, error) {
func (storage *SimpleRESTStorage) Watch(ctx request.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
storage.lock.Lock()
defer storage.lock.Unlock()
storage.checkContext(ctx)

View File

@ -53,6 +53,10 @@ type APIGroupVersion struct {
// define a version "v1beta1" but want to use the Kubernetes "v1" internal objects. If
// empty, defaults to GroupVersion.
OptionsExternalVersion *schema.GroupVersion
// MetaGroupVersion defaults to "meta.k8s.io/v1" and is the scheme group version used to decode
// common API implementations like ListOptions. Future changes will allow this to vary by group
// version (for when the inevitable meta/v2 group emerges).
MetaGroupVersion *schema.GroupVersion
Mapper meta.RESTMapper

View File

@ -29,6 +29,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
@ -87,6 +88,8 @@ type RequestScope struct {
Resource schema.GroupVersionResource
Kind schema.GroupVersionKind
Subresource string
MetaGroupVersion schema.GroupVersion
}
func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Request) {
@ -264,8 +267,8 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
ctx := scope.ContextFunc(req)
ctx = request.WithNamespace(ctx, namespace)
opts := api.ListOptions{}
if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &opts); err != nil {
opts := metainternalversion.ListOptions{}
if err := metainternalversion.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.MetaGroupVersion, &opts); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
@ -912,8 +915,8 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
}
}
listOptions := api.ListOptions{}
if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &listOptions); err != nil {
listOptions := metainternalversion.ListOptions{}
if err := metainternalversion.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.MetaGroupVersion, &listOptions); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}

View File

@ -521,6 +521,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Resource: a.group.GroupVersion.WithResource(resource),
Subresource: subresource,
Kind: fqKindToRegister,
MetaGroupVersion: metav1.SchemeGroupVersion,
}
if a.group.MetaGroupVersion != nil {
reqScope.MetaGroupVersion = *a.group.MetaGroupVersion
}
for _, action := range actions {
versionedObject := storageMeta.ProducesObject(action.Verb)

View File

@ -25,6 +25,7 @@ import (
kubeerr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
@ -221,7 +222,7 @@ func (e *Store) NewList() runtime.Object {
// List returns a list of items matching labels and field according to the
// store's PredicateFunc.
func (e *Store) List(ctx genericapirequest.Context, options *api.ListOptions) (runtime.Object, error) {
func (e *Store) List(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
label := labels.Everything()
if options != nil && options.LabelSelector != nil {
label = options.LabelSelector
@ -244,10 +245,10 @@ func (e *Store) List(ctx genericapirequest.Context, options *api.ListOptions) (r
// ListPredicate returns a list of all the items matching the given
// SelectionPredicate.
func (e *Store) ListPredicate(ctx genericapirequest.Context, p storage.SelectionPredicate, options *api.ListOptions) (runtime.Object, error) {
func (e *Store) ListPredicate(ctx genericapirequest.Context, p storage.SelectionPredicate, options *metainternalversion.ListOptions) (runtime.Object, error) {
if options == nil {
// By default we should serve the request from etcd.
options = &api.ListOptions{ResourceVersion: ""}
options = &metainternalversion.ListOptions{ResourceVersion: ""}
}
list := e.NewListFunc()
if name, ok := p.MatchesSingle(); ok {
@ -830,7 +831,7 @@ func (e *Store) Delete(ctx genericapirequest.Context, name string, options *api.
// are removing all objects of a given type) with the current API (it's technically
// possibly with storage API, but watch is not delivered correctly then).
// It will be possible to fix it with v3 etcd API.
func (e *Store) DeleteCollection(ctx genericapirequest.Context, options *api.DeleteOptions, listOptions *api.ListOptions) (runtime.Object, error) {
func (e *Store) DeleteCollection(ctx genericapirequest.Context, options *api.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error) {
listObj, err := e.List(ctx, listOptions)
if err != nil {
return nil, err
@ -921,7 +922,7 @@ func (e *Store) finalizeDelete(obj runtime.Object, runHooks bool) (runtime.Objec
// WatchPredicate. If possible, you should customize PredicateFunc to produce
// a matcher that matches by key. SelectionPredicate does this for you
// automatically.
func (e *Store) Watch(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error) {
func (e *Store) Watch(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
label := labels.Everything()
if options != nil && options.LabelSelector != nil {
label = options.LabelSelector

View File

@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
@ -239,7 +240,7 @@ func TestStoreListResourceVersion(t *testing.T) {
waitListCh := make(chan runtime.Object, 1)
go func(listRev uint64) {
option := &api.ListOptions{ResourceVersion: strconv.FormatUint(listRev, 10)}
option := &metainternalversion.ListOptions{ResourceVersion: strconv.FormatUint(listRev, 10)}
// It will wait until we create the second pod.
l, err := registry.List(ctx, option)
if err != nil {
@ -1078,7 +1079,7 @@ func TestStoreDeleteCollection(t *testing.T) {
}
// Delete all pods.
deleted, err := registry.DeleteCollection(testContext, nil, &api.ListOptions{})
deleted, err := registry.DeleteCollection(testContext, nil, &metainternalversion.ListOptions{})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@ -1119,7 +1120,7 @@ func TestStoreDeleteCollectionNotFound(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
_, err := registry.DeleteCollection(testContext, nil, &api.ListOptions{})
_, err := registry.DeleteCollection(testContext, nil, &metainternalversion.ListOptions{})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@ -1157,7 +1158,7 @@ func TestStoreDeleteCollectionWithWatch(t *testing.T) {
}
defer watcher.Stop()
if _, err := registry.DeleteCollection(testContext, nil, &api.ListOptions{}); err != nil {
if _, err := registry.DeleteCollection(testContext, nil, &metainternalversion.ListOptions{}); err != nil {
t.Fatalf("Unexpected error: %v", err)
}

View File

@ -21,6 +21,7 @@ import (
"net/http"
"net/url"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -71,7 +72,7 @@ type Lister interface {
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
NewList() runtime.Object
// List selects resources in the storage which match to the selector. 'options' can be nil.
List(ctx genericapirequest.Context, options *api.ListOptions) (runtime.Object, error)
List(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (runtime.Object, error)
}
// Exporter is an object that knows how to strip a RESTful resource for export
@ -152,7 +153,7 @@ type CollectionDeleter interface {
// them or return an invalid request error.
// DeleteCollection may not be atomic - i.e. it may delete some objects and still
// return an error after it. On success, returns a list of deleted objects.
DeleteCollection(ctx genericapirequest.Context, options *api.DeleteOptions, listOptions *api.ListOptions) (runtime.Object, error)
DeleteCollection(ctx genericapirequest.Context, options *api.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error)
}
// Creater is an object that can create an instance of a RESTful object.
@ -225,7 +226,7 @@ type Watcher interface {
// are supported; an error should be returned if 'field' tries to select on a field that
// isn't supported. 'resourceVersion' allows for continuing/starting a watch at a
// particular version.
Watch(ctx genericapirequest.Context, options *api.ListOptions) (watch.Interface, error)
Watch(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
}
// StandardStorage is an interface covering the common verbs. Provided for testing whether a

View File

@ -24,6 +24,7 @@ import (
"time"
"k8s.io/apimachinery/pkg/api/errors"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/fields"
@ -1196,7 +1197,7 @@ func (t *Tester) testListMatchLabels(obj runtime.Object, assignFn AssignFunc) {
filtered := []runtime.Object{objs[1]}
selector := labels.SelectorFromSet(labels.Set(testLabels))
options := &api.ListOptions{LabelSelector: selector}
options := &metainternalversion.ListOptions{LabelSelector: selector}
listObj, err := t.storage.(rest.Lister).List(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -1238,7 +1239,7 @@ func (t *Tester) testWatchFields(obj runtime.Object, emitFn EmitFunc, fieldsPass
for _, field := range fieldsPass {
for _, action := range actions {
options := &api.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"}
options := &metainternalversion.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"}
watcher, err := t.storage.(rest.Watcher).Watch(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v, %v", err, action)
@ -1262,7 +1263,7 @@ func (t *Tester) testWatchFields(obj runtime.Object, emitFn EmitFunc, fieldsPass
for _, field := range fieldsFail {
for _, action := range actions {
options := &api.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"}
options := &metainternalversion.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"}
watcher, err := t.storage.(rest.Watcher).Watch(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -1287,7 +1288,7 @@ func (t *Tester) testWatchLabels(obj runtime.Object, emitFn EmitFunc, labelsPass
for _, label := range labelsPass {
for _, action := range actions {
options := &api.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"}
options := &metainternalversion.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"}
watcher, err := t.storage.(rest.Watcher).Watch(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v", err)
@ -1310,7 +1311,7 @@ func (t *Tester) testWatchLabels(obj runtime.Object, emitFn EmitFunc, labelsPass
for _, label := range labelsFail {
for _, action := range actions {
options := &api.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"}
options := &metainternalversion.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"}
watcher, err := t.storage.(rest.Watcher).Watch(ctx, options)
if err != nil {
t.Errorf("unexpected error: %v", err)

View File

@ -59,6 +59,10 @@ type APIGroupInfo struct {
// If nil, defaults to groupMeta.GroupVersion.
// TODO: Remove this when https://github.com/kubernetes/kubernetes/issues/19018 is fixed.
OptionsExternalVersion *schema.GroupVersion
// MetaGroupVersion defaults to "meta.k8s.io/v1" and is the scheme group version used to decode
// common API implementations like ListOptions. Future changes will allow this to vary by group
// version (for when the inevitable meta/v2 group emerges).
MetaGroupVersion *schema.GroupVersion
// Scheme includes all of the types used by this group and how to convert between them (or
// to convert objects from outside of this group that are accepted in this API).
@ -322,7 +326,8 @@ func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupVersion schema.GroupVersion) *genericapi.APIGroupVersion {
return &genericapi.APIGroupVersion{
GroupVersion: groupVersion,
GroupVersion: groupVersion,
MetaGroupVersion: apiGroupInfo.MetaGroupVersion,
ParameterCodec: apiGroupInfo.ParameterCodec,
Serializer: apiGroupInfo.NegotiatedSerializer,