Skip to content

Commit

Permalink
Merge pull request kubernetes#5758 from smarterclayton/get_input_para…
Browse files Browse the repository at this point in the history
…meters

Cleanup REST interfaces across the codebase
  • Loading branch information
derekwaynecarr committed Mar 23, 2015
2 parents a7f3d7d + 9bb797f commit 3864cad
Show file tree
Hide file tree
Showing 41 changed files with 307 additions and 317 deletions.
81 changes: 48 additions & 33 deletions pkg/apiserver/interfaces.go → pkg/api/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package apiserver
package rest

import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
Expand All @@ -26,60 +26,65 @@ import (

// RESTStorage is a generic interface for RESTful storage services.
// Resources which are exported to the RESTful API of apiserver need to implement this interface. It is expected
// that objects may implement any of the REST* interfaces.
// TODO: implement dynamic introspection (so GenericREST objects can indicate what they implement)
type RESTStorage interface {
// that objects may implement any of the below interfaces.
type Storage interface {
// New returns an empty object that can be used with Create and Update after request data has been put into it.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
New() runtime.Object
}

type RESTLister interface {
// Lister is an object that can retrieve resources that match the provided field and label criteria.
type Lister interface {
// NewList returns an empty object that can be used with the List call.
// 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.
List(ctx api.Context, label labels.Selector, field fields.Selector) (runtime.Object, error)
}

type RESTGetter interface {
// Get finds a resource in the storage by id and returns it.
// Getter is an object that can retrieve a named RESTful resource.
type Getter interface {
// Get finds a resource in the storage by name and returns it.
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
// returned error value err when the specified resource is not found.
Get(ctx api.Context, id string) (runtime.Object, error)
Get(ctx api.Context, name string) (runtime.Object, error)
}

type RESTDeleter interface {
// Deleter is an object that can delete a named RESTful resource.
type Deleter interface {
// Delete finds a resource in the storage and deletes it.
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
// returned error value err when the specified resource is not found.
// Delete *may* return the object that was deleted, or a status object indicating additional
// information about deletion.
Delete(ctx api.Context, id string) (runtime.Object, error)
Delete(ctx api.Context, name string) (runtime.Object, error)
}

type RESTGracefulDeleter interface {
// GracefulDeleter knows how to pass deletion options to allow delayed deletion of a
// RESTful object.
type GracefulDeleter interface {
// Delete finds a resource in the storage and deletes it.
// If options are provided, the resource will attempt to honor them or return an invalid
// request error.
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
// returned error value err when the specified resource is not found.
// Delete *may* return the object that was deleted, or a status object indicating additional
// information about deletion.
Delete(ctx api.Context, id string, options *api.DeleteOptions) (runtime.Object, error)
Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error)
}

// GracefulDeleteAdapter adapts the RESTDeleter interface to RESTGracefulDeleter
// GracefulDeleteAdapter adapts the Deleter interface to GracefulDeleter
type GracefulDeleteAdapter struct {
RESTDeleter
Deleter
}

// Delete implements RESTGracefulDeleter in terms of RESTDeleter
func (w GracefulDeleteAdapter) Delete(ctx api.Context, id string, options *api.DeleteOptions) (runtime.Object, error) {
return w.RESTDeleter.Delete(ctx, id)
// Delete implements RESTGracefulDeleter in terms of Deleter
func (w GracefulDeleteAdapter) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) {
return w.Deleter.Delete(ctx, name)
}

type RESTCreater interface {
// Creater is an object that can create an instance of a RESTful object.
type Creater interface {
// New returns an empty object that can be used with Create after request data has been put into it.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
New() runtime.Object
Expand All @@ -88,7 +93,8 @@ type RESTCreater interface {
Create(ctx api.Context, obj runtime.Object) (runtime.Object, error)
}

type RESTUpdater interface {
// Updater is an object that can update an instance of a RESTful object.
type Updater interface {
// New returns an empty object that can be used with Update after request data has been put into it.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
New() runtime.Object
Expand All @@ -99,34 +105,43 @@ type RESTUpdater interface {
Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error)
}

type RESTPatcher interface {
RESTGetter
RESTUpdater
// CreaterUpdater is a storage object that must support both create and update.
// Go prevents embedded interfaces that implement the same method.
type CreaterUpdater interface {
Creater
Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error)
}

// RESTResult indicates the result of a REST transformation.
type RESTResult struct {
// The result of this operation. May be nil if the operation has no meaningful
// result (like Delete)
runtime.Object
// CreaterUpdater must satisfy the Updater interface.
var _ Updater = CreaterUpdater(nil)

// May be set true to indicate that the Update operation resulted in the object
// being created.
Created bool
type Patcher interface {
Getter
Updater
}

// ResourceWatcher should be implemented by all RESTStorage objects that
// Watcher should be implemented by all Storage objects that
// want to offer the ability to watch for changes through the watch api.
type ResourceWatcher interface {
type Watcher interface {
// 'label' selects on labels; 'field' selects on the object's fields. Not all fields
// 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 api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
}

// StandardStorage is an interface covering the common verbs. Provided for testing whether a
// resource satisfies the normal storage methods. Use Storage when passing opaque storage objects.
type StandardStorage interface {
Getter
Lister
CreaterUpdater
GracefulDeleter
Watcher
}

// Redirector know how to return a remote resource's location.
type Redirector interface {
// ResourceLocation should return the remote location of the given resource, or an error.
ResourceLocation(ctx api.Context, id string) (remoteLocation string, err error)
ResourceLocation(ctx api.Context, name string) (remoteLocation string, err error)
}
32 changes: 16 additions & 16 deletions pkg/api/rest/resttest/resttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ import (

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

type Tester struct {
*testing.T
storage apiserver.RESTStorage
storage rest.Storage
storageError injectErrorFunc
clusterScope bool
}

type injectErrorFunc func(err error)

func New(t *testing.T, storage apiserver.RESTStorage, storageError injectErrorFunc) *Tester {
func New(t *testing.T, storage rest.Storage, storageError injectErrorFunc) *Tester {
return &Tester{
T: t,
storage: storage,
Expand Down Expand Up @@ -85,7 +85,7 @@ func (t *Tester) TestCreateResetsUserData(valid runtime.Object) {
objectMeta.UID = "bad-uid"
objectMeta.CreationTimestamp = now

obj, err := t.storage.(apiserver.RESTCreater).Create(api.NewDefaultContext(), valid)
obj, err := t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
Expand All @@ -111,7 +111,7 @@ func (t *Tester) TestCreateHasMetadata(valid runtime.Object) {
context = api.NewContext()
}

obj, err := t.storage.(apiserver.RESTCreater).Create(context, valid)
obj, err := t.storage.(rest.Creater).Create(context, valid)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
Expand All @@ -131,7 +131,7 @@ func (t *Tester) TestCreateGeneratesName(valid runtime.Object) {

objectMeta.GenerateName = "test-"

_, err = t.storage.(apiserver.RESTCreater).Create(api.NewDefaultContext(), valid)
_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
Expand All @@ -148,7 +148,7 @@ func (t *Tester) TestCreateGeneratesNameReturnsServerTimeout(valid runtime.Objec

objectMeta.GenerateName = "test-"
t.withStorageError(errors.NewAlreadyExists("kind", "thing"), func() {
_, err := t.storage.(apiserver.RESTCreater).Create(api.NewDefaultContext(), valid)
_, err := t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
if err == nil || !errors.IsServerTimeout(err) {
t.Fatalf("Unexpected error: %v", err)
}
Expand All @@ -158,7 +158,7 @@ func (t *Tester) TestCreateGeneratesNameReturnsServerTimeout(valid runtime.Objec
func (t *Tester) TestCreateInvokesValidation(invalid ...runtime.Object) {
for i, obj := range invalid {
ctx := api.NewDefaultContext()
_, err := t.storage.(apiserver.RESTCreater).Create(ctx, obj)
_, err := t.storage.(rest.Creater).Create(ctx, obj)
if !errors.IsInvalid(err) {
t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err)
}
Expand All @@ -173,7 +173,7 @@ func (t *Tester) TestCreateRejectsMismatchedNamespace(valid runtime.Object) {

objectMeta.Namespace = "not-default"

_, err = t.storage.(apiserver.RESTCreater).Create(api.NewDefaultContext(), valid)
_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
Expand All @@ -189,7 +189,7 @@ func (t *Tester) TestCreateRejectsNamespace(valid runtime.Object) {

objectMeta.Namespace = "not-default"

_, err = t.storage.(apiserver.RESTCreater).Create(api.NewDefaultContext(), valid)
_, err = t.storage.(rest.Creater).Create(api.NewDefaultContext(), valid)
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
Expand All @@ -210,11 +210,11 @@ func (t *Tester) TestDeleteNoGraceful(createFn func() runtime.Object, wasGracefu
}

ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
_, err = t.storage.(apiserver.RESTGracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10))
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if _, err := t.storage.(apiserver.RESTGetter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
t.Errorf("unexpected error, object should not exist: %v", err)
}
if wasGracefulFn() {
Expand All @@ -229,11 +229,11 @@ func (t *Tester) TestDeleteGracefulHasDefault(existing runtime.Object, expectedG
}

ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
_, err = t.storage.(apiserver.RESTGracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if _, err := t.storage.(apiserver.RESTGetter).Get(ctx, objectMeta.Name); err != nil {
if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); err != nil {
t.Errorf("unexpected error, object should exist: %v", err)
}
if !wasGracefulFn() {
Expand All @@ -248,11 +248,11 @@ func (t *Tester) TestDeleteGracefulUsesZeroOnNil(existing runtime.Object, expect
}

ctx := api.WithNamespace(api.NewContext(), objectMeta.Namespace)
_, err = t.storage.(apiserver.RESTGracefulDeleter).Delete(ctx, objectMeta.Name, nil)
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if _, err := t.storage.(apiserver.RESTGetter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
t.Errorf("unexpected error, object should exist: %v", err)
}
}
25 changes: 13 additions & 12 deletions pkg/apiserver/api_installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"

"github.com/emicklei/go-restful"
Expand Down Expand Up @@ -94,7 +95,7 @@ func (a *APIInstaller) newWebService() *restful.WebService {
return ws
}

func (a *APIInstaller) registerResourceHandlers(path string, storage RESTStorage, ws *restful.WebService, watchHandler, redirectHandler, proxyHandler http.Handler) error {
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, watchHandler, redirectHandler, proxyHandler http.Handler) error {
admit := a.group.Admit
context := a.group.Context

Expand All @@ -121,7 +122,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage RESTStorage
versionedObject := indirectArbitraryPointer(versionedPtr)

var versionedList interface{}
if lister, ok := storage.(RESTLister); ok {
if lister, ok := storage.(rest.Lister); ok {
list := lister.NewList()
_, listKind, err := a.group.Typer.ObjectVersionAndKind(list)
versionedListPtr, err := a.group.Creater.New(a.group.Version, listKind)
Expand All @@ -137,15 +138,15 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage RESTStorage
}

// what verbs are supported by the storage, used to know what verbs we support per path
creater, isCreater := storage.(RESTCreater)
lister, isLister := storage.(RESTLister)
getter, isGetter := storage.(RESTGetter)
deleter, isDeleter := storage.(RESTDeleter)
gracefulDeleter, isGracefulDeleter := storage.(RESTGracefulDeleter)
updater, isUpdater := storage.(RESTUpdater)
patcher, isPatcher := storage.(RESTPatcher)
_, isWatcher := storage.(ResourceWatcher)
_, isRedirector := storage.(Redirector)
creater, isCreater := storage.(rest.Creater)
lister, isLister := storage.(rest.Lister)
getter, isGetter := storage.(rest.Getter)
deleter, isDeleter := storage.(rest.Deleter)
gracefulDeleter, isGracefulDeleter := storage.(rest.GracefulDeleter)
updater, isUpdater := storage.(rest.Updater)
patcher, isPatcher := storage.(rest.Patcher)
_, isWatcher := storage.(rest.Watcher)
_, isRedirector := storage.(rest.Redirector)

var versionedDeleterObject runtime.Object
switch {
Expand All @@ -157,7 +158,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage RESTStorage
versionedDeleterObject = object
isDeleter = true
case isDeleter:
gracefulDeleter = GracefulDeleteAdapter{deleter}
gracefulDeleter = rest.GracefulDeleteAdapter{deleter}
}

var ctxFn ContextFunc
Expand Down
7 changes: 4 additions & 3 deletions pkg/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
Expand Down Expand Up @@ -90,12 +91,12 @@ type Mux interface {
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
}

// APIGroupVersion is a helper for exposing RESTStorage objects as http.Handlers via go-restful
// APIGroupVersion is a helper for exposing rest.Storage objects as http.Handlers via go-restful
// It handles URLs of the form:
// /${storage_key}[/${object_name}]
// Where 'storage_key' points to a RESTStorage object stored in storage.
// Where 'storage_key' points to a rest.Storage object stored in storage.
type APIGroupVersion struct {
Storage map[string]RESTStorage
Storage map[string]rest.Storage

Root string
Version string
Expand Down
Loading

0 comments on commit 3864cad

Please sign in to comment.