mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Split RESTStorage into separate interfaces
Omit unimplemented interfaces from Swagger
This commit is contained in:
parent
a52b216324
commit
22c99c98e2
@ -129,62 +129,70 @@ func registerResourceHandlers(ws *restful.WebService, version string, path strin
|
||||
nameParam := ws.PathParameter("name", "name of the "+kind).DataType("string")
|
||||
namespaceParam := ws.PathParameter("namespace", "object name and auth scope, such as for teams and projects").DataType("string")
|
||||
|
||||
ws.Route(
|
||||
addParamIf(
|
||||
ws.POST(path).To(h).
|
||||
Doc("create a "+kind).
|
||||
Operation("create"+kind).
|
||||
Reads(versionedObject), // from the request
|
||||
namespaceParam, namespaceScope))
|
||||
|
||||
// TODO: This seems like a hack. Add NewList() to storage?
|
||||
listKind := kind + "List"
|
||||
if _, ok := kinds[listKind]; !ok {
|
||||
glog.V(1).Infof("no list type: %v\n", listKind)
|
||||
createRoute := ws.POST(path).To(h).
|
||||
Doc("create a " + kind).
|
||||
Operation("create" + kind).
|
||||
Reads(versionedObject) // from the request
|
||||
addParamIf(createRoute, namespaceParam, namespaceScope)
|
||||
if _, ok := storage.(RESTCreater); ok {
|
||||
ws.Route(createRoute.Reads(versionedObject)) // from the request
|
||||
} else {
|
||||
versionedListPtr, err := api.Scheme.New(version, listKind)
|
||||
if err != nil {
|
||||
glog.Errorf("error making list: %v\n", err)
|
||||
} else {
|
||||
versionedList := indirectArbitraryPointer(versionedListPtr)
|
||||
glog.V(3).Infoln("type: ", reflect.TypeOf(versionedList))
|
||||
ws.Route(
|
||||
addParamIf(
|
||||
ws.GET(path).To(h).
|
||||
Doc("list objects of kind "+kind).
|
||||
Operation("list"+kind).
|
||||
Returns(http.StatusOK, "OK", versionedList),
|
||||
namespaceParam, namespaceScope))
|
||||
}
|
||||
ws.Route(createRoute.Returns(http.StatusMethodNotAllowed, "creating objects is not supported", nil))
|
||||
}
|
||||
|
||||
ws.Route(
|
||||
addParamIf(
|
||||
ws.GET(path+"/{name}").To(h).
|
||||
Doc("read the specified "+kind).
|
||||
Operation("read"+kind).
|
||||
Param(nameParam).
|
||||
Writes(versionedObject), // on the response
|
||||
namespaceParam, namespaceScope))
|
||||
listRoute := ws.GET(path).To(h).
|
||||
Doc("list objects of kind " + kind).
|
||||
Operation("list" + kind)
|
||||
addParamIf(listRoute, namespaceParam, namespaceScope)
|
||||
if lister, ok := storage.(RESTLister); ok {
|
||||
list := lister.NewList()
|
||||
_, listKind, err := api.Scheme.ObjectVersionAndKind(list)
|
||||
versionedListPtr, err := api.Scheme.New(version, listKind)
|
||||
if err != nil {
|
||||
glog.Errorf("error making list object: %v\n", err)
|
||||
return
|
||||
}
|
||||
versionedList := indirectArbitraryPointer(versionedListPtr)
|
||||
glog.V(3).Infoln("type: ", reflect.TypeOf(versionedList))
|
||||
ws.Route(listRoute.Returns(http.StatusOK, "OK", versionedList))
|
||||
} else {
|
||||
ws.Route(listRoute.Returns(http.StatusMethodNotAllowed, "listing objects is not supported", nil))
|
||||
}
|
||||
|
||||
ws.Route(
|
||||
addParamIf(
|
||||
ws.PUT(path+"/{name}").To(h).
|
||||
Doc("update the specified "+kind).
|
||||
Operation("update"+kind).
|
||||
Param(nameParam).
|
||||
Reads(versionedObject), // from the request
|
||||
namespaceParam, namespaceScope))
|
||||
getRoute := ws.GET(path + "/{name}").To(h).
|
||||
Doc("read the specified " + kind).
|
||||
Operation("read" + kind).
|
||||
Param(nameParam)
|
||||
addParamIf(getRoute, namespaceParam, namespaceScope)
|
||||
if _, ok := storage.(RESTGetter); ok {
|
||||
ws.Route(getRoute.Writes(versionedObject)) // on the response
|
||||
} else {
|
||||
ws.Route(ws.GET(path+"/{name}").To(h).
|
||||
Returns(http.StatusMethodNotAllowed, "reading individual objects is not supported", nil))
|
||||
}
|
||||
|
||||
updateRoute := ws.PUT(path + "/{name}").To(h).
|
||||
Doc("update the specified " + kind).
|
||||
Operation("update" + kind).
|
||||
Param(nameParam)
|
||||
addParamIf(updateRoute, namespaceParam, namespaceScope)
|
||||
if _, ok := storage.(RESTUpdater); ok {
|
||||
ws.Route(updateRoute.Reads(versionedObject)) // from the request
|
||||
} else {
|
||||
ws.Route(updateRoute.Returns(http.StatusMethodNotAllowed, "updating objects is not supported", nil))
|
||||
}
|
||||
|
||||
// TODO: Support PATCH
|
||||
|
||||
ws.Route(
|
||||
addParamIf(
|
||||
ws.DELETE(path+"/{name}").To(h).
|
||||
Doc("delete the specified "+kind).
|
||||
Operation("delete"+kind).
|
||||
Param(nameParam),
|
||||
namespaceParam, namespaceScope))
|
||||
deleteRoute := ws.DELETE(path + "/{name}").To(h).
|
||||
Doc("delete the specified " + kind).
|
||||
Operation("delete" + kind).
|
||||
Param(nameParam)
|
||||
addParamIf(deleteRoute, namespaceParam, namespaceScope)
|
||||
if _, ok := storage.(RESTDeleter); ok {
|
||||
ws.Route(deleteRoute)
|
||||
} else {
|
||||
ws.Route(deleteRoute.Returns(http.StatusMethodNotAllowed, "deleting objects is not supported", nil))
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the given param to the given route builder if shouldAdd is true. Does nothing if shouldAdd is false.
|
||||
|
@ -183,6 +183,10 @@ func (storage *SimpleRESTStorage) New() runtime.Object {
|
||||
return &Simple{}
|
||||
}
|
||||
|
||||
func (storage *SimpleRESTStorage) NewList() runtime.Object {
|
||||
return &SimpleList{}
|
||||
}
|
||||
|
||||
func (storage *SimpleRESTStorage) Create(ctx api.Context, obj runtime.Object) (<-chan RESTResult, error) {
|
||||
storage.created = obj.(*Simple)
|
||||
if err := storage.errors["create"]; err != nil {
|
||||
@ -288,6 +292,68 @@ func TestNotFound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type UnimplementedRESTStorage struct{}
|
||||
|
||||
func (UnimplementedRESTStorage) New() runtime.Object {
|
||||
return &Simple{}
|
||||
}
|
||||
|
||||
func TestMethodNotAllowed(t *testing.T) {
|
||||
type T struct {
|
||||
Method string
|
||||
Path string
|
||||
}
|
||||
cases := map[string]T{
|
||||
"GET object": {"GET", "/prefix/version/foo/bar"},
|
||||
"GET list": {"GET", "/prefix/version/foo"},
|
||||
"POST list": {"POST", "/prefix/version/foo"},
|
||||
"PUT object": {"PUT", "/prefix/version/foo/bar"},
|
||||
"DELETE object": {"DELETE", "/prefix/version/foo/bar"},
|
||||
//"watch list": {"GET", "/prefix/version/watch/foo"},
|
||||
//"watch object": {"GET", "/prefix/version/watch/foo/bar"},
|
||||
"proxy object": {"GET", "/prefix/version/proxy/foo/bar"},
|
||||
"redirect object": {"GET", "/prefix/version/redirect/foo/bar"},
|
||||
}
|
||||
handler := Handle(map[string]RESTStorage{
|
||||
"foo": UnimplementedRESTStorage{},
|
||||
}, codec, "/prefix", testVersion, selfLinker, admissionControl)
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
client := http.Client{}
|
||||
for k, v := range cases {
|
||||
request, err := http.NewRequest(v.Method, server.URL+v.Path, bytes.NewReader([]byte(`{"kind":"Simple","apiVersion":"version"}`)))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
defer response.Body.Close()
|
||||
data, _ := ioutil.ReadAll(response.Body)
|
||||
t.Logf("resp: %s", string(data))
|
||||
if response.StatusCode != http.StatusMethodNotAllowed {
|
||||
t.Errorf("%s: expected %d for %s, Got %s", k, http.StatusMethodNotAllowed, v.Method, string(data))
|
||||
continue
|
||||
}
|
||||
obj, err := codec.Decode(data)
|
||||
if err != nil {
|
||||
t.Errorf("%s: unexpected decode error: %v", k, err)
|
||||
continue
|
||||
}
|
||||
status, ok := obj.(*api.Status)
|
||||
if !ok {
|
||||
t.Errorf("%s: unexpected object: %#v", k, obj)
|
||||
continue
|
||||
}
|
||||
if status.Reason != api.StatusReasonMethodNotAllowed {
|
||||
t.Errorf("%s: unexpected status: %#v", k, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
handler := Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionControl)
|
||||
server := httptest.NewServer(handler)
|
||||
|
@ -24,28 +24,43 @@ 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.
|
||||
// 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, field labels.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(ctx api.Context, id string) (<-chan RESTResult, error)
|
||||
}
|
||||
|
||||
type RESTCreater interface {
|
||||
// Create creates a new version of a resource.
|
||||
Create(ctx api.Context, obj runtime.Object) (<-chan RESTResult, error)
|
||||
}
|
||||
|
||||
type RESTUpdater interface {
|
||||
// Update finds a resource in the storage and updates it. Some implementations
|
||||
// may allow updates creates the object - they should set the Created flag of
|
||||
// the returned RESTResultto true. In the event of an asynchronous error returned
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
@ -106,7 +107,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
redirector, ok := storage.(Redirector)
|
||||
if !ok {
|
||||
httplog.LogOf(req, w).Addf("'%v' is not a redirector", kind)
|
||||
notFound(w, req)
|
||||
errorJSON(errors.NewMethodNotSupported(kind, "proxy"), r.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
)
|
||||
@ -53,7 +54,7 @@ func (r *RedirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
redirector, ok := storage.(Redirector)
|
||||
if !ok {
|
||||
httplog.LogOf(req, w).Addf("'%v' is not a redirector", kind)
|
||||
notFound(w, req)
|
||||
errorJSON(errors.NewMethodNotSupported(kind, "redirect"), r.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
|
||||
@ -48,14 +48,13 @@ func (h *RESTHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
notFound(w, req)
|
||||
return
|
||||
}
|
||||
storage := h.storage[kind]
|
||||
if storage == nil {
|
||||
httplog.LogOf(req, w).Addf("'%v' has no storage object", kind)
|
||||
storage, ok := h.storage[kind]
|
||||
if !ok {
|
||||
notFound(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
h.handleRESTStorage(parts, req, w, storage, namespace)
|
||||
h.handleRESTStorage(parts, req, w, storage, namespace, kind)
|
||||
}
|
||||
|
||||
// Sets the SelfLink field of the object.
|
||||
@ -148,7 +147,7 @@ func curry(f func(runtime.Object, *http.Request) error, req *http.Request) func(
|
||||
// sync=[false|true] Synchronous request (only applies to create, update, delete operations)
|
||||
// timeout=<duration> Timeout for synchronous requests, only applies if sync=true
|
||||
// labels=<label-selector> Used for filtering list operations
|
||||
func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w http.ResponseWriter, storage RESTStorage, namespace string) {
|
||||
func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w http.ResponseWriter, storage RESTStorage, namespace, kind string) {
|
||||
ctx := api.WithNamespace(api.NewContext(), namespace)
|
||||
sync := req.URL.Query().Get("sync") == "true"
|
||||
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
||||
@ -166,7 +165,12 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
}
|
||||
list, err := storage.List(ctx, label, field)
|
||||
lister, ok := storage.(RESTLister)
|
||||
if !ok {
|
||||
errorJSON(errors.NewMethodNotSupported(kind, "list"), h.codec, w)
|
||||
return
|
||||
}
|
||||
list, err := lister.List(ctx, label, field)
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
@ -177,7 +181,12 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
||||
}
|
||||
writeJSON(http.StatusOK, h.codec, list, w)
|
||||
case 2:
|
||||
item, err := storage.Get(ctx, parts[1])
|
||||
getter, ok := storage.(RESTGetter)
|
||||
if !ok {
|
||||
errorJSON(errors.NewMethodNotSupported(kind, "get"), h.codec, w)
|
||||
return
|
||||
}
|
||||
item, err := getter.Get(ctx, parts[1])
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
@ -196,6 +205,12 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
||||
notFound(w, req)
|
||||
return
|
||||
}
|
||||
creater, ok := storage.(RESTCreater)
|
||||
if !ok {
|
||||
errorJSON(errors.NewMethodNotSupported(kind, "create"), h.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := readBody(req)
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
@ -215,7 +230,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
||||
return
|
||||
}
|
||||
|
||||
out, err := storage.Create(ctx, obj)
|
||||
out, err := creater.Create(ctx, obj)
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
@ -228,6 +243,11 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
||||
notFound(w, req)
|
||||
return
|
||||
}
|
||||
deleter, ok := storage.(RESTDeleter)
|
||||
if !ok {
|
||||
errorJSON(errors.NewMethodNotSupported(kind, "delete"), h.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
// invoke admission control
|
||||
err := h.admissionControl.Admit(admission.NewAttributesRecord(nil, namespace, parts[0], "DELETE"))
|
||||
@ -236,7 +256,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
||||
return
|
||||
}
|
||||
|
||||
out, err := storage.Delete(ctx, parts[1])
|
||||
out, err := deleter.Delete(ctx, parts[1])
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
@ -249,6 +269,12 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
||||
notFound(w, req)
|
||||
return
|
||||
}
|
||||
updater, ok := storage.(RESTUpdater)
|
||||
if !ok {
|
||||
errorJSON(errors.NewMethodNotSupported(kind, "create"), h.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := readBody(req)
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
@ -268,7 +294,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
||||
return
|
||||
}
|
||||
|
||||
out, err := storage.Update(ctx, obj)
|
||||
out, err := updater.Update(ctx, obj)
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
@ -98,34 +99,35 @@ func (h *WatchHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
notFound(w, req)
|
||||
return
|
||||
}
|
||||
if watcher, ok := storage.(ResourceWatcher); ok {
|
||||
label, field, resourceVersion, err := getWatchParams(req.URL.Query())
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
}
|
||||
watching, err := watcher.Watch(ctx, label, field, resourceVersion)
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: This is one watch per connection. We want to multiplex, so that
|
||||
// multiple watches of the same thing don't create two watches downstream.
|
||||
watchServer := &WatchServer{watching, h.codec, func(obj runtime.Object) {
|
||||
if err := h.setSelfLinkAddName(obj, req); err != nil {
|
||||
glog.Errorf("Failed to set self link for object %#v", obj)
|
||||
}
|
||||
}}
|
||||
if isWebsocketRequest(req) {
|
||||
websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(w), req)
|
||||
} else {
|
||||
watchServer.ServeHTTP(w, req)
|
||||
}
|
||||
watcher, ok := storage.(ResourceWatcher)
|
||||
if !ok {
|
||||
errorJSON(errors.NewMethodNotSupported(kind, "watch"), h.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
notFound(w, req)
|
||||
label, field, resourceVersion, err := getWatchParams(req.URL.Query())
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
}
|
||||
watching, err := watcher.Watch(ctx, label, field, resourceVersion)
|
||||
if err != nil {
|
||||
errorJSON(err, h.codec, w)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: This is one watch per connection. We want to multiplex, so that
|
||||
// multiple watches of the same thing don't create two watches downstream.
|
||||
watchServer := &WatchServer{watching, h.codec, func(obj runtime.Object) {
|
||||
if err := h.setSelfLinkAddName(obj, req); err != nil {
|
||||
glog.Errorf("Failed to set self link for object %#v", obj)
|
||||
}
|
||||
}}
|
||||
if isWebsocketRequest(req) {
|
||||
websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(w), req)
|
||||
} else {
|
||||
watchServer.ServeHTTP(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
// WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
@ -94,7 +95,7 @@ func (m *Master) createMasterServiceIfNeeded(serviceName string, port int) error
|
||||
}
|
||||
// Kids, don't do this at home: this is a hack. There's no good way to call the business
|
||||
// logic which lives in the REST object from here.
|
||||
c, err := m.storage["services"].Create(ctx, svc)
|
||||
c, err := m.storage["services"].(apiserver.RESTCreater).Create(ctx, svc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ import (
|
||||
// TODO: considering that the only difference between the various client types
|
||||
// and RESTStorage type is the type of the arguments, maybe use "go generate" to
|
||||
// write a specialized adaptor for every client type?
|
||||
//
|
||||
// TODO: this also means that pod and node API endpoints have to be colocated in the same
|
||||
// process
|
||||
func RESTStorageToNodes(storage apiserver.RESTStorage) client.NodesInterface {
|
||||
return &nodeAdaptor{storage}
|
||||
}
|
||||
@ -61,7 +64,7 @@ func (n *nodeAdaptor) Create(minion *api.Node) (*api.Node, error) {
|
||||
// List lists all the nodes in the cluster.
|
||||
func (n *nodeAdaptor) List() (*api.NodeList, error) {
|
||||
ctx := api.NewContext()
|
||||
obj, err := n.storage.List(ctx, labels.Everything(), labels.Everything())
|
||||
obj, err := n.storage.(apiserver.RESTLister).List(ctx, labels.Everything(), labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -71,7 +74,7 @@ func (n *nodeAdaptor) List() (*api.NodeList, error) {
|
||||
// Get gets an existing minion
|
||||
func (n *nodeAdaptor) Get(name string) (*api.Node, error) {
|
||||
ctx := api.NewContext()
|
||||
obj, err := n.storage.Get(ctx, name)
|
||||
obj, err := n.storage.(apiserver.RESTGetter).Get(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -122,6 +122,10 @@ func (*REST) New() runtime.Object {
|
||||
return &api.ReplicationController{}
|
||||
}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &api.ReplicationControllerList{}
|
||||
}
|
||||
|
||||
// Update replaces a given ReplicationController instance with an existing
|
||||
// instance in storage.registry.
|
||||
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
||||
|
@ -105,3 +105,7 @@ func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult,
|
||||
func (rs REST) New() runtime.Object {
|
||||
return &api.Endpoints{}
|
||||
}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &api.EndpointsList{}
|
||||
}
|
||||
|
@ -128,6 +128,10 @@ func (*REST) New() runtime.Object {
|
||||
return &api.Event{}
|
||||
}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &api.EventList{}
|
||||
}
|
||||
|
||||
// Update returns an error: Events are not mutable.
|
||||
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
||||
return nil, fmt.Errorf("not allowed: 'Event' objects are not mutable")
|
||||
|
@ -104,6 +104,10 @@ func (rs *REST) New() runtime.Object {
|
||||
return &api.Node{}
|
||||
}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &api.NodeList{}
|
||||
}
|
||||
|
||||
// Update satisfies the RESTStorage interface.
|
||||
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
||||
minion, ok := obj.(*api.Node)
|
||||
|
@ -161,6 +161,10 @@ func (*REST) New() runtime.Object {
|
||||
return &api.Pod{}
|
||||
}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &api.PodList{}
|
||||
}
|
||||
|
||||
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
||||
pod := obj.(*api.Pod)
|
||||
if !api.ValidNamespace(ctx, &pod.ObjectMeta) {
|
||||
|
@ -217,6 +217,10 @@ func (*REST) New() runtime.Object {
|
||||
return &api.Service{}
|
||||
}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &api.Service{}
|
||||
}
|
||||
|
||||
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
||||
service := obj.(*api.Service)
|
||||
if !api.ValidNamespace(ctx, &service.ObjectMeta) {
|
||||
|
Loading…
Reference in New Issue
Block a user