refactor names for the apiserver handling chain

Kubernetes-commit: 4389f715768661731f0aae7438b2cc8414c9746a
This commit is contained in:
deads2k 2017-05-04 14:18:38 -04:00 committed by Kubernetes Publisher
parent 234a9303e9
commit 49f3eb05d8
13 changed files with 195 additions and 245 deletions

View File

@ -35,7 +35,6 @@ go_test(
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
@ -48,6 +47,7 @@ go_library(
"config_selfclient.go",
"doc.go",
"genericapiserver.go",
"handler.go",
"healthz.go",
"hooks.go",
"serve.go",
@ -55,11 +55,13 @@ go_library(
tags = ["automanaged"],
deps = [
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
"//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/emicklei/go-restful-swagger12:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/pborman/uuid:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
@ -83,6 +85,7 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/endpoints:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",

View File

@ -53,7 +53,6 @@ import (
genericregistry "k8s.io/apiserver/pkg/registry/generic"
genericfilters "k8s.io/apiserver/pkg/server/filters"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes"
restclient "k8s.io/client-go/rest"
certutil "k8s.io/client-go/util/cert"
@ -110,10 +109,6 @@ type Config struct {
// Will default to a value based on secure serving info and available ipv4 IPs.
ExternalAddress string
// FallThroughHandler is the final HTTP handler in the chain. If it is nil, one will be created for you.
// It comes after all filters and the API handling
FallThroughHandler *mux.PathRecorderMux
//===========================================================================
// Fields you probably don't care about changing
//===========================================================================
@ -312,6 +307,17 @@ func (c *Config) Complete() completedConfig {
},
}
}
if c.OpenAPIConfig.Info == nil {
c.OpenAPIConfig.Info = &spec.Info{}
}
if c.OpenAPIConfig.Info.Version == "" {
if c.Version != nil {
c.OpenAPIConfig.Info.Version = strings.Split(c.Version.String(), "-")[0]
} else {
c.OpenAPIConfig.Info.Version = "unversioned"
}
}
}
if c.SwaggerConfig != nil && len(c.SwaggerConfig.WebServicesUrl) == 0 {
if c.SecureServingInfo != nil {
@ -342,9 +348,6 @@ func (c *Config) Complete() completedConfig {
tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
c.Authorizer = authorizerunion.New(tokenAuthorizer, c.Authorizer)
}
if c.FallThroughHandler == nil {
c.FallThroughHandler = mux.NewPathRecorderMux()
}
return completedConfig{c}
}
@ -354,38 +357,10 @@ func (c *Config) SkipComplete() completedConfig {
return completedConfig{c}
}
// New returns a new instance of GenericAPIServer from the given config.
// Certain config fields will be set to a default value if unset,
// including:
// ServiceClusterIPRange
// ServiceNodePortRange
// MasterCount
// ReadWritePort
// PublicAddress
// Public fields:
// Handler -- The returned GenericAPIServer has a field TopHandler which is an
// http.Handler which handles all the endpoints provided by the GenericAPIServer,
// including the API, the UI, and miscellaneous debugging endpoints. All
// these are subject to authorization and authentication.
// InsecureHandler -- an http.Handler which handles all the same
// endpoints as Handler, but no authorization and authentication is done.
// Public methods:
// HandleWithAuth -- Allows caller to add an http.Handler for an endpoint
// that uses the same authentication and authorization (if any is configured)
// as the GenericAPIServer's built-in endpoints.
// If the caller wants to add additional endpoints not using the GenericAPIServer's
// auth, then the caller should create a handler for those endpoints, which delegates the
// any unhandled paths to "Handler".
func (c completedConfig) New() (*GenericAPIServer, error) {
s, err := c.constructServer()
if err != nil {
return nil, err
}
// New creates a new server which logically combines the handling chain with the passed server.
func (c completedConfig) New(delegationTarget DelegationTarget) (*GenericAPIServer, error) {
// The delegationTarget and the config must agree on the RequestContextMapper
return c.buildHandlers(s, nil)
}
func (c completedConfig) constructServer() (*GenericAPIServer, error) {
if c.Serializer == nil {
return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil")
}
@ -393,7 +368,10 @@ func (c completedConfig) constructServer() (*GenericAPIServer, error) {
return nil, fmt.Errorf("Genericapiserver.New() called with config.LoopbackClientConfig == nil")
}
handlerContainer := mux.NewAPIContainer(http.NewServeMux(), c.Serializer, c.FallThroughHandler)
handlerChainBuilder := func(handler http.Handler) http.Handler {
return c.BuildHandlerChainFunc(handler, c.Config)
}
apiServerHandler := NewAPIServerHandler(c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
s := &GenericAPIServer{
discoveryAddresses: c.DiscoveryAddresses,
@ -408,10 +386,9 @@ func (c completedConfig) constructServer() (*GenericAPIServer, error) {
SecureServingInfo: c.SecureServingInfo,
ExternalAddress: c.ExternalAddress,
HandlerContainer: handlerContainer,
FallThroughHandler: c.FallThroughHandler,
Handler: apiServerHandler,
listedPathProvider: routes.ListedPathProviders{handlerContainer, c.FallThroughHandler},
listedPathProvider: apiServerHandler,
swaggerConfig: c.SwaggerConfig,
openAPIConfig: c.OpenAPIConfig,
@ -424,20 +401,6 @@ func (c completedConfig) constructServer() (*GenericAPIServer, error) {
DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer),
}
return s, nil
}
// NewWithDelegate creates a new server which logically combines the handling chain with the passed server.
func (c completedConfig) NewWithDelegate(delegationTarget DelegationTarget) (*GenericAPIServer, error) {
// some pieces of the delegationTarget take precendence. Callers should already have ensured that these
// were wired correctly. Documenting them here.
// c.RequestContextMapper = delegationTarget.RequestContextMapper()
s, err := c.constructServer()
if err != nil {
return nil, err
}
for k, v := range delegationTarget.PostStartHooks() {
s.postStartHooks[k] = v
}
@ -459,38 +422,17 @@ func (c completedConfig) NewWithDelegate(delegationTarget DelegationTarget) (*Ge
s.listedPathProvider = routes.ListedPathProviders{s.listedPathProvider, delegationTarget}
installAPI(s, c.Config)
// use the UnprotectedHandler from the delegation target to ensure that we don't attempt to double authenticator, authorize,
// or some other part of the filter chain in delegation cases.
return c.buildHandlers(s, delegationTarget.UnprotectedHandler())
}
// buildHandlers builds our handling chain
func (c completedConfig) buildHandlers(s *GenericAPIServer, delegate http.Handler) (*GenericAPIServer, error) {
if s.openAPIConfig != nil {
if s.openAPIConfig.Info == nil {
s.openAPIConfig.Info = &spec.Info{}
}
if s.openAPIConfig.Info.Version == "" {
if c.Version != nil {
s.openAPIConfig.Info.Version = strings.Split(c.Version.String(), "-")[0]
} else {
s.openAPIConfig.Info.Version = "unversioned"
}
}
}
installAPI(s, c.Config)
if delegate != nil {
s.FallThroughHandler.NotFoundHandler(delegate)
} else if c.EnableIndex {
s.FallThroughHandler.NotFoundHandler(routes.IndexLister{
if delegationTarget.UnprotectedHandler() == nil && c.EnableIndex {
s.Handler.PostGoRestfulMux.NotFoundHandler(routes.IndexLister{
StatusCode: http.StatusNotFound,
PathProvider: s.listedPathProvider,
})
}
s.Handler = c.BuildHandlerChainFunc(s.HandlerContainer.ServeMux, c.Config)
return s, nil
}
@ -510,28 +452,28 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
func installAPI(s *GenericAPIServer, c *Config) {
if c.EnableIndex {
routes.Index{}.Install(s.listedPathProvider, c.FallThroughHandler)
routes.Index{}.Install(s.listedPathProvider, s.Handler.PostGoRestfulMux)
}
if c.SwaggerConfig != nil && c.EnableSwaggerUI {
routes.SwaggerUI{}.Install(s.FallThroughHandler)
routes.SwaggerUI{}.Install(s.Handler.PostGoRestfulMux)
}
if c.EnableProfiling {
routes.Profiling{}.Install(s.FallThroughHandler)
routes.Profiling{}.Install(s.Handler.PostGoRestfulMux)
if c.EnableContentionProfiling {
goruntime.SetBlockProfileRate(1)
}
}
if c.EnableMetrics {
if c.EnableProfiling {
routes.MetricsWithReset{}.Install(s.FallThroughHandler)
routes.MetricsWithReset{}.Install(s.Handler.PostGoRestfulMux)
} else {
routes.DefaultMetrics{}.Install(s.FallThroughHandler)
routes.DefaultMetrics{}.Install(s.Handler.PostGoRestfulMux)
}
}
routes.Version{Version: c.Version}.Install(s.HandlerContainer)
routes.Version{Version: c.Version}.Install(s.Handler.GoRestfulContainer)
if c.EnableDiscovery {
s.HandlerContainer.Add(s.DiscoveryGroupManager.WebService())
s.Handler.GoRestfulContainer.Add(s.DiscoveryGroupManager.WebService())
}
}

View File

@ -28,7 +28,6 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
"k8s.io/client-go/rest"
)
@ -38,7 +37,6 @@ func TestNewWithDelegate(t *testing.T) {
delegateConfig.RequestContextMapper = genericapirequest.NewRequestContextMapper()
delegateConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
delegateConfig.LoopbackClientConfig = &rest.Config{}
delegateConfig.FallThroughHandler = mux.NewPathRecorderMux()
delegateConfig.SwaggerConfig = DefaultSwaggerConfig()
delegateHealthzCalled := false
@ -47,14 +45,13 @@ func TestNewWithDelegate(t *testing.T) {
return fmt.Errorf("delegate failed healthcheck")
}))
delegateConfig.FallThroughHandler.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusForbidden)
})
delegateServer, err := delegateConfig.SkipComplete().New()
delegateServer, err := delegateConfig.SkipComplete().New(EmptyDelegate)
if err != nil {
t.Fatal(err)
}
delegateServer.Handler.PostGoRestfulMux.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusForbidden)
})
delegateServer.AddPostStartHook("delegate-post-start-hook", func(context PostStartHookContext) error {
return nil
@ -68,7 +65,6 @@ func TestNewWithDelegate(t *testing.T) {
wrappingConfig.RequestContextMapper = genericapirequest.NewRequestContextMapper()
wrappingConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
wrappingConfig.LoopbackClientConfig = &rest.Config{}
wrappingConfig.FallThroughHandler = mux.NewPathRecorderMux()
wrappingConfig.SwaggerConfig = DefaultSwaggerConfig()
wrappingHealthzCalled := false
@ -77,14 +73,13 @@ func TestNewWithDelegate(t *testing.T) {
return fmt.Errorf("wrapping failed healthcheck")
}))
wrappingConfig.FallThroughHandler.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
})
wrappingServer, err := wrappingConfig.Complete().NewWithDelegate(delegateServer)
wrappingServer, err := wrappingConfig.Complete().New(delegateServer)
if err != nil {
t.Fatal(err)
}
wrappingServer.Handler.PostGoRestfulMux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
})
wrappingServer.AddPostStartHook("wrapping-post-start-hook", func(context PostStartHookContext) error {
return nil

View File

@ -42,8 +42,6 @@ import (
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
genericmux "k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes"
restclient "k8s.io/client-go/rest"
)
@ -101,9 +99,6 @@ type GenericAPIServer struct {
// requestContextMapper provides a way to get the context for a request. It may be nil.
requestContextMapper apirequest.RequestContextMapper
// The registered APIs
HandlerContainer *genericmux.APIContainer
SecureServingInfo *SecureServingInfo
// numerical ports, set after listening
@ -121,10 +116,8 @@ type GenericAPIServer struct {
Serializer runtime.NegotiatedSerializer
// "Outputs"
Handler http.Handler
// FallThroughHandler is the final HTTP handler in the chain.
// It comes after all filters and the API handling
FallThroughHandler *mux.PathRecorderMux
// Handler holdes the handlers being used by this API server
Handler *APIServerHandler
// listedPathProvider is a lister which provides the set of paths to show at /
listedPathProvider routes.ListedPathProvider
@ -171,7 +164,7 @@ type DelegationTarget interface {
}
func (s *GenericAPIServer) UnprotectedHandler() http.Handler {
return s.HandlerContainer.ServeMux
return s.Handler.GoRestfulContainer.ServeMux
}
func (s *GenericAPIServer) PostStartHooks() map[string]postStartHookEntry {
return s.postStartHooks
@ -233,12 +226,12 @@ type preparedGenericAPIServer struct {
// PrepareRun does post API installation setup steps.
func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
if s.swaggerConfig != nil {
routes.Swagger{Config: s.swaggerConfig}.Install(s.HandlerContainer)
routes.Swagger{Config: s.swaggerConfig}.Install(s.Handler.GoRestfulContainer)
}
if s.openAPIConfig != nil {
routes.OpenAPI{
Config: s.openAPIConfig,
}.Install(s.HandlerContainer, s.FallThroughHandler)
}.Install(s.Handler.GoRestfulContainer, s.Handler.PostGoRestfulMux)
}
s.installHealthz()
@ -306,7 +299,7 @@ func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *A
apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion
}
if err := apiGroupVersion.InstallREST(s.HandlerContainer.Container); err != nil {
if err := apiGroupVersion.InstallREST(s.Handler.GoRestfulContainer); err != nil {
return fmt.Errorf("Unable to setup API %v: %v", apiGroupInfo, err)
}
}
@ -329,7 +322,7 @@ func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo
}
// Install the version handler.
// Add a handler at /<apiPrefix> to enumerate the supported api versions.
s.HandlerContainer.Add(discovery.NewLegacyRootAPIHandler(s.discoveryAddresses, s.Serializer, apiPrefix, apiVersions).WebService())
s.Handler.GoRestfulContainer.Add(discovery.NewLegacyRootAPIHandler(s.discoveryAddresses, s.Serializer, apiPrefix, apiVersions).WebService())
return nil
}
@ -373,7 +366,7 @@ func (s *GenericAPIServer) InstallAPIGroup(apiGroupInfo *APIGroupInfo) error {
}
s.DiscoveryGroupManager.AddGroup(apiGroup)
s.HandlerContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup).WebService())
s.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup).WebService())
return nil
}

