mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #33478 from sttts/sttts-separate-apiserver-filters
Automatic merge from submit-queue Separate handler filters in pkg/apiserver into pkg/apiserver/filters ... in order to make dependencies explicit towards better composability.
This commit is contained in:
		@@ -27,7 +27,6 @@ import (
 | 
				
			|||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	rt "runtime"
 | 
						rt "runtime"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/admission"
 | 
						"k8s.io/kubernetes/pkg/admission"
 | 
				
			||||||
@@ -210,22 +209,6 @@ func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{},
 | 
				
			|||||||
	errorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, w, &http.Request{Header: headers})
 | 
						errorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, w, &http.Request{Header: headers})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func InstallServiceErrorHandler(s runtime.NegotiatedSerializer, container *restful.Container) {
 | 
					 | 
				
			||||||
	container.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
 | 
					 | 
				
			||||||
		serviceErrorHandler(s, serviceErr, request, response)
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
 | 
					 | 
				
			||||||
	errorNegotiated(
 | 
					 | 
				
			||||||
		apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", serviceErr.Message, 0, false),
 | 
					 | 
				
			||||||
		s,
 | 
					 | 
				
			||||||
		unversioned.GroupVersion{},
 | 
					 | 
				
			||||||
		response.ResponseWriter,
 | 
					 | 
				
			||||||
		request.Request,
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Adds a service to return the supported api versions at the legacy /api.
 | 
					// Adds a service to return the supported api versions at the legacy /api.
 | 
				
			||||||
func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, getAPIVersionsFunc func(req *restful.Request) *unversioned.APIVersions) {
 | 
					func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, getAPIVersionsFunc func(req *restful.Request) *unversioned.APIVersions) {
 | 
				
			||||||
	// TODO: InstallREST should register each version automatically
 | 
						// TODO: InstallREST should register each version automatically
 | 
				
			||||||
@@ -501,12 +484,3 @@ func readBody(req *http.Request) ([]byte, error) {
 | 
				
			|||||||
	defer req.Body.Close()
 | 
						defer req.Body.Close()
 | 
				
			||||||
	return ioutil.ReadAll(req.Body)
 | 
						return ioutil.ReadAll(req.Body)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// splitPath returns the segments for a URL path.
 | 
					 | 
				
			||||||
func splitPath(path string) []string {
 | 
					 | 
				
			||||||
	path = strings.Trim(path, "/")
 | 
					 | 
				
			||||||
	if path == "" {
 | 
					 | 
				
			||||||
		return []string{}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return strings.Split(path, "/")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,6 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
@@ -252,10 +251,6 @@ func handleLinker(storage map[string]rest.Storage, selfLinker runtime.SelfLinker
 | 
				
			|||||||
	return handleInternal(storage, admissionControl, selfLinker)
 | 
						return handleInternal(storage, admissionControl, selfLinker)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newTestRequestInfoResolver() *RequestInfoResolver {
 | 
					 | 
				
			||||||
	return &RequestInfoResolver{sets.NewString("api", "apis"), sets.NewString("api")}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func handleInternal(storage map[string]rest.Storage, admissionControl admission.Interface, selfLinker runtime.SelfLinker) http.Handler {
 | 
					func handleInternal(storage map[string]rest.Storage, admissionControl admission.Interface, selfLinker runtime.SelfLinker) http.Handler {
 | 
				
			||||||
	container := restful.NewContainer()
 | 
						container := restful.NewContainer()
 | 
				
			||||||
	container.Router(restful.CurlyRouter{})
 | 
						container.Router(restful.CurlyRouter{})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,25 +76,6 @@ func notFound(w http.ResponseWriter, req *http.Request) {
 | 
				
			|||||||
	fmt.Fprintf(w, "Not Found: %#v", req.RequestURI)
 | 
						fmt.Fprintf(w, "Not Found: %#v", req.RequestURI)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// badGatewayError renders a simple bad gateway error.
 | 
					 | 
				
			||||||
func badGatewayError(w http.ResponseWriter, req *http.Request) {
 | 
					 | 
				
			||||||
	w.WriteHeader(http.StatusBadGateway)
 | 
					 | 
				
			||||||
	fmt.Fprintf(w, "Bad Gateway: %#v", req.RequestURI)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// forbidden renders a simple forbidden error
 | 
					 | 
				
			||||||
func forbidden(w http.ResponseWriter, req *http.Request) {
 | 
					 | 
				
			||||||
	w.WriteHeader(http.StatusForbidden)
 | 
					 | 
				
			||||||
	fmt.Fprintf(w, "Forbidden: %#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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package audit
 | 
					package filters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
@@ -27,7 +27,6 @@ import (
 | 
				
			|||||||
	"github.com/golang/glog"
 | 
						"github.com/golang/glog"
 | 
				
			||||||
	"github.com/pborman/uuid"
 | 
						"github.com/pborman/uuid"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apiserver"
 | 
					 | 
				
			||||||
	utilnet "k8s.io/kubernetes/pkg/util/net"
 | 
						utilnet "k8s.io/kubernetes/pkg/util/net"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,7 +84,7 @@ var _ http.Hijacker = &fancyResponseWriterDelegator{}
 | 
				
			|||||||
// 2. the response line containing:
 | 
					// 2. the response line containing:
 | 
				
			||||||
//    - the unique id from 1
 | 
					//    - the unique id from 1
 | 
				
			||||||
//    - response code
 | 
					//    - response code
 | 
				
			||||||
func WithAudit(handler http.Handler, attributeGetter apiserver.RequestAttributeGetter, out io.Writer) http.Handler {
 | 
					func WithAudit(handler http.Handler, attributeGetter RequestAttributeGetter, out io.Writer) http.Handler {
 | 
				
			||||||
	if out == nil {
 | 
						if out == nil {
 | 
				
			||||||
		return handler
 | 
							return handler
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package audit
 | 
					package filters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
@@ -85,7 +85,7 @@ func (*fakeRequestContextMapper) Update(req *http.Request, context api.Context)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestAudit(t *testing.T) {
 | 
					func TestAudit(t *testing.T) {
 | 
				
			||||||
	var buf bytes.Buffer
 | 
						var buf bytes.Buffer
 | 
				
			||||||
	attributeGetter := apiserver.NewRequestAttributeGetter(&fakeRequestContextMapper{},
 | 
						attributeGetter := NewRequestAttributeGetter(&fakeRequestContextMapper{},
 | 
				
			||||||
		&apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")})
 | 
							&apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")})
 | 
				
			||||||
	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)
 | 
				
			||||||
							
								
								
									
										91
									
								
								pkg/apiserver/filters/authorization.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								pkg/apiserver/filters/authorization.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/golang/glog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apiserver"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.
 | 
				
			||||||
 | 
					func WithAuthorization(handler http.Handler, getAttribs RequestAttributeGetter, a authorizer.Authorizer) http.Handler {
 | 
				
			||||||
 | 
						if a == nil {
 | 
				
			||||||
 | 
							glog.Warningf("Authorization is disabled")
 | 
				
			||||||
 | 
							return handler
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
							authorized, reason, err := a.Authorize(getAttribs.GetAttribs(req))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								internalError(w, req, err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !authorized {
 | 
				
			||||||
 | 
								glog.V(4).Infof("Forbidden: %#v, Reason: %s", req.RequestURI, reason)
 | 
				
			||||||
 | 
								forbidden(w, req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							handler.ServeHTTP(w, req)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RequestAttributeGetter is a function that extracts authorizer.Attributes from an http.Request
 | 
				
			||||||
 | 
					type RequestAttributeGetter interface {
 | 
				
			||||||
 | 
						GetAttribs(req *http.Request) (attribs authorizer.Attributes)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type requestAttributeGetter struct {
 | 
				
			||||||
 | 
						requestContextMapper api.RequestContextMapper
 | 
				
			||||||
 | 
						requestInfoResolver  *apiserver.RequestInfoResolver
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewAttributeGetter returns an object which implements the RequestAttributeGetter interface.
 | 
				
			||||||
 | 
					func NewRequestAttributeGetter(requestContextMapper api.RequestContextMapper, requestInfoResolver *apiserver.RequestInfoResolver) RequestAttributeGetter {
 | 
				
			||||||
 | 
						return &requestAttributeGetter{requestContextMapper, requestInfoResolver}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attributes {
 | 
				
			||||||
 | 
						attribs := authorizer.AttributesRecord{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx, ok := r.requestContextMapper.Get(req)
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							user, ok := api.UserFrom(ctx)
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								attribs.User = user
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requestInfo, _ := r.requestInfoResolver.GetRequestInfo(req)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Start with common attributes that apply to resource and non-resource requests
 | 
				
			||||||
 | 
						attribs.ResourceRequest = requestInfo.IsResourceRequest
 | 
				
			||||||
 | 
						attribs.Path = requestInfo.Path
 | 
				
			||||||
 | 
						attribs.Verb = requestInfo.Verb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						attribs.APIGroup = requestInfo.APIGroup
 | 
				
			||||||
 | 
						attribs.APIVersion = requestInfo.APIVersion
 | 
				
			||||||
 | 
						attribs.Resource = requestInfo.Resource
 | 
				
			||||||
 | 
						attribs.Subresource = requestInfo.Subresource
 | 
				
			||||||
 | 
						attribs.Namespace = requestInfo.Namespace
 | 
				
			||||||
 | 
						attribs.Name = requestInfo.Name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &attribs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										111
									
								
								pkg/apiserver/filters/authorization_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								pkg/apiserver/filters/authorization_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apis/extensions"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apiserver"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetAttribs(t *testing.T) {
 | 
				
			||||||
 | 
						r := &requestAttributeGetter{api.NewRequestContextMapper(), &apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testcases := map[string]struct {
 | 
				
			||||||
 | 
							Verb               string
 | 
				
			||||||
 | 
							Path               string
 | 
				
			||||||
 | 
							ExpectedAttributes *authorizer.AttributesRecord
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							"non-resource root": {
 | 
				
			||||||
 | 
								Verb: "POST",
 | 
				
			||||||
 | 
								Path: "/",
 | 
				
			||||||
 | 
								ExpectedAttributes: &authorizer.AttributesRecord{
 | 
				
			||||||
 | 
									Verb: "post",
 | 
				
			||||||
 | 
									Path: "/",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"non-resource api prefix": {
 | 
				
			||||||
 | 
								Verb: "GET",
 | 
				
			||||||
 | 
								Path: "/api/",
 | 
				
			||||||
 | 
								ExpectedAttributes: &authorizer.AttributesRecord{
 | 
				
			||||||
 | 
									Verb: "get",
 | 
				
			||||||
 | 
									Path: "/api/",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"non-resource group api prefix": {
 | 
				
			||||||
 | 
								Verb: "GET",
 | 
				
			||||||
 | 
								Path: "/apis/extensions/",
 | 
				
			||||||
 | 
								ExpectedAttributes: &authorizer.AttributesRecord{
 | 
				
			||||||
 | 
									Verb: "get",
 | 
				
			||||||
 | 
									Path: "/apis/extensions/",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							"resource": {
 | 
				
			||||||
 | 
								Verb: "POST",
 | 
				
			||||||
 | 
								Path: "/api/v1/nodes/mynode",
 | 
				
			||||||
 | 
								ExpectedAttributes: &authorizer.AttributesRecord{
 | 
				
			||||||
 | 
									Verb:            "create",
 | 
				
			||||||
 | 
									Path:            "/api/v1/nodes/mynode",
 | 
				
			||||||
 | 
									ResourceRequest: true,
 | 
				
			||||||
 | 
									Resource:        "nodes",
 | 
				
			||||||
 | 
									APIVersion:      "v1",
 | 
				
			||||||
 | 
									Name:            "mynode",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"namespaced resource": {
 | 
				
			||||||
 | 
								Verb: "PUT",
 | 
				
			||||||
 | 
								Path: "/api/v1/namespaces/myns/pods/mypod",
 | 
				
			||||||
 | 
								ExpectedAttributes: &authorizer.AttributesRecord{
 | 
				
			||||||
 | 
									Verb:            "update",
 | 
				
			||||||
 | 
									Path:            "/api/v1/namespaces/myns/pods/mypod",
 | 
				
			||||||
 | 
									ResourceRequest: true,
 | 
				
			||||||
 | 
									Namespace:       "myns",
 | 
				
			||||||
 | 
									Resource:        "pods",
 | 
				
			||||||
 | 
									APIVersion:      "v1",
 | 
				
			||||||
 | 
									Name:            "mypod",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"API group resource": {
 | 
				
			||||||
 | 
								Verb: "GET",
 | 
				
			||||||
 | 
								Path: "/apis/extensions/v1beta1/namespaces/myns/jobs",
 | 
				
			||||||
 | 
								ExpectedAttributes: &authorizer.AttributesRecord{
 | 
				
			||||||
 | 
									Verb:            "list",
 | 
				
			||||||
 | 
									Path:            "/apis/extensions/v1beta1/namespaces/myns/jobs",
 | 
				
			||||||
 | 
									ResourceRequest: true,
 | 
				
			||||||
 | 
									APIGroup:        extensions.GroupName,
 | 
				
			||||||
 | 
									APIVersion:      "v1beta1",
 | 
				
			||||||
 | 
									Namespace:       "myns",
 | 
				
			||||||
 | 
									Resource:        "jobs",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for k, tc := range testcases {
 | 
				
			||||||
 | 
							req, _ := http.NewRequest(tc.Verb, tc.Path, nil)
 | 
				
			||||||
 | 
							attribs := r.GetAttribs(req)
 | 
				
			||||||
 | 
							if !reflect.DeepEqual(attribs, tc.ExpectedAttributes) {
 | 
				
			||||||
 | 
								t.Errorf("%s: expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedAttributes, attribs)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								pkg/apiserver/filters/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								pkg/apiserver/filters/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 contains all the http handler chain filters which
 | 
				
			||||||
 | 
					// _are_ api related.
 | 
				
			||||||
 | 
					package filters // import "k8s.io/kubernetes/pkg/apiserver/filters"
 | 
				
			||||||
							
								
								
									
										43
									
								
								pkg/apiserver/filters/errors.go
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										43
									
								
								pkg/apiserver/filters/errors.go
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 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 (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// badGatewayError renders a simple bad gateway error.
 | 
				
			||||||
 | 
					func badGatewayError(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
						w.WriteHeader(http.StatusBadGateway)
 | 
				
			||||||
 | 
						fmt.Fprintf(w, "Bad Gateway: %#v", req.RequestURI)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// forbidden renders a simple forbidden error
 | 
				
			||||||
 | 
					func forbidden(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
						w.WriteHeader(http.StatusForbidden)
 | 
				
			||||||
 | 
						fmt.Fprintf(w, "Forbidden: %#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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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 filters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
@@ -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 filters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
@@ -264,7 +264,7 @@ func TestImpersonationFilter(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	requestContextMapper = api.NewRequestContextMapper()
 | 
						requestContextMapper := api.NewRequestContextMapper()
 | 
				
			||||||
	var ctx api.Context
 | 
						var ctx api.Context
 | 
				
			||||||
	var actualUser user.Info
 | 
						var actualUser user.Info
 | 
				
			||||||
	var lock sync.Mutex
 | 
						var lock sync.Mutex
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
Copyright 2014 The Kubernetes Authors.
 | 
					Copyright 2015 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.
 | 
				
			||||||
@@ -19,156 +19,12 @@ package apiserver
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"runtime/debug"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/golang/glog"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/errors"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/httplog"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/runtime"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal
 | 
					 | 
				
			||||||
// CRUDdy GET/POST/PUT/DELETE actions on REST objects.
 | 
					 | 
				
			||||||
// TODO: find a way to keep this up to date automatically.  Maybe dynamically populate list as handlers added to
 | 
					 | 
				
			||||||
// master's Mux.
 | 
					 | 
				
			||||||
var specialVerbs = sets.NewString("proxy", "redirect", "watch")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// specialVerbsNoSubresources contains root verbs which do not allow subresources
 | 
					 | 
				
			||||||
var specialVerbsNoSubresources = sets.NewString("proxy", "redirect")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// namespaceSubresources contains subresources of namespace
 | 
					 | 
				
			||||||
// this list allows the parser to distinguish between a namespace subresource, and a namespaced resource
 | 
					 | 
				
			||||||
var namespaceSubresources = sets.NewString("status", "finalize")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NamespaceSubResourcesForTest exports namespaceSubresources for testing in pkg/master/master_test.go, so we never drift
 | 
					 | 
				
			||||||
var NamespaceSubResourcesForTest = sets.NewString(namespaceSubresources.List()...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsReadOnlyReq() is true for any (or at least many) request which has no observable
 | 
					 | 
				
			||||||
// side effects on state of apiserver (though there may be internal side effects like
 | 
					 | 
				
			||||||
// caching and logging).
 | 
					 | 
				
			||||||
func IsReadOnlyReq(req http.Request) bool {
 | 
					 | 
				
			||||||
	if req.Method == "GET" {
 | 
					 | 
				
			||||||
		// TODO: add OPTIONS and HEAD if we ever support those.
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ReadOnly passes all GET requests on to handler, and returns an error on all other requests.
 | 
					 | 
				
			||||||
func ReadOnly(handler http.Handler) http.Handler {
 | 
					 | 
				
			||||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
					 | 
				
			||||||
		if IsReadOnlyReq(*req) {
 | 
					 | 
				
			||||||
			handler.ServeHTTP(w, req)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		w.WriteHeader(http.StatusForbidden)
 | 
					 | 
				
			||||||
		fmt.Fprintf(w, "This is a read-only endpoint.")
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RecoverPanics wraps an http Handler to recover and log panics.
 | 
					 | 
				
			||||||
func RecoverPanics(handler http.Handler, resolver *RequestInfoResolver) http.Handler {
 | 
					 | 
				
			||||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
					 | 
				
			||||||
		defer runtime.HandleCrash(func(err interface{}) {
 | 
					 | 
				
			||||||
			http.Error(w, "This request caused apisever to panic. Look in log for details.", http.StatusInternalServerError)
 | 
					 | 
				
			||||||
			glog.Errorf("APIServer panic'd on %v %v: %v\n%s\n", req.Method, req.RequestURI, err, debug.Stack())
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		logger := httplog.NewLogged(req, &w)
 | 
					 | 
				
			||||||
		requestInfo, err := resolver.GetRequestInfo(req)
 | 
					 | 
				
			||||||
		if err != nil || requestInfo.Verb != "proxy" {
 | 
					 | 
				
			||||||
			logger.StacktraceWhen(
 | 
					 | 
				
			||||||
				httplog.StatusIsNot(
 | 
					 | 
				
			||||||
					http.StatusOK,
 | 
					 | 
				
			||||||
					http.StatusCreated,
 | 
					 | 
				
			||||||
					http.StatusAccepted,
 | 
					 | 
				
			||||||
					http.StatusBadRequest,
 | 
					 | 
				
			||||||
					http.StatusMovedPermanently,
 | 
					 | 
				
			||||||
					http.StatusTemporaryRedirect,
 | 
					 | 
				
			||||||
					http.StatusConflict,
 | 
					 | 
				
			||||||
					http.StatusNotFound,
 | 
					 | 
				
			||||||
					http.StatusUnauthorized,
 | 
					 | 
				
			||||||
					http.StatusForbidden,
 | 
					 | 
				
			||||||
					http.StatusNotModified,
 | 
					 | 
				
			||||||
					errors.StatusUnprocessableEntity,
 | 
					 | 
				
			||||||
					http.StatusSwitchingProtocols,
 | 
					 | 
				
			||||||
				),
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		defer logger.Log()
 | 
					 | 
				
			||||||
		// Dispatch to the internal handler
 | 
					 | 
				
			||||||
		handler.ServeHTTP(w, req)
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RequestAttributeGetter is a function that extracts authorizer.Attributes from an http.Request
 | 
					 | 
				
			||||||
type RequestAttributeGetter interface {
 | 
					 | 
				
			||||||
	GetAttribs(req *http.Request) (attribs authorizer.Attributes)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type requestAttributeGetter struct {
 | 
					 | 
				
			||||||
	requestContextMapper api.RequestContextMapper
 | 
					 | 
				
			||||||
	requestInfoResolver  *RequestInfoResolver
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewAttributeGetter returns an object which implements the RequestAttributeGetter interface.
 | 
					 | 
				
			||||||
func NewRequestAttributeGetter(requestContextMapper api.RequestContextMapper, requestInfoResolver *RequestInfoResolver) RequestAttributeGetter {
 | 
					 | 
				
			||||||
	return &requestAttributeGetter{requestContextMapper, requestInfoResolver}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attributes {
 | 
					 | 
				
			||||||
	attribs := authorizer.AttributesRecord{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx, ok := r.requestContextMapper.Get(req)
 | 
					 | 
				
			||||||
	if ok {
 | 
					 | 
				
			||||||
		user, ok := api.UserFrom(ctx)
 | 
					 | 
				
			||||||
		if ok {
 | 
					 | 
				
			||||||
			attribs.User = user
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	requestInfo, _ := r.requestInfoResolver.GetRequestInfo(req)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Start with common attributes that apply to resource and non-resource requests
 | 
					 | 
				
			||||||
	attribs.ResourceRequest = requestInfo.IsResourceRequest
 | 
					 | 
				
			||||||
	attribs.Path = requestInfo.Path
 | 
					 | 
				
			||||||
	attribs.Verb = requestInfo.Verb
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	attribs.APIGroup = requestInfo.APIGroup
 | 
					 | 
				
			||||||
	attribs.APIVersion = requestInfo.APIVersion
 | 
					 | 
				
			||||||
	attribs.Resource = requestInfo.Resource
 | 
					 | 
				
			||||||
	attribs.Subresource = requestInfo.Subresource
 | 
					 | 
				
			||||||
	attribs.Namespace = requestInfo.Namespace
 | 
					 | 
				
			||||||
	attribs.Name = requestInfo.Name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return &attribs
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WithAuthorizationCheck passes all authorized requests on to handler, and returns a forbidden error otherwise.
 | 
					 | 
				
			||||||
func WithAuthorization(handler http.Handler, getAttribs RequestAttributeGetter, a authorizer.Authorizer) http.Handler {
 | 
					 | 
				
			||||||
	if a == nil {
 | 
					 | 
				
			||||||
		glog.Warningf("Authorization is disabled")
 | 
					 | 
				
			||||||
		return handler
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
					 | 
				
			||||||
		authorized, reason, err := a.Authorize(getAttribs.GetAttribs(req))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			internalError(w, req, err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !authorized {
 | 
					 | 
				
			||||||
			glog.V(4).Infof("Forbidden: %#v, Reason: %s", req.RequestURI, reason)
 | 
					 | 
				
			||||||
			forbidden(w, req)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		handler.ServeHTTP(w, req)
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RequestInfo holds information parsed from the http.Request
 | 
					// RequestInfo holds information parsed from the http.Request
 | 
				
			||||||
type RequestInfo struct {
 | 
					type RequestInfo struct {
 | 
				
			||||||
	// IsResourceRequest indicates whether or not the request is for an API resource or subresource
 | 
						// IsResourceRequest indicates whether or not the request is for an API resource or subresource
 | 
				
			||||||
@@ -195,6 +51,22 @@ type RequestInfo struct {
 | 
				
			|||||||
	Parts []string
 | 
						Parts []string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal
 | 
				
			||||||
 | 
					// CRUDdy GET/POST/PUT/DELETE actions on REST objects.
 | 
				
			||||||
 | 
					// TODO: find a way to keep this up to date automatically.  Maybe dynamically populate list as handlers added to
 | 
				
			||||||
 | 
					// master's Mux.
 | 
				
			||||||
 | 
					var specialVerbs = sets.NewString("proxy", "redirect", "watch")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// specialVerbsNoSubresources contains root verbs which do not allow subresources
 | 
				
			||||||
 | 
					var specialVerbsNoSubresources = sets.NewString("proxy", "redirect")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// namespaceSubresources contains subresources of namespace
 | 
				
			||||||
 | 
					// this list allows the parser to distinguish between a namespace subresource, and a namespaced resource
 | 
				
			||||||
 | 
					var namespaceSubresources = sets.NewString("status", "finalize")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NamespaceSubResourcesForTest exports namespaceSubresources for testing in pkg/master/master_test.go, so we never drift
 | 
				
			||||||
 | 
					var NamespaceSubResourcesForTest = sets.NewString(namespaceSubresources.List()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RequestInfoResolver struct {
 | 
					type RequestInfoResolver struct {
 | 
				
			||||||
	APIPrefixes          sets.String
 | 
						APIPrefixes          sets.String
 | 
				
			||||||
	GrouplessAPIPrefixes sets.String
 | 
						GrouplessAPIPrefixes sets.String
 | 
				
			||||||
@@ -334,3 +206,12 @@ func (r *RequestInfoResolver) GetRequestInfo(req *http.Request) (RequestInfo, er
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return requestInfo, nil
 | 
						return requestInfo, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// splitPath returns the segments for a URL path.
 | 
				
			||||||
 | 
					func splitPath(path string) []string {
 | 
				
			||||||
 | 
						path = strings.Trim(path, "/")
 | 
				
			||||||
 | 
						if path == "" {
 | 
				
			||||||
 | 
							return []string{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.Split(path, "/")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -18,14 +18,11 @@ package apiserver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
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/api/testapi"
 | 
						"k8s.io/kubernetes/pkg/api/testapi"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/extensions"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/sets"
 | 
						"k8s.io/kubernetes/pkg/util/sets"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,106 +40,6 @@ func pathWithPrefix(prefix, resource, namespace, name string) string {
 | 
				
			|||||||
	return testapi.Default.ResourcePathWithPrefix(prefix, resource, namespace, name)
 | 
						return testapi.Default.ResourcePathWithPrefix(prefix, resource, namespace, name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestReadOnly(t *testing.T) {
 | 
					 | 
				
			||||||
	server := httptest.NewServer(ReadOnly(http.HandlerFunc(
 | 
					 | 
				
			||||||
		func(w http.ResponseWriter, req *http.Request) {
 | 
					 | 
				
			||||||
			if req.Method != "GET" {
 | 
					 | 
				
			||||||
				t.Errorf("Unexpected call: %v", req.Method)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	)))
 | 
					 | 
				
			||||||
	defer server.Close()
 | 
					 | 
				
			||||||
	for _, verb := range []string{"GET", "POST", "PUT", "DELETE", "CREATE"} {
 | 
					 | 
				
			||||||
		req, err := http.NewRequest(verb, server.URL, nil)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Fatalf("Couldn't make request: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		http.DefaultClient.Do(req)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestGetAttribs(t *testing.T) {
 | 
					 | 
				
			||||||
	r := &requestAttributeGetter{api.NewRequestContextMapper(), &RequestInfoResolver{sets.NewString("api", "apis"), sets.NewString("api")}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	testcases := map[string]struct {
 | 
					 | 
				
			||||||
		Verb               string
 | 
					 | 
				
			||||||
		Path               string
 | 
					 | 
				
			||||||
		ExpectedAttributes *authorizer.AttributesRecord
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		"non-resource root": {
 | 
					 | 
				
			||||||
			Verb: "POST",
 | 
					 | 
				
			||||||
			Path: "/",
 | 
					 | 
				
			||||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
					 | 
				
			||||||
				Verb: "post",
 | 
					 | 
				
			||||||
				Path: "/",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"non-resource api prefix": {
 | 
					 | 
				
			||||||
			Verb: "GET",
 | 
					 | 
				
			||||||
			Path: "/api/",
 | 
					 | 
				
			||||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
					 | 
				
			||||||
				Verb: "get",
 | 
					 | 
				
			||||||
				Path: "/api/",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"non-resource group api prefix": {
 | 
					 | 
				
			||||||
			Verb: "GET",
 | 
					 | 
				
			||||||
			Path: "/apis/extensions/",
 | 
					 | 
				
			||||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
					 | 
				
			||||||
				Verb: "get",
 | 
					 | 
				
			||||||
				Path: "/apis/extensions/",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		"resource": {
 | 
					 | 
				
			||||||
			Verb: "POST",
 | 
					 | 
				
			||||||
			Path: "/api/v1/nodes/mynode",
 | 
					 | 
				
			||||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
					 | 
				
			||||||
				Verb:            "create",
 | 
					 | 
				
			||||||
				Path:            "/api/v1/nodes/mynode",
 | 
					 | 
				
			||||||
				ResourceRequest: true,
 | 
					 | 
				
			||||||
				Resource:        "nodes",
 | 
					 | 
				
			||||||
				APIVersion:      "v1",
 | 
					 | 
				
			||||||
				Name:            "mynode",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"namespaced resource": {
 | 
					 | 
				
			||||||
			Verb: "PUT",
 | 
					 | 
				
			||||||
			Path: "/api/v1/namespaces/myns/pods/mypod",
 | 
					 | 
				
			||||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
					 | 
				
			||||||
				Verb:            "update",
 | 
					 | 
				
			||||||
				Path:            "/api/v1/namespaces/myns/pods/mypod",
 | 
					 | 
				
			||||||
				ResourceRequest: true,
 | 
					 | 
				
			||||||
				Namespace:       "myns",
 | 
					 | 
				
			||||||
				Resource:        "pods",
 | 
					 | 
				
			||||||
				APIVersion:      "v1",
 | 
					 | 
				
			||||||
				Name:            "mypod",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"API group resource": {
 | 
					 | 
				
			||||||
			Verb: "GET",
 | 
					 | 
				
			||||||
			Path: "/apis/extensions/v1beta1/namespaces/myns/jobs",
 | 
					 | 
				
			||||||
			ExpectedAttributes: &authorizer.AttributesRecord{
 | 
					 | 
				
			||||||
				Verb:            "list",
 | 
					 | 
				
			||||||
				Path:            "/apis/extensions/v1beta1/namespaces/myns/jobs",
 | 
					 | 
				
			||||||
				ResourceRequest: true,
 | 
					 | 
				
			||||||
				APIGroup:        extensions.GroupName,
 | 
					 | 
				
			||||||
				APIVersion:      "v1beta1",
 | 
					 | 
				
			||||||
				Namespace:       "myns",
 | 
					 | 
				
			||||||
				Resource:        "jobs",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for k, tc := range testcases {
 | 
					 | 
				
			||||||
		req, _ := http.NewRequest(tc.Verb, tc.Path, nil)
 | 
					 | 
				
			||||||
		attribs := r.GetAttribs(req)
 | 
					 | 
				
			||||||
		if !reflect.DeepEqual(attribs, tc.ExpectedAttributes) {
 | 
					 | 
				
			||||||
			t.Errorf("%s: expected\n\t%#v\ngot\n\t%#v", k, tc.ExpectedAttributes, attribs)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestGetAPIRequestInfo(t *testing.T) {
 | 
					func TestGetAPIRequestInfo(t *testing.T) {
 | 
				
			||||||
	successCases := []struct {
 | 
						successCases := []struct {
 | 
				
			||||||
		method              string
 | 
							method              string
 | 
				
			||||||
@@ -202,12 +99,12 @@ func TestGetAPIRequestInfo(t *testing.T) {
 | 
				
			|||||||
		{"POST", "/apis/extensions/v1beta3/namespaces/other/pods", "create", "api", "extensions", "v1beta3", "other", "pods", "", "", []string{"pods"}},
 | 
							{"POST", "/apis/extensions/v1beta3/namespaces/other/pods", "create", "api", "extensions", "v1beta3", "other", "pods", "", "", []string{"pods"}},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	requestInfoResolver := newTestRequestInfoResolver()
 | 
						resolver := newTestRequestInfoResolver()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, successCase := range successCases {
 | 
						for _, successCase := range successCases {
 | 
				
			||||||
		req, _ := http.NewRequest(successCase.method, successCase.url, nil)
 | 
							req, _ := http.NewRequest(successCase.method, successCase.url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		apiRequestInfo, err := requestInfoResolver.GetRequestInfo(req)
 | 
							apiRequestInfo, err := resolver.GetRequestInfo(req)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Errorf("Unexpected error for url: %s %v", successCase.url, err)
 | 
								t.Errorf("Unexpected error for url: %s %v", successCase.url, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -250,7 +147,7 @@ func TestGetAPIRequestInfo(t *testing.T) {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Errorf("Unexpected error %v", err)
 | 
								t.Errorf("Unexpected error %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		apiRequestInfo, err := requestInfoResolver.GetRequestInfo(req)
 | 
							apiRequestInfo, err := resolver.GetRequestInfo(req)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Errorf("%s: Unexpected error %v", k, err)
 | 
								t.Errorf("%s: Unexpected error %v", k, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -281,12 +178,12 @@ func TestGetNonAPIRequestInfo(t *testing.T) {
 | 
				
			|||||||
		"empty":                        {"", false},
 | 
							"empty":                        {"", false},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	requestInfoResolver := newTestRequestInfoResolver()
 | 
						resolver := newTestRequestInfoResolver()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for testName, tc := range tests {
 | 
						for testName, tc := range tests {
 | 
				
			||||||
		req, _ := http.NewRequest("GET", tc.url, nil)
 | 
							req, _ := http.NewRequest("GET", tc.url, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		apiRequestInfo, err := requestInfoResolver.GetRequestInfo(req)
 | 
							apiRequestInfo, err := resolver.GetRequestInfo(req)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Errorf("%s: Unexpected error %v", testName, err)
 | 
								t.Errorf("%s: Unexpected error %v", testName, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -295,3 +192,7 @@ func TestGetNonAPIRequestInfo(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newTestRequestInfoResolver() *RequestInfoResolver {
 | 
				
			||||||
 | 
						return &RequestInfoResolver{sets.NewString("api", "apis"), sets.NewString("api")}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								pkg/apiserver/serviceerror.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								pkg/apiserver/serviceerror.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 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 apiserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/emicklei/go-restful"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						apierrors "k8s.io/kubernetes/pkg/api/errors"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InstallServiceErrorHandler(s runtime.NegotiatedSerializer, container *restful.Container) {
 | 
				
			||||||
 | 
						container.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
 | 
				
			||||||
 | 
							serviceErrorHandler(s, serviceErr, request, response)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
 | 
				
			||||||
 | 
						errorNegotiated(
 | 
				
			||||||
 | 
							apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", serviceErr.Message, 0, false),
 | 
				
			||||||
 | 
							s,
 | 
				
			||||||
 | 
							unversioned.GroupVersion{},
 | 
				
			||||||
 | 
							response.ResponseWriter,
 | 
				
			||||||
 | 
							request.Request,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -36,7 +36,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/unversioned"
 | 
						"k8s.io/kubernetes/pkg/api/unversioned"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apiserver"
 | 
						"k8s.io/kubernetes/pkg/apiserver"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apiserver/audit"
 | 
						apiserverfilters "k8s.io/kubernetes/pkg/apiserver/filters"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/auth/authenticator"
 | 
						"k8s.io/kubernetes/pkg/auth/authenticator"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
						"k8s.io/kubernetes/pkg/auth/authorizer"
 | 
				
			||||||
	authhandlers "k8s.io/kubernetes/pkg/auth/handlers"
 | 
						authhandlers "k8s.io/kubernetes/pkg/auth/handlers"
 | 
				
			||||||
@@ -363,19 +363,17 @@ func (s *GenericAPIServer) buildHandlerChains(c *Config, handler http.Handler) (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// insecure filters
 | 
						// insecure filters
 | 
				
			||||||
	insecure = handler
 | 
						insecure = handler
 | 
				
			||||||
	insecure = api.WithRequestContext(insecure, c.RequestContextMapper)
 | 
						insecure = genericfilters.WithPanicRecovery(insecure, s.NewRequestInfoResolver())
 | 
				
			||||||
	insecure = apiserver.RecoverPanics(insecure, s.NewRequestInfoResolver())
 | 
					 | 
				
			||||||
	insecure = genericfilters.WithTimeoutForNonLongRunningRequests(insecure, longRunningFunc)
 | 
						insecure = genericfilters.WithTimeoutForNonLongRunningRequests(insecure, longRunningFunc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// secure filters
 | 
						// secure filters
 | 
				
			||||||
	attributeGetter := apiserver.NewRequestAttributeGetter(c.RequestContextMapper, s.NewRequestInfoResolver())
 | 
						attributeGetter := apiserverfilters.NewRequestAttributeGetter(c.RequestContextMapper, s.NewRequestInfoResolver())
 | 
				
			||||||
	secure = handler
 | 
						secure = handler
 | 
				
			||||||
	secure = apiserver.WithAuthorization(secure, attributeGetter, c.Authorizer)
 | 
						secure = apiserverfilters.WithAuthorization(secure, attributeGetter, c.Authorizer)
 | 
				
			||||||
	secure = apiserver.WithImpersonation(secure, c.RequestContextMapper, c.Authorizer)
 | 
						secure = apiserverfilters.WithImpersonation(secure, c.RequestContextMapper, c.Authorizer)
 | 
				
			||||||
	secure = audit.WithAudit(secure, attributeGetter, s.auditWriter) // before impersonation to read original user
 | 
						secure = apiserverfilters.WithAudit(secure, attributeGetter, s.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 = api.WithRequestContext(secure, c.RequestContextMapper)
 | 
						secure = genericfilters.WithPanicRecovery(secure, s.NewRequestInfoResolver())
 | 
				
			||||||
	secure = apiserver.RecoverPanics(secure, s.NewRequestInfoResolver())
 | 
					 | 
				
			||||||
	secure = genericfilters.WithTimeoutForNonLongRunningRequests(secure, longRunningFunc)
 | 
						secure = genericfilters.WithTimeoutForNonLongRunningRequests(secure, longRunningFunc)
 | 
				
			||||||
	secure = genericfilters.WithMaxInFlightLimit(secure, c.MaxRequestsInFlight, longRunningFunc)
 | 
						secure = genericfilters.WithMaxInFlightLimit(secure, c.MaxRequestsInFlight, longRunningFunc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										64
									
								
								pkg/genericapiserver/filters/panics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								pkg/genericapiserver/filters/panics.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"runtime/debug"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/golang/glog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/api/errors"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apiserver"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/httplog"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/util/runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithPanicRecovery wraps an http Handler to recover and log panics.
 | 
				
			||||||
 | 
					func WithPanicRecovery(handler http.Handler, resolver *apiserver.RequestInfoResolver) http.Handler {
 | 
				
			||||||
 | 
						return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
							defer runtime.HandleCrash(func(err interface{}) {
 | 
				
			||||||
 | 
								http.Error(w, "This request caused apisever to panic. Look in log for details.", http.StatusInternalServerError)
 | 
				
			||||||
 | 
								glog.Errorf("APIServer panic'd on %v %v: %v\n%s\n", req.Method, req.RequestURI, err, debug.Stack())
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							logger := httplog.NewLogged(req, &w)
 | 
				
			||||||
 | 
							requestInfo, err := resolver.GetRequestInfo(req)
 | 
				
			||||||
 | 
							if err != nil || requestInfo.Verb != "proxy" {
 | 
				
			||||||
 | 
								logger.StacktraceWhen(
 | 
				
			||||||
 | 
									httplog.StatusIsNot(
 | 
				
			||||||
 | 
										http.StatusOK,
 | 
				
			||||||
 | 
										http.StatusCreated,
 | 
				
			||||||
 | 
										http.StatusAccepted,
 | 
				
			||||||
 | 
										http.StatusBadRequest,
 | 
				
			||||||
 | 
										http.StatusMovedPermanently,
 | 
				
			||||||
 | 
										http.StatusTemporaryRedirect,
 | 
				
			||||||
 | 
										http.StatusConflict,
 | 
				
			||||||
 | 
										http.StatusNotFound,
 | 
				
			||||||
 | 
										http.StatusUnauthorized,
 | 
				
			||||||
 | 
										http.StatusForbidden,
 | 
				
			||||||
 | 
										http.StatusNotModified,
 | 
				
			||||||
 | 
										errors.StatusUnprocessableEntity,
 | 
				
			||||||
 | 
										http.StatusSwitchingProtocols,
 | 
				
			||||||
 | 
									),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer logger.Log()
 | 
				
			||||||
 | 
							// Dispatch to the internal handler
 | 
				
			||||||
 | 
							handler.ServeHTTP(w, req)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user