Move REST* interfaces into pkg/api/rest

Dependency chain is now api -> api/rest -> apiserver.  Makes the
interfaces much cleaner to read, and cleans up some inconsistenties
that crept in along the way.
This commit is contained in:
Clayton Coleman
2015-03-21 12:24:16 -04:00
parent df672504c2
commit d46087db50
30 changed files with 216 additions and 187 deletions

View File

@@ -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"
@@ -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
@@ -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)
@@ -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 {
@@ -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

View File

@@ -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"
@@ -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

View File

@@ -34,6 +34,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
apierrs "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/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@@ -124,26 +125,26 @@ type defaultAPIServer struct {
}
// uses the default settings
func handle(storage map[string]RESTStorage) http.Handler {
func handle(storage map[string]rest.Storage) http.Handler {
return handleInternal(storage, admissionControl, mapper, selfLinker)
}
// tests with a deny admission controller
func handleDeny(storage map[string]RESTStorage) http.Handler {
func handleDeny(storage map[string]rest.Storage) http.Handler {
return handleInternal(storage, deny.NewAlwaysDeny(), mapper, selfLinker)
}
// tests using the new namespace scope mechanism
func handleNamespaced(storage map[string]RESTStorage) http.Handler {
func handleNamespaced(storage map[string]rest.Storage) http.Handler {
return handleInternal(storage, admissionControl, namespaceMapper, selfLinker)
}
// tests using a custom self linker
func handleLinker(storage map[string]RESTStorage, selfLinker runtime.SelfLinker) http.Handler {
func handleLinker(storage map[string]rest.Storage, selfLinker runtime.SelfLinker) http.Handler {
return handleInternal(storage, admissionControl, mapper, selfLinker)
}
func handleInternal(storage map[string]RESTStorage, admissionControl admission.Interface, mapper meta.RESTMapper, selfLinker runtime.SelfLinker) http.Handler {
func handleInternal(storage map[string]rest.Storage, admissionControl admission.Interface, mapper meta.RESTMapper, selfLinker runtime.SelfLinker) http.Handler {
group := &APIGroupVersion{
Storage: storage,
@@ -365,7 +366,7 @@ func TestNotFound(t *testing.T) {
"watch missing storage": {"GET", "/api/version/watch/", http.StatusNotFound},
"watch with bad method": {"POST", "/api/version/watch/foo/bar", http.StatusMethodNotAllowed},
}
handler := handle(map[string]RESTStorage{
handler := handle(map[string]rest.Storage{
"foo": &SimpleRESTStorage{},
})
server := httptest.NewServer(handler)
@@ -395,7 +396,7 @@ func (UnimplementedRESTStorage) New() runtime.Object {
return &Simple{}
}
// TestUnimplementedRESTStorage ensures that if a RESTStorage does not implement a given
// TestUnimplementedRESTStorage ensures that if a rest.Storage does not implement a given
// method, that it is literally not registered with the server. In the past,
// we registered everything, and returned method not supported if it didn't support
// a verb. Now we literally do not register a storage if it does not implement anything.
@@ -417,7 +418,7 @@ func TestUnimplementedRESTStorage(t *testing.T) {
"proxy object": {"GET", "/api/version/proxy/foo/bar", http.StatusNotFound},
"redirect object": {"GET", "/api/version/redirect/foo/bar", http.StatusNotFound},
}
handler := handle(map[string]RESTStorage{
handler := handle(map[string]rest.Storage{
"foo": UnimplementedRESTStorage{},
})
server := httptest.NewServer(handler)
@@ -444,7 +445,7 @@ func TestUnimplementedRESTStorage(t *testing.T) {
}
func TestVersion(t *testing.T) {
handler := handle(map[string]RESTStorage{})
handler := handle(map[string]rest.Storage{})
server := httptest.NewServer(handler)
defer server.Close()
client := http.Client{}
@@ -487,7 +488,7 @@ func TestList(t *testing.T) {
{"/api/version/simple", "", "/api/version/simple", false},
}
for i, testCase := range testCases {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{expectedResourceNamespace: testCase.namespace}
storage["simple"] = &simpleStorage
selfLinker := &setTestSelfLinker{
@@ -525,7 +526,7 @@ func TestList(t *testing.T) {
}
func TestErrorList(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{
errors: map[string]error{"list": fmt.Errorf("test Error")},
}
@@ -545,7 +546,7 @@ func TestErrorList(t *testing.T) {
}
func TestNonEmptyList(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{
list: []Simple{
{
@@ -593,7 +594,7 @@ func TestNonEmptyList(t *testing.T) {
}
func TestSelfLinkSkipsEmptyName(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{
list: []Simple{
{
@@ -640,7 +641,7 @@ func TestSelfLinkSkipsEmptyName(t *testing.T) {
}
func TestGet(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{
item: Simple{
Other: "foo",
@@ -679,7 +680,7 @@ func TestGet(t *testing.T) {
}
func TestGetAlternateSelfLink(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{
item: Simple{
Other: "foo",
@@ -717,7 +718,7 @@ func TestGetAlternateSelfLink(t *testing.T) {
}
func TestGetNamespaceSelfLink(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{
item: Simple{
Other: "foo",
@@ -754,7 +755,7 @@ func TestGetNamespaceSelfLink(t *testing.T) {
}
}
func TestGetMissing(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{
errors: map[string]error{"get": apierrs.NewNotFound("simple", "id")},
}
@@ -774,7 +775,7 @@ func TestGetMissing(t *testing.T) {
}
func TestDelete(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -797,7 +798,7 @@ func TestDelete(t *testing.T) {
}
func TestDeleteWithOptions(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -834,11 +835,11 @@ func TestDeleteWithOptions(t *testing.T) {
}
func TestLegacyDelete(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = LegacyRESTStorage{&simpleStorage}
var _ RESTDeleter = storage["simple"].(LegacyRESTStorage)
var _ rest.Deleter = storage["simple"].(LegacyRESTStorage)
handler := handle(storage)
server := httptest.NewServer(handler)
defer server.Close()
@@ -861,7 +862,7 @@ func TestLegacyDelete(t *testing.T) {
}
func TestLegacyDeleteIgnoresOptions(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = LegacyRESTStorage{&simpleStorage}
@@ -893,7 +894,7 @@ func TestLegacyDeleteIgnoresOptions(t *testing.T) {
}
func TestDeleteInvokesAdmissionControl(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -913,7 +914,7 @@ func TestDeleteInvokesAdmissionControl(t *testing.T) {
}
func TestDeleteMissing(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
ID := "id"
simpleStorage := SimpleRESTStorage{
errors: map[string]error{"delete": apierrs.NewNotFound("simple", ID)},
@@ -936,7 +937,7 @@ func TestDeleteMissing(t *testing.T) {
}
func TestPatch(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
ID := "id"
item := &Simple{
ObjectMeta: api.ObjectMeta{
@@ -973,7 +974,7 @@ func TestPatch(t *testing.T) {
}
func TestPatchRequiresMatchingName(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
ID := "id"
item := &Simple{
ObjectMeta: api.ObjectMeta{
@@ -1000,7 +1001,7 @@ func TestPatchRequiresMatchingName(t *testing.T) {
}
func TestUpdate(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -1043,7 +1044,7 @@ func TestUpdate(t *testing.T) {
}
func TestUpdateInvokesAdmissionControl(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -1076,7 +1077,7 @@ func TestUpdateInvokesAdmissionControl(t *testing.T) {
}
func TestUpdateRequiresMatchingName(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -1105,7 +1106,7 @@ func TestUpdateRequiresMatchingName(t *testing.T) {
}
func TestUpdateAllowsMissingNamespace(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -1138,7 +1139,7 @@ func TestUpdateAllowsMissingNamespace(t *testing.T) {
// when the object name and namespace can't be retrieved, skip name checking
func TestUpdateAllowsMismatchedNamespaceOnError(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -1179,7 +1180,7 @@ func TestUpdateAllowsMismatchedNamespaceOnError(t *testing.T) {
}
func TestUpdatePreventsMismatchedNamespace(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
simpleStorage := SimpleRESTStorage{}
ID := "id"
storage["simple"] = &simpleStorage
@@ -1212,7 +1213,7 @@ func TestUpdatePreventsMismatchedNamespace(t *testing.T) {
}
func TestUpdateMissing(t *testing.T) {
storage := map[string]RESTStorage{}
storage := map[string]rest.Storage{}
ID := "id"
simpleStorage := SimpleRESTStorage{
errors: map[string]error{"update": apierrs.NewNotFound("simple", ID)},
@@ -1246,7 +1247,7 @@ func TestUpdateMissing(t *testing.T) {
}
func TestCreateNotFound(t *testing.T) {
handler := handle(map[string]RESTStorage{
handler := handle(map[string]rest.Storage{
"simple": &SimpleRESTStorage{
// storage.Create can fail with not found error in theory.
// See https://github.com/GoogleCloudPlatform/kubernetes/pull/486#discussion_r15037092.
@@ -1275,7 +1276,7 @@ func TestCreateNotFound(t *testing.T) {
}
func TestCreateChecksDecode(t *testing.T) {
handler := handle(map[string]RESTStorage{"simple": &SimpleRESTStorage{}})
handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
server := httptest.NewServer(handler)
defer server.Close()
client := http.Client{}
@@ -1299,7 +1300,7 @@ func TestCreateChecksDecode(t *testing.T) {
}
func TestUpdateChecksDecode(t *testing.T) {
handler := handle(map[string]RESTStorage{"simple": &SimpleRESTStorage{}})
handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
server := httptest.NewServer(handler)
defer server.Close()
client := http.Client{}
@@ -1367,7 +1368,7 @@ func TestCreate(t *testing.T) {
namespace: "default",
expectedSet: "/api/version/foo/bar?namespace=default",
}
handler := handleLinker(map[string]RESTStorage{"foo": &storage}, selfLinker)
handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker)
server := httptest.NewServer(handler)
defer server.Close()
client := http.Client{}
@@ -1423,7 +1424,7 @@ func TestCreateInNamespace(t *testing.T) {
namespace: "other",
expectedSet: "/api/version/foo/bar?namespace=other",
}
handler := handleLinker(map[string]RESTStorage{"foo": &storage}, selfLinker)
handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker)
server := httptest.NewServer(handler)
defer server.Close()
client := http.Client{}
@@ -1479,7 +1480,7 @@ func TestCreateInvokesAdmissionControl(t *testing.T) {
namespace: "other",
expectedSet: "/api/version/foo/bar?namespace=other",
}
handler := handleInternal(map[string]RESTStorage{"foo": &storage}, deny.NewAlwaysDeny(), mapper, selfLinker)
handler := handleInternal(map[string]rest.Storage{"foo": &storage}, deny.NewAlwaysDeny(), mapper, selfLinker)
server := httptest.NewServer(handler)
defer server.Close()
client := http.Client{}
@@ -1539,7 +1540,7 @@ func TestDelayReturnsError(t *testing.T) {
return nil, apierrs.NewAlreadyExists("foo", "bar")
},
}
handler := handle(map[string]RESTStorage{"foo": &storage})
handler := handle(map[string]rest.Storage{"foo": &storage})
server := httptest.NewServer(handler)
defer server.Close()
@@ -1603,7 +1604,7 @@ func TestCreateTimeout(t *testing.T) {
return obj, nil
},
}
handler := handle(map[string]RESTStorage{
handler := handle(map[string]rest.Storage{
"foo": &storage,
})
server := httptest.NewServer(handler)
@@ -1637,7 +1638,7 @@ func TestCORSAllowedOrigins(t *testing.T) {
}
handler := CORS(
handle(map[string]RESTStorage{}),
handle(map[string]rest.Storage{}),
allowedOriginRegexps, nil, nil, "true",
)
server := httptest.NewServer(handler)

View File

@@ -14,5 +14,5 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package apiserver contains the code that provides a RESTful api service.
// Package apiserver contains the code that provides a rest.ful api service.
package apiserver

View File

@@ -1,132 +0,0 @@
/*
Copyright 2014 Google Inc. 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 apiserver
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
// 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 {
// 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 {
// 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.
// 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)
}
type RESTDeleter 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)
}
type RESTGracefulDeleter 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)
}
// GracefulDeleteAdapter adapts the RESTDeleter interface to RESTGracefulDeleter
type GracefulDeleteAdapter struct {
RESTDeleter
}
// 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)
}
type RESTCreater 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
// Create creates a new version of a resource.
Create(ctx api.Context, obj runtime.Object) (runtime.Object, error)
}
type RESTUpdater 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
// Update finds a resource in the storage and updates it. Some implementations
// may allow updates creates the object - they should set the created boolean
// to true.
Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error)
}
type RESTPatcher interface {
RESTGetter
RESTUpdater
}
// 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
// May be set true to indicate that the Update operation resulted in the object
// being created.
Created bool
}
// ResourceWatcher should be implemented by all RESTStorage objects that
// want to offer the ability to watch for changes through the watch api.
type ResourceWatcher 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)
}
// 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)
}

View File

@@ -32,6 +32,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@@ -79,7 +80,7 @@ var tagsToAttrs = map[string]util.StringSet{
// specified by items implementing Redirector.
type ProxyHandler struct {
prefix string
storage map[string]RESTStorage
storage map[string]rest.Storage
codec runtime.Codec
context api.RequestContextMapper
apiRequestInfoResolver *APIRequestInfoResolver
@@ -112,15 +113,15 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
id := parts[1]
rest := ""
remainder := ""
if len(parts) > 2 {
proxyParts := parts[2:]
rest = strings.Join(proxyParts, "/")
remainder = strings.Join(proxyParts, "/")
if strings.HasSuffix(req.URL.Path, "/") {
// The original path had a trailing slash, which has been stripped
// by KindAndNamespace(). We should add it back because some
// servers (like etcd) require it.
rest = rest + "/"
remainder = remainder + "/"
}
}
storage, ok := r.storage[resource]
@@ -132,7 +133,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
apiResource = resource
redirector, ok := storage.(Redirector)
redirector, ok := storage.(rest.Redirector)
if !ok {
httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource)
httpCode = errorJSON(errors.NewMethodNotSupported(resource, "proxy"), r.codec, w)
@@ -160,7 +161,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// hosts for paths.
destURL.Host = location
}
destURL.Path = rest
destURL.Path = remainder
destURL.RawQuery = req.URL.RawQuery
newReq, err := http.NewRequest(req.Method, destURL.String(), req.Body)
if err != nil {

View File

@@ -28,6 +28,7 @@ import (
"strings"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"golang.org/x/net/html"
"golang.org/x/net/websocket"
)
@@ -280,10 +281,10 @@ func TestProxy(t *testing.T) {
expectedResourceNamespace: item.reqNamespace,
}
namespaceHandler := handleNamespaced(map[string]RESTStorage{"foo": simpleStorage})
namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
namespaceServer := httptest.NewServer(namespaceHandler)
defer namespaceServer.Close()
legacyNamespaceHandler := handle(map[string]RESTStorage{"foo": simpleStorage})
legacyNamespaceHandler := handle(map[string]rest.Storage{"foo": simpleStorage})
legacyNamespaceServer := httptest.NewServer(legacyNamespaceHandler)
defer legacyNamespaceServer.Close()
@@ -340,7 +341,7 @@ func TestProxyUpgrade(t *testing.T) {
expectedResourceNamespace: "myns",
}
namespaceHandler := handleNamespaced(map[string]RESTStorage{"foo": simpleStorage})
namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
server := httptest.NewServer(namespaceHandler)
defer server.Close()

View File

@@ -23,12 +23,13 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
type RedirectHandler struct {
storage map[string]RESTStorage
storage map[string]rest.Storage
codec runtime.Codec
context api.RequestContextMapper
apiRequestInfoResolver *APIRequestInfoResolver
@@ -71,7 +72,7 @@ func (r *RedirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
apiResource = resource
redirector, ok := storage.(Redirector)
redirector, ok := storage.(rest.Redirector)
if !ok {
httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource)
httpCode = errorJSON(errors.NewMethodNotSupported(resource, "redirect"), r.codec, w)

View File

@@ -22,6 +22,8 @@ import (
"net/http/httptest"
"net/url"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
)
func TestRedirect(t *testing.T) {
@@ -29,7 +31,7 @@ func TestRedirect(t *testing.T) {
errors: map[string]error{},
expectedResourceNamespace: "default",
}
handler := handle(map[string]RESTStorage{"foo": simpleStorage})
handler := handle(map[string]rest.Storage{"foo": simpleStorage})
server := httptest.NewServer(handler)
defer server.Close()
@@ -80,7 +82,7 @@ func TestRedirectWithNamespaces(t *testing.T) {
errors: map[string]error{},
expectedResourceNamespace: "other",
}
handler := handleNamespaced(map[string]RESTStorage{"foo": simpleStorage})
handler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
server := httptest.NewServer(handler)
defer server.Close()

View File

@@ -26,6 +26,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@@ -58,8 +59,8 @@ type ScopeNamer interface {
GenerateListLink(req *restful.Request) (path, query string, err error)
}
// GetResource returns a function that handles retrieving a single resource from a RESTStorage object.
func GetResource(r RESTGetter, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec) restful.RouteFunction {
// GetResource returns a function that handles retrieving a single resource from a rest.Storage object.
func GetResource(r rest.Getter, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
namespace, name, err := namer.Name(req)
@@ -101,8 +102,8 @@ func parseSelectorQueryParams(query url.Values, version, apiResource string) (la
return label, field, nil
}
// ListResource returns a function that handles retrieving a list of resources from a RESTStorage object.
func ListResource(r RESTLister, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, version, apiResource string) restful.RouteFunction {
// ListResource returns a function that handles retrieving a list of resources from a rest.Storage object.
func ListResource(r rest.Lister, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, version, apiResource string) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
@@ -134,7 +135,7 @@ func ListResource(r RESTLister, ctxFn ContextFunc, namer ScopeNamer, codec runti
}
// CreateResource returns a function that will handle a resource creation.
func CreateResource(r RESTCreater, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, typer runtime.ObjectTyper, resource string, admit admission.Interface) restful.RouteFunction {
func CreateResource(r rest.Creater, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, typer runtime.ObjectTyper, resource string, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
@@ -191,7 +192,7 @@ func CreateResource(r RESTCreater, ctxFn ContextFunc, namer ScopeNamer, codec ru
// PatchResource returns a function that will handle a resource patch
// TODO: Eventually PatchResource should just use AtomicUpdate and this routine should be a bit cleaner
func PatchResource(r RESTPatcher, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, typer runtime.ObjectTyper, resource string, admit admission.Interface) restful.RouteFunction {
func PatchResource(r rest.Patcher, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, typer runtime.ObjectTyper, resource string, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
@@ -266,7 +267,7 @@ func PatchResource(r RESTPatcher, ctxFn ContextFunc, namer ScopeNamer, codec run
}
// UpdateResource returns a function that will handle a resource update
func UpdateResource(r RESTUpdater, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, typer runtime.ObjectTyper, resource string, admit admission.Interface) restful.RouteFunction {
func UpdateResource(r rest.Updater, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, typer runtime.ObjectTyper, resource string, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
@@ -330,7 +331,7 @@ func UpdateResource(r RESTUpdater, ctxFn ContextFunc, namer ScopeNamer, codec ru
}
// DeleteResource returns a function that will handle a resource deletion
func DeleteResource(r RESTGracefulDeleter, checkBody bool, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, resource, kind string, admit admission.Interface) restful.RouteFunction {
func DeleteResource(r rest.GracefulDeleter, checkBody bool, ctxFn ContextFunc, namer ScopeNamer, codec runtime.Codec, resource, kind string, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
@@ -376,7 +377,7 @@ func DeleteResource(r RESTGracefulDeleter, checkBody bool, ctxFn ContextFunc, na
return
}
// if the RESTDeleter returns a nil object, fill out a status. Callers may return a valid
// if the rest.Deleter returns a nil object, fill out a status. Callers may return a valid
// object with the response.
if result == nil {
result = &api.Status{

View File

@@ -26,6 +26,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@@ -36,7 +37,7 @@ import (
)
type WatchHandler struct {
storage map[string]RESTStorage
storage map[string]rest.Storage
codec runtime.Codec
linker runtime.SelfLinker
info *APIRequestInfoResolver
@@ -90,7 +91,7 @@ func (h *WatchHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
apiResource = requestInfo.Resource
watcher, ok := storage.(ResourceWatcher)
watcher, ok := storage.(rest.Watcher)
if !ok {
httpCode = errorJSON(errors.NewMethodNotSupported(requestInfo.Resource, "watch"), h.codec, w)
return

View File

@@ -25,6 +25,7 @@ import (
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@@ -49,8 +50,8 @@ var watchTestTable = []struct {
func TestWatchWebsocket(t *testing.T) {
simpleStorage := &SimpleRESTStorage{}
_ = ResourceWatcher(simpleStorage) // Give compile error if this doesn't work.
handler := handle(map[string]RESTStorage{"foo": simpleStorage})
_ = rest.Watcher(simpleStorage) // Give compile error if this doesn't work.
handler := handle(map[string]rest.Storage{"foo": simpleStorage})
server := httptest.NewServer(handler)
defer server.Close()
@@ -102,7 +103,7 @@ func TestWatchWebsocket(t *testing.T) {
func TestWatchHTTP(t *testing.T) {
simpleStorage := &SimpleRESTStorage{}
handler := handle(map[string]RESTStorage{"foo": simpleStorage})
handler := handle(map[string]rest.Storage{"foo": simpleStorage})
server := httptest.NewServer(handler)
defer server.Close()
client := http.Client{}
@@ -167,7 +168,7 @@ func TestWatchParamParsing(t *testing.T) {
return label, value, nil
})
simpleStorage := &SimpleRESTStorage{}
handler := handle(map[string]RESTStorage{"foo": simpleStorage})
handler := handle(map[string]rest.Storage{"foo": simpleStorage})
server := httptest.NewServer(handler)
defer server.Close()
@@ -237,7 +238,7 @@ func TestWatchParamParsing(t *testing.T) {
func TestWatchProtocolSelection(t *testing.T) {
simpleStorage := &SimpleRESTStorage{}
handler := handle(map[string]RESTStorage{"foo": simpleStorage})
handler := handle(map[string]rest.Storage{"foo": simpleStorage})
server := httptest.NewServer(handler)
defer server.Close()
defer server.CloseClientConnections()