View File

@ -47,7 +47,6 @@ import (
"k8s.io/apiserver/pkg/endpoints/discovery"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server/mux"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
restclient "k8s.io/client-go/rest"
)
@ -90,7 +89,6 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
config.RequestContextMapper = genericapirequest.NewRequestContextMapper()
config.LegacyAPIGroupPrefixes = sets.NewString("/api")
config.LoopbackClientConfig = &restclient.Config{}
config.FallThroughHandler = mux.NewPathRecorderMux()
// TODO restore this test, but right now, eliminate our cycle
// config.OpenAPIConfig = DefaultOpenAPIConfig(testGetOpenAPIDefinitions, runtime.NewScheme())
@ -108,7 +106,7 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
func newMaster(t *testing.T) (*GenericAPIServer, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
etcdserver, config, assert := setUp(t)
s, err := config.Complete().New()
s, err := config.Complete().New(EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the server: %v", err)
}
@ -140,7 +138,7 @@ func TestInstallAPIGroups(t *testing.T) {
config.LegacyAPIGroupPrefixes = sets.NewString("/apiPrefix")
config.DiscoveryAddresses = discovery.DefaultAddresses{DefaultAddress: "ExternalAddress"}
s, err := config.SkipComplete().New()
s, err := config.SkipComplete().New(EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the server: %v", err)
}
@ -304,7 +302,7 @@ func TestPrepareRun(t *testing.T) {
assert.NotNil(config.SwaggerConfig)
// assert.NotNil(config.OpenAPIConfig)
server := httptest.NewServer(s.HandlerContainer.ServeMux)
server := httptest.NewServer(s.Handler.GoRestfulContainer.ServeMux)
defer server.Close()
s.PrepareRun()
@ -345,13 +343,13 @@ func TestCustomHandlerChain(t *testing.T) {
called = true
})
s, err := config.SkipComplete().New()
s, err := config.SkipComplete().New(EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the server: %v", err)
}
s.FallThroughHandler.Handle("/nonswagger", handler)
s.FallThroughHandler.Handle("/secret", handler)
s.Handler.PostGoRestfulMux.Handle("/nonswagger", handler)
s.Handler.PostGoRestfulMux.Handle("/secret", handler)
type Test struct {
handler http.Handler
@ -400,7 +398,7 @@ func TestNotRestRoutesHaveAuth(t *testing.T) {
kubeVersion := fakeVersion()
config.Version = &kubeVersion
s, err := config.SkipComplete().New()
s, err := config.SkipComplete().New(EmptyDelegate)
if err != nil {
t.Fatalf("Error in bringing up the server: %v", err)
}

128
pkg/server/handler.go Normal file
View File

@ -0,0 +1,128 @@
/*
Copyright 2017 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 server
import (
"bytes"
"fmt"
"net/http"
rt "runtime"
"sort"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/server/mux"
genericmux "k8s.io/apiserver/pkg/server/mux"
)
// APIServerHandlers holds the different http.Handlers used by the API server.
// This includes the full handler chain, the gorestful handler (used for the API) which falls through to the postGoRestful handler
// and the postGoRestful handler (which can contain a fallthrough of its own)
type APIServerHandler struct {
// FullHandlerChain is the one that is eventually served with. It should include the full filter
// chain and then call the GoRestfulContainer.
FullHandlerChain http.Handler
// The registered APIs
GoRestfulContainer *restful.Container
// PostGoRestfulMux is the final HTTP handler in the chain.
// It comes after all filters and the API handling
PostGoRestfulMux *mux.PathRecorderMux
}
// HandlerChainBuilderFn is used to wrap the GoRestfulContainer handler using the provided handler chain.
// It is normally used to apply filtering like authentication and authorization
type HandlerChainBuilderFn func(apiHandler http.Handler) http.Handler
func NewAPIServerHandler(s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler {
postGoRestfulMux := genericmux.NewPathRecorderMux()
if notFoundHandler != nil {
postGoRestfulMux.NotFoundHandler(notFoundHandler)
}
gorestfulContainer := restful.NewContainer()
gorestfulContainer.ServeMux = http.NewServeMux()
gorestfulContainer.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*}
gorestfulContainer.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) {
logStackOnRecover(s, panicReason, httpWriter)
})
gorestfulContainer.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
serviceErrorHandler(s, serviceErr, request, response)
})
// register the defaultHandler for everything. This will allow an unhandled request to fall through to another handler instead of
// ending up with a forced 404
gorestfulContainer.Handle("/", postGoRestfulMux)
return &APIServerHandler{
FullHandlerChain: handlerChainBuilder(gorestfulContainer.ServeMux),
GoRestfulContainer: gorestfulContainer,
PostGoRestfulMux: postGoRestfulMux,
}
}
// ListedPaths returns the paths that should be shown under /
func (a *APIServerHandler) ListedPaths() []string {
var handledPaths []string
// Extract the paths handled using restful.WebService
for _, ws := range a.GoRestfulContainer.RegisteredWebServices() {
handledPaths = append(handledPaths, ws.RootPath())
}
handledPaths = append(handledPaths, a.PostGoRestfulMux.ListedPaths()...)
sort.Strings(handledPaths)
return handledPaths
}
//TODO: Unify with RecoverPanics?
func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
for i := 2; ; i++ {
_, file, line, ok := rt.Caller(i)
if !ok {
break
}
buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
}
glog.Errorln(buffer.String())
headers := http.Header{}
if ct := w.Header().Get("Content-Type"); len(ct) > 0 {
headers.Set("Accept", ct)
}
responsewriters.ErrorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", schema.GroupResource{}, "", "", 0, false), s, schema.GroupVersion{}, w, &http.Request{Header: headers})
}
func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, resp *restful.Response) {
responsewriters.ErrorNegotiated(
apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false),
s,
schema.GroupVersion{},
resp,
request.Request,
)
}
// ServeHTTP makes it an http.Handler
func (a *APIServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.FullHandlerChain.ServeHTTP(w, r)
}

View File

@ -41,5 +41,5 @@ func (s *GenericAPIServer) installHealthz() {
defer s.healthzLock.Unlock()
s.healthzCreated = true
healthz.InstallHandler(s.FallThroughHandler, s.healthzChecks...)
healthz.InstallHandler(s.Handler.PostGoRestfulMux, s.healthzChecks...)
}

View File

@ -19,19 +19,12 @@ go_test(
go_library(
name = "go_default_library",
srcs = [
"container.go",
"doc.go",
"pathrecorder.go",
],
tags = ["automanaged"],
deps = [
"//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
],
)

View File

@ -1,102 +0,0 @@
/*
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 mux
import (
"bytes"
"fmt"
"net/http"
rt "runtime"
"sort"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
)
// APIContainer is a restful container which in addition support registering
// handlers that do not show up in swagger or in /
type APIContainer struct {
*restful.Container
}
// NewAPIContainer constructs a new container for APIs
func NewAPIContainer(mux *http.ServeMux, s runtime.NegotiatedSerializer, defaultMux http.Handler) *APIContainer {
c := APIContainer{
Container: restful.NewContainer(),
}
c.Container.ServeMux = mux
c.Container.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*}
c.Container.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) {
logStackOnRecover(s, panicReason, httpWriter)
})
c.Container.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
serviceErrorHandler(s, serviceErr, request, response)
})
// register the defaultHandler for everything. This will allow an unhandled request to fall through to another handler instead of
// ending up with a forced 404
c.Container.Handle("/", defaultMux)
return &c
}
// ListedPaths returns the paths of the webservices for listing on /.
func (c *APIContainer) ListedPaths() []string {
var handledPaths []string
// Extract the paths handled using restful.WebService
for _, ws := range c.RegisteredWebServices() {
handledPaths = append(handledPaths, ws.RootPath())
}
sort.Strings(handledPaths)
return handledPaths
}
//TODO: Unify with RecoverPanics?
func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
for i := 2; ; i++ {
_, file, line, ok := rt.Caller(i)
if !ok {
break
}
buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
}
glog.Errorln(buffer.String())
headers := http.Header{}
if ct := w.Header().Get("Content-Type"); len(ct) > 0 {
headers.Set("Accept", ct)
}
responsewriters.ErrorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", schema.GroupResource{}, "", "", 0, false), s, schema.GroupVersion{}, w, &http.Request{Header: headers})
}
func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, resp *restful.Response) {
responsewriters.ErrorNegotiated(
apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false),
s,
schema.GroupVersion{},
resp,
request.Request,
)
}

View File

@ -475,7 +475,7 @@ NextTest:
return
}
s, err := config.Complete().New()
s, err := config.Complete().New(server.EmptyDelegate)
if err != nil {
t.Errorf("%q - failed creating the server: %v", title, err)
return

View File

@ -17,6 +17,8 @@ limitations under the License.
package routes
import (
"github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/openapi"
"k8s.io/apiserver/pkg/server/mux"
apiserveropenapi "k8s.io/apiserver/pkg/server/openapi"
@ -30,7 +32,7 @@ type OpenAPI struct {
}
// Install adds the SwaggerUI webservice to the given mux.
func (oa OpenAPI) Install(c *mux.APIContainer, mux *mux.PathRecorderMux) {
func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) {
err := apiserveropenapi.RegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux)
if err != nil {
glog.Fatalf("Failed to register open api spec for root: %v", err)

View File

@ -17,8 +17,7 @@ limitations under the License.
package routes
import (
"k8s.io/apiserver/pkg/server/mux"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-swagger12"
)
@ -31,7 +30,7 @@ type Swagger struct {
}
// Install adds the SwaggerUI webservice to the given mux.
func (s Swagger) Install(c *mux.APIContainer) {
func (s Swagger) Install(c *restful.Container) {
s.Config.WebServices = c.RegisteredWebServices()
swagger.RegisterSwaggerService(*s.Config, c.Container)
swagger.RegisterSwaggerService(*s.Config, c)
}

View File

@ -23,7 +23,6 @@ import (
"k8s.io/apimachinery/pkg/version"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/server/mux"
)
// Version provides a webservice with version information.
@ -32,7 +31,7 @@ type Version struct {
}
// Install registers the APIServer's `/version` handler.
func (v Version) Install(c *mux.APIContainer) {
func (v Version) Install(c *restful.Container) {
if v.Version == nil {
return
}