remove dependency on gorestful for rest handling
Kubernetes-commit: c5e2f7c229d8194386de97df7ff6c1b0f71be4c7
This commit is contained in:
parent
5e858945af
commit
540c5cf18e
|
@ -679,6 +679,29 @@ func (r *GetWithOptionsRESTStorage) NewGetOptions() (runtime.Object, bool, strin
|
||||||
|
|
||||||
var _ rest.GetterWithOptions = &GetWithOptionsRESTStorage{}
|
var _ rest.GetterWithOptions = &GetWithOptionsRESTStorage{}
|
||||||
|
|
||||||
|
type GetWithOptionsRootRESTStorage struct {
|
||||||
|
*SimpleTypedStorage
|
||||||
|
optionsReceived runtime.Object
|
||||||
|
takesPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GetWithOptionsRootRESTStorage) Get(ctx request.Context, name string, options runtime.Object) (runtime.Object, error) {
|
||||||
|
if _, ok := options.(*genericapitesting.SimpleGetOptions); !ok {
|
||||||
|
return nil, fmt.Errorf("Unexpected options object: %#v", options)
|
||||||
|
}
|
||||||
|
r.optionsReceived = options
|
||||||
|
return r.SimpleTypedStorage.Get(ctx, name, &metav1.GetOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GetWithOptionsRootRESTStorage) NewGetOptions() (runtime.Object, bool, string) {
|
||||||
|
if len(r.takesPath) > 0 {
|
||||||
|
return &genericapitesting.SimpleGetOptions{}, true, r.takesPath
|
||||||
|
}
|
||||||
|
return &genericapitesting.SimpleGetOptions{}, false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ rest.GetterWithOptions = &GetWithOptionsRootRESTStorage{}
|
||||||
|
|
||||||
type NamedCreaterRESTStorage struct {
|
type NamedCreaterRESTStorage struct {
|
||||||
*SimpleRESTStorage
|
*SimpleRESTStorage
|
||||||
createdName string
|
createdName string
|
||||||
|
@ -1519,87 +1542,123 @@ func TestGetWithOptionsRouteParams(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetWithOptions(t *testing.T) {
|
func TestGetWithOptions(t *testing.T) {
|
||||||
storage := map[string]rest.Storage{}
|
|
||||||
simpleStorage := GetWithOptionsRESTStorage{
|
tests := []struct {
|
||||||
SimpleRESTStorage: &SimpleRESTStorage{
|
name string
|
||||||
item: genericapitesting.Simple{
|
rootScoped bool
|
||||||
Other: "foo",
|
requestURL string
|
||||||
},
|
expectedPath string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic",
|
||||||
|
requestURL: "/namespaces/default/simple/id?param1=test1¶m2=test2",
|
||||||
|
expectedPath: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with path",
|
||||||
|
requestURL: "/namespaces/default/simple/id/a/different/path?param1=test1¶m2=test2",
|
||||||
|
expectedPath: "a/different/path",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "as subresource",
|
||||||
|
requestURL: "/namespaces/default/simple/id/subresource/another/different/path?param1=test1¶m2=test2",
|
||||||
|
expectedPath: "another/different/path",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cluster-scoped basic",
|
||||||
|
rootScoped: true,
|
||||||
|
requestURL: "/simple/id?param1=test1¶m2=test2",
|
||||||
|
expectedPath: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cluster-scoped basic with path",
|
||||||
|
rootScoped: true,
|
||||||
|
requestURL: "/simple/id/a/cluster/path?param1=test1¶m2=test2",
|
||||||
|
expectedPath: "a/cluster/path",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cluster-scoped basic as subresource",
|
||||||
|
rootScoped: true,
|
||||||
|
requestURL: "/simple/id/subresource/another/cluster/path?param1=test1¶m2=test2",
|
||||||
|
expectedPath: "another/cluster/path",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
storage["simple"] = &simpleStorage
|
|
||||||
handler := handle(storage)
|
|
||||||
server := httptest.NewServer(handler)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id?param1=test1¶m2=test2")
|
for _, test := range tests {
|
||||||
if err != nil {
|
simpleStorage := GetWithOptionsRESTStorage{
|
||||||
t.Fatalf("unexpected error: %v", err)
|
SimpleRESTStorage: &SimpleRESTStorage{
|
||||||
}
|
item: genericapitesting.Simple{
|
||||||
if resp.StatusCode != http.StatusOK {
|
Other: "foo",
|
||||||
t.Fatalf("unexpected response: %#v", resp)
|
},
|
||||||
}
|
},
|
||||||
var itemOut genericapitesting.Simple
|
takesPath: "atAPath",
|
||||||
body, err := extractBody(resp, &itemOut)
|
}
|
||||||
if err != nil {
|
simpleRootStorage := GetWithOptionsRootRESTStorage{
|
||||||
t.Errorf("unexpected error: %v", err)
|
SimpleTypedStorage: &SimpleTypedStorage{
|
||||||
}
|
baseType: &genericapitesting.SimpleRoot{}, // a root scoped type
|
||||||
|
item: &genericapitesting.SimpleRoot{
|
||||||
|
Other: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
takesPath: "atAPath",
|
||||||
|
}
|
||||||
|
|
||||||
if itemOut.Name != simpleStorage.item.Name {
|
storage := map[string]rest.Storage{}
|
||||||
t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
|
if test.rootScoped {
|
||||||
}
|
storage["simple"] = &simpleRootStorage
|
||||||
|
storage["simple/subresource"] = &simpleRootStorage
|
||||||
|
} else {
|
||||||
|
storage["simple"] = &simpleStorage
|
||||||
|
storage["simple/subresource"] = &simpleStorage
|
||||||
|
}
|
||||||
|
handler := handle(storage)
|
||||||
|
server := httptest.NewServer(handler)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
opts, ok := simpleStorage.optionsReceived.(*genericapitesting.SimpleGetOptions)
|
resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + test.requestURL)
|
||||||
if !ok {
|
if err != nil {
|
||||||
t.Errorf("Unexpected options object received: %#v", simpleStorage.optionsReceived)
|
t.Errorf("%s: %v", test.name, err)
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
if opts.Param1 != "test1" || opts.Param2 != "test2" {
|
if resp.StatusCode != http.StatusOK {
|
||||||
t.Errorf("Did not receive expected options: %#v", opts)
|
t.Errorf("%s: unexpected response: %#v", test.name, resp)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var itemOut genericapitesting.Simple
|
||||||
|
body, err := extractBody(resp, &itemOut)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %v", test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if itemOut.Name != simpleStorage.item.Name {
|
||||||
|
t.Errorf("%s: Unexpected data: %#v, expected %#v (%s)", test.name, itemOut, simpleStorage.item, string(body))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var opts *genericapitesting.SimpleGetOptions
|
||||||
|
var ok bool
|
||||||
|
if test.rootScoped {
|
||||||
|
opts, ok = simpleRootStorage.optionsReceived.(*genericapitesting.SimpleGetOptions)
|
||||||
|
} else {
|
||||||
|
opts, ok = simpleStorage.optionsReceived.(*genericapitesting.SimpleGetOptions)
|
||||||
|
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: Unexpected options object received: %#v", test.name, simpleStorage.optionsReceived)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if opts.Param1 != "test1" || opts.Param2 != "test2" {
|
||||||
|
t.Errorf("%s: Did not receive expected options: %#v", test.name, opts)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if opts.Path != test.expectedPath {
|
||||||
|
t.Errorf("%s: Unexpected path value. Expected: %s. Actual: %s.", test.name, test.expectedPath, opts.Path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetWithOptionsAndPath(t *testing.T) {
|
|
||||||
storage := map[string]rest.Storage{}
|
|
||||||
simpleStorage := GetWithOptionsRESTStorage{
|
|
||||||
SimpleRESTStorage: &SimpleRESTStorage{
|
|
||||||
item: genericapitesting.Simple{
|
|
||||||
Other: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
takesPath: "atAPath",
|
|
||||||
}
|
|
||||||
storage["simple"] = &simpleStorage
|
|
||||||
handler := handle(storage)
|
|
||||||
server := httptest.NewServer(handler)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id/a/different/path?param1=test1¶m2=test2&atAPath=not")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
t.Fatalf("unexpected response: %#v", resp)
|
|
||||||
}
|
|
||||||
var itemOut genericapitesting.Simple
|
|
||||||
body, err := extractBody(resp, &itemOut)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if itemOut.Name != simpleStorage.item.Name {
|
|
||||||
t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
opts, ok := simpleStorage.optionsReceived.(*genericapitesting.SimpleGetOptions)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unexpected options object received: %#v", simpleStorage.optionsReceived)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if opts.Param1 != "test1" || opts.Param2 != "test2" || opts.Path != "a/different/path" {
|
|
||||||
t.Errorf("Did not receive expected options: %#v", opts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestGetAlternateSelfLink(t *testing.T) {
|
func TestGetAlternateSelfLink(t *testing.T) {
|
||||||
storage := map[string]rest.Storage{}
|
storage := map[string]rest.Storage{}
|
||||||
simpleStorage := SimpleRESTStorage{
|
simpleStorage := SimpleRESTStorage{
|
||||||
|
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/emicklei/go-restful"
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -78,48 +77,47 @@ func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Reque
|
||||||
|
|
||||||
// getterFunc performs a get request with the given context and object name. The request
|
// getterFunc performs a get request with the given context and object name. The request
|
||||||
// may be used to deserialize an options object to pass to the getter.
|
// may be used to deserialize an options object to pass to the getter.
|
||||||
type getterFunc func(ctx request.Context, name string, req *restful.Request) (runtime.Object, error)
|
type getterFunc func(ctx request.Context, name string, req *http.Request) (runtime.Object, error)
|
||||||
|
|
||||||
// MaxRetryWhenPatchConflicts is the maximum number of conflicts retry during a patch operation before returning failure
|
// MaxRetryWhenPatchConflicts is the maximum number of conflicts retry during a patch operation before returning failure
|
||||||
const MaxRetryWhenPatchConflicts = 5
|
const MaxRetryWhenPatchConflicts = 5
|
||||||
|
|
||||||
// getResourceHandler is an HTTP handler function for get requests. It delegates to the
|
// getResourceHandler is an HTTP handler function for get requests. It delegates to the
|
||||||
// passed-in getterFunc to perform the actual get.
|
// passed-in getterFunc to perform the actual get.
|
||||||
func getResourceHandler(scope RequestScope, getter getterFunc) restful.RouteFunction {
|
func getResourceHandler(scope RequestScope, getter getterFunc) http.HandlerFunc {
|
||||||
return func(req *restful.Request, res *restful.Response) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
w := res.ResponseWriter
|
namespace, name, err := scope.Namer.Name(req)
|
||||||
namespace, name, err := scope.Namer.Name(req.Request)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx := scope.ContextFunc(req.Request)
|
ctx := scope.ContextFunc(req)
|
||||||
ctx = request.WithNamespace(ctx, namespace)
|
ctx = request.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
result, err := getter(ctx, name, req)
|
result, err := getter(ctx, name, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
responsewriters.WriteObject(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
|
responsewriters.WriteObject(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResource returns a function that handles retrieving a single resource from a rest.Storage object.
|
// GetResource returns a function that handles retrieving a single resource from a rest.Storage object.
|
||||||
func GetResource(r rest.Getter, e rest.Exporter, scope RequestScope) restful.RouteFunction {
|
func GetResource(r rest.Getter, e rest.Exporter, scope RequestScope) http.HandlerFunc {
|
||||||
return getResourceHandler(scope,
|
return getResourceHandler(scope,
|
||||||
func(ctx request.Context, name string, req *restful.Request) (runtime.Object, error) {
|
func(ctx request.Context, name string, req *http.Request) (runtime.Object, error) {
|
||||||
// For performance tracking purposes.
|
// For performance tracking purposes.
|
||||||
trace := utiltrace.New("Get " + req.Request.URL.Path)
|
trace := utiltrace.New("Get " + req.URL.Path)
|
||||||
defer trace.LogIfLong(500 * time.Millisecond)
|
defer trace.LogIfLong(500 * time.Millisecond)
|
||||||
|
|
||||||
// check for export
|
// check for export
|
||||||
options := metav1.GetOptions{}
|
options := metav1.GetOptions{}
|
||||||
if values := req.Request.URL.Query(); len(values) > 0 {
|
if values := req.URL.Query(); len(values) > 0 {
|
||||||
exports := metav1.ExportOptions{}
|
exports := metav1.ExportOptions{}
|
||||||
if err := metainternalversion.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, &exports); err != nil {
|
if err := metainternalversion.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, &exports); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -140,48 +138,55 @@ func GetResource(r rest.Getter, e rest.Exporter, scope RequestScope) restful.Rou
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object.
|
// GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object.
|
||||||
func GetResourceWithOptions(r rest.GetterWithOptions, scope RequestScope) restful.RouteFunction {
|
func GetResourceWithOptions(r rest.GetterWithOptions, scope RequestScope, isSubresource bool) http.HandlerFunc {
|
||||||
return getResourceHandler(scope,
|
return getResourceHandler(scope,
|
||||||
func(ctx request.Context, name string, req *restful.Request) (runtime.Object, error) {
|
func(ctx request.Context, name string, req *http.Request) (runtime.Object, error) {
|
||||||
opts, subpath, subpathKey := r.NewGetOptions()
|
opts, subpath, subpathKey := r.NewGetOptions()
|
||||||
if err := getRequestOptions(req, scope, opts, subpath, subpathKey); err != nil {
|
if err := getRequestOptions(req, scope, opts, subpath, subpathKey, isSubresource); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return r.Get(ctx, name, opts)
|
return r.Get(ctx, name, opts)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRequestOptions(req *restful.Request, scope RequestScope, into runtime.Object, subpath bool, subpathKey string) error {
|
// getRequestOptions parses out options and can include path information. The path information shouldn't include the subresource.
|
||||||
|
func getRequestOptions(req *http.Request, scope RequestScope, into runtime.Object, subpath bool, subpathKey string, isSubresource bool) error {
|
||||||
if into == nil {
|
if into == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
query := req.Request.URL.Query()
|
query := req.URL.Query()
|
||||||
if subpath {
|
if subpath {
|
||||||
newQuery := make(url.Values)
|
newQuery := make(url.Values)
|
||||||
for k, v := range query {
|
for k, v := range query {
|
||||||
newQuery[k] = v
|
newQuery[k] = v
|
||||||
}
|
}
|
||||||
newQuery[subpathKey] = []string{req.PathParameter("path")}
|
|
||||||
|
ctx := scope.ContextFunc(req)
|
||||||
|
requestInfo, _ := request.RequestInfoFrom(ctx)
|
||||||
|
startingIndex := 2
|
||||||
|
if isSubresource {
|
||||||
|
startingIndex = 3
|
||||||
|
}
|
||||||
|
newQuery[subpathKey] = []string{strings.Join(requestInfo.Parts[startingIndex:], "/")}
|
||||||
query = newQuery
|
query = newQuery
|
||||||
}
|
}
|
||||||
return scope.ParameterCodec.DecodeParameters(query, scope.Kind.GroupVersion(), into)
|
return scope.ParameterCodec.DecodeParameters(query, scope.Kind.GroupVersion(), into)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectResource returns a function that handles a connect request on a rest.Storage object.
|
// ConnectResource returns a function that handles a connect request on a rest.Storage object.
|
||||||
func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string) restful.RouteFunction {
|
func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string, isSubresource bool) http.HandlerFunc {
|
||||||
return func(req *restful.Request, res *restful.Response) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
w := res.ResponseWriter
|
namespace, name, err := scope.Namer.Name(req)
|
||||||
namespace, name, err := scope.Namer.Name(req.Request)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx := scope.ContextFunc(req.Request)
|
ctx := scope.ContextFunc(req)
|
||||||
ctx = request.WithNamespace(ctx, namespace)
|
ctx = request.WithNamespace(ctx, namespace)
|
||||||
opts, subpath, subpathKey := connecter.NewConnectOptions()
|
opts, subpath, subpathKey := connecter.NewConnectOptions()
|
||||||
if err := getRequestOptions(req, scope, opts, subpath, subpathKey); err != nil {
|
if err := getRequestOptions(req, scope, opts, subpath, subpathKey, isSubresource); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if admit.Handles(admission.Connect) {
|
if admit.Handles(admission.Connect) {
|
||||||
|
@ -194,62 +199,59 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi
|
||||||
|
|
||||||
err = admit.Admit(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo))
|
err = admit.Admit(admission.NewAttributesRecord(connectRequest, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, userInfo))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req, res: res})
|
handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req, w: w})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handler.ServeHTTP(w, req.Request)
|
handler.ServeHTTP(w, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// responder implements rest.Responder for assisting a connector in writing objects or errors.
|
// responder implements rest.Responder for assisting a connector in writing objects or errors.
|
||||||
type responder struct {
|
type responder struct {
|
||||||
scope RequestScope
|
scope RequestScope
|
||||||
req *restful.Request
|
req *http.Request
|
||||||
res *restful.Response
|
w http.ResponseWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *responder) Object(statusCode int, obj runtime.Object) {
|
func (r *responder) Object(statusCode int, obj runtime.Object) {
|
||||||
responsewriters.WriteObject(statusCode, r.scope.Kind.GroupVersion(), r.scope.Serializer, obj, r.res.ResponseWriter, r.req.Request)
|
responsewriters.WriteObject(statusCode, r.scope.Kind.GroupVersion(), r.scope.Serializer, obj, r.w, r.req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *responder) Error(err error) {
|
func (r *responder) Error(err error) {
|
||||||
r.scope.err(err, r.res.ResponseWriter, r.req.Request)
|
r.scope.err(err, r.w, r.req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListResource returns a function that handles retrieving a list of resources from a rest.Storage object.
|
func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch bool, minRequestTimeout time.Duration) http.HandlerFunc {
|
||||||
func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch bool, minRequestTimeout time.Duration) restful.RouteFunction {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
return func(req *restful.Request, res *restful.Response) {
|
|
||||||
// For performance tracking purposes.
|
// For performance tracking purposes.
|
||||||
trace := utiltrace.New("List " + req.Request.URL.Path)
|
trace := utiltrace.New("List " + req.URL.Path)
|
||||||
|
|
||||||
w := res.ResponseWriter
|
namespace, err := scope.Namer.Namespace(req)
|
||||||
|
|
||||||
namespace, err := scope.Namer.Namespace(req.Request)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watches for single objects are routed to this function.
|
// Watches for single objects are routed to this function.
|
||||||
// Treat a /name parameter the same as a field selector entry.
|
// Treat a /name parameter the same as a field selector entry.
|
||||||
hasName := true
|
hasName := true
|
||||||
_, name, err := scope.Namer.Name(req.Request)
|
_, name, err := scope.Namer.Name(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hasName = false
|
hasName = false
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := scope.ContextFunc(req.Request)
|
ctx := scope.ContextFunc(req)
|
||||||
ctx = request.WithNamespace(ctx, namespace)
|
ctx = request.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
opts := metainternalversion.ListOptions{}
|
opts := metainternalversion.ListOptions{}
|
||||||
if err := metainternalversion.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.MetaGroupVersion, &opts); err != nil {
|
if err := metainternalversion.ParameterCodec.DecodeParameters(req.URL.Query(), scope.MetaGroupVersion, &opts); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +264,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
|
||||||
if opts.FieldSelector, err = opts.FieldSelector.Transform(fn); err != nil {
|
if opts.FieldSelector, err = opts.FieldSelector.Transform(fn); err != nil {
|
||||||
// TODO: allow bad request to set field causes based on query parameters
|
// TODO: allow bad request to set field causes based on query parameters
|
||||||
err = errors.NewBadRequest(err.Error())
|
err = errors.NewBadRequest(err.Error())
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +280,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
|
||||||
// and a field selector, since just the name is
|
// and a field selector, since just the name is
|
||||||
// sufficient to narrow down the request to a
|
// sufficient to narrow down the request to a
|
||||||
// single object.
|
// single object.
|
||||||
scope.err(errors.NewBadRequest("both a name and a field selector provided; please provide one or the other."), res.ResponseWriter, req.Request)
|
scope.err(errors.NewBadRequest("both a name and a field selector provided; please provide one or the other."), w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
opts.FieldSelector = nameSelector
|
opts.FieldSelector = nameSelector
|
||||||
|
@ -293,14 +295,14 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
|
||||||
if timeout == 0 && minRequestTimeout > 0 {
|
if timeout == 0 && minRequestTimeout > 0 {
|
||||||
timeout = time.Duration(float64(minRequestTimeout) * (rand.Float64() + 1.0))
|
timeout = time.Duration(float64(minRequestTimeout) * (rand.Float64() + 1.0))
|
||||||
}
|
}
|
||||||
glog.V(2).Infof("Starting watch for %s, rv=%s labels=%s fields=%s timeout=%s", req.Request.URL.Path, opts.ResourceVersion, opts.LabelSelector, opts.FieldSelector, timeout)
|
glog.V(2).Infof("Starting watch for %s, rv=%s labels=%s fields=%s timeout=%s", req.URL.Path, opts.ResourceVersion, opts.LabelSelector, opts.FieldSelector, timeout)
|
||||||
|
|
||||||
watcher, err := rw.Watch(ctx, &opts)
|
watcher, err := rw.Watch(ctx, &opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serveWatch(watcher, scope, req, res, timeout)
|
serveWatch(watcher, scope, req, w, timeout)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,67 +311,65 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
|
||||||
trace.Step("About to List from storage")
|
trace.Step("About to List from storage")
|
||||||
result, err := r.List(ctx, &opts)
|
result, err := r.List(ctx, &opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Listing from storage done")
|
trace.Step("Listing from storage done")
|
||||||
numberOfItems, err := setListSelfLink(result, req, scope.Namer)
|
numberOfItems, err := setListSelfLink(result, req, scope.Namer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Self-linking done")
|
trace.Step("Self-linking done")
|
||||||
// Ensure empty lists return a non-nil items slice
|
// Ensure empty lists return a non-nil items slice
|
||||||
if numberOfItems == 0 && meta.IsListType(result) {
|
if numberOfItems == 0 && meta.IsListType(result) {
|
||||||
if err := meta.SetList(result, []runtime.Object{}); err != nil {
|
if err := meta.SetList(result, []runtime.Object{}); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responsewriters.WriteObject(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
|
responsewriters.WriteObject(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
|
||||||
trace.Step(fmt.Sprintf("Writing http response done (%d items)", numberOfItems))
|
trace.Step(fmt.Sprintf("Writing http response done (%d items)", numberOfItems))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface, includeName bool) restful.RouteFunction {
|
func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface, includeName bool) http.HandlerFunc {
|
||||||
return func(req *restful.Request, res *restful.Response) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
// For performance tracking purposes.
|
// For performance tracking purposes.
|
||||||
trace := utiltrace.New("Create " + req.Request.URL.Path)
|
trace := utiltrace.New("Create " + req.URL.Path)
|
||||||
defer trace.LogIfLong(500 * time.Millisecond)
|
defer trace.LogIfLong(500 * time.Millisecond)
|
||||||
|
|
||||||
w := res.ResponseWriter
|
|
||||||
|
|
||||||
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
||||||
timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
|
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
namespace, name string
|
namespace, name string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if includeName {
|
if includeName {
|
||||||
namespace, name, err = scope.Namer.Name(req.Request)
|
namespace, name, err = scope.Namer.Name(req)
|
||||||
} else {
|
} else {
|
||||||
namespace, err = scope.Namer.Namespace(req.Request)
|
namespace, err = scope.Namer.Namespace(req)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := scope.ContextFunc(req.Request)
|
ctx := scope.ContextFunc(req)
|
||||||
ctx = request.WithNamespace(ctx, namespace)
|
ctx = request.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
gv := scope.Kind.GroupVersion()
|
gv := scope.Kind.GroupVersion()
|
||||||
s, err := negotiation.NegotiateInputSerializer(req.Request, scope.Serializer)
|
s, err := negotiation.NegotiateInputSerializer(req, scope.Serializer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
decoder := scope.Serializer.DecoderToVersion(s.Serializer, schema.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal})
|
decoder := scope.Serializer.DecoderToVersion(s.Serializer, schema.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal})
|
||||||
|
|
||||||
body, err := readBody(req.Request)
|
body, err := readBody(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,12 +379,12 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
|
||||||
obj, gvk, err := decoder.Decode(body, &defaultGVK, original)
|
obj, gvk, err := decoder.Decode(body, &defaultGVK, original)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = transformDecodeError(typer, err, original, gvk, body)
|
err = transformDecodeError(typer, err, original, gvk, body)
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if gvk.GroupVersion() != gv {
|
if gvk.GroupVersion() != gv {
|
||||||
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", gvk.GroupVersion().String(), gv.String()))
|
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", gvk.GroupVersion().String(), gv.String()))
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Conversion done")
|
trace.Step("Conversion done")
|
||||||
|
@ -394,7 +394,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
|
||||||
|
|
||||||
err = admit.Admit(admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo))
|
err = admit.Admit(admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,28 +408,28 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
|
||||||
return out, err
|
return out, err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Object stored in database")
|
trace.Step("Object stored in database")
|
||||||
|
|
||||||
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Self-link added")
|
trace.Step("Self-link added")
|
||||||
|
|
||||||
responsewriters.WriteObject(http.StatusCreated, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
|
responsewriters.WriteObject(http.StatusCreated, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNamedResource returns a function that will handle a resource creation with name.
|
// CreateNamedResource returns a function that will handle a resource creation with name.
|
||||||
func CreateNamedResource(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful.RouteFunction {
|
func CreateNamedResource(r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) http.HandlerFunc {
|
||||||
return createHandler(r, scope, typer, admit, true)
|
return createHandler(r, scope, typer, admit, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateResource returns a function that will handle a resource creation.
|
// CreateResource returns a function that will handle a resource creation.
|
||||||
func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful.RouteFunction {
|
func CreateResource(r rest.Creater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) http.HandlerFunc {
|
||||||
return createHandler(&namedCreaterAdapter{r}, scope, typer, admit, false)
|
return createHandler(&namedCreaterAdapter{r}, scope, typer, admit, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,47 +443,45 @@ func (c *namedCreaterAdapter) Create(ctx request.Context, name string, obj runti
|
||||||
|
|
||||||
// PatchResource returns a function that will handle a resource patch
|
// PatchResource returns a function that will handle a resource patch
|
||||||
// TODO: Eventually PatchResource should just use GuaranteedUpdate and this routine should be a bit cleaner
|
// TODO: Eventually PatchResource should just use GuaranteedUpdate and this routine should be a bit cleaner
|
||||||
func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface, converter runtime.ObjectConvertor) restful.RouteFunction {
|
func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface, converter runtime.ObjectConvertor) http.HandlerFunc {
|
||||||
return func(req *restful.Request, res *restful.Response) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
w := res.ResponseWriter
|
|
||||||
|
|
||||||
// TODO: we either want to remove timeout or document it (if we
|
// TODO: we either want to remove timeout or document it (if we
|
||||||
// document, move timeout out of this function and declare it in
|
// document, move timeout out of this function and declare it in
|
||||||
// api_installer)
|
// api_installer)
|
||||||
timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
|
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
||||||
|
|
||||||
namespace, name, err := scope.Namer.Name(req.Request)
|
namespace, name, err := scope.Namer.Name(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := scope.ContextFunc(req.Request)
|
ctx := scope.ContextFunc(req)
|
||||||
ctx = request.WithNamespace(ctx, namespace)
|
ctx = request.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion())
|
versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle this in negotiation
|
// TODO: handle this in negotiation
|
||||||
contentType := req.HeaderParameter("Content-Type")
|
contentType := req.Header.Get("Content-Type")
|
||||||
// Remove "; charset=" if included in header.
|
// Remove "; charset=" if included in header.
|
||||||
if idx := strings.Index(contentType, ";"); idx > 0 {
|
if idx := strings.Index(contentType, ";"); idx > 0 {
|
||||||
contentType = contentType[:idx]
|
contentType = contentType[:idx]
|
||||||
}
|
}
|
||||||
patchType := types.PatchType(contentType)
|
patchType := types.PatchType(contentType)
|
||||||
|
|
||||||
patchJS, err := readBody(req.Request)
|
patchJS, err := readBody(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s, ok := runtime.SerializerInfoForMediaType(scope.Serializer.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
s, ok := runtime.SerializerInfoForMediaType(scope.Serializer.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
if !ok {
|
if !ok {
|
||||||
scope.err(fmt.Errorf("no serializer defined for JSON"), res.ResponseWriter, req.Request)
|
scope.err(fmt.Errorf("no serializer defined for JSON"), w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gv := scope.Kind.GroupVersion()
|
gv := scope.Kind.GroupVersion()
|
||||||
|
@ -504,16 +502,16 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
|
||||||
result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS,
|
result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS,
|
||||||
scope.Namer, scope.Copier, scope.Creater, scope.Defaulter, scope.UnsafeConvertor, scope.Kind, scope.Resource, codec)
|
scope.Namer, scope.Copier, scope.Creater, scope.Defaulter, scope.UnsafeConvertor, scope.Kind, scope.Resource, codec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
responsewriters.WriteObject(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
|
responsewriters.WriteObject(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -755,34 +753,32 @@ func patchResource(
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateResource returns a function that will handle a resource update
|
// UpdateResource returns a function that will handle a resource update
|
||||||
func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful.RouteFunction {
|
func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) http.HandlerFunc {
|
||||||
return func(req *restful.Request, res *restful.Response) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
// For performance tracking purposes.
|
// For performance tracking purposes.
|
||||||
trace := utiltrace.New("Update " + req.Request.URL.Path)
|
trace := utiltrace.New("Update " + req.URL.Path)
|
||||||
defer trace.LogIfLong(500 * time.Millisecond)
|
defer trace.LogIfLong(500 * time.Millisecond)
|
||||||
|
|
||||||
w := res.ResponseWriter
|
|
||||||
|
|
||||||
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
||||||
timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
|
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
||||||
|
|
||||||
namespace, name, err := scope.Namer.Name(req.Request)
|
namespace, name, err := scope.Namer.Name(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx := scope.ContextFunc(req.Request)
|
ctx := scope.ContextFunc(req)
|
||||||
ctx = request.WithNamespace(ctx, namespace)
|
ctx = request.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
body, err := readBody(req.Request)
|
body, err := readBody(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := negotiation.NegotiateInputSerializer(req.Request, scope.Serializer)
|
s, err := negotiation.NegotiateInputSerializer(req, scope.Serializer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defaultGVK := scope.Kind
|
defaultGVK := scope.Kind
|
||||||
|
@ -791,18 +787,18 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
|
||||||
obj, gvk, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, original)
|
obj, gvk, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, original)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = transformDecodeError(typer, err, original, gvk, body)
|
err = transformDecodeError(typer, err, original, gvk, body)
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if gvk.GroupVersion() != defaultGVK.GroupVersion() {
|
if gvk.GroupVersion() != defaultGVK.GroupVersion() {
|
||||||
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)", gvk.GroupVersion(), defaultGVK.GroupVersion()))
|
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)", gvk.GroupVersion(), defaultGVK.GroupVersion()))
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Conversion done")
|
trace.Step("Conversion done")
|
||||||
|
|
||||||
if err := checkName(obj, name, namespace, scope.Namer); err != nil {
|
if err := checkName(obj, name, namespace, scope.Namer); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,13 +818,13 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
|
||||||
return obj, err
|
return obj, err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Object stored in database")
|
trace.Step("Object stored in database")
|
||||||
|
|
||||||
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Self-link added")
|
trace.Step("Self-link added")
|
||||||
|
@ -837,41 +833,39 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
|
||||||
if wasCreated {
|
if wasCreated {
|
||||||
status = http.StatusCreated
|
status = http.StatusCreated
|
||||||
}
|
}
|
||||||
responsewriters.WriteObject(status, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
|
responsewriters.WriteObject(status, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteResource returns a function that will handle a resource deletion
|
// DeleteResource returns a function that will handle a resource deletion
|
||||||
func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestScope, admit admission.Interface) restful.RouteFunction {
|
func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestScope, admit admission.Interface) http.HandlerFunc {
|
||||||
return func(req *restful.Request, res *restful.Response) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
// For performance tracking purposes.
|
// For performance tracking purposes.
|
||||||
trace := utiltrace.New("Delete " + req.Request.URL.Path)
|
trace := utiltrace.New("Delete " + req.URL.Path)
|
||||||
defer trace.LogIfLong(500 * time.Millisecond)
|
defer trace.LogIfLong(500 * time.Millisecond)
|
||||||
|
|
||||||
w := res.ResponseWriter
|
|
||||||
|
|
||||||
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
||||||
timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
|
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
||||||
|
|
||||||
namespace, name, err := scope.Namer.Name(req.Request)
|
namespace, name, err := scope.Namer.Name(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx := scope.ContextFunc(req.Request)
|
ctx := scope.ContextFunc(req)
|
||||||
ctx = request.WithNamespace(ctx, namespace)
|
ctx = request.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
options := &metav1.DeleteOptions{}
|
options := &metav1.DeleteOptions{}
|
||||||
if allowsOptions {
|
if allowsOptions {
|
||||||
body, err := readBody(req.Request)
|
body, err := readBody(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(body) > 0 {
|
if len(body) > 0 {
|
||||||
s, err := negotiation.NegotiateInputSerializer(req.Request, metainternalversion.Codecs)
|
s, err := negotiation.NegotiateInputSerializer(req, metainternalversion.Codecs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// For backwards compatibility, we need to allow existing clients to submit per group DeleteOptions
|
// For backwards compatibility, we need to allow existing clients to submit per group DeleteOptions
|
||||||
|
@ -879,17 +873,17 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
|
||||||
defaultGVK := scope.MetaGroupVersion.WithKind("DeleteOptions")
|
defaultGVK := scope.MetaGroupVersion.WithKind("DeleteOptions")
|
||||||
obj, _, err := metainternalversion.Codecs.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
|
obj, _, err := metainternalversion.Codecs.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if obj != options {
|
if obj != options {
|
||||||
scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), res.ResponseWriter, req.Request)
|
scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if values := req.Request.URL.Query(); len(values) > 0 {
|
if values := req.URL.Query(); len(values) > 0 {
|
||||||
if err := metainternalversion.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, options); err != nil {
|
if err := metainternalversion.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, options); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -901,7 +895,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
|
||||||
|
|
||||||
err = admit.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo))
|
err = admit.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -914,7 +908,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
|
||||||
return obj, err
|
return obj, err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trace.Step("Object deleted from database")
|
trace.Step("Object deleted from database")
|
||||||
|
@ -944,30 +938,28 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
|
||||||
// when a non-status response is returned, set the self link
|
// when a non-status response is returned, set the self link
|
||||||
if _, ok := result.(*metav1.Status); !ok {
|
if _, ok := result.(*metav1.Status); !ok {
|
||||||
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
if err := setSelfLink(result, req, scope.Namer); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responsewriters.WriteObject(status, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
|
responsewriters.WriteObject(status, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCollection returns a function that will handle a collection deletion
|
// DeleteCollection returns a function that will handle a collection deletion
|
||||||
func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestScope, admit admission.Interface) restful.RouteFunction {
|
func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestScope, admit admission.Interface) http.HandlerFunc {
|
||||||
return func(req *restful.Request, res *restful.Response) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
w := res.ResponseWriter
|
|
||||||
|
|
||||||
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
||||||
timeout := parseTimeout(req.Request.URL.Query().Get("timeout"))
|
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
||||||
|
|
||||||
namespace, err := scope.Namer.Namespace(req.Request)
|
namespace, err := scope.Namer.Namespace(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := scope.ContextFunc(req.Request)
|
ctx := scope.ContextFunc(req)
|
||||||
ctx = request.WithNamespace(ctx, namespace)
|
ctx = request.WithNamespace(ctx, namespace)
|
||||||
|
|
||||||
if admit != nil && admit.Handles(admission.Delete) {
|
if admit != nil && admit.Handles(admission.Delete) {
|
||||||
|
@ -975,14 +967,14 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
|
||||||
|
|
||||||
err = admit.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo))
|
err = admit.Admit(admission.NewAttributesRecord(nil, nil, scope.Kind, namespace, "", scope.Resource, scope.Subresource, admission.Delete, userInfo))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listOptions := metainternalversion.ListOptions{}
|
listOptions := metainternalversion.ListOptions{}
|
||||||
if err := metainternalversion.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.MetaGroupVersion, &listOptions); err != nil {
|
if err := metainternalversion.ParameterCodec.DecodeParameters(req.URL.Query(), scope.MetaGroupVersion, &listOptions); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,32 +987,32 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
|
||||||
if listOptions.FieldSelector, err = listOptions.FieldSelector.Transform(fn); err != nil {
|
if listOptions.FieldSelector, err = listOptions.FieldSelector.Transform(fn); err != nil {
|
||||||
// TODO: allow bad request to set field causes based on query parameters
|
// TODO: allow bad request to set field causes based on query parameters
|
||||||
err = errors.NewBadRequest(err.Error())
|
err = errors.NewBadRequest(err.Error())
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options := &metav1.DeleteOptions{}
|
options := &metav1.DeleteOptions{}
|
||||||
if checkBody {
|
if checkBody {
|
||||||
body, err := readBody(req.Request)
|
body, err := readBody(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(body) > 0 {
|
if len(body) > 0 {
|
||||||
s, err := negotiation.NegotiateInputSerializer(req.Request, scope.Serializer)
|
s, err := negotiation.NegotiateInputSerializer(req, scope.Serializer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions")
|
defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions")
|
||||||
obj, _, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
|
obj, _, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if obj != options {
|
if obj != options {
|
||||||
scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), res.ResponseWriter, req.Request)
|
scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1030,7 +1022,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
|
||||||
return r.DeleteCollection(ctx, options, &listOptions)
|
return r.DeleteCollection(ctx, options, &listOptions)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1048,12 +1040,12 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
|
||||||
// when a non-status response is returned, set the self link
|
// when a non-status response is returned, set the self link
|
||||||
if _, ok := result.(*metav1.Status); !ok {
|
if _, ok := result.(*metav1.Status); !ok {
|
||||||
if _, err := setListSelfLink(result, req, scope.Namer); err != nil {
|
if _, err := setListSelfLink(result, req, scope.Namer); err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responsewriters.WriteObjectNegotiated(scope.Serializer, scope.Kind.GroupVersion(), w, req.Request, http.StatusOK, result)
|
responsewriters.WriteObjectNegotiated(scope.Serializer, scope.Kind.GroupVersion(), w, req, http.StatusOK, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1113,9 +1105,9 @@ func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime
|
||||||
|
|
||||||
// setSelfLink sets the self link of an object (or the child items in a list) to the base URL of the request
|
// setSelfLink sets the self link of an object (or the child items in a list) to the base URL of the request
|
||||||
// plus the path and query generated by the provided linkFunc
|
// plus the path and query generated by the provided linkFunc
|
||||||
func setSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) error {
|
func setSelfLink(obj runtime.Object, req *http.Request, namer ScopeNamer) error {
|
||||||
// TODO: SelfLink generation should return a full URL?
|
// TODO: SelfLink generation should return a full URL?
|
||||||
uri, err := namer.GenerateLink(req.Request, obj)
|
uri, err := namer.GenerateLink(req, obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1160,12 +1152,12 @@ func checkName(obj runtime.Object, name, namespace string, namer ScopeNamer) err
|
||||||
|
|
||||||
// setListSelfLink sets the self link of a list to the base URL, then sets the self links
|
// setListSelfLink sets the self link of a list to the base URL, then sets the self links
|
||||||
// on all child objects returned. Returns the number of items in the list.
|
// on all child objects returned. Returns the number of items in the list.
|
||||||
func setListSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) (int, error) {
|
func setListSelfLink(obj runtime.Object, req *http.Request, namer ScopeNamer) (int, error) {
|
||||||
if !meta.IsListType(obj) {
|
if !meta.IsListType(obj) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
uri, err := namer.GenerateListLink(req.Request)
|
uri, err := namer.GenerateListLink(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ import (
|
||||||
"k8s.io/apiserver/pkg/server/httplog"
|
"k8s.io/apiserver/pkg/server/httplog"
|
||||||
"k8s.io/apiserver/pkg/util/wsstream"
|
"k8s.io/apiserver/pkg/util/wsstream"
|
||||||
|
|
||||||
"github.com/emicklei/go-restful"
|
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,18 +61,18 @@ func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
|
||||||
|
|
||||||
// serveWatch handles serving requests to the server
|
// serveWatch handles serving requests to the server
|
||||||
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
|
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
|
||||||
func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Request, res *restful.Response, timeout time.Duration) {
|
func serveWatch(watcher watch.Interface, scope RequestScope, req *http.Request, w http.ResponseWriter, timeout time.Duration) {
|
||||||
// negotiate for the stream serializer
|
// negotiate for the stream serializer
|
||||||
serializer, err := negotiation.NegotiateOutputStreamSerializer(req.Request, scope.Serializer)
|
serializer, err := negotiation.NegotiateOutputStreamSerializer(req, scope.Serializer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, res.ResponseWriter, req.Request)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
framer := serializer.StreamSerializer.Framer
|
framer := serializer.StreamSerializer.Framer
|
||||||
streamSerializer := serializer.StreamSerializer.Serializer
|
streamSerializer := serializer.StreamSerializer.Serializer
|
||||||
embedded := serializer.Serializer
|
embedded := serializer.Serializer
|
||||||
if framer == nil {
|
if framer == nil {
|
||||||
scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), res.ResponseWriter, req.Request)
|
scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
encoder := scope.Serializer.EncoderForVersion(streamSerializer, scope.Kind.GroupVersion())
|
encoder := scope.Serializer.EncoderForVersion(streamSerializer, scope.Kind.GroupVersion())
|
||||||
|
@ -107,7 +106,7 @@ func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Reques
|
||||||
TimeoutFactory: &realTimeoutFactory{timeout},
|
TimeoutFactory: &realTimeoutFactory{timeout},
|
||||||
}
|
}
|
||||||
|
|
||||||
server.ServeHTTP(res.ResponseWriter, req.Request)
|
server.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.
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers"
|
"k8s.io/apiserver/pkg/endpoints/handlers"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
|
||||||
"k8s.io/apiserver/pkg/endpoints/metrics"
|
"k8s.io/apiserver/pkg/endpoints/metrics"
|
||||||
|
@ -560,9 +561,9 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
case "GET": // Get a resource.
|
case "GET": // Get a resource.
|
||||||
var handler restful.RouteFunction
|
var handler restful.RouteFunction
|
||||||
if isGetterWithOptions {
|
if isGetterWithOptions {
|
||||||
handler = handlers.GetResourceWithOptions(getterWithOptions, reqScope)
|
handler = restfulGetResourceWithOptions(getterWithOptions, reqScope, hasSubresource)
|
||||||
} else {
|
} else {
|
||||||
handler = handlers.GetResource(getter, exporter, reqScope)
|
handler = restfulGetResource(getter, exporter, reqScope)
|
||||||
}
|
}
|
||||||
handler = metrics.InstrumentRouteFunc(action.Verb, resource, handler)
|
handler = metrics.InstrumentRouteFunc(action.Verb, resource, handler)
|
||||||
doc := "read the specified " + kind
|
doc := "read the specified " + kind
|
||||||
|
@ -593,7 +594,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
if hasSubresource {
|
if hasSubresource {
|
||||||
doc = "list " + subresource + " of objects of kind " + kind
|
doc = "list " + subresource + " of objects of kind " + kind
|
||||||
}
|
}
|
||||||
handler := metrics.InstrumentRouteFunc(action.Verb, resource, handlers.ListResource(lister, watcher, reqScope, false, a.minRequestTimeout))
|
handler := metrics.InstrumentRouteFunc(action.Verb, resource, restfulListResource(lister, watcher, reqScope, false, a.minRequestTimeout))
|
||||||
route := ws.GET(action.Path).To(handler).
|
route := ws.GET(action.Path).To(handler).
|
||||||
Doc(doc).
|
Doc(doc).
|
||||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||||
|
@ -625,7 +626,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
if hasSubresource {
|
if hasSubresource {
|
||||||
doc = "replace " + subresource + " of the specified " + kind
|
doc = "replace " + subresource + " of the specified " + kind
|
||||||
}
|
}
|
||||||
handler := metrics.InstrumentRouteFunc(action.Verb, resource, handlers.UpdateResource(updater, reqScope, a.group.Typer, admit))
|
handler := metrics.InstrumentRouteFunc(action.Verb, resource, restfulUpdateResource(updater, reqScope, a.group.Typer, admit))
|
||||||
route := ws.PUT(action.Path).To(handler).
|
route := ws.PUT(action.Path).To(handler).
|
||||||
Doc(doc).
|
Doc(doc).
|
||||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||||
|
@ -641,7 +642,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
if hasSubresource {
|
if hasSubresource {
|
||||||
doc = "partially update " + subresource + " of the specified " + kind
|
doc = "partially update " + subresource + " of the specified " + kind
|
||||||
}
|
}
|
||||||
handler := metrics.InstrumentRouteFunc(action.Verb, resource, handlers.PatchResource(patcher, reqScope, admit, mapping.ObjectConvertor))
|
handler := metrics.InstrumentRouteFunc(action.Verb, resource, restfulPatchResource(patcher, reqScope, admit, mapping.ObjectConvertor))
|
||||||
route := ws.PATCH(action.Path).To(handler).
|
route := ws.PATCH(action.Path).To(handler).
|
||||||
Doc(doc).
|
Doc(doc).
|
||||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||||
|
@ -656,9 +657,9 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
case "POST": // Create a resource.
|
case "POST": // Create a resource.
|
||||||
var handler restful.RouteFunction
|
var handler restful.RouteFunction
|
||||||
if isNamedCreater {
|
if isNamedCreater {
|
||||||
handler = handlers.CreateNamedResource(namedCreater, reqScope, a.group.Typer, admit)
|
handler = restfulCreateNamedResource(namedCreater, reqScope, a.group.Typer, admit)
|
||||||
} else {
|
} else {
|
||||||
handler = handlers.CreateResource(creater, reqScope, a.group.Typer, admit)
|
handler = restfulCreateResource(creater, reqScope, a.group.Typer, admit)
|
||||||
}
|
}
|
||||||
handler = metrics.InstrumentRouteFunc(action.Verb, resource, handler)
|
handler = metrics.InstrumentRouteFunc(action.Verb, resource, handler)
|
||||||
article := getArticleForNoun(kind, " ")
|
article := getArticleForNoun(kind, " ")
|
||||||
|
@ -682,7 +683,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
if hasSubresource {
|
if hasSubresource {
|
||||||
doc = "delete " + subresource + " of" + article + kind
|
doc = "delete " + subresource + " of" + article + kind
|
||||||
}
|
}
|
||||||
handler := metrics.InstrumentRouteFunc(action.Verb, resource, handlers.DeleteResource(gracefulDeleter, isGracefulDeleter, reqScope, admit))
|
handler := metrics.InstrumentRouteFunc(action.Verb, resource, restfulDeleteResource(gracefulDeleter, isGracefulDeleter, reqScope, admit))
|
||||||
route := ws.DELETE(action.Path).To(handler).
|
route := ws.DELETE(action.Path).To(handler).
|
||||||
Doc(doc).
|
Doc(doc).
|
||||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||||
|
@ -703,7 +704,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
if hasSubresource {
|
if hasSubresource {
|
||||||
doc = "delete collection of " + subresource + " of a " + kind
|
doc = "delete collection of " + subresource + " of a " + kind
|
||||||
}
|
}
|
||||||
handler := metrics.InstrumentRouteFunc(action.Verb, resource, handlers.DeleteCollection(collectionDeleter, isCollectionDeleter, reqScope, admit))
|
handler := metrics.InstrumentRouteFunc(action.Verb, resource, restfulDeleteCollection(collectionDeleter, isCollectionDeleter, reqScope, admit))
|
||||||
route := ws.DELETE(action.Path).To(handler).
|
route := ws.DELETE(action.Path).To(handler).
|
||||||
Doc(doc).
|
Doc(doc).
|
||||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||||
|
@ -722,7 +723,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
if hasSubresource {
|
if hasSubresource {
|
||||||
doc = "watch changes to " + subresource + " of an object of kind " + kind
|
doc = "watch changes to " + subresource + " of an object of kind " + kind
|
||||||
}
|
}
|
||||||
handler := metrics.InstrumentRouteFunc(action.Verb, resource, handlers.ListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
|
handler := metrics.InstrumentRouteFunc(action.Verb, resource, restfulListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
|
||||||
route := ws.GET(action.Path).To(handler).
|
route := ws.GET(action.Path).To(handler).
|
||||||
Doc(doc).
|
Doc(doc).
|
||||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||||
|
@ -741,7 +742,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
if hasSubresource {
|
if hasSubresource {
|
||||||
doc = "watch individual changes to a list of " + subresource + " of " + kind
|
doc = "watch individual changes to a list of " + subresource + " of " + kind
|
||||||
}
|
}
|
||||||
handler := metrics.InstrumentRouteFunc(action.Verb, resource, handlers.ListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
|
handler := metrics.InstrumentRouteFunc(action.Verb, resource, restfulListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
|
||||||
route := ws.GET(action.Path).To(handler).
|
route := ws.GET(action.Path).To(handler).
|
||||||
Doc(doc).
|
Doc(doc).
|
||||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||||
|
@ -772,7 +773,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||||
if hasSubresource {
|
if hasSubresource {
|
||||||
doc = "connect " + method + " requests to " + subresource + " of " + kind
|
doc = "connect " + method + " requests to " + subresource + " of " + kind
|
||||||
}
|
}
|
||||||
handler := metrics.InstrumentRouteFunc(action.Verb, resource, handlers.ConnectResource(connecter, reqScope, admit, path))
|
handler := metrics.InstrumentRouteFunc(action.Verb, resource, restfulConnectResource(connecter, reqScope, admit, path, hasSubresource))
|
||||||
route := ws.Method(method).Path(action.Path).
|
route := ws.Method(method).Path(action.Path).
|
||||||
To(handler).
|
To(handler).
|
||||||
Doc(doc).
|
Doc(doc).
|
||||||
|
@ -979,3 +980,63 @@ func isVowel(c rune) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restfulListResource(r rest.Lister, rw rest.Watcher, scope handlers.RequestScope, forceWatch bool, minRequestTimeout time.Duration) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.ListResource(r, rw, scope, forceWatch, minRequestTimeout)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulCreateNamedResource(r rest.NamedCreater, scope handlers.RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.CreateNamedResource(r, scope, typer, admit)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulCreateResource(r rest.Creater, scope handlers.RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.CreateResource(r, scope, typer, admit)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulDeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.DeleteResource(r, allowsOptions, scope, admit)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulDeleteCollection(r rest.CollectionDeleter, checkBody bool, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.DeleteCollection(r, checkBody, scope, admit)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulUpdateResource(r rest.Updater, scope handlers.RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.UpdateResource(r, scope, typer, admit)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulPatchResource(r rest.Patcher, scope handlers.RequestScope, admit admission.Interface, converter runtime.ObjectConvertor) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.PatchResource(r, scope, admit, converter)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulGetResource(r rest.Getter, e rest.Exporter, scope handlers.RequestScope) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.GetResource(r, e, scope)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulGetResourceWithOptions(r rest.GetterWithOptions, scope handlers.RequestScope, isSubresource bool) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.GetResourceWithOptions(r, scope, isSubresource)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func restfulConnectResource(connecter rest.Connecter, scope handlers.RequestScope, admit admission.Interface, restPath string, isSubresource bool) restful.RouteFunction {
|
||||||
|
return func(req *restful.Request, res *restful.Response) {
|
||||||
|
handlers.ConnectResource(connecter, scope, admit, restPath, isSubresource)(res.ResponseWriter, req.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue