git subrepo clone (merge) https://github.com/kubernetes-incubator/metrics-server.git metrics-server

subrepo:
  subdir:   "metrics-server"
  merged:   "92d8412"
upstream:
  origin:   "https://github.com/kubernetes-incubator/metrics-server.git"
  branch:   "master"
  commit:   "92d8412"
git-subrepo:
  version:  "0.4.0"
  origin:   "???"
  commit:   "???"
diff --git a/metrics-server/vendor/k8s.io/apiserver/pkg/server/config.go b/metrics-server/vendor/k8s.io/apiserver/pkg/server/config.go
new file mode 100644
index 0000000..41ae90a
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apiserver/pkg/server/config.go
@@ -0,0 +1,616 @@
+/*
+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 server
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	goruntime "runtime"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/emicklei/go-restful-swagger12"
+	"github.com/go-openapi/spec"
+	"github.com/pborman/uuid"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	"k8s.io/apimachinery/pkg/util/sets"
+	utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
+	"k8s.io/apimachinery/pkg/version"
+	"k8s.io/apiserver/pkg/admission"
+	"k8s.io/apiserver/pkg/audit"
+	auditpolicy "k8s.io/apiserver/pkg/audit/policy"
+	"k8s.io/apiserver/pkg/authentication/authenticator"
+	"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
+	authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
+	"k8s.io/apiserver/pkg/authentication/user"
+	"k8s.io/apiserver/pkg/authorization/authorizer"
+	"k8s.io/apiserver/pkg/authorization/authorizerfactory"
+	authorizerunion "k8s.io/apiserver/pkg/authorization/union"
+	"k8s.io/apiserver/pkg/endpoints/discovery"
+	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
+	apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi"
+	apirequest "k8s.io/apiserver/pkg/endpoints/request"
+	"k8s.io/apiserver/pkg/features"
+	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/routes"
+	serverstore "k8s.io/apiserver/pkg/server/storage"
+	utilfeature "k8s.io/apiserver/pkg/util/feature"
+	"k8s.io/client-go/informers"
+	restclient "k8s.io/client-go/rest"
+	certutil "k8s.io/client-go/util/cert"
+	openapicommon "k8s.io/kube-openapi/pkg/common"
+
+	// install apis
+	"github.com/golang/glog"
+	_ "k8s.io/apiserver/pkg/apis/apiserver/install"
+)
+
+const (
+	// DefaultLegacyAPIPrefix is where the legacy APIs will be located.
+	DefaultLegacyAPIPrefix = "/api"
+
+	// APIGroupPrefix is where non-legacy API group will be located.
+	APIGroupPrefix = "/apis"
+)
+
+// Config is a structure used to configure a GenericAPIServer.
+// Its members are sorted roughly in order of importance for composers.
+type Config struct {
+	// SecureServing is required to serve https
+	SecureServing *SecureServingInfo
+
+	// Authentication is the configuration for authentication
+	Authentication AuthenticationInfo
+
+	// Authorization is the configuration for authorization
+	Authorization AuthorizationInfo
+
+	// LoopbackClientConfig is a config for a privileged loopback connection to the API server
+	// This is required for proper functioning of the PostStartHooks on a GenericAPIServer
+	// TODO: move into SecureServing(WithLoopback) as soon as insecure serving is gone
+	LoopbackClientConfig *restclient.Config
+	// RuleResolver is required to get the list of rules that apply to a given user
+	// in a given namespace
+	RuleResolver authorizer.RuleResolver
+	// AdmissionControl performs deep inspection of a given request (including content)
+	// to set values and determine whether its allowed
+	AdmissionControl      admission.Interface
+	CorsAllowedOriginList []string
+
+	EnableSwaggerUI bool
+	EnableIndex     bool
+	EnableProfiling bool
+	EnableDiscovery bool
+	// Requires generic profiling enabled
+	EnableContentionProfiling bool
+	EnableMetrics             bool
+
+	DisabledPostStartHooks sets.String
+
+	// Version will enable the /version endpoint if non-nil
+	Version *version.Info
+	// LegacyAuditWriter is the destination for audit logs. If nil, they will not be written.
+	LegacyAuditWriter io.Writer
+	// AuditBackend is where audit events are sent to.
+	AuditBackend audit.Backend
+	// AuditPolicyChecker makes the decision of whether and how to audit log a request.
+	AuditPolicyChecker auditpolicy.Checker
+	// ExternalAddress is the host name to use for external (public internet) facing URLs (e.g. Swagger)
+	// Will default to a value based on secure serving info and available ipv4 IPs.
+	ExternalAddress string
+
+	//===========================================================================
+	// Fields you probably don't care about changing
+	//===========================================================================
+
+	// BuildHandlerChainFunc allows you to build custom handler chains by decorating the apiHandler.
+	BuildHandlerChainFunc func(apiHandler http.Handler, c *Config) (secure http.Handler)
+	// HandlerChainWaitGroup allows you to wait for all chain handlers exit after the server shutdown.
+	HandlerChainWaitGroup *utilwaitgroup.SafeWaitGroup
+	// DiscoveryAddresses is used to build the IPs pass to discovery. If nil, the ExternalAddress is
+	// always reported
+	DiscoveryAddresses discovery.Addresses
+	// The default set of healthz checks. There might be more added via AddHealthzChecks dynamically.
+	HealthzChecks []healthz.HealthzChecker
+	// LegacyAPIGroupPrefixes is used to set up URL parsing for authorization and for validating requests
+	// to InstallLegacyAPIGroup. New API servers don't generally have legacy groups at all.
+	LegacyAPIGroupPrefixes sets.String
+	// RequestInfoResolver is used to assign attributes (used by admission and authorization) based on a request URL.
+	// Use-cases that are like kubelets may need to customize this.
+	RequestInfoResolver apirequest.RequestInfoResolver
+	// Serializer is required and provides the interface for serializing and converting objects to and from the wire
+	// The default (api.Codecs) usually works fine.
+	Serializer runtime.NegotiatedSerializer
+	// OpenAPIConfig will be used in generating OpenAPI spec. This is nil by default. Use DefaultOpenAPIConfig for "working" defaults.
+	OpenAPIConfig *openapicommon.Config
+	// SwaggerConfig will be used in generating Swagger spec. This is nil by default. Use DefaultSwaggerConfig for "working" defaults.
+	SwaggerConfig *swagger.Config
+
+	// RESTOptionsGetter is used to construct RESTStorage types via the generic registry.
+	RESTOptionsGetter genericregistry.RESTOptionsGetter
+
+	// If specified, all requests except those which match the LongRunningFunc predicate will timeout
+	// after this duration.
+	RequestTimeout time.Duration
+	// If specified, long running requests such as watch will be allocated a random timeout between this value, and
+	// twice this value.  Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
+	MinRequestTimeout int
+	// MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further
+	// request has to wait. Applies only to non-mutating requests.
+	MaxRequestsInFlight int
+	// MaxMutatingRequestsInFlight is the maximum number of parallel mutating requests. Every further
+	// request has to wait.
+	MaxMutatingRequestsInFlight int
+	// Predicate which is true for paths of long-running http requests
+	LongRunningFunc apirequest.LongRunningRequestCheck
+
+	// EnableAPIResponseCompression indicates whether API Responses should support compression
+	// if the client requests it via Accept-Encoding
+	EnableAPIResponseCompression bool
+
+	// MergedResourceConfig indicates which groupVersion enabled and its resources enabled/disabled.
+	// This is composed of genericapiserver defaultAPIResourceConfig and those parsed from flags.
+	// If not specify any in flags, then genericapiserver will only enable defaultAPIResourceConfig.
+	MergedResourceConfig *serverstore.ResourceConfig
+
+	//===========================================================================
+	// values below here are targets for removal
+	//===========================================================================
+
+	// The port on PublicAddress where a read-write server will be installed.
+	// Defaults to 6443 if not set.
+	ReadWritePort int
+	// PublicAddress is the IP address where members of the cluster (kubelet,
+	// kube-proxy, services, etc.) can reach the GenericAPIServer.
+	// If nil or 0.0.0.0, the host's default interface will be used.
+	PublicAddress net.IP
+}
+
+type RecommendedConfig struct {
+	Config
+
+	// SharedInformerFactory provides shared informers for Kubernetes resources. This value is set by
+	// RecommendedOptions.CoreAPI.ApplyTo called by RecommendedOptions.ApplyTo. It uses an in-cluster client config
+	// by default, or the kubeconfig given with kubeconfig command line flag.
+	SharedInformerFactory informers.SharedInformerFactory
+
+	// ClientConfig holds the kubernetes client configuration.
+	// This value is set by RecommendedOptions.CoreAPI.ApplyTo called by RecommendedOptions.ApplyTo.
+	// By default in-cluster client config is used.
+	ClientConfig *restclient.Config
+}
+
+type SecureServingInfo struct {
+	// Listener is the secure server network listener.
+	Listener net.Listener
+
+	// Cert is the main server cert which is used if SNI does not match. Cert must be non-nil and is
+	// allowed to be in SNICerts.
+	Cert *tls.Certificate
+
+	// SNICerts are the TLS certificates by name used for SNI.
+	SNICerts map[string]*tls.Certificate
+
+	// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
+	ClientCA *x509.CertPool
+
+	// MinTLSVersion optionally overrides the minimum TLS version supported.
+	// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
+	MinTLSVersion uint16
+
+	// CipherSuites optionally overrides the list of allowed cipher suites for the server.
+	// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
+	CipherSuites []uint16
+
+	// HTTP2MaxStreamsPerConnection is the limit that the api server imposes on each client.
+	// A value of zero means to use the default provided by golang's HTTP/2 support.
+	HTTP2MaxStreamsPerConnection int
+}
+
+type AuthenticationInfo struct {
+	// Authenticator determines which subject is making the request
+	Authenticator authenticator.Request
+	// SupportsBasicAuth indicates that's at least one Authenticator supports basic auth
+	// If this is true, a basic auth challenge is returned on authentication failure
+	// TODO(roberthbailey): Remove once the server no longer supports http basic auth.
+	SupportsBasicAuth bool
+}
+
+type AuthorizationInfo struct {
+	// Authorizer determines whether the subject is allowed to make the request based only
+	// on the RequestURI
+	Authorizer authorizer.Authorizer
+}
+
+// NewConfig returns a Config struct with the default values
+func NewConfig(codecs serializer.CodecFactory) *Config {
+	return &Config{
+		Serializer:                   codecs,
+		ReadWritePort:                443,
+		BuildHandlerChainFunc:        DefaultBuildHandlerChain,
+		HandlerChainWaitGroup:        new(utilwaitgroup.SafeWaitGroup),
+		LegacyAPIGroupPrefixes:       sets.NewString(DefaultLegacyAPIPrefix),
+		DisabledPostStartHooks:       sets.NewString(),
+		HealthzChecks:                []healthz.HealthzChecker{healthz.PingHealthz},
+		EnableIndex:                  true,
+		EnableDiscovery:              true,
+		EnableProfiling:              true,
+		EnableMetrics:                true,
+		MaxRequestsInFlight:          400,
+		MaxMutatingRequestsInFlight:  200,
+		RequestTimeout:               time.Duration(60) * time.Second,
+		MinRequestTimeout:            1800,
+		EnableAPIResponseCompression: utilfeature.DefaultFeatureGate.Enabled(features.APIResponseCompression),
+
+		// Default to treating watch as a long-running operation
+		// Generic API servers have no inherent long-running subresources
+		LongRunningFunc: genericfilters.BasicLongRunningRequestCheck(sets.NewString("watch"), sets.NewString()),
+	}
+}
+
+// NewRecommendedConfig returns a RecommendedConfig struct with the default values
+func NewRecommendedConfig(codecs serializer.CodecFactory) *RecommendedConfig {
+	return &RecommendedConfig{
+		Config: *NewConfig(codecs),
+	}
+}
+
+func DefaultOpenAPIConfig(getDefinitions openapicommon.GetOpenAPIDefinitions, defNamer *apiopenapi.DefinitionNamer) *openapicommon.Config {
+	return &openapicommon.Config{
+		ProtocolList:   []string{"https"},
+		IgnorePrefixes: []string{"/swaggerapi"},
+		Info: &spec.Info{
+			InfoProps: spec.InfoProps{
+				Title: "Generic API Server",
+			},
+		},
+		DefaultResponse: &spec.Response{
+			ResponseProps: spec.ResponseProps{
+				Description: "Default Response.",
+			},
+		},
+		GetOperationIDAndTags: apiopenapi.GetOperationIDAndTags,
+		GetDefinitionName:     defNamer.GetDefinitionName,
+		GetDefinitions:        getDefinitions,
+	}
+}
+
+// DefaultSwaggerConfig returns a default configuration without WebServiceURL and
+// WebServices set.
+func DefaultSwaggerConfig() *swagger.Config {
+	return &swagger.Config{
+		ApiPath:         "/swaggerapi",
+		SwaggerPath:     "/swaggerui/",
+		SwaggerFilePath: "/swagger-ui/",
+		SchemaFormatHandler: func(typeName string) string {
+			switch typeName {
+			case "metav1.Time", "*metav1.Time":
+				return "date-time"
+			}
+			return ""
+		},
+	}
+}
+
+func (c *AuthenticationInfo) ApplyClientCert(clientCAFile string, servingInfo *SecureServingInfo) error {
+	if servingInfo != nil {
+		if len(clientCAFile) > 0 {
+			clientCAs, err := certutil.CertsFromFile(clientCAFile)
+			if err != nil {
+				return fmt.Errorf("unable to load client CA file: %v", err)
+			}
+			if servingInfo.ClientCA == nil {
+				servingInfo.ClientCA = x509.NewCertPool()
+			}
+			for _, cert := range clientCAs {
+				servingInfo.ClientCA.AddCert(cert)
+			}
+		}
+	}
+
+	return nil
+}
+
+type completedConfig struct {
+	*Config
+
+	//===========================================================================
+	// values below here are filled in during completion
+	//===========================================================================
+
+	// SharedInformerFactory provides shared informers for resources
+	SharedInformerFactory informers.SharedInformerFactory
+}
+
+type CompletedConfig struct {
+	// Embed a private pointer that cannot be instantiated outside of this package.
+	*completedConfig
+}
+
+// Complete fills in any fields not set that are required to have valid data and can be derived
+// from other fields. If you're going to `ApplyOptions`, do that first. It's mutating the receiver.
+func (c *Config) Complete(informers informers.SharedInformerFactory) CompletedConfig {
+	host := c.ExternalAddress
+	if host == "" && c.PublicAddress != nil {
+		host = c.PublicAddress.String()
+	}
+
+	// if there is no port, and we have a ReadWritePort, use that
+	if _, _, err := net.SplitHostPort(host); err != nil && c.ReadWritePort != 0 {
+		host = net.JoinHostPort(host, strconv.Itoa(c.ReadWritePort))
+	}
+	c.ExternalAddress = host
+
+	if c.OpenAPIConfig != nil && c.OpenAPIConfig.SecurityDefinitions != nil {
+		// Setup OpenAPI security: all APIs will have the same authentication for now.
+		c.OpenAPIConfig.DefaultSecurity = []map[string][]string{}
+		keys := []string{}
+		for k := range *c.OpenAPIConfig.SecurityDefinitions {
+			keys = append(keys, k)
+		}
+		sort.Strings(keys)
+		for _, k := range keys {
+			c.OpenAPIConfig.DefaultSecurity = append(c.OpenAPIConfig.DefaultSecurity, map[string][]string{k: {}})
+		}
+		if c.OpenAPIConfig.CommonResponses == nil {
+			c.OpenAPIConfig.CommonResponses = map[int]spec.Response{}
+		}
+		if _, exists := c.OpenAPIConfig.CommonResponses[http.StatusUnauthorized]; !exists {
+			c.OpenAPIConfig.CommonResponses[http.StatusUnauthorized] = spec.Response{
+				ResponseProps: spec.ResponseProps{
+					Description: "Unauthorized",
+				},
+			}
+		}
+
+		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.SecureServing != nil {
+			c.SwaggerConfig.WebServicesUrl = "https://" + c.ExternalAddress
+		} else {
+			c.SwaggerConfig.WebServicesUrl = "http://" + c.ExternalAddress
+		}
+	}
+	if c.DiscoveryAddresses == nil {
+		c.DiscoveryAddresses = discovery.DefaultAddresses{DefaultAddress: c.ExternalAddress}
+	}
+
+	// If the loopbackclientconfig is specified AND it has a token for use against the API server
+	// wrap the authenticator and authorizer in loopback authentication logic
+	if c.Authentication.Authenticator != nil && c.Authorization.Authorizer != nil && c.LoopbackClientConfig != nil && len(c.LoopbackClientConfig.BearerToken) > 0 {
+		privilegedLoopbackToken := c.LoopbackClientConfig.BearerToken
+		var uid = uuid.NewRandom().String()
+		tokens := make(map[string]*user.DefaultInfo)
+		tokens[privilegedLoopbackToken] = &user.DefaultInfo{
+			Name:   user.APIServerUser,
+			UID:    uid,
+			Groups: []string{user.SystemPrivilegedGroup},
+		}
+
+		tokenAuthenticator := authenticatorfactory.NewFromTokens(tokens)
+		c.Authentication.Authenticator = authenticatorunion.New(tokenAuthenticator, c.Authentication.Authenticator)
+
+		tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
+		c.Authorization.Authorizer = authorizerunion.New(tokenAuthorizer, c.Authorization.Authorizer)
+	}
+
+	if c.RequestInfoResolver == nil {
+		c.RequestInfoResolver = NewRequestInfoResolver(c)
+	}
+
+	return CompletedConfig{&completedConfig{c, informers}}
+}
+
+// Complete fills in any fields not set that are required to have valid data and can be derived
+// from other fields. If you're going to `ApplyOptions`, do that first. It's mutating the receiver.
+func (c *RecommendedConfig) Complete() CompletedConfig {
+	return c.Config.Complete(c.SharedInformerFactory)
+}
+
+// New creates a new server which logically combines the handling chain with the passed server.
+// name is used to differentiate for logging. The handler chain in particular can be difficult as it starts delgating.
+// delegationTarget may not be nil.
+func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*GenericAPIServer, error) {
+	if c.Serializer == nil {
+		return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil")
+	}
+	if c.LoopbackClientConfig == nil {
+		return nil, fmt.Errorf("Genericapiserver.New() called with config.LoopbackClientConfig == nil")
+	}
+
+	handlerChainBuilder := func(handler http.Handler) http.Handler {
+		return c.BuildHandlerChainFunc(handler, c.Config)
+	}
+	apiServerHandler := NewAPIServerHandler(name, c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
+
+	s := &GenericAPIServer{
+		discoveryAddresses:     c.DiscoveryAddresses,
+		LoopbackClientConfig:   c.LoopbackClientConfig,
+		legacyAPIGroupPrefixes: c.LegacyAPIGroupPrefixes,
+		admissionControl:       c.AdmissionControl,
+		Serializer:             c.Serializer,
+		AuditBackend:           c.AuditBackend,
+		delegationTarget:       delegationTarget,
+		HandlerChainWaitGroup:  c.HandlerChainWaitGroup,
+
+		minRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second,
+		ShutdownTimeout:   c.RequestTimeout,
+
+		SecureServingInfo: c.SecureServing,
+		ExternalAddress:   c.ExternalAddress,
+
+		Handler: apiServerHandler,
+
+		listedPathProvider: apiServerHandler,
+
+		swaggerConfig: c.SwaggerConfig,
+		openAPIConfig: c.OpenAPIConfig,
+
+		postStartHooks:         map[string]postStartHookEntry{},
+		preShutdownHooks:       map[string]preShutdownHookEntry{},
+		disabledPostStartHooks: c.DisabledPostStartHooks,
+
+		healthzChecks: c.HealthzChecks,
+
+		DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer),
+
+		enableAPIResponseCompression: c.EnableAPIResponseCompression,
+	}
+
+	for k, v := range delegationTarget.PostStartHooks() {
+		s.postStartHooks[k] = v
+	}
+
+	for k, v := range delegationTarget.PreShutdownHooks() {
+		s.preShutdownHooks[k] = v
+	}
+
+	genericApiServerHookName := "generic-apiserver-start-informers"
+	if c.SharedInformerFactory != nil && !s.isPostStartHookRegistered(genericApiServerHookName) {
+		err := s.AddPostStartHook(genericApiServerHookName, func(context PostStartHookContext) error {
+			c.SharedInformerFactory.Start(context.StopCh)
+			return nil
+		})
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	for _, delegateCheck := range delegationTarget.HealthzChecks() {
+		skip := false
+		for _, existingCheck := range c.HealthzChecks {
+			if existingCheck.Name() == delegateCheck.Name() {
+				skip = true
+				break
+			}
+		}
+		if skip {
+			continue
+		}
+
+		s.healthzChecks = append(s.healthzChecks, delegateCheck)
+	}
+
+	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.
+	if delegationTarget.UnprotectedHandler() == nil && c.EnableIndex {
+		s.Handler.NonGoRestfulMux.NotFoundHandler(routes.IndexLister{
+			StatusCode:   http.StatusNotFound,
+			PathProvider: s.listedPathProvider,
+		})
+	}
+
+	return s, nil
+}
+
+func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
+	handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)
+	handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc)
+	handler = genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)
+	if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
+		handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
+	} else {
+		handler = genericapifilters.WithLegacyAudit(handler, c.LegacyAuditWriter)
+	}
+	failedHandler := genericapifilters.Unauthorized(c.Serializer, c.Authentication.SupportsBasicAuth)
+	if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
+		failedHandler = genericapifilters.WithFailedAuthenticationAudit(failedHandler, c.AuditBackend, c.AuditPolicyChecker)
+	}
+	handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler)
+	handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
+	handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc, c.RequestTimeout)
+	handler = genericfilters.WithWaitGroup(handler, c.LongRunningFunc, c.HandlerChainWaitGroup)
+	handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
+	handler = genericfilters.WithPanicRecovery(handler)
+	return handler
+}
+
+func installAPI(s *GenericAPIServer, c *Config) {
+	if c.EnableIndex {
+		routes.Index{}.Install(s.listedPathProvider, s.Handler.NonGoRestfulMux)
+	}
+	if c.SwaggerConfig != nil && c.EnableSwaggerUI {
+		routes.SwaggerUI{}.Install(s.Handler.NonGoRestfulMux)
+	}
+	if c.EnableProfiling {
+		routes.Profiling{}.Install(s.Handler.NonGoRestfulMux)
+		if c.EnableContentionProfiling {
+			goruntime.SetBlockProfileRate(1)
+		}
+		// so far, only logging related endpoints are considered valid to add for these debug flags.
+		routes.DebugFlags{}.Install(s.Handler.NonGoRestfulMux, "v", routes.StringFlagPutHandler(
+			routes.StringFlagSetterFunc(func(val string) (string, error) {
+				var level glog.Level
+				if err := level.Set(val); err != nil {
+					return "", fmt.Errorf("failed set glog.logging.verbosity %s: %v", val, err)
+				}
+				return "successfully set glog.logging.verbosity to " + val, nil
+			}),
+		))
+	}
+	if c.EnableMetrics {
+		if c.EnableProfiling {
+			routes.MetricsWithReset{}.Install(s.Handler.NonGoRestfulMux)
+		} else {
+			routes.DefaultMetrics{}.Install(s.Handler.NonGoRestfulMux)
+		}
+	}
+
+	routes.Version{Version: c.Version}.Install(s.Handler.GoRestfulContainer)
+
+	if c.EnableDiscovery {
+		s.Handler.GoRestfulContainer.Add(s.DiscoveryGroupManager.WebService())
+	}
+}
+
+func NewRequestInfoResolver(c *Config) *apirequest.RequestInfoFactory {
+	apiPrefixes := sets.NewString(strings.Trim(APIGroupPrefix, "/")) // all possible API prefixes
+	legacyAPIPrefixes := sets.String{}                               // APIPrefixes that won't have groups (legacy)
+	for legacyAPIPrefix := range c.LegacyAPIGroupPrefixes {
+		apiPrefixes.Insert(strings.Trim(legacyAPIPrefix, "/"))
+		legacyAPIPrefixes.Insert(strings.Trim(legacyAPIPrefix, "/"))
+	}
+
+	return &apirequest.RequestInfoFactory{
+		APIPrefixes:          apiPrefixes,
+		GrouplessAPIPrefixes: legacyAPIPrefixes,
+	}
+}