|
| 1 | +package app |
| 2 | + |
| 3 | +import ( |
| 4 | + "net/http" |
| 5 | + |
| 6 | + corev1 "k8s.io/api/core/v1" |
| 7 | + apiextensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver" |
| 8 | + "k8s.io/apimachinery/pkg/runtime" |
| 9 | + "k8s.io/apiserver/pkg/endpoints/filterlatency" |
| 10 | + genericapifilters "k8s.io/apiserver/pkg/endpoints/filters" |
| 11 | + genericfeatures "k8s.io/apiserver/pkg/features" |
| 12 | + "k8s.io/apiserver/pkg/server" |
| 13 | + genericfilters "k8s.io/apiserver/pkg/server/filters" |
| 14 | + "k8s.io/apiserver/pkg/server/routine" |
| 15 | + flowcontrolrequest "k8s.io/apiserver/pkg/util/flowcontrol/request" |
| 16 | + "k8s.io/apiserver/pkg/util/webhook" |
| 17 | + "k8s.io/client-go/discovery" |
| 18 | + utilversion "k8s.io/component-base/version" |
| 19 | + aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver" |
| 20 | + aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme" |
| 21 | + "k8s.io/kubernetes/pkg/api/legacyscheme" |
| 22 | + "k8s.io/kubernetes/pkg/controlplane" |
| 23 | + controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver" |
| 24 | + "k8s.io/kubernetes/pkg/controlplane/apiserver/options" |
| 25 | + generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi" |
| 26 | + admissionregistrationrest "k8s.io/kubernetes/pkg/registry/admissionregistration/rest" |
| 27 | + apiserverinternalrest "k8s.io/kubernetes/pkg/registry/apiserverinternal/rest" |
| 28 | + authenticationrest "k8s.io/kubernetes/pkg/registry/authentication/rest" |
| 29 | + authorizationrest "k8s.io/kubernetes/pkg/registry/authorization/rest" |
| 30 | + coordinationrest "k8s.io/kubernetes/pkg/registry/coordination/rest" |
| 31 | + discoveryrest "k8s.io/kubernetes/pkg/registry/discovery/rest" |
| 32 | + eventsrest "k8s.io/kubernetes/pkg/registry/events/rest" |
| 33 | + flowcontrolrest "k8s.io/kubernetes/pkg/registry/flowcontrol/rest" |
| 34 | + rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest" |
| 35 | + svmrest "k8s.io/kubernetes/pkg/registry/storagemigration/rest" |
| 36 | + |
| 37 | + datumfilters "go.datumapis.com/datum/pkg/server/filters" |
| 38 | +) |
| 39 | + |
| 40 | +type Config struct { |
| 41 | + Options options.CompletedOptions |
| 42 | + |
| 43 | + Aggregator *aggregatorapiserver.Config |
| 44 | + ControlPlane *controlplaneapiserver.Config |
| 45 | + APIExtensions *apiextensionsapiserver.Config |
| 46 | + |
| 47 | + ExtraConfig |
| 48 | +} |
| 49 | + |
| 50 | +type ExtraConfig struct { |
| 51 | +} |
| 52 | + |
| 53 | +type completedConfig struct { |
| 54 | + Options options.CompletedOptions |
| 55 | + |
| 56 | + Aggregator aggregatorapiserver.CompletedConfig |
| 57 | + ControlPlane controlplaneapiserver.CompletedConfig |
| 58 | + APIExtensions apiextensionsapiserver.CompletedConfig |
| 59 | + |
| 60 | + ExtraConfig |
| 61 | +} |
| 62 | + |
| 63 | +type CompletedConfig struct { |
| 64 | + // Embed a private pointer that cannot be instantiated outside of this package. |
| 65 | + *completedConfig |
| 66 | +} |
| 67 | + |
| 68 | +func (c *CompletedConfig) GenericStorageProviders(discovery discovery.DiscoveryInterface) ([]controlplaneapiserver.RESTStorageProvider, error) { |
| 69 | + return []controlplaneapiserver.RESTStorageProvider{ |
| 70 | + c.ControlPlane.NewCoreGenericConfig(), |
| 71 | + apiserverinternalrest.StorageProvider{}, |
| 72 | + authenticationrest.RESTStorageProvider{Authenticator: c.ControlPlane.Generic.Authentication.Authenticator, APIAudiences: c.ControlPlane.Generic.Authentication.APIAudiences}, |
| 73 | + authorizationrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer, RuleResolver: c.ControlPlane.Generic.RuleResolver}, |
| 74 | + coordinationrest.RESTStorageProvider{}, |
| 75 | + rbacrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer}, |
| 76 | + svmrest.RESTStorageProvider{}, |
| 77 | + flowcontrolrest.RESTStorageProvider{InformerFactory: c.ControlPlane.Generic.SharedInformerFactory}, |
| 78 | + admissionregistrationrest.RESTStorageProvider{Authorizer: c.ControlPlane.Generic.Authorization.Authorizer, DiscoveryClient: discovery}, |
| 79 | + eventsrest.RESTStorageProvider{TTL: c.ControlPlane.EventTTL}, |
| 80 | + discoveryrest.StorageProvider{}, |
| 81 | + }, nil |
| 82 | +} |
| 83 | + |
| 84 | +func (c *Config) Complete() (CompletedConfig, error) { |
| 85 | + return CompletedConfig{&completedConfig{ |
| 86 | + Options: c.Options, |
| 87 | + |
| 88 | + Aggregator: c.Aggregator.Complete(), |
| 89 | + ControlPlane: c.ControlPlane.Complete(), |
| 90 | + APIExtensions: c.APIExtensions.Complete(), |
| 91 | + |
| 92 | + ExtraConfig: c.ExtraConfig, |
| 93 | + }}, nil |
| 94 | +} |
| 95 | + |
| 96 | +func NewConfig(opts options.CompletedOptions) (*Config, error) { |
| 97 | + c := &Config{ |
| 98 | + Options: opts, |
| 99 | + } |
| 100 | + |
| 101 | + apiResourceConfigSource := controlplane.DefaultAPIResourceConfigSource() |
| 102 | + apiResourceConfigSource.DisableResources(corev1.SchemeGroupVersion.WithResource("serviceaccounts")) |
| 103 | + |
| 104 | + genericConfig, versionedInformers, storageFactory, err := controlplaneapiserver.BuildGenericConfig( |
| 105 | + opts, |
| 106 | + []*runtime.Scheme{legacyscheme.Scheme, apiextensionsapiserver.Scheme, aggregatorscheme.Scheme}, |
| 107 | + apiResourceConfigSource, |
| 108 | + generatedopenapi.GetOpenAPIDefinitions, |
| 109 | + ) |
| 110 | + if err != nil { |
| 111 | + return nil, err |
| 112 | + } |
| 113 | + |
| 114 | + genericConfig.BuildHandlerChainFunc = DefaultBuildHandlerChain |
| 115 | + |
| 116 | + serviceResolver := webhook.NewDefaultServiceResolver() |
| 117 | + kubeAPIs, pluginInitializer, err := controlplaneapiserver.CreateConfig(opts, genericConfig, versionedInformers, storageFactory, serviceResolver, nil) |
| 118 | + if err != nil { |
| 119 | + return nil, err |
| 120 | + } |
| 121 | + c.ControlPlane = kubeAPIs |
| 122 | + c.ControlPlane.Generic.EffectiveVersion = utilversion.DefaultKubeEffectiveVersion() |
| 123 | + |
| 124 | + authInfoResolver := webhook.NewDefaultAuthenticationInfoResolverWrapper(kubeAPIs.ProxyTransport, kubeAPIs.Generic.EgressSelector, kubeAPIs.Generic.LoopbackClientConfig, kubeAPIs.Generic.TracerProvider) |
| 125 | + apiExtensions, err := controlplaneapiserver.CreateAPIExtensionsConfig(*kubeAPIs.Generic, kubeAPIs.VersionedInformers, pluginInitializer, opts, 3, serviceResolver, authInfoResolver) |
| 126 | + if err != nil { |
| 127 | + return nil, err |
| 128 | + } |
| 129 | + c.APIExtensions = apiExtensions |
| 130 | + |
| 131 | + // TODO(jreese) create an admission plugin that will prohibit the creation of |
| 132 | + // a Secret with a type of `kubernetes.io/service-account-token` |
| 133 | + c.APIExtensions.GenericConfig.DisabledPostStartHooks.Insert("start-legacy-token-tracking-controller") |
| 134 | + |
| 135 | + aggregator, err := controlplaneapiserver.CreateAggregatorConfig(*kubeAPIs.Generic, opts, kubeAPIs.VersionedInformers, serviceResolver, kubeAPIs.ProxyTransport, kubeAPIs.Extra.PeerProxy, pluginInitializer) |
| 136 | + if err != nil { |
| 137 | + return nil, err |
| 138 | + } |
| 139 | + c.Aggregator = aggregator |
| 140 | + c.Aggregator.ExtraConfig.DisableRemoteAvailableConditionController = true |
| 141 | + // TODO(jreese) better version handling |
| 142 | + c.Aggregator.GenericConfig.EffectiveVersion = utilversion.DefaultKubeEffectiveVersion() |
| 143 | + |
| 144 | + return c, nil |
| 145 | +} |
| 146 | + |
| 147 | +// Taken from https://github.com/kubernetes/kubernetes/blob/50fc400f178d2078d0ca46aee955ee26375fc437/staging/src/k8s.io/apiserver/pkg/server/config.go#L1004 |
| 148 | +// |
| 149 | +// Modified to inject the following filters at the necessary locations: |
| 150 | +// - datumfilters.OrganizationContextAuthorizationDecorator |
| 151 | +// - datumfilters.ProjectListOrganizationConstraintDecorator |
| 152 | +// - datumfilters.OrganizationContextHandler |
| 153 | +// |
| 154 | +// This is done to improve the UX that customers will experience while |
| 155 | +// interacting with the Datum API server. |
| 156 | +// |
| 157 | +// Some handlers have not been added as a result of not having access to |
| 158 | +// lifecycleSignals in server.Config. TODO(jreese) need to look into this more |
| 159 | +func DefaultBuildHandlerChain(apiHandler http.Handler, c *server.Config) http.Handler { |
| 160 | + handler := apiHandler |
| 161 | + |
| 162 | + handler = filterlatency.TrackCompleted(handler) |
| 163 | + handler = genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer) |
| 164 | + handler = datumfilters.OrganizationContextAuthorizationDecorator(handler) |
| 165 | + handler = filterlatency.TrackStarted(handler, c.TracerProvider, "authorization") |
| 166 | + |
| 167 | + if c.FlowControl != nil { |
| 168 | + workEstimatorCfg := flowcontrolrequest.DefaultWorkEstimatorConfig() |
| 169 | + requestWorkEstimator := flowcontrolrequest.NewWorkEstimator( |
| 170 | + c.StorageObjectCountTracker.Get, c.FlowControl.GetInterestedWatchCount, workEstimatorCfg, c.FlowControl.GetMaxSeats) |
| 171 | + handler = filterlatency.TrackCompleted(handler) |
| 172 | + handler = genericfilters.WithPriorityAndFairness(handler, c.LongRunningFunc, c.FlowControl, requestWorkEstimator, c.RequestTimeout/4) |
| 173 | + handler = filterlatency.TrackStarted(handler, c.TracerProvider, "priorityandfairness") |
| 174 | + } else { |
| 175 | + handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc) |
| 176 | + } |
| 177 | + |
| 178 | + handler = filterlatency.TrackCompleted(handler) |
| 179 | + handler = genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer) |
| 180 | + handler = filterlatency.TrackStarted(handler, c.TracerProvider, "impersonation") |
| 181 | + |
| 182 | + handler = filterlatency.TrackCompleted(handler) |
| 183 | + handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyRuleEvaluator, c.LongRunningFunc) |
| 184 | + handler = filterlatency.TrackStarted(handler, c.TracerProvider, "audit") |
| 185 | + |
| 186 | + failedHandler := genericapifilters.Unauthorized(c.Serializer) |
| 187 | + failedHandler = genericapifilters.WithFailedAuthenticationAudit(failedHandler, c.AuditBackend, c.AuditPolicyRuleEvaluator) |
| 188 | + |
| 189 | + failedHandler = filterlatency.TrackCompleted(failedHandler) |
| 190 | + handler = filterlatency.TrackCompleted(handler) |
| 191 | + handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences, c.Authentication.RequestHeaderConfig) |
| 192 | + handler = filterlatency.TrackStarted(handler, c.TracerProvider, "authentication") |
| 193 | + |
| 194 | + handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true") |
| 195 | + |
| 196 | + // WithWarningRecorder must be wrapped by the timeout handler |
| 197 | + // to make the addition of warning headers threadsafe |
| 198 | + handler = genericapifilters.WithWarningRecorder(handler) |
| 199 | + |
| 200 | + // WithTimeoutForNonLongRunningRequests will call the rest of the request handling in a go-routine with the |
| 201 | + // context with deadline. The go-routine can keep running, while the timeout logic will return a timeout to the client. |
| 202 | + handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc) |
| 203 | + |
| 204 | + handler = genericapifilters.WithRequestDeadline(handler, c.AuditBackend, c.AuditPolicyRuleEvaluator, |
| 205 | + c.LongRunningFunc, c.Serializer, c.RequestTimeout) |
| 206 | + handler = genericfilters.WithWaitGroup(handler, c.LongRunningFunc, c.NonLongRunningRequestWaitGroup) |
| 207 | + // if c.ShutdownWatchTerminationGracePeriod > 0 { |
| 208 | + // handler = genericfilters.WithWatchTerminationDuringShutdown(handler, c.lifecycleSignals, c.WatchRequestWaitGroup) |
| 209 | + // } |
| 210 | + if c.SecureServing != nil && !c.SecureServing.DisableHTTP2 && c.GoawayChance > 0 { |
| 211 | + handler = genericfilters.WithProbabilisticGoaway(handler, c.GoawayChance) |
| 212 | + } |
| 213 | + handler = genericapifilters.WithCacheControl(handler) |
| 214 | + handler = genericfilters.WithHSTS(handler, c.HSTSDirectives) |
| 215 | + // if c.ShutdownSendRetryAfter { |
| 216 | + // handler = genericfilters.WithRetryAfter(handler, c.lifecycleSignals.NotAcceptingNewRequest.Signaled()) |
| 217 | + // } |
| 218 | + handler = genericfilters.WithHTTPLogging(handler) |
| 219 | + if c.FeatureGate.Enabled(genericfeatures.APIServerTracing) { |
| 220 | + handler = genericapifilters.WithTracing(handler, c.TracerProvider) |
| 221 | + } |
| 222 | + handler = genericapifilters.WithLatencyTrackers(handler) |
| 223 | + // WithRoutine will execute future handlers in a separate goroutine and serving |
| 224 | + // handler in current goroutine to minimize the stack memory usage. It must be |
| 225 | + // after WithPanicRecover() to be protected from panics. |
| 226 | + if c.FeatureGate.Enabled(genericfeatures.APIServingWithRoutine) { |
| 227 | + handler = routine.WithRoutine(handler, c.LongRunningFunc) |
| 228 | + } |
| 229 | + |
| 230 | + handler = datumfilters.OrganizationProjectListConstraintDecorator(handler) |
| 231 | + handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver) |
| 232 | + handler = genericapifilters.WithRequestReceivedTimestamp(handler) |
| 233 | + // handler = genericapifilters.WithMuxAndDiscoveryComplete(handler, c.lifecycleSignals.MuxAndDiscoveryComplete.Signaled()) |
| 234 | + handler = genericfilters.WithPanicRecovery(handler, c.RequestInfoResolver) |
| 235 | + handler = genericapifilters.WithAuditInit(handler) |
| 236 | + |
| 237 | + handler = datumfilters.OrganizationContextHandler(handler, c.Serializer) |
| 238 | + |
| 239 | + return handler |
| 240 | +} |
0 commit comments