mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 16:29:21 +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")
|
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")
|
namespaceParam := ws.PathParameter("namespace", "object name and auth scope, such as for teams and projects").DataType("string")
|
||||||
|
|
||||||
ws.Route(
|
createRoute := ws.POST(path).To(h).
|
||||||
addParamIf(
|
Doc("create a " + kind).
|
||||||
ws.POST(path).To(h).
|
Operation("create" + kind).
|
||||||
Doc("create a "+kind).
|
Reads(versionedObject) // from the request
|
||||||
Operation("create"+kind).
|
addParamIf(createRoute, namespaceParam, namespaceScope)
|
||||||
Reads(versionedObject), // from the request
|
if _, ok := storage.(RESTCreater); ok {
|
||||||
namespaceParam, namespaceScope))
|
ws.Route(createRoute.Reads(versionedObject)) // from the request
|
||||||
|
|
||||||
// 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)
|
|
||||||
} else {
|
} else {
|
||||||
versionedListPtr, err := api.Scheme.New(version, listKind)
|
ws.Route(createRoute.Returns(http.StatusMethodNotAllowed, "creating objects is not supported", nil))
|
||||||
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(
|
listRoute := ws.GET(path).To(h).
|
||||||
addParamIf(
|
Doc("list objects of kind " + kind).
|
||||||
ws.GET(path+"/{name}").To(h).
|
Operation("list" + kind)
|
||||||
Doc("read the specified "+kind).
|
addParamIf(listRoute, namespaceParam, namespaceScope)
|
||||||
Operation("read"+kind).
|
if lister, ok := storage.(RESTLister); ok {
|
||||||
Param(nameParam).
|
list := lister.NewList()
|
||||||
Writes(versionedObject), // on the response
|
_, listKind, err := api.Scheme.ObjectVersionAndKind(list)
|
||||||
namespaceParam, namespaceScope))
|
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(
|
getRoute := ws.GET(path + "/{name}").To(h).
|
||||||
addParamIf(
|
Doc("read the specified " + kind).
|
||||||
ws.PUT(path+"/{name}").To(h).
|
Operation("read" + kind).
|
||||||
Doc("update the specified "+kind).
|
Param(nameParam)
|
||||||
Operation("update"+kind).
|
addParamIf(getRoute, namespaceParam, namespaceScope)
|
||||||
Param(nameParam).
|
if _, ok := storage.(RESTGetter); ok {
|
||||||
Reads(versionedObject), // from the request
|
ws.Route(getRoute.Writes(versionedObject)) // on the response
|
||||||
namespaceParam, namespaceScope))
|
} 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
|
// TODO: Support PATCH
|
||||||
|
deleteRoute := ws.DELETE(path + "/{name}").To(h).
|
||||||
ws.Route(
|
Doc("delete the specified " + kind).
|
||||||
addParamIf(
|
Operation("delete" + kind).
|
||||||
ws.DELETE(path+"/{name}").To(h).
|
Param(nameParam)
|
||||||
Doc("delete the specified "+kind).
|
addParamIf(deleteRoute, namespaceParam, namespaceScope)
|
||||||
Operation("delete"+kind).
|
if _, ok := storage.(RESTDeleter); ok {
|
||||||
Param(nameParam),
|
ws.Route(deleteRoute)
|
||||||
namespaceParam, namespaceScope))
|
} 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.
|
// 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{}
|
return &Simple{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (storage *SimpleRESTStorage) NewList() runtime.Object {
|
||||||
|
return &SimpleList{}
|
||||||
|
}
|
||||||
|
|
||||||
func (storage *SimpleRESTStorage) Create(ctx api.Context, obj runtime.Object) (<-chan RESTResult, error) {
|
func (storage *SimpleRESTStorage) Create(ctx api.Context, obj runtime.Object) (<-chan RESTResult, error) {
|
||||||
storage.created = obj.(*Simple)
|
storage.created = obj.(*Simple)
|
||||||
if err := storage.errors["create"]; err != nil {
|
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) {
|
func TestVersion(t *testing.T) {
|
||||||
handler := Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionControl)
|
handler := Handle(map[string]RESTStorage{}, codec, "/prefix", testVersion, selfLinker, admissionControl)
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
|
@ -24,28 +24,43 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// RESTStorage is a generic interface for RESTful storage services.
|
// 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 {
|
type RESTStorage interface {
|
||||||
// New returns an empty object that can be used with Create and Update after request data has been put into it.
|
// 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)
|
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
|
||||||
New() 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 selects resources in the storage which match to the selector.
|
||||||
List(ctx api.Context, label, field labels.Selector) (runtime.Object, error)
|
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.
|
// 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
|
// 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.
|
// returned error value err when the specified resource is not found.
|
||||||
Get(ctx api.Context, id string) (runtime.Object, error)
|
Get(ctx api.Context, id string) (runtime.Object, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RESTDeleter interface {
|
||||||
// Delete finds a resource in the storage and deletes it.
|
// Delete finds a resource in the storage and deletes it.
|
||||||
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
|
// 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.
|
// returned error value err when the specified resource is not found.
|
||||||
Delete(ctx api.Context, id string) (<-chan RESTResult, error)
|
Delete(ctx api.Context, id string) (<-chan RESTResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RESTCreater interface {
|
||||||
// Create creates a new version of a resource.
|
// Create creates a new version of a resource.
|
||||||
Create(ctx api.Context, obj runtime.Object) (<-chan RESTResult, error)
|
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
|
// 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
|
// 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
|
// the returned RESTResultto true. In the event of an asynchronous error returned
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
@ -106,7 +107,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
redirector, ok := storage.(Redirector)
|
redirector, ok := storage.(Redirector)
|
||||||
if !ok {
|
if !ok {
|
||||||
httplog.LogOf(req, w).Addf("'%v' is not a redirector", kind)
|
httplog.LogOf(req, w).Addf("'%v' is not a redirector", kind)
|
||||||
notFound(w, req)
|
errorJSON(errors.NewMethodNotSupported(kind, "proxy"), r.codec, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
@ -53,7 +54,7 @@ func (r *RedirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
redirector, ok := storage.(Redirector)
|
redirector, ok := storage.(Redirector)
|
||||||
if !ok {
|
if !ok {
|
||||||
httplog.LogOf(req, w).Addf("'%v' is not a redirector", kind)
|
httplog.LogOf(req, w).Addf("'%v' is not a redirector", kind)
|
||||||
notFound(w, req)
|
errorJSON(errors.NewMethodNotSupported(kind, "redirect"), r.codec, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"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/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
|
||||||
@ -48,14 +48,13 @@ func (h *RESTHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
storage := h.storage[kind]
|
storage, ok := h.storage[kind]
|
||||||
if storage == nil {
|
if !ok {
|
||||||
httplog.LogOf(req, w).Addf("'%v' has no storage object", kind)
|
|
||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.handleRESTStorage(parts, req, w, storage, namespace)
|
h.handleRESTStorage(parts, req, w, storage, namespace, kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the SelfLink field of the object.
|
// 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)
|
// sync=[false|true] Synchronous request (only applies to create, update, delete operations)
|
||||||
// timeout=<duration> Timeout for synchronous requests, only applies if sync=true
|
// timeout=<duration> Timeout for synchronous requests, only applies if sync=true
|
||||||
// labels=<label-selector> Used for filtering list operations
|
// 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)
|
ctx := api.WithNamespace(api.NewContext(), namespace)
|
||||||
sync := req.URL.Query().Get("sync") == "true"
|
sync := req.URL.Query().Get("sync") == "true"
|
||||||
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
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)
|
errorJSON(err, h.codec, w)
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
return
|
return
|
||||||
@ -177,7 +181,12 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
}
|
}
|
||||||
writeJSON(http.StatusOK, h.codec, list, w)
|
writeJSON(http.StatusOK, h.codec, list, w)
|
||||||
case 2:
|
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 {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
return
|
return
|
||||||
@ -196,6 +205,12 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
creater, ok := storage.(RESTCreater)
|
||||||
|
if !ok {
|
||||||
|
errorJSON(errors.NewMethodNotSupported(kind, "create"), h.codec, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
body, err := readBody(req)
|
body, err := readBody(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
@ -215,7 +230,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := storage.Create(ctx, obj)
|
out, err := creater.Create(ctx, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
return
|
return
|
||||||
@ -228,6 +243,11 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
deleter, ok := storage.(RESTDeleter)
|
||||||
|
if !ok {
|
||||||
|
errorJSON(errors.NewMethodNotSupported(kind, "delete"), h.codec, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// invoke admission control
|
// invoke admission control
|
||||||
err := h.admissionControl.Admit(admission.NewAttributesRecord(nil, namespace, parts[0], "DELETE"))
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := storage.Delete(ctx, parts[1])
|
out, err := deleter.Delete(ctx, parts[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
return
|
return
|
||||||
@ -249,6 +269,12 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
updater, ok := storage.(RESTUpdater)
|
||||||
|
if !ok {
|
||||||
|
errorJSON(errors.NewMethodNotSupported(kind, "create"), h.codec, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
body, err := readBody(req)
|
body, err := readBody(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
@ -268,7 +294,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := storage.Update(ctx, obj)
|
out, err := updater.Update(ctx, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(err, h.codec, w)
|
errorJSON(err, h.codec, w)
|
||||||
return
|
return
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
@ -98,34 +99,35 @@ func (h *WatchHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if watcher, ok := storage.(ResourceWatcher); ok {
|
watcher, ok := storage.(ResourceWatcher)
|
||||||
label, field, resourceVersion, err := getWatchParams(req.URL.Query())
|
if !ok {
|
||||||
if err != nil {
|
errorJSON(errors.NewMethodNotSupported(kind, "watch"), h.codec, w)
|
||||||
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)
|
|
||||||
}
|
|
||||||
return
|
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.
|
// WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"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
|
// 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.
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@ import (
|
|||||||
// TODO: considering that the only difference between the various client types
|
// 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
|
// and RESTStorage type is the type of the arguments, maybe use "go generate" to
|
||||||
// write a specialized adaptor for every client type?
|
// 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 {
|
func RESTStorageToNodes(storage apiserver.RESTStorage) client.NodesInterface {
|
||||||
return &nodeAdaptor{storage}
|
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.
|
// List lists all the nodes in the cluster.
|
||||||
func (n *nodeAdaptor) List() (*api.NodeList, error) {
|
func (n *nodeAdaptor) List() (*api.NodeList, error) {
|
||||||
ctx := api.NewContext()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -71,7 +74,7 @@ func (n *nodeAdaptor) List() (*api.NodeList, error) {
|
|||||||
// Get gets an existing minion
|
// Get gets an existing minion
|
||||||
func (n *nodeAdaptor) Get(name string) (*api.Node, error) {
|
func (n *nodeAdaptor) Get(name string) (*api.Node, error) {
|
||||||
ctx := api.NewContext()
|
ctx := api.NewContext()
|
||||||
obj, err := n.storage.Get(ctx, name)
|
obj, err := n.storage.(apiserver.RESTGetter).Get(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,10 @@ func (*REST) New() runtime.Object {
|
|||||||
return &api.ReplicationController{}
|
return &api.ReplicationController{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*REST) NewList() runtime.Object {
|
||||||
|
return &api.ReplicationControllerList{}
|
||||||
|
}
|
||||||
|
|
||||||
// Update replaces a given ReplicationController instance with an existing
|
// Update replaces a given ReplicationController instance with an existing
|
||||||
// instance in storage.registry.
|
// instance in storage.registry.
|
||||||
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
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 {
|
func (rs REST) New() runtime.Object {
|
||||||
return &api.Endpoints{}
|
return &api.Endpoints{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*REST) NewList() runtime.Object {
|
||||||
|
return &api.EndpointsList{}
|
||||||
|
}
|
||||||
|
@ -128,6 +128,10 @@ func (*REST) New() runtime.Object {
|
|||||||
return &api.Event{}
|
return &api.Event{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*REST) NewList() runtime.Object {
|
||||||
|
return &api.EventList{}
|
||||||
|
}
|
||||||
|
|
||||||
// Update returns an error: Events are not mutable.
|
// Update returns an error: Events are not mutable.
|
||||||
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
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")
|
return nil, fmt.Errorf("not allowed: 'Event' objects are not mutable")
|
||||||
|
@ -104,6 +104,10 @@ func (rs *REST) New() runtime.Object {
|
|||||||
return &api.Node{}
|
return &api.Node{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*REST) NewList() runtime.Object {
|
||||||
|
return &api.NodeList{}
|
||||||
|
}
|
||||||
|
|
||||||
// Update satisfies the RESTStorage interface.
|
// Update satisfies the RESTStorage interface.
|
||||||
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
||||||
minion, ok := obj.(*api.Node)
|
minion, ok := obj.(*api.Node)
|
||||||
|
@ -161,6 +161,10 @@ func (*REST) New() runtime.Object {
|
|||||||
return &api.Pod{}
|
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) {
|
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
if !api.ValidNamespace(ctx, &pod.ObjectMeta) {
|
if !api.ValidNamespace(ctx, &pod.ObjectMeta) {
|
||||||
|
@ -217,6 +217,10 @@ func (*REST) New() runtime.Object {
|
|||||||
return &api.Service{}
|
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) {
|
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
|
||||||
service := obj.(*api.Service)
|
service := obj.(*api.Service)
|
||||||
if !api.ValidNamespace(ctx, &service.ObjectMeta) {
|
if !api.ValidNamespace(ctx, &service.ObjectMeta) {
|
||||||
|
Loading…
Reference in New Issue
Block a user