From ee7daeb2a73ba05ed82e33085b6800da2793bd10 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 10 Jan 2024 07:30:16 -0800 Subject: [PATCH] APIServer: Move shared code to a utility/helper function (#80261) --- pkg/cmd/grafana/apiserver/cmd.go | 26 ++---- pkg/cmd/grafana/apiserver/server.go | 60 ++----------- pkg/services/grafana-apiserver/common.go | 2 +- pkg/services/grafana-apiserver/helper.go | 89 +++++++++++++++++++ .../grafana-apiserver/request_handler.go | 4 +- pkg/services/grafana-apiserver/service.go | 66 +------------- 6 files changed, 113 insertions(+), 134 deletions(-) create mode 100644 pkg/services/grafana-apiserver/helper.go diff --git a/pkg/cmd/grafana/apiserver/cmd.go b/pkg/cmd/grafana/apiserver/cmd.go index e4967ab56c0..175387a464b 100644 --- a/pkg/cmd/grafana/apiserver/cmd.go +++ b/pkg/cmd/grafana/apiserver/cmd.go @@ -4,11 +4,13 @@ import ( "os" "path" - "github.com/grafana/grafana/pkg/aggregator" - "github.com/grafana/grafana/pkg/services/grafana-apiserver/utils" "k8s.io/client-go/tools/clientcmd" "k8s.io/klog/v2" + "github.com/grafana/grafana/pkg/aggregator" + grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver" + "github.com/grafana/grafana/pkg/services/grafana-apiserver/utils" + "github.com/spf13/cobra" genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/apiserver/pkg/server/options" @@ -56,7 +58,7 @@ func newCommandStartExampleAPIServer(o *APIServerOptions, stopCh <-chan struct{} // Register standard k8s flags with the command line o.RecommendedOptions = options.NewRecommendedOptions( defaultEtcdPathPrefix, - Codecs.LegacyCodec(), // the codec is passed to etcd and not used + grafanaapiserver.Codecs.LegacyCodec(), // the codec is passed to etcd and not used ) o.RecommendedOptions.AddFlags(cmd.Flags()) @@ -111,20 +113,10 @@ func run(serverOptions *aggregator.AggregatorServerOptions) error { } // Install the API Group+version - for _, b := range serverOptions.Builders { - g, err := b.GetAPIGroupInfo(Scheme, Codecs, config.GenericConfig.RESTOptionsGetter) - if err != nil { - klog.Errorf("Error getting group info for prerequisite API group: %s", err) - return err - } - if g == nil || len(g.PrioritizedVersions) < 1 { - continue - } - err = aggregator.GenericAPIServer.InstallAPIGroup(g) - if err != nil { - klog.Errorf("Error installing prerequisite API groups for aggregator: %s", err) - return err - } + err = grafanaapiserver.InstallAPIs(aggregator.GenericAPIServer, config.GenericConfig.RESTOptionsGetter, serverOptions.Builders) + if err != nil { + klog.Errorf("Error installing apis: %s", err) + return err } if err := clientcmd.WriteToFile( diff --git a/pkg/cmd/grafana/apiserver/server.go b/pkg/cmd/grafana/apiserver/server.go index caa042456ec..3e02b573b02 100644 --- a/pkg/cmd/grafana/apiserver/server.go +++ b/pkg/cmd/grafana/apiserver/server.go @@ -6,22 +6,15 @@ import ( "net" "path" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" utilerrors "k8s.io/apimachinery/pkg/util/errors" - openapinamer "k8s.io/apiserver/pkg/endpoints/openapi" genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/apiserver/pkg/server/options" - "k8s.io/apiserver/pkg/util/openapi" "k8s.io/client-go/tools/clientcmd" netutils "k8s.io/utils/net" - "github.com/grafana/grafana/pkg/services/grafana-apiserver/utils" - "github.com/grafana/grafana/pkg/registry/apis/example" grafanaAPIServer "github.com/grafana/grafana/pkg/services/grafana-apiserver" + "github.com/grafana/grafana/pkg/services/grafana-apiserver/utils" ) const ( @@ -29,26 +22,6 @@ const ( dataPath = "data/grafana-apiserver" // same as grafana core ) -var ( - Scheme = runtime.NewScheme() - Codecs = serializer.NewCodecFactory(Scheme) -) - -func init() { - unversionedVersion := schema.GroupVersion{Group: "", Version: "v1"} - unversionedTypes := []runtime.Object{ - &metav1.Status{}, - &metav1.WatchEvent{}, - &metav1.APIVersions{}, - &metav1.APIGroupList{}, - &metav1.APIGroup{}, - &metav1.APIResourceList{}, - } - // we need to add the options to empty v1 - metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"}) - Scheme.AddUnversionedTypes(unversionedVersion, unversionedTypes...) -} - // APIServerOptions contains the state for the apiserver type APIServerOptions struct { builders []grafanaAPIServer.APIGroupBuilder @@ -84,7 +57,7 @@ func (o *APIServerOptions) LoadAPIGroupBuilders(args []string) error { // Install schemas for _, b := range o.builders { - if err := b.InstallSchema(Scheme); err != nil { + if err := b.InstallSchema(grafanaAPIServer.Scheme); err != nil { return err } } @@ -160,7 +133,7 @@ func (o *APIServerOptions) Config() (*genericapiserver.RecommendedConfig, error) o.RecommendedOptions.CoreAPI = nil } - serverConfig := genericapiserver.NewRecommendedConfig(Codecs) + serverConfig := genericapiserver.NewRecommendedConfig(grafanaAPIServer.Codecs) if o.RecommendedOptions.CoreAPI == nil { if err := o.ModifiedApplyTo(serverConfig); err != nil { @@ -176,16 +149,8 @@ func (o *APIServerOptions) Config() (*genericapiserver.RecommendedConfig, error) serverConfig.DisabledPostStartHooks = serverConfig.DisabledPostStartHooks.Insert("priority-and-fairness-config-consumer") // Add OpenAPI specs for each group+version - defsGetter := grafanaAPIServer.GetOpenAPIDefinitions(o.builders) - serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig( - openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter), - openapinamer.NewDefinitionNamer(Scheme)) - - serverConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config( - openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter), - openapinamer.NewDefinitionNamer(Scheme)) - - return serverConfig, nil + err := grafanaAPIServer.SetupConfig(serverConfig, o.builders) + return serverConfig, err } // Validate validates APIServerOptions @@ -212,18 +177,9 @@ func (o *APIServerOptions) RunAPIServer(config *genericapiserver.RecommendedConf } // Install the API Group+version - for _, b := range o.builders { - g, err := b.GetAPIGroupInfo(Scheme, Codecs, completedConfig.RESTOptionsGetter) - if err != nil { - return err - } - if g == nil || len(g.PrioritizedVersions) < 1 { - continue - } - err = server.InstallAPIGroup(g) - if err != nil { - return err - } + err = grafanaAPIServer.InstallAPIs(server, config.RESTOptionsGetter, o.builders) + if err != nil { + return err } // write the local config to disk diff --git a/pkg/services/grafana-apiserver/common.go b/pkg/services/grafana-apiserver/common.go index 44661d9658d..5845bfc3c22 100644 --- a/pkg/services/grafana-apiserver/common.go +++ b/pkg/services/grafana-apiserver/common.go @@ -25,7 +25,7 @@ type APIGroupBuilder interface { // Build the group+version behavior GetAPIGroupInfo( scheme *runtime.Scheme, - codecs serializer.CodecFactory, // pointer? + codecs serializer.CodecFactory, optsGetter generic.RESTOptionsGetter, ) (*genericapiserver.APIGroupInfo, error) diff --git a/pkg/services/grafana-apiserver/helper.go b/pkg/services/grafana-apiserver/helper.go new file mode 100644 index 00000000000..2bceed7c52d --- /dev/null +++ b/pkg/services/grafana-apiserver/helper.go @@ -0,0 +1,89 @@ +package grafanaapiserver + +import ( + "fmt" + "net/http" + goruntime "runtime" + "strings" + "time" + + "k8s.io/apimachinery/pkg/version" + openapinamer "k8s.io/apiserver/pkg/endpoints/openapi" + "k8s.io/apiserver/pkg/registry/generic" + genericapiserver "k8s.io/apiserver/pkg/server" + "k8s.io/apiserver/pkg/util/openapi" + "k8s.io/client-go/kubernetes/scheme" + + "github.com/grafana/grafana/pkg/setting" +) + +func SetupConfig(serverConfig *genericapiserver.RecommendedConfig, builders []APIGroupBuilder) error { + defsGetter := GetOpenAPIDefinitions(builders) + serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig( + openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter), + openapinamer.NewDefinitionNamer(Scheme, scheme.Scheme)) + + serverConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config( + openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter), + openapinamer.NewDefinitionNamer(Scheme, scheme.Scheme)) + + // Add the custom routes to service discovery + serverConfig.OpenAPIV3Config.PostProcessSpec = getOpenAPIPostProcessor(builders) + + // Set the swagger build versions + serverConfig.OpenAPIConfig.Info.Version = setting.BuildVersion + serverConfig.OpenAPIV3Config.Info.Version = setting.BuildVersion + + serverConfig.SkipOpenAPIInstallation = false + serverConfig.BuildHandlerChainFunc = func(delegateHandler http.Handler, c *genericapiserver.Config) http.Handler { + // Call DefaultBuildHandlerChain on the main entrypoint http.Handler + // See https://github.com/kubernetes/apiserver/blob/v0.28.0/pkg/server/config.go#L906 + // DefaultBuildHandlerChain provides many things, notably CORS, HSTS, cache-control, authz and latency tracking + requestHandler, err := getAPIHandler( + delegateHandler, + c.LoopbackClientConfig, + builders) + if err != nil { + panic(fmt.Sprintf("could not build handler chain func: %s", err.Error())) + } + return genericapiserver.DefaultBuildHandlerChain(requestHandler, c) + } + + k8sVersion, err := getK8sApiserverVersion() + if err != nil { + return err + } + before, after, _ := strings.Cut(setting.BuildVersion, ".") + serverConfig.Version = &version.Info{ + Major: before, + Minor: after, + GoVersion: goruntime.Version(), + Platform: fmt.Sprintf("%s/%s", goruntime.GOOS, goruntime.GOARCH), + Compiler: goruntime.Compiler, + GitTreeState: setting.BuildBranch, + GitCommit: setting.BuildCommit, + BuildDate: time.Unix(setting.BuildStamp, 0).UTC().Format(time.DateTime), + GitVersion: k8sVersion, + } + return nil +} + +func InstallAPIs(server *genericapiserver.GenericAPIServer, + optsGetter generic.RESTOptionsGetter, + builders []APIGroupBuilder, +) error { + for _, b := range builders { + g, err := b.GetAPIGroupInfo(Scheme, Codecs, optsGetter) + if err != nil { + return err + } + if g == nil || len(g.PrioritizedVersions) < 1 { + continue + } + err = server.InstallAPIGroup(g) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/services/grafana-apiserver/request_handler.go b/pkg/services/grafana-apiserver/request_handler.go index 175b7413083..48199878a67 100644 --- a/pkg/services/grafana-apiserver/request_handler.go +++ b/pkg/services/grafana-apiserver/request_handler.go @@ -16,7 +16,7 @@ type requestHandler struct { router *mux.Router } -func GetAPIHandler(delegateHandler http.Handler, restConfig *restclient.Config, builders []APIGroupBuilder) (http.Handler, error) { +func getAPIHandler(delegateHandler http.Handler, restConfig *restclient.Config, builders []APIGroupBuilder) (http.Handler, error) { useful := false // only true if any routes exist anywhere router := mux.NewRouter() @@ -120,7 +120,7 @@ func (h *methodNotAllowedHandler) ServeHTTP(w http.ResponseWriter, req *http.Req // Modify the the OpenAPI spec to include the additional routes. // Currently this requires: https://github.com/kubernetes/kube-openapi/pull/420 // In future k8s release, the hook will use Config3 rather than the same hook for both v2 and v3 -func GetOpenAPIPostProcessor(builders []APIGroupBuilder) func(*spec3.OpenAPI) (*spec3.OpenAPI, error) { +func getOpenAPIPostProcessor(builders []APIGroupBuilder) func(*spec3.OpenAPI) (*spec3.OpenAPI, error) { return func(s *spec3.OpenAPI) (*spec3.OpenAPI, error) { if s.Paths == nil { return s, nil diff --git a/pkg/services/grafana-apiserver/service.go b/pkg/services/grafana-apiserver/service.go index a0c8e64ddee..2056d8c3bf7 100644 --- a/pkg/services/grafana-apiserver/service.go +++ b/pkg/services/grafana-apiserver/service.go @@ -6,11 +6,9 @@ import ( "net/http" "net/http/httptest" "path" - goruntime "runtime" "runtime/debug" "strconv" "strings" - "time" "github.com/go-logr/logr" "github.com/grafana/dskit/services" @@ -21,13 +19,9 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/apimachinery/pkg/version" - openapinamer "k8s.io/apiserver/pkg/endpoints/openapi" "k8s.io/apiserver/pkg/endpoints/responsewriter" genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/apiserver/pkg/server/options" - "k8s.io/apiserver/pkg/util/openapi" - "k8s.io/client-go/kubernetes/scheme" clientrest "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/component-base/logs" @@ -325,53 +319,10 @@ func (s *service) start(ctx context.Context) error { serverConfig.TracerProvider = s.tracing.GetTracerProvider() // Add OpenAPI specs for each group+version - defsGetter := GetOpenAPIDefinitions(builders) - serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig( - openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter), - openapinamer.NewDefinitionNamer(Scheme, scheme.Scheme)) - - serverConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config( - openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(defsGetter), - openapinamer.NewDefinitionNamer(Scheme, scheme.Scheme)) - - // Add the custom routes to service discovery - serverConfig.OpenAPIV3Config.PostProcessSpec = GetOpenAPIPostProcessor(builders) - - // Set the swagger build versions - serverConfig.OpenAPIConfig.Info.Version = setting.BuildVersion - serverConfig.OpenAPIV3Config.Info.Version = setting.BuildVersion - - serverConfig.SkipOpenAPIInstallation = false - serverConfig.BuildHandlerChainFunc = func(delegateHandler http.Handler, c *genericapiserver.Config) http.Handler { - // Call DefaultBuildHandlerChain on the main entrypoint http.Handler - // See https://github.com/kubernetes/apiserver/blob/v0.28.0/pkg/server/config.go#L906 - // DefaultBuildHandlerChain provides many things, notably CORS, HSTS, cache-control, authz and latency tracking - requestHandler, err := GetAPIHandler( - delegateHandler, - c.LoopbackClientConfig, - builders) - if err != nil { - panic(fmt.Sprintf("could not build handler chain func: %s", err.Error())) - } - return genericapiserver.DefaultBuildHandlerChain(requestHandler, c) - } - - k8sVersion, err := getK8sApiserverVersion() + err := SetupConfig(serverConfig, builders) if err != nil { return err } - before, after, _ := strings.Cut(setting.BuildVersion, ".") - serverConfig.Version = &version.Info{ - Major: before, - Minor: after, - GoVersion: goruntime.Version(), - Platform: fmt.Sprintf("%s/%s", goruntime.GOOS, goruntime.GOARCH), - Compiler: goruntime.Compiler, - GitTreeState: setting.BuildBranch, - GitCommit: setting.BuildCommit, - BuildDate: time.Unix(setting.BuildStamp, 0).UTC().Format(time.DateTime), - GitVersion: k8sVersion, - } // Create the server server, err := serverConfig.Complete().New("grafana-apiserver", genericapiserver.NewEmptyDelegate()) @@ -380,18 +331,9 @@ func (s *service) start(ctx context.Context) error { } // Install the API Group+version - for _, b := range builders { - g, err := b.GetAPIGroupInfo(Scheme, Codecs, serverConfig.RESTOptionsGetter) - if err != nil { - return err - } - if g == nil || len(g.PrioritizedVersions) < 1 { - continue - } - err = server.InstallAPIGroup(g) - if err != nil { - return err - } + err = InstallAPIs(server, serverConfig.RESTOptionsGetter, builders) + if err != nil { + return err } // Used by the proxy wrapper registered in ProvideService