mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-09 13:12:20 +00:00
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:
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
@@ -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 {
|
||||
|
@@ -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()
|
||||
|
@@ -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)
|
||||
|
@@ -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()
|
||||
|
||||
|
@@ -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{
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
Reference in New Issue
Block a user