mirror of https://github.com/k3s-io/k3s
Store RequestInfo in Context
... in order to replace the manual RequestInfoResolver dependency injection through out the code.pull/6/head
parent
53631cf4f8
commit
10cbaf7ce0
|
@ -40,7 +40,6 @@ import (
|
||||||
|
|
||||||
type APIInstaller struct {
|
type APIInstaller struct {
|
||||||
group *APIGroupVersion
|
group *APIGroupVersion
|
||||||
info *RequestInfoResolver
|
|
||||||
prefix string // Path prefix where API resources are to be registered.
|
prefix string // Path prefix where API resources are to be registered.
|
||||||
minRequestTimeout time.Duration
|
minRequestTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
@ -70,8 +69,7 @@ func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []unversion
|
||||||
prefix: a.prefix + "/proxy/",
|
prefix: a.prefix + "/proxy/",
|
||||||
storage: a.group.Storage,
|
storage: a.group.Storage,
|
||||||
serializer: a.group.Serializer,
|
serializer: a.group.Serializer,
|
||||||
context: a.group.Context,
|
mapper: a.group.Context,
|
||||||
requestInfoResolver: a.info,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register the paths in a deterministic (sorted) order to get a deterministic swagger spec.
|
// Register the paths in a deterministic (sorted) order to get a deterministic swagger spec.
|
||||||
|
|
|
@ -67,10 +67,6 @@ type APIGroupVersion struct {
|
||||||
// GroupVersion is the external group version
|
// GroupVersion is the external group version
|
||||||
GroupVersion unversioned.GroupVersion
|
GroupVersion unversioned.GroupVersion
|
||||||
|
|
||||||
// RequestInfoResolver is used to parse URLs for the legacy proxy handler. Don't use this for anything else
|
|
||||||
// TODO: refactor proxy handler to use sub resources
|
|
||||||
RequestInfoResolver *RequestInfoResolver
|
|
||||||
|
|
||||||
// OptionsExternalVersion controls the Kubernetes APIVersion used for common objects in the apiserver
|
// OptionsExternalVersion controls the Kubernetes APIVersion used for common objects in the apiserver
|
||||||
// schema like api.Status, api.DeleteOptions, and api.ListOptions. Other implementors may
|
// schema like api.Status, api.DeleteOptions, and api.ListOptions. Other implementors may
|
||||||
// define a version "v1beta1" but want to use the Kubernetes "v1" internal objects. If
|
// define a version "v1beta1" but want to use the Kubernetes "v1" internal objects. If
|
||||||
|
@ -175,7 +171,6 @@ func (g *APIGroupVersion) newInstaller() *APIInstaller {
|
||||||
prefix := path.Join(g.Root, g.GroupVersion.Group, g.GroupVersion.Version)
|
prefix := path.Join(g.Root, g.GroupVersion.Group, g.GroupVersion.Version)
|
||||||
installer := &APIInstaller{
|
installer := &APIInstaller{
|
||||||
group: g,
|
group: g,
|
||||||
info: g.RequestInfoResolver,
|
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
minRequestTimeout: g.MinRequestTimeout,
|
minRequestTimeout: g.MinRequestTimeout,
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,11 +39,14 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/api/rest"
|
"k8s.io/kubernetes/pkg/api/rest"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver/filters"
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
apiservertesting "k8s.io/kubernetes/pkg/apiserver/testing"
|
apiservertesting "k8s.io/kubernetes/pkg/apiserver/testing"
|
||||||
"k8s.io/kubernetes/pkg/fields"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/util/diff"
|
"k8s.io/kubernetes/pkg/util/diff"
|
||||||
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
"k8s.io/kubernetes/pkg/watch"
|
"k8s.io/kubernetes/pkg/watch"
|
||||||
"k8s.io/kubernetes/pkg/watch/versioned"
|
"k8s.io/kubernetes/pkg/watch/versioned"
|
||||||
"k8s.io/kubernetes/plugin/pkg/admission/admit"
|
"k8s.io/kubernetes/plugin/pkg/admission/admit"
|
||||||
|
@ -259,8 +262,6 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission.
|
||||||
template := APIGroupVersion{
|
template := APIGroupVersion{
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
|
|
||||||
RequestInfoResolver: newTestRequestInfoResolver(),
|
|
||||||
|
|
||||||
Creater: api.Scheme,
|
Creater: api.Scheme,
|
||||||
Convertor: api.Scheme,
|
Convertor: api.Scheme,
|
||||||
Copier: api.Scheme,
|
Copier: api.Scheme,
|
||||||
|
@ -2388,7 +2389,6 @@ func TestUpdateREST(t *testing.T) {
|
||||||
return &APIGroupVersion{
|
return &APIGroupVersion{
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
Root: "/" + prefix,
|
Root: "/" + prefix,
|
||||||
RequestInfoResolver: newTestRequestInfoResolver(),
|
|
||||||
Creater: api.Scheme,
|
Creater: api.Scheme,
|
||||||
Convertor: api.Scheme,
|
Convertor: api.Scheme,
|
||||||
Copier: api.Scheme,
|
Copier: api.Scheme,
|
||||||
|
@ -2473,7 +2473,6 @@ func TestParentResourceIsRequired(t *testing.T) {
|
||||||
"simple/sub": storage,
|
"simple/sub": storage,
|
||||||
},
|
},
|
||||||
Root: "/" + prefix,
|
Root: "/" + prefix,
|
||||||
RequestInfoResolver: newTestRequestInfoResolver(),
|
|
||||||
Creater: api.Scheme,
|
Creater: api.Scheme,
|
||||||
Convertor: api.Scheme,
|
Convertor: api.Scheme,
|
||||||
Copier: api.Scheme,
|
Copier: api.Scheme,
|
||||||
|
@ -2505,7 +2504,6 @@ func TestParentResourceIsRequired(t *testing.T) {
|
||||||
"simple/sub": storage,
|
"simple/sub": storage,
|
||||||
},
|
},
|
||||||
Root: "/" + prefix,
|
Root: "/" + prefix,
|
||||||
RequestInfoResolver: newTestRequestInfoResolver(),
|
|
||||||
Creater: api.Scheme,
|
Creater: api.Scheme,
|
||||||
Convertor: api.Scheme,
|
Convertor: api.Scheme,
|
||||||
Copier: api.Scheme,
|
Copier: api.Scheme,
|
||||||
|
@ -3131,8 +3129,6 @@ func TestXGSubresource(t *testing.T) {
|
||||||
group := APIGroupVersion{
|
group := APIGroupVersion{
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
|
|
||||||
RequestInfoResolver: newTestRequestInfoResolver(),
|
|
||||||
|
|
||||||
Creater: api.Scheme,
|
Creater: api.Scheme,
|
||||||
Convertor: api.Scheme,
|
Convertor: api.Scheme,
|
||||||
Copier: api.Scheme,
|
Copier: api.Scheme,
|
||||||
|
@ -3159,8 +3155,7 @@ func TestXGSubresource(t *testing.T) {
|
||||||
panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
|
panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := defaultAPIServer{mux, container}
|
server := newTestServer(defaultAPIServer{mux, container})
|
||||||
server := httptest.NewServer(handler)
|
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/subsimple")
|
resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/subsimple")
|
||||||
|
@ -3249,3 +3244,16 @@ func BenchmarkUpdateProtobuf(b *testing.B) {
|
||||||
}
|
}
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestServer(handler http.Handler) *httptest.Server {
|
||||||
|
handler = filters.WithRequestInfo(handler, newTestRequestInfoResolver(), requestContextMapper)
|
||||||
|
handler = api.WithRequestContext(handler, requestContextMapper)
|
||||||
|
return httptest.NewServer(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestRequestInfoResolver() *request.RequestInfoResolver {
|
||||||
|
return &request.RequestInfoResolver{
|
||||||
|
APIPrefixes: sets.NewString("api", "apis"),
|
||||||
|
GrouplessAPIPrefixes: sets.NewString("api"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -76,6 +76,13 @@ func notFound(w http.ResponseWriter, req *http.Request) {
|
||||||
fmt.Fprintf(w, "Not Found: %#v", req.RequestURI)
|
fmt.Fprintf(w, "Not Found: %#v", req.RequestURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// internalError renders a simple internal error
|
||||||
|
func internalError(w http.ResponseWriter, req *http.Request, err error) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
fmt.Fprintf(w, "Internal Server Error: %#v", req.RequestURI)
|
||||||
|
runtime.HandleError(err)
|
||||||
|
}
|
||||||
|
|
||||||
// errAPIPrefixNotFound indicates that a RequestInfo resolution failed because the request isn't under
|
// errAPIPrefixNotFound indicates that a RequestInfo resolution failed because the request isn't under
|
||||||
// any known API prefixes
|
// any known API prefixes
|
||||||
type errAPIPrefixNotFound struct {
|
type errAPIPrefixNotFound struct {
|
||||||
|
|
|
@ -89,7 +89,11 @@ func WithAudit(handler http.Handler, attributeGetter RequestAttributeGetter, out
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
attribs := attributeGetter.GetAttribs(req)
|
attribs, err := attributeGetter.GetAttribs(req)
|
||||||
|
if err != nil {
|
||||||
|
internalError(w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
asuser := req.Header.Get("Impersonate-User")
|
asuser := req.Header.Get("Impersonate-User")
|
||||||
if len(asuser) == 0 {
|
if len(asuser) == 0 {
|
||||||
asuser = "<self>"
|
asuser = "<self>"
|
||||||
|
|
|
@ -29,9 +29,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apiserver"
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
"k8s.io/kubernetes/pkg/auth/user"
|
"k8s.io/kubernetes/pkg/auth/user"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type simpleResponseWriter struct {
|
type simpleResponseWriter struct {
|
||||||
|
@ -72,22 +71,14 @@ func (*fakeHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeRequestContextMapper struct{}
|
|
||||||
|
|
||||||
func (*fakeRequestContextMapper) Get(req *http.Request) (api.Context, bool) {
|
|
||||||
return api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "admin"}), true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*fakeRequestContextMapper) Update(req *http.Request, context api.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAudit(t *testing.T) {
|
func TestAudit(t *testing.T) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
attributeGetter := NewRequestAttributeGetter(&fakeRequestContextMapper{},
|
|
||||||
&apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")})
|
attributeGetter := NewRequestAttributeGetter(&fakeRequestContextMapper{
|
||||||
|
user: &user.DefaultInfo{Name: "admin"},
|
||||||
|
})
|
||||||
handler := WithAudit(&fakeHTTPHandler{}, attributeGetter, &buf)
|
handler := WithAudit(&fakeHTTPHandler{}, attributeGetter, &buf)
|
||||||
|
|
||||||
req, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
|
req, _ := http.NewRequest("GET", "/api/v1/namespaces/default/pods", nil)
|
||||||
req.RemoteAddr = "127.0.0.1"
|
req.RemoteAddr = "127.0.0.1"
|
||||||
handler.ServeHTTP(httptest.NewRecorder(), req)
|
handler.ServeHTTP(httptest.NewRecorder(), req)
|
||||||
|
@ -110,3 +101,26 @@ func TestAudit(t *testing.T) {
|
||||||
t.Errorf("Unexpected second line of audit: %s", line[1])
|
t.Errorf("Unexpected second line of audit: %s", line[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeRequestContextMapper struct {
|
||||||
|
user *user.DefaultInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *fakeRequestContextMapper) Get(req *http.Request) (api.Context, bool) {
|
||||||
|
ctx := api.NewContext()
|
||||||
|
if m.user != nil {
|
||||||
|
ctx = api.WithUser(ctx, m.user)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver := newTestRequestInfoResolver()
|
||||||
|
info, err := resolver.GetRequestInfo(req)
|
||||||
|
if err == nil {
|
||||||
|
ctx = request.WithRequestInfo(ctx, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*fakeRequestContextMapper) Update(req *http.Request, context api.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -17,12 +17,13 @@ limitations under the License.
|
||||||
package filters
|
package filters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apiserver"
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +34,12 @@ func WithAuthorization(handler http.Handler, getAttribs RequestAttributeGetter,
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
authorized, reason, err := a.Authorize(getAttribs.GetAttribs(req))
|
attrs, err := getAttribs.GetAttribs(req)
|
||||||
|
if err != nil {
|
||||||
|
internalError(w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authorized, reason, err := a.Authorize(attrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
internalError(w, req, err)
|
internalError(w, req, err)
|
||||||
return
|
return
|
||||||
|
@ -49,31 +55,35 @@ func WithAuthorization(handler http.Handler, getAttribs RequestAttributeGetter,
|
||||||
|
|
||||||
// RequestAttributeGetter is a function that extracts authorizer.Attributes from an http.Request
|
// RequestAttributeGetter is a function that extracts authorizer.Attributes from an http.Request
|
||||||
type RequestAttributeGetter interface {
|
type RequestAttributeGetter interface {
|
||||||
GetAttribs(req *http.Request) (attribs authorizer.Attributes)
|
GetAttribs(req *http.Request) (authorizer.Attributes, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type requestAttributeGetter struct {
|
type requestAttributeGetter struct {
|
||||||
requestContextMapper api.RequestContextMapper
|
requestContextMapper api.RequestContextMapper
|
||||||
requestInfoResolver *apiserver.RequestInfoResolver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAttributeGetter returns an object which implements the RequestAttributeGetter interface.
|
// NewAttributeGetter returns an object which implements the RequestAttributeGetter interface.
|
||||||
func NewRequestAttributeGetter(requestContextMapper api.RequestContextMapper, requestInfoResolver *apiserver.RequestInfoResolver) RequestAttributeGetter {
|
func NewRequestAttributeGetter(requestContextMapper api.RequestContextMapper) RequestAttributeGetter {
|
||||||
return &requestAttributeGetter{requestContextMapper, requestInfoResolver}
|
return &requestAttributeGetter{requestContextMapper}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attributes {
|
func (r *requestAttributeGetter) GetAttribs(req *http.Request) (authorizer.Attributes, error) {
|
||||||
attribs := authorizer.AttributesRecord{}
|
attribs := authorizer.AttributesRecord{}
|
||||||
|
|
||||||
ctx, ok := r.requestContextMapper.Get(req)
|
ctx, ok := r.requestContextMapper.Get(req)
|
||||||
if ok {
|
if !ok {
|
||||||
|
return nil, errors.New("no context found for request")
|
||||||
|
}
|
||||||
|
|
||||||
user, ok := api.UserFrom(ctx)
|
user, ok := api.UserFrom(ctx)
|
||||||
if ok {
|
if ok {
|
||||||
attribs.User = user
|
attribs.User = user
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
requestInfo, _ := r.requestInfoResolver.GetRequestInfo(req)
|
requestInfo, found := request.RequestInfoFrom(ctx)
|
||||||
|
if !found {
|
||||||
|
return nil, errors.New("no RequestInfo found in the context")
|
||||||
|
}
|
||||||
|
|
||||||
// Start with common attributes that apply to resource and non-resource requests
|
// Start with common attributes that apply to resource and non-resource requests
|
||||||
attribs.ResourceRequest = requestInfo.IsResourceRequest
|
attribs.ResourceRequest = requestInfo.IsResourceRequest
|
||||||
|
@ -87,5 +97,5 @@ func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attrib
|
||||||
attribs.Namespace = requestInfo.Namespace
|
attribs.Namespace = requestInfo.Namespace
|
||||||
attribs.Name = requestInfo.Name
|
attribs.Name = requestInfo.Name
|
||||||
|
|
||||||
return &attribs
|
return &attribs, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,18 @@ package filters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/apiserver"
|
|
||||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetAttribs(t *testing.T) {
|
func TestGetAttribs(t *testing.T) {
|
||||||
r := &requestAttributeGetter{api.NewRequestContextMapper(), &apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")}}
|
mapper := api.NewRequestContextMapper()
|
||||||
|
attributeGetter := NewRequestAttributeGetter(mapper)
|
||||||
|
|
||||||
testcases := map[string]struct {
|
testcases := map[string]struct {
|
||||||
Verb string
|
Verb string
|
||||||
|
@ -103,8 +103,20 @@ func TestGetAttribs(t *testing.T) {
|
||||||
|
|
||||||
for k, tc := range testcases {
|
for k, tc := range testcases {
|
||||||
req, _ := http.NewRequest(tc.Verb, tc.Path, nil)
|
req, _ := http.NewRequest(tc.Verb, tc.Path, nil)
|
||||||
attribs := r.GetAttribs(req)
|
req.RemoteAddr = "127.0.0.1"
|
||||||
if !reflect.DeepEqual(attribs, tc.ExpectedAttributes) {
|
|
||||||
|
var attribs authorizer.Attributes
|
||||||
|
var err error
|
||||||
|
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
attribs, err = attributeGetter.GetAttribs(req)
|
||||||
|
})
|
||||||
|
handler = WithRequestInfo(handler, newTestRequestInfoResolver(), mapper)
|
||||||
|
handler = api.WithRequestContext(handler, mapper)
|
||||||
|
handler.ServeHTTP(httptest.NewRecorder(), req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: unexpected error: %v", k, err)
|
||||||
|
} else if !reflect.DeepEqual(attribs, tc.ExpectedAttributes) {
|
||||||
t.Errorf("%s: expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedAttributes, attribs)
|
t.Errorf("%s: expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedAttributes, attribs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithRequestInfo attaches a RequestInfo to the context.
|
||||||
|
func WithRequestInfo(handler http.Handler, resolver *request.RequestInfoResolver, requestContextMapper api.RequestContextMapper) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
ctx, ok := requestContextMapper.Get(req)
|
||||||
|
if !ok {
|
||||||
|
internalError(w, req, errors.New("no context found for request"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := resolver.GetRequestInfo(req)
|
||||||
|
if err != nil {
|
||||||
|
internalError(w, req, fmt.Errorf("failed to create RequestInfo: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
requestContextMapper.Update(req, request.WithRequestInfo(ctx, info))
|
||||||
|
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestRequestInfoResolver() *request.RequestInfoResolver {
|
||||||
|
return &request.RequestInfoResolver{
|
||||||
|
APIPrefixes: sets.NewString("api", "apis"),
|
||||||
|
GrouplessAPIPrefixes: sets.NewString("api"),
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package apiserver
|
package apiserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -27,7 +28,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/errors"
|
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
||||||
"k8s.io/kubernetes/pkg/api/rest"
|
"k8s.io/kubernetes/pkg/api/rest"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/apiserver/metrics"
|
"k8s.io/kubernetes/pkg/apiserver/metrics"
|
||||||
|
@ -38,6 +39,7 @@ import (
|
||||||
proxyutil "k8s.io/kubernetes/pkg/util/proxy"
|
proxyutil "k8s.io/kubernetes/pkg/util/proxy"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProxyHandler provides a http.Handler which will proxy traffic to locations
|
// ProxyHandler provides a http.Handler which will proxy traffic to locations
|
||||||
|
@ -46,8 +48,7 @@ type ProxyHandler struct {
|
||||||
prefix string
|
prefix string
|
||||||
storage map[string]rest.Storage
|
storage map[string]rest.Storage
|
||||||
serializer runtime.NegotiatedSerializer
|
serializer runtime.NegotiatedSerializer
|
||||||
context api.RequestContextMapper
|
mapper api.RequestContextMapper
|
||||||
requestInfoResolver *RequestInfoResolver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
@ -59,8 +60,19 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
reqStart := time.Now()
|
reqStart := time.Now()
|
||||||
defer metrics.Monitor(&verb, &apiResource, net.GetHTTPClient(req), w.Header().Get("Content-Type"), httpCode, reqStart)
|
defer metrics.Monitor(&verb, &apiResource, net.GetHTTPClient(req), w.Header().Get("Content-Type"), httpCode, reqStart)
|
||||||
|
|
||||||
requestInfo, err := r.requestInfoResolver.GetRequestInfo(req)
|
ctx, ok := r.mapper.Get(req)
|
||||||
if err != nil || !requestInfo.IsResourceRequest {
|
if !ok {
|
||||||
|
internalError(w, req, errors.New("Error getting request context"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
requestInfo, ok := request.RequestInfoFrom(ctx)
|
||||||
|
if !ok {
|
||||||
|
internalError(w, req, errors.New("Error getting RequestInfo from context"))
|
||||||
|
httpCode = http.StatusInternalServerError
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !requestInfo.IsResourceRequest {
|
||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
httpCode = http.StatusNotFound
|
httpCode = http.StatusNotFound
|
||||||
return
|
return
|
||||||
|
@ -68,10 +80,6 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
verb = requestInfo.Verb
|
verb = requestInfo.Verb
|
||||||
namespace, resource, parts := requestInfo.Namespace, requestInfo.Resource, requestInfo.Parts
|
namespace, resource, parts := requestInfo.Namespace, requestInfo.Resource, requestInfo.Parts
|
||||||
|
|
||||||
ctx, ok := r.context.Get(req)
|
|
||||||
if !ok {
|
|
||||||
ctx = api.NewContext()
|
|
||||||
}
|
|
||||||
ctx = api.WithNamespace(ctx, namespace)
|
ctx = api.WithNamespace(ctx, namespace)
|
||||||
if len(parts) < 2 {
|
if len(parts) < 2 {
|
||||||
notFound(w, req)
|
notFound(w, req)
|
||||||
|
@ -104,7 +112,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
redirector, ok := storage.(rest.Redirector)
|
redirector, ok := storage.(rest.Redirector)
|
||||||
if !ok {
|
if !ok {
|
||||||
httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource)
|
httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource)
|
||||||
httpCode = errorNegotiated(errors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.serializer, gv, w, req)
|
httpCode = errorNegotiated(apierrors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.serializer, gv, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@ func TestProxyRequestContentLengthAndTransferEncoding(t *testing.T) {
|
||||||
expectedResourceNamespace: "default",
|
expectedResourceNamespace: "default",
|
||||||
}
|
}
|
||||||
namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
||||||
server := httptest.NewServer(namespaceHandler)
|
server := newTestServer(namespaceHandler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
// Dial the proxy server
|
// Dial the proxy server
|
||||||
|
@ -310,7 +310,7 @@ func TestProxy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
||||||
namespaceServer := httptest.NewServer(namespaceHandler)
|
namespaceServer := newTestServer(namespaceHandler)
|
||||||
defer namespaceServer.Close()
|
defer namespaceServer.Close()
|
||||||
|
|
||||||
// test each supported URL pattern for finding the redirection resource in the proxy in a particular namespace
|
// test each supported URL pattern for finding the redirection resource in the proxy in a particular namespace
|
||||||
|
@ -432,7 +432,7 @@ func TestProxyUpgrade(t *testing.T) {
|
||||||
|
|
||||||
namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
||||||
|
|
||||||
server := httptest.NewServer(namespaceHandler)
|
server := newTestServer(namespaceHandler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
ws, err := websocket.Dial("ws://"+server.Listener.Addr().String()+"/"+prefix+"/"+newGroupVersion.Group+"/"+newGroupVersion.Version+"/proxy/namespaces/myns/foo/123", "", "http://127.0.0.1/")
|
ws, err := websocket.Dial("ws://"+server.Listener.Addr().String()+"/"+prefix+"/"+newGroupVersion.Group+"/"+newGroupVersion.Version+"/proxy/namespaces/myns/foo/123", "", "http://127.0.0.1/")
|
||||||
|
@ -496,7 +496,7 @@ func TestRedirectOnMissingTrailingSlash(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
handler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
|
||||||
server := httptest.NewServer(handler)
|
server := newTestServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
proxyTestPattern := "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/proxy/namespaces/ns/foo/id" + item.path
|
proxyTestPattern := "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/proxy/namespaces/ns/foo/id" + item.path
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package request contains everything around extracting info from
|
||||||
|
// a http request object.
|
||||||
|
// TODO: this package is temporary. Handlers must move into pkg/apiserver/handlers to avoid dependency cycle
|
||||||
|
package request // import "k8s.io/kubernetes/pkg/apiserver/request"
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015 The Kubernetes Authors.
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package apiserver
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -68,8 +68,8 @@ var namespaceSubresources = sets.NewString("status", "finalize")
|
||||||
var NamespaceSubResourcesForTest = sets.NewString(namespaceSubresources.List()...)
|
var NamespaceSubResourcesForTest = sets.NewString(namespaceSubresources.List()...)
|
||||||
|
|
||||||
type RequestInfoResolver struct {
|
type RequestInfoResolver struct {
|
||||||
APIPrefixes sets.String
|
APIPrefixes sets.String // without leading and trailing slashes
|
||||||
GrouplessAPIPrefixes sets.String
|
GrouplessAPIPrefixes sets.String // without leading and trailing slashes
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO write an integration test against the swagger doc to test the RequestInfo and match up behavior to responses
|
// TODO write an integration test against the swagger doc to test the RequestInfo and match up behavior to responses
|
||||||
|
@ -103,7 +103,7 @@ type RequestInfoResolver struct {
|
||||||
// /api
|
// /api
|
||||||
// /healthz
|
// /healthz
|
||||||
// /
|
// /
|
||||||
func (r *RequestInfoResolver) GetRequestInfo(req *http.Request) (RequestInfo, error) {
|
func (r *RequestInfoResolver) GetRequestInfo(req *http.Request) (*RequestInfo, error) {
|
||||||
// start with a non-resource request until proven otherwise
|
// start with a non-resource request until proven otherwise
|
||||||
requestInfo := RequestInfo{
|
requestInfo := RequestInfo{
|
||||||
IsResourceRequest: false,
|
IsResourceRequest: false,
|
||||||
|
@ -114,12 +114,12 @@ func (r *RequestInfoResolver) GetRequestInfo(req *http.Request) (RequestInfo, er
|
||||||
currentParts := splitPath(req.URL.Path)
|
currentParts := splitPath(req.URL.Path)
|
||||||
if len(currentParts) < 3 {
|
if len(currentParts) < 3 {
|
||||||
// return a non-resource request
|
// return a non-resource request
|
||||||
return requestInfo, nil
|
return &requestInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !r.APIPrefixes.Has(currentParts[0]) {
|
if !r.APIPrefixes.Has(currentParts[0]) {
|
||||||
// return a non-resource request
|
// return a non-resource request
|
||||||
return requestInfo, nil
|
return &requestInfo, nil
|
||||||
}
|
}
|
||||||
requestInfo.APIPrefix = currentParts[0]
|
requestInfo.APIPrefix = currentParts[0]
|
||||||
currentParts = currentParts[1:]
|
currentParts = currentParts[1:]
|
||||||
|
@ -128,7 +128,7 @@ func (r *RequestInfoResolver) GetRequestInfo(req *http.Request) (RequestInfo, er
|
||||||
// one part (APIPrefix) has already been consumed, so this is actually "do we have four parts?"
|
// one part (APIPrefix) has already been consumed, so this is actually "do we have four parts?"
|
||||||
if len(currentParts) < 3 {
|
if len(currentParts) < 3 {
|
||||||
// return a non-resource request
|
// return a non-resource request
|
||||||
return requestInfo, nil
|
return &requestInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
requestInfo.APIGroup = currentParts[0]
|
requestInfo.APIGroup = currentParts[0]
|
||||||
|
@ -142,7 +142,7 @@ func (r *RequestInfoResolver) GetRequestInfo(req *http.Request) (RequestInfo, er
|
||||||
// handle input of form /{specialVerb}/*
|
// handle input of form /{specialVerb}/*
|
||||||
if specialVerbs.Has(currentParts[0]) {
|
if specialVerbs.Has(currentParts[0]) {
|
||||||
if len(currentParts) < 2 {
|
if len(currentParts) < 2 {
|
||||||
return requestInfo, fmt.Errorf("unable to determine kind and namespace from url, %v", req.URL)
|
return &requestInfo, fmt.Errorf("unable to determine kind and namespace from url, %v", req.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestInfo.Verb = currentParts[0]
|
requestInfo.Verb = currentParts[0]
|
||||||
|
@ -204,7 +204,25 @@ func (r *RequestInfoResolver) GetRequestInfo(req *http.Request) (RequestInfo, er
|
||||||
requestInfo.Verb = "deletecollection"
|
requestInfo.Verb = "deletecollection"
|
||||||
}
|
}
|
||||||
|
|
||||||
return requestInfo, nil
|
return &requestInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type requestInfoKeyType int
|
||||||
|
|
||||||
|
// requestInfoKey is the RequestInfo key for the context. It's of private type here. Because
|
||||||
|
// keys are interfaces and interfaces are equal when the type and the value is equal, this
|
||||||
|
// does not conflict with the keys defined in pkg/api.
|
||||||
|
const requestInfoKey requestInfoKeyType = iota
|
||||||
|
|
||||||
|
// WithRequestInfo returns a copy of parent in which the request info value is set
|
||||||
|
func WithRequestInfo(parent api.Context, info *RequestInfo) api.Context {
|
||||||
|
return api.WithValue(parent, requestInfoKey, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestInfoFrom returns the value of the RequestInfo key on the ctx
|
||||||
|
func RequestInfoFrom(ctx api.Context) (*RequestInfo, bool) {
|
||||||
|
info, ok := ctx.Value(requestInfoKey).(*RequestInfo)
|
||||||
|
return info, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// splitPath returns the segments for a URL path.
|
// splitPath returns the segments for a URL path.
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2014 The Kubernetes Authors.
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package apiserver
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -194,5 +194,8 @@ func TestGetNonAPIRequestInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestRequestInfoResolver() *RequestInfoResolver {
|
func newTestRequestInfoResolver() *RequestInfoResolver {
|
||||||
return &RequestInfoResolver{sets.NewString("api", "apis"), sets.NewString("api")}
|
return &RequestInfoResolver{
|
||||||
|
APIPrefixes: sets.NewString("api", "apis"),
|
||||||
|
GrouplessAPIPrefixes: sets.NewString("api"),
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -354,17 +354,21 @@ func (s *GenericAPIServer) buildHandlerChains(c *Config, handler http.Handler) (
|
||||||
|
|
||||||
// insecure filters
|
// insecure filters
|
||||||
insecure = handler
|
insecure = handler
|
||||||
insecure = genericfilters.WithPanicRecovery(insecure, s.NewRequestInfoResolver())
|
insecure = genericfilters.WithPanicRecovery(insecure, c.RequestContextMapper)
|
||||||
|
insecure = apiserverfilters.WithRequestInfo(insecure, s.NewRequestInfoResolver(), c.RequestContextMapper)
|
||||||
|
insecure = api.WithRequestContext(insecure, c.RequestContextMapper)
|
||||||
insecure = genericfilters.WithTimeoutForNonLongRunningRequests(insecure, c.LongRunningFunc)
|
insecure = genericfilters.WithTimeoutForNonLongRunningRequests(insecure, c.LongRunningFunc)
|
||||||
|
|
||||||
// secure filters
|
// secure filters
|
||||||
attributeGetter := apiserverfilters.NewRequestAttributeGetter(c.RequestContextMapper, s.NewRequestInfoResolver())
|
attributeGetter := apiserverfilters.NewRequestAttributeGetter(c.RequestContextMapper)
|
||||||
secure = handler
|
secure = handler
|
||||||
secure = apiserverfilters.WithAuthorization(secure, attributeGetter, c.Authorizer)
|
secure = apiserverfilters.WithAuthorization(secure, attributeGetter, c.Authorizer)
|
||||||
secure = apiserverfilters.WithImpersonation(secure, c.RequestContextMapper, c.Authorizer)
|
secure = apiserverfilters.WithImpersonation(secure, c.RequestContextMapper, c.Authorizer)
|
||||||
secure = apiserverfilters.WithAudit(secure, attributeGetter, c.AuditWriter) // before impersonation to read original user
|
secure = apiserverfilters.WithAudit(secure, attributeGetter, c.AuditWriter) // before impersonation to read original user
|
||||||
secure = authhandlers.WithAuthentication(secure, c.RequestContextMapper, c.Authenticator, authhandlers.Unauthorized(c.SupportsBasicAuth))
|
secure = authhandlers.WithAuthentication(secure, c.RequestContextMapper, c.Authenticator, authhandlers.Unauthorized(c.SupportsBasicAuth))
|
||||||
secure = genericfilters.WithPanicRecovery(secure, s.NewRequestInfoResolver())
|
secure = genericfilters.WithPanicRecovery(secure, c.RequestContextMapper)
|
||||||
|
secure = apiserverfilters.WithRequestInfo(secure, s.NewRequestInfoResolver(), c.RequestContextMapper)
|
||||||
|
secure = api.WithRequestContext(secure, c.RequestContextMapper)
|
||||||
secure = genericfilters.WithTimeoutForNonLongRunningRequests(secure, c.LongRunningFunc)
|
secure = genericfilters.WithTimeoutForNonLongRunningRequests(secure, c.LongRunningFunc)
|
||||||
secure = genericfilters.WithMaxInFlightLimit(secure, c.MaxRequestsInFlight, c.LongRunningFunc)
|
secure = genericfilters.WithMaxInFlightLimit(secure, c.MaxRequestsInFlight, c.LongRunningFunc)
|
||||||
|
|
||||||
|
|
|
@ -22,14 +22,15 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api/errors"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apiserver"
|
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
"k8s.io/kubernetes/pkg/httplog"
|
"k8s.io/kubernetes/pkg/httplog"
|
||||||
"k8s.io/kubernetes/pkg/util/runtime"
|
"k8s.io/kubernetes/pkg/util/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithPanicRecovery wraps an http Handler to recover and log panics.
|
// WithPanicRecovery wraps an http Handler to recover and log panics.
|
||||||
func WithPanicRecovery(handler http.Handler, resolver *apiserver.RequestInfoResolver) http.Handler {
|
func WithPanicRecovery(handler http.Handler, requestContextMapper api.RequestContextMapper) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
defer runtime.HandleCrash(func(err interface{}) {
|
defer runtime.HandleCrash(func(err interface{}) {
|
||||||
http.Error(w, "This request caused apisever to panic. Look in log for details.", http.StatusInternalServerError)
|
http.Error(w, "This request caused apisever to panic. Look in log for details.", http.StatusInternalServerError)
|
||||||
|
@ -37,8 +38,19 @@ func WithPanicRecovery(handler http.Handler, resolver *apiserver.RequestInfoReso
|
||||||
})
|
})
|
||||||
|
|
||||||
logger := httplog.NewLogged(req, &w)
|
logger := httplog.NewLogged(req, &w)
|
||||||
requestInfo, err := resolver.GetRequestInfo(req)
|
|
||||||
if err != nil || requestInfo.Verb != "proxy" {
|
var requestInfo *request.RequestInfo
|
||||||
|
ctx, ok := requestContextMapper.Get(req)
|
||||||
|
if !ok {
|
||||||
|
glog.Errorf("no context found for request, handler chain must be wrong")
|
||||||
|
} else {
|
||||||
|
requestInfo, ok = request.RequestInfoFrom(ctx)
|
||||||
|
if !ok {
|
||||||
|
glog.Errorf("no RequestInfo found in context, handler chain must be wrong")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok || requestInfo.Verb != "proxy" {
|
||||||
logger.StacktraceWhen(
|
logger.StacktraceWhen(
|
||||||
httplog.StatusIsNot(
|
httplog.StatusIsNot(
|
||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
|
@ -52,12 +64,13 @@ func WithPanicRecovery(handler http.Handler, resolver *apiserver.RequestInfoReso
|
||||||
http.StatusUnauthorized,
|
http.StatusUnauthorized,
|
||||||
http.StatusForbidden,
|
http.StatusForbidden,
|
||||||
http.StatusNotModified,
|
http.StatusNotModified,
|
||||||
errors.StatusUnprocessableEntity,
|
apierrors.StatusUnprocessableEntity,
|
||||||
http.StatusSwitchingProtocols,
|
http.StatusSwitchingProtocols,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
defer logger.Log()
|
defer logger.Log()
|
||||||
|
|
||||||
// Dispatch to the internal handler
|
// Dispatch to the internal handler
|
||||||
handler.ServeHTTP(w, req)
|
handler.ServeHTTP(w, req)
|
||||||
})
|
})
|
||||||
|
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/apimachinery"
|
"k8s.io/kubernetes/pkg/apimachinery"
|
||||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||||
"k8s.io/kubernetes/pkg/apiserver"
|
"k8s.io/kubernetes/pkg/apiserver"
|
||||||
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
"k8s.io/kubernetes/pkg/genericapiserver/openapi"
|
"k8s.io/kubernetes/pkg/genericapiserver/openapi"
|
||||||
"k8s.io/kubernetes/pkg/genericapiserver/openapi/common"
|
"k8s.io/kubernetes/pkg/genericapiserver/openapi/common"
|
||||||
"k8s.io/kubernetes/pkg/genericapiserver/options"
|
"k8s.io/kubernetes/pkg/genericapiserver/options"
|
||||||
|
@ -187,8 +188,8 @@ func (s *GenericAPIServer) MinRequestTimeout() time.Duration {
|
||||||
return s.minRequestTimeout
|
return s.minRequestTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GenericAPIServer) NewRequestInfoResolver() *apiserver.RequestInfoResolver {
|
func (s *GenericAPIServer) NewRequestInfoResolver() *request.RequestInfoResolver {
|
||||||
return &apiserver.RequestInfoResolver{
|
return &request.RequestInfoResolver{
|
||||||
APIPrefixes: sets.NewString(strings.Trim(s.legacyAPIPrefix, "/"), strings.Trim(s.apiPrefix, "/")), // all possible API prefixes
|
APIPrefixes: sets.NewString(strings.Trim(s.legacyAPIPrefix, "/"), strings.Trim(s.apiPrefix, "/")), // all possible API prefixes
|
||||||
GrouplessAPIPrefixes: sets.NewString(strings.Trim(s.legacyAPIPrefix, "/")), // APIPrefixes that won't have groups (legacy)
|
GrouplessAPIPrefixes: sets.NewString(strings.Trim(s.legacyAPIPrefix, "/")), // APIPrefixes that won't have groups (legacy)
|
||||||
}
|
}
|
||||||
|
@ -452,8 +453,6 @@ func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
|
||||||
|
|
||||||
func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupVersion unversioned.GroupVersion) (*apiserver.APIGroupVersion, error) {
|
func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupVersion unversioned.GroupVersion) (*apiserver.APIGroupVersion, error) {
|
||||||
return &apiserver.APIGroupVersion{
|
return &apiserver.APIGroupVersion{
|
||||||
RequestInfoResolver: s.NewRequestInfoResolver(),
|
|
||||||
|
|
||||||
GroupVersion: groupVersion,
|
GroupVersion: groupVersion,
|
||||||
|
|
||||||
ParameterCodec: apiGroupInfo.ParameterCodec,
|
ParameterCodec: apiGroupInfo.ParameterCodec,
|
||||||
|
|
|
@ -787,7 +787,6 @@ func (m *Master) thirdpartyapi(group, kind, version, pluralResource string) *api
|
||||||
return &apiserver.APIGroupVersion{
|
return &apiserver.APIGroupVersion{
|
||||||
Root: apiRoot,
|
Root: apiRoot,
|
||||||
GroupVersion: externalVersion,
|
GroupVersion: externalVersion,
|
||||||
RequestInfoResolver: m.NewRequestInfoResolver(),
|
|
||||||
|
|
||||||
Creater: thirdpartyresourcedata.NewObjectCreator(group, version, api.Scheme),
|
Creater: thirdpartyresourcedata.NewObjectCreator(group, version, api.Scheme),
|
||||||
Convertor: api.Scheme,
|
Convertor: api.Scheme,
|
||||||
|
|
|
@ -47,7 +47,8 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||||
"k8s.io/kubernetes/pkg/apiserver"
|
"k8s.io/kubernetes/pkg/apiserver/request"
|
||||||
|
"k8s.io/kubernetes/pkg/generated/openapi"
|
||||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
"k8s.io/kubernetes/pkg/registry/core/endpoint"
|
"k8s.io/kubernetes/pkg/registry/core/endpoint"
|
||||||
|
@ -72,7 +73,6 @@ import (
|
||||||
"github.com/go-openapi/validate"
|
"github.com/go-openapi/validate"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"k8s.io/kubernetes/pkg/generated/openapi"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// setUp is a convience function for setting up for (most) tests.
|
// setUp is a convience function for setting up for (most) tests.
|
||||||
|
@ -192,7 +192,7 @@ func TestNamespaceSubresources(t *testing.T) {
|
||||||
master, etcdserver, _, _ := newMaster(t)
|
master, etcdserver, _, _ := newMaster(t)
|
||||||
defer etcdserver.Terminate(t)
|
defer etcdserver.Terminate(t)
|
||||||
|
|
||||||
expectedSubresources := apiserver.NamespaceSubResourcesForTest
|
expectedSubresources := request.NamespaceSubResourcesForTest
|
||||||
foundSubresources := sets.NewString()
|
foundSubresources := sets.NewString()
|
||||||
|
|
||||||
for k := range master.v1ResourcesStorage {
|
for k := range master.v1ResourcesStorage {
|
||||||
|
|
Loading…
Reference in New Issue