blob: 41ae90af6891df499d5cce5f9d440c82e8dccf0a [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package server
18
19import (
20 "crypto/tls"
21 "crypto/x509"
22 "fmt"
23 "io"
24 "net"
25 "net/http"
26 goruntime "runtime"
27 "sort"
28 "strconv"
29 "strings"
30 "time"
31
32 "github.com/emicklei/go-restful-swagger12"
33 "github.com/go-openapi/spec"
34 "github.com/pborman/uuid"
35
36 "k8s.io/apimachinery/pkg/runtime"
37 "k8s.io/apimachinery/pkg/runtime/serializer"
38 "k8s.io/apimachinery/pkg/util/sets"
39 utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
40 "k8s.io/apimachinery/pkg/version"
41 "k8s.io/apiserver/pkg/admission"
42 "k8s.io/apiserver/pkg/audit"
43 auditpolicy "k8s.io/apiserver/pkg/audit/policy"
44 "k8s.io/apiserver/pkg/authentication/authenticator"
45 "k8s.io/apiserver/pkg/authentication/authenticatorfactory"
46 authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
47 "k8s.io/apiserver/pkg/authentication/user"
48 "k8s.io/apiserver/pkg/authorization/authorizer"
49 "k8s.io/apiserver/pkg/authorization/authorizerfactory"
50 authorizerunion "k8s.io/apiserver/pkg/authorization/union"
51 "k8s.io/apiserver/pkg/endpoints/discovery"
52 genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
53 apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi"
54 apirequest "k8s.io/apiserver/pkg/endpoints/request"
55 "k8s.io/apiserver/pkg/features"
56 genericregistry "k8s.io/apiserver/pkg/registry/generic"
57 genericfilters "k8s.io/apiserver/pkg/server/filters"
58 "k8s.io/apiserver/pkg/server/healthz"
59 "k8s.io/apiserver/pkg/server/routes"
60 serverstore "k8s.io/apiserver/pkg/server/storage"
61 utilfeature "k8s.io/apiserver/pkg/util/feature"
62 "k8s.io/client-go/informers"
63 restclient "k8s.io/client-go/rest"
64 certutil "k8s.io/client-go/util/cert"
65 openapicommon "k8s.io/kube-openapi/pkg/common"
66
67 // install apis
68 "github.com/golang/glog"
69 _ "k8s.io/apiserver/pkg/apis/apiserver/install"
70)
71
72const (
73 // DefaultLegacyAPIPrefix is where the legacy APIs will be located.
74 DefaultLegacyAPIPrefix = "/api"
75
76 // APIGroupPrefix is where non-legacy API group will be located.
77 APIGroupPrefix = "/apis"
78)
79
80// Config is a structure used to configure a GenericAPIServer.
81// Its members are sorted roughly in order of importance for composers.
82type Config struct {
83 // SecureServing is required to serve https
84 SecureServing *SecureServingInfo
85
86 // Authentication is the configuration for authentication
87 Authentication AuthenticationInfo
88
89 // Authorization is the configuration for authorization
90 Authorization AuthorizationInfo
91
92 // LoopbackClientConfig is a config for a privileged loopback connection to the API server
93 // This is required for proper functioning of the PostStartHooks on a GenericAPIServer
94 // TODO: move into SecureServing(WithLoopback) as soon as insecure serving is gone
95 LoopbackClientConfig *restclient.Config
96 // RuleResolver is required to get the list of rules that apply to a given user
97 // in a given namespace
98 RuleResolver authorizer.RuleResolver
99 // AdmissionControl performs deep inspection of a given request (including content)
100 // to set values and determine whether its allowed
101 AdmissionControl admission.Interface
102 CorsAllowedOriginList []string
103
104 EnableSwaggerUI bool
105 EnableIndex bool
106 EnableProfiling bool
107 EnableDiscovery bool
108 // Requires generic profiling enabled
109 EnableContentionProfiling bool
110 EnableMetrics bool
111
112 DisabledPostStartHooks sets.String
113
114 // Version will enable the /version endpoint if non-nil
115 Version *version.Info
116 // LegacyAuditWriter is the destination for audit logs. If nil, they will not be written.
117 LegacyAuditWriter io.Writer
118 // AuditBackend is where audit events are sent to.
119 AuditBackend audit.Backend
120 // AuditPolicyChecker makes the decision of whether and how to audit log a request.
121 AuditPolicyChecker auditpolicy.Checker
122 // ExternalAddress is the host name to use for external (public internet) facing URLs (e.g. Swagger)
123 // Will default to a value based on secure serving info and available ipv4 IPs.
124 ExternalAddress string
125
126 //===========================================================================
127 // Fields you probably don't care about changing
128 //===========================================================================
129
130 // BuildHandlerChainFunc allows you to build custom handler chains by decorating the apiHandler.
131 BuildHandlerChainFunc func(apiHandler http.Handler, c *Config) (secure http.Handler)
132 // HandlerChainWaitGroup allows you to wait for all chain handlers exit after the server shutdown.
133 HandlerChainWaitGroup *utilwaitgroup.SafeWaitGroup
134 // DiscoveryAddresses is used to build the IPs pass to discovery. If nil, the ExternalAddress is
135 // always reported
136 DiscoveryAddresses discovery.Addresses
137 // The default set of healthz checks. There might be more added via AddHealthzChecks dynamically.
138 HealthzChecks []healthz.HealthzChecker
139 // LegacyAPIGroupPrefixes is used to set up URL parsing for authorization and for validating requests
140 // to InstallLegacyAPIGroup. New API servers don't generally have legacy groups at all.
141 LegacyAPIGroupPrefixes sets.String
142 // RequestInfoResolver is used to assign attributes (used by admission and authorization) based on a request URL.
143 // Use-cases that are like kubelets may need to customize this.
144 RequestInfoResolver apirequest.RequestInfoResolver
145 // Serializer is required and provides the interface for serializing and converting objects to and from the wire
146 // The default (api.Codecs) usually works fine.
147 Serializer runtime.NegotiatedSerializer
148 // OpenAPIConfig will be used in generating OpenAPI spec. This is nil by default. Use DefaultOpenAPIConfig for "working" defaults.
149 OpenAPIConfig *openapicommon.Config
150 // SwaggerConfig will be used in generating Swagger spec. This is nil by default. Use DefaultSwaggerConfig for "working" defaults.
151 SwaggerConfig *swagger.Config
152
153 // RESTOptionsGetter is used to construct RESTStorage types via the generic registry.
154 RESTOptionsGetter genericregistry.RESTOptionsGetter
155
156 // If specified, all requests except those which match the LongRunningFunc predicate will timeout
157 // after this duration.
158 RequestTimeout time.Duration
159 // If specified, long running requests such as watch will be allocated a random timeout between this value, and
160 // twice this value. Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
161 MinRequestTimeout int
162 // MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further
163 // request has to wait. Applies only to non-mutating requests.
164 MaxRequestsInFlight int
165 // MaxMutatingRequestsInFlight is the maximum number of parallel mutating requests. Every further
166 // request has to wait.
167 MaxMutatingRequestsInFlight int
168 // Predicate which is true for paths of long-running http requests
169 LongRunningFunc apirequest.LongRunningRequestCheck
170
171 // EnableAPIResponseCompression indicates whether API Responses should support compression
172 // if the client requests it via Accept-Encoding
173 EnableAPIResponseCompression bool
174
175 // MergedResourceConfig indicates which groupVersion enabled and its resources enabled/disabled.
176 // This is composed of genericapiserver defaultAPIResourceConfig and those parsed from flags.
177 // If not specify any in flags, then genericapiserver will only enable defaultAPIResourceConfig.
178 MergedResourceConfig *serverstore.ResourceConfig
179
180 //===========================================================================
181 // values below here are targets for removal
182 //===========================================================================
183
184 // The port on PublicAddress where a read-write server will be installed.
185 // Defaults to 6443 if not set.
186 ReadWritePort int
187 // PublicAddress is the IP address where members of the cluster (kubelet,
188 // kube-proxy, services, etc.) can reach the GenericAPIServer.
189 // If nil or 0.0.0.0, the host's default interface will be used.
190 PublicAddress net.IP
191}
192
193type RecommendedConfig struct {
194 Config
195
196 // SharedInformerFactory provides shared informers for Kubernetes resources. This value is set by
197 // RecommendedOptions.CoreAPI.ApplyTo called by RecommendedOptions.ApplyTo. It uses an in-cluster client config
198 // by default, or the kubeconfig given with kubeconfig command line flag.
199 SharedInformerFactory informers.SharedInformerFactory
200
201 // ClientConfig holds the kubernetes client configuration.
202 // This value is set by RecommendedOptions.CoreAPI.ApplyTo called by RecommendedOptions.ApplyTo.
203 // By default in-cluster client config is used.
204 ClientConfig *restclient.Config
205}
206
207type SecureServingInfo struct {
208 // Listener is the secure server network listener.
209 Listener net.Listener
210
211 // Cert is the main server cert which is used if SNI does not match. Cert must be non-nil and is
212 // allowed to be in SNICerts.
213 Cert *tls.Certificate
214
215 // SNICerts are the TLS certificates by name used for SNI.
216 SNICerts map[string]*tls.Certificate
217
218 // ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
219 ClientCA *x509.CertPool
220
221 // MinTLSVersion optionally overrides the minimum TLS version supported.
222 // Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
223 MinTLSVersion uint16
224
225 // CipherSuites optionally overrides the list of allowed cipher suites for the server.
226 // Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
227 CipherSuites []uint16
228
229 // HTTP2MaxStreamsPerConnection is the limit that the api server imposes on each client.
230 // A value of zero means to use the default provided by golang's HTTP/2 support.
231 HTTP2MaxStreamsPerConnection int
232}
233
234type AuthenticationInfo struct {
235 // Authenticator determines which subject is making the request
236 Authenticator authenticator.Request
237 // SupportsBasicAuth indicates that's at least one Authenticator supports basic auth
238 // If this is true, a basic auth challenge is returned on authentication failure
239 // TODO(roberthbailey): Remove once the server no longer supports http basic auth.
240 SupportsBasicAuth bool
241}
242
243type AuthorizationInfo struct {
244 // Authorizer determines whether the subject is allowed to make the request based only
245 // on the RequestURI
246 Authorizer authorizer.Authorizer
247}
248
249// NewConfig returns a Config struct with the default values
250func NewConfig(codecs serializer.CodecFactory) *Config {
251 return &Config{
252 Serializer: codecs,
253 ReadWritePort: 443,
254 BuildHandlerChainFunc: DefaultBuildHandlerChain,
255 HandlerChainWaitGroup: new(utilwaitgroup.SafeWaitGroup),
256 LegacyAPIGroupPrefixes: sets.NewString(DefaultLegacyAPIPrefix),
257 DisabledPostStartHooks: sets.NewString(),
258 HealthzChecks: []healthz.HealthzChecker{healthz.PingHealthz},
259 EnableIndex: true,
260 EnableDiscovery: true,
261 EnableProfiling: true,
262 EnableMetrics: true,
263 MaxRequestsInFlight: 400,
264 MaxMutatingRequestsInFlight: 200,
265 RequestTimeout: time.Duration(60) * time.Second,
266 MinRequestTimeout: 1800,
267 EnableAPIResponseCompression: utilfeature.DefaultFeatureGate.Enabled(features.APIResponseCompression),
268
269 // Default to treating watch as a long-running operation
270 // Generic API servers have no inherent long-running subresources
271 LongRunningFunc: genericfilters.BasicLongRunningRequestCheck(sets.NewString("watch"), sets.NewString()),
272 }
273}
274
275// NewRecommendedConfig returns a RecommendedConfig struct with the default values
276func NewRecommendedConfig(codecs serializer.CodecFactory) *RecommendedConfig {
277 return &RecommendedConfig{
278 Config: *NewConfig(codecs),
279 }
280}
281
282func DefaultOpenAPIConfig(getDefinitions openapicommon.GetOpenAPIDefinitions, defNamer *apiopenapi.DefinitionNamer) *openapicommon.Config {
283 return &openapicommon.Config{
284 ProtocolList: []string{"https"},
285 IgnorePrefixes: []string{"/swaggerapi"},
286 Info: &spec.Info{
287 InfoProps: spec.InfoProps{
288 Title: "Generic API Server",
289 },
290 },
291 DefaultResponse: &spec.Response{
292 ResponseProps: spec.ResponseProps{
293 Description: "Default Response.",
294 },
295 },
296 GetOperationIDAndTags: apiopenapi.GetOperationIDAndTags,
297 GetDefinitionName: defNamer.GetDefinitionName,
298 GetDefinitions: getDefinitions,
299 }
300}
301
302// DefaultSwaggerConfig returns a default configuration without WebServiceURL and
303// WebServices set.
304func DefaultSwaggerConfig() *swagger.Config {
305 return &swagger.Config{
306 ApiPath: "/swaggerapi",
307 SwaggerPath: "/swaggerui/",
308 SwaggerFilePath: "/swagger-ui/",
309 SchemaFormatHandler: func(typeName string) string {
310 switch typeName {
311 case "metav1.Time", "*metav1.Time":
312 return "date-time"
313 }
314 return ""
315 },
316 }
317}
318
319func (c *AuthenticationInfo) ApplyClientCert(clientCAFile string, servingInfo *SecureServingInfo) error {
320 if servingInfo != nil {
321 if len(clientCAFile) > 0 {
322 clientCAs, err := certutil.CertsFromFile(clientCAFile)
323 if err != nil {
324 return fmt.Errorf("unable to load client CA file: %v", err)
325 }
326 if servingInfo.ClientCA == nil {
327 servingInfo.ClientCA = x509.NewCertPool()
328 }
329 for _, cert := range clientCAs {
330 servingInfo.ClientCA.AddCert(cert)
331 }
332 }
333 }
334
335 return nil
336}
337
338type completedConfig struct {
339 *Config
340
341 //===========================================================================
342 // values below here are filled in during completion
343 //===========================================================================
344
345 // SharedInformerFactory provides shared informers for resources
346 SharedInformerFactory informers.SharedInformerFactory
347}
348
349type CompletedConfig struct {
350 // Embed a private pointer that cannot be instantiated outside of this package.
351 *completedConfig
352}
353
354// Complete fills in any fields not set that are required to have valid data and can be derived
355// from other fields. If you're going to `ApplyOptions`, do that first. It's mutating the receiver.
356func (c *Config) Complete(informers informers.SharedInformerFactory) CompletedConfig {
357 host := c.ExternalAddress
358 if host == "" && c.PublicAddress != nil {
359 host = c.PublicAddress.String()
360 }
361
362 // if there is no port, and we have a ReadWritePort, use that
363 if _, _, err := net.SplitHostPort(host); err != nil && c.ReadWritePort != 0 {
364 host = net.JoinHostPort(host, strconv.Itoa(c.ReadWritePort))
365 }
366 c.ExternalAddress = host
367
368 if c.OpenAPIConfig != nil && c.OpenAPIConfig.SecurityDefinitions != nil {
369 // Setup OpenAPI security: all APIs will have the same authentication for now.
370 c.OpenAPIConfig.DefaultSecurity = []map[string][]string{}
371 keys := []string{}
372 for k := range *c.OpenAPIConfig.SecurityDefinitions {
373 keys = append(keys, k)
374 }
375 sort.Strings(keys)
376 for _, k := range keys {
377 c.OpenAPIConfig.DefaultSecurity = append(c.OpenAPIConfig.DefaultSecurity, map[string][]string{k: {}})
378 }
379 if c.OpenAPIConfig.CommonResponses == nil {
380 c.OpenAPIConfig.CommonResponses = map[int]spec.Response{}
381 }
382 if _, exists := c.OpenAPIConfig.CommonResponses[http.StatusUnauthorized]; !exists {
383 c.OpenAPIConfig.CommonResponses[http.StatusUnauthorized] = spec.Response{
384 ResponseProps: spec.ResponseProps{
385 Description: "Unauthorized",
386 },
387 }
388 }
389
390 if c.OpenAPIConfig.Info == nil {
391 c.OpenAPIConfig.Info = &spec.Info{}
392 }
393 if c.OpenAPIConfig.Info.Version == "" {
394 if c.Version != nil {
395 c.OpenAPIConfig.Info.Version = strings.Split(c.Version.String(), "-")[0]
396 } else {
397 c.OpenAPIConfig.Info.Version = "unversioned"
398 }
399 }
400 }
401 if c.SwaggerConfig != nil && len(c.SwaggerConfig.WebServicesUrl) == 0 {
402 if c.SecureServing != nil {
403 c.SwaggerConfig.WebServicesUrl = "https://" + c.ExternalAddress
404 } else {
405 c.SwaggerConfig.WebServicesUrl = "http://" + c.ExternalAddress
406 }
407 }
408 if c.DiscoveryAddresses == nil {
409 c.DiscoveryAddresses = discovery.DefaultAddresses{DefaultAddress: c.ExternalAddress}
410 }
411
412 // If the loopbackclientconfig is specified AND it has a token for use against the API server
413 // wrap the authenticator and authorizer in loopback authentication logic
414 if c.Authentication.Authenticator != nil && c.Authorization.Authorizer != nil && c.LoopbackClientConfig != nil && len(c.LoopbackClientConfig.BearerToken) > 0 {
415 privilegedLoopbackToken := c.LoopbackClientConfig.BearerToken
416 var uid = uuid.NewRandom().String()
417 tokens := make(map[string]*user.DefaultInfo)
418 tokens[privilegedLoopbackToken] = &user.DefaultInfo{
419 Name: user.APIServerUser,
420 UID: uid,
421 Groups: []string{user.SystemPrivilegedGroup},
422 }
423
424 tokenAuthenticator := authenticatorfactory.NewFromTokens(tokens)
425 c.Authentication.Authenticator = authenticatorunion.New(tokenAuthenticator, c.Authentication.Authenticator)
426
427 tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
428 c.Authorization.Authorizer = authorizerunion.New(tokenAuthorizer, c.Authorization.Authorizer)
429 }
430
431 if c.RequestInfoResolver == nil {
432 c.RequestInfoResolver = NewRequestInfoResolver(c)
433 }
434
435 return CompletedConfig{&completedConfig{c, informers}}
436}
437
438// Complete fills in any fields not set that are required to have valid data and can be derived
439// from other fields. If you're going to `ApplyOptions`, do that first. It's mutating the receiver.
440func (c *RecommendedConfig) Complete() CompletedConfig {
441 return c.Config.Complete(c.SharedInformerFactory)
442}
443
444// New creates a new server which logically combines the handling chain with the passed server.
445// name is used to differentiate for logging. The handler chain in particular can be difficult as it starts delgating.
446// delegationTarget may not be nil.
447func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*GenericAPIServer, error) {
448 if c.Serializer == nil {
449 return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil")
450 }
451 if c.LoopbackClientConfig == nil {
452 return nil, fmt.Errorf("Genericapiserver.New() called with config.LoopbackClientConfig == nil")
453 }
454
455 handlerChainBuilder := func(handler http.Handler) http.Handler {
456 return c.BuildHandlerChainFunc(handler, c.Config)
457 }
458 apiServerHandler := NewAPIServerHandler(name, c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
459
460 s := &GenericAPIServer{
461 discoveryAddresses: c.DiscoveryAddresses,
462 LoopbackClientConfig: c.LoopbackClientConfig,
463 legacyAPIGroupPrefixes: c.LegacyAPIGroupPrefixes,
464 admissionControl: c.AdmissionControl,
465 Serializer: c.Serializer,
466 AuditBackend: c.AuditBackend,
467 delegationTarget: delegationTarget,
468 HandlerChainWaitGroup: c.HandlerChainWaitGroup,
469
470 minRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second,
471 ShutdownTimeout: c.RequestTimeout,
472
473 SecureServingInfo: c.SecureServing,
474 ExternalAddress: c.ExternalAddress,
475
476 Handler: apiServerHandler,
477
478 listedPathProvider: apiServerHandler,
479
480 swaggerConfig: c.SwaggerConfig,
481 openAPIConfig: c.OpenAPIConfig,
482
483 postStartHooks: map[string]postStartHookEntry{},
484 preShutdownHooks: map[string]preShutdownHookEntry{},
485 disabledPostStartHooks: c.DisabledPostStartHooks,
486
487 healthzChecks: c.HealthzChecks,
488
489 DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer),
490
491 enableAPIResponseCompression: c.EnableAPIResponseCompression,
492 }
493
494 for k, v := range delegationTarget.PostStartHooks() {
495 s.postStartHooks[k] = v
496 }
497
498 for k, v := range delegationTarget.PreShutdownHooks() {
499 s.preShutdownHooks[k] = v
500 }
501
502 genericApiServerHookName := "generic-apiserver-start-informers"
503 if c.SharedInformerFactory != nil && !s.isPostStartHookRegistered(genericApiServerHookName) {
504 err := s.AddPostStartHook(genericApiServerHookName, func(context PostStartHookContext) error {
505 c.SharedInformerFactory.Start(context.StopCh)
506 return nil
507 })
508 if err != nil {
509 return nil, err
510 }
511 }
512
513 for _, delegateCheck := range delegationTarget.HealthzChecks() {
514 skip := false
515 for _, existingCheck := range c.HealthzChecks {
516 if existingCheck.Name() == delegateCheck.Name() {
517 skip = true
518 break
519 }
520 }
521 if skip {
522 continue
523 }
524
525 s.healthzChecks = append(s.healthzChecks, delegateCheck)
526 }
527
528 s.listedPathProvider = routes.ListedPathProviders{s.listedPathProvider, delegationTarget}
529
530 installAPI(s, c.Config)
531
532 // use the UnprotectedHandler from the delegation target to ensure that we don't attempt to double authenticator, authorize,
533 // or some other part of the filter chain in delegation cases.
534 if delegationTarget.UnprotectedHandler() == nil && c.EnableIndex {
535 s.Handler.NonGoRestfulMux.NotFoundHandler(routes.IndexLister{
536 StatusCode: http.StatusNotFound,
537 PathProvider: s.listedPathProvider,
538 })
539 }
540
541 return s, nil
542}
543
544func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
545 handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)
546 handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc)
547 handler = genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)
548 if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
549 handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
550 } else {
551 handler = genericapifilters.WithLegacyAudit(handler, c.LegacyAuditWriter)
552 }
553 failedHandler := genericapifilters.Unauthorized(c.Serializer, c.Authentication.SupportsBasicAuth)
554 if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
555 failedHandler = genericapifilters.WithFailedAuthenticationAudit(failedHandler, c.AuditBackend, c.AuditPolicyChecker)
556 }
557 handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler)
558 handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
559 handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc, c.RequestTimeout)
560 handler = genericfilters.WithWaitGroup(handler, c.LongRunningFunc, c.HandlerChainWaitGroup)
561 handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
562 handler = genericfilters.WithPanicRecovery(handler)
563 return handler
564}
565
566func installAPI(s *GenericAPIServer, c *Config) {
567 if c.EnableIndex {
568 routes.Index{}.Install(s.listedPathProvider, s.Handler.NonGoRestfulMux)
569 }
570 if c.SwaggerConfig != nil && c.EnableSwaggerUI {
571 routes.SwaggerUI{}.Install(s.Handler.NonGoRestfulMux)
572 }
573 if c.EnableProfiling {
574 routes.Profiling{}.Install(s.Handler.NonGoRestfulMux)
575 if c.EnableContentionProfiling {
576 goruntime.SetBlockProfileRate(1)
577 }
578 // so far, only logging related endpoints are considered valid to add for these debug flags.
579 routes.DebugFlags{}.Install(s.Handler.NonGoRestfulMux, "v", routes.StringFlagPutHandler(
580 routes.StringFlagSetterFunc(func(val string) (string, error) {
581 var level glog.Level
582 if err := level.Set(val); err != nil {
583 return "", fmt.Errorf("failed set glog.logging.verbosity %s: %v", val, err)
584 }
585 return "successfully set glog.logging.verbosity to " + val, nil
586 }),
587 ))
588 }
589 if c.EnableMetrics {
590 if c.EnableProfiling {
591 routes.MetricsWithReset{}.Install(s.Handler.NonGoRestfulMux)
592 } else {
593 routes.DefaultMetrics{}.Install(s.Handler.NonGoRestfulMux)
594 }
595 }
596
597 routes.Version{Version: c.Version}.Install(s.Handler.GoRestfulContainer)
598
599 if c.EnableDiscovery {
600 s.Handler.GoRestfulContainer.Add(s.DiscoveryGroupManager.WebService())
601 }
602}
603
604func NewRequestInfoResolver(c *Config) *apirequest.RequestInfoFactory {
605 apiPrefixes := sets.NewString(strings.Trim(APIGroupPrefix, "/")) // all possible API prefixes
606 legacyAPIPrefixes := sets.String{} // APIPrefixes that won't have groups (legacy)
607 for legacyAPIPrefix := range c.LegacyAPIGroupPrefixes {
608 apiPrefixes.Insert(strings.Trim(legacyAPIPrefix, "/"))
609 legacyAPIPrefixes.Insert(strings.Trim(legacyAPIPrefix, "/"))
610 }
611
612 return &apirequest.RequestInfoFactory{
613 APIPrefixes: apiPrefixes,
614 GrouplessAPIPrefixes: legacyAPIPrefixes,
615 }
616}