From 440f9a6ffbf8da30b069cee5c7e071b866c81f92 Mon Sep 17 00:00:00 2001 From: Todd Treece <360020+toddtreece@users.noreply.github.com> Date: Mon, 25 Sep 2023 18:31:58 -0400 Subject: [PATCH] K8s: Register apiserver as background service, and list real playlists (#75338) Co-authored-by: Ryan McKinley --- go.mod | 4 +- go.sum | 6 -- pkg/apis/playlist/v1/handler.go | 92 ++++++++++++------- pkg/apis/playlist/v1/register.go | 30 ++++-- pkg/apis/playlist/v1/types.go | 4 +- pkg/apis/playlist/v1/zz_generated.deepcopy.go | 2 +- pkg/apis/wireset.go | 10 ++ pkg/registry/apis/apis.go | 25 +++++ pkg/registry/apis/wireset.go | 12 +++ .../backgroundsvcs/background_services.go | 11 ++- pkg/server/module_server.go | 17 ++-- pkg/server/wire.go | 4 + .../grafana-apiserver}/common.go | 2 +- pkg/services/grafana-apiserver/openapi.go | 3 +- pkg/services/grafana-apiserver/service.go | 91 ++++++++++++++---- pkg/services/grafana-apiserver/wireset.go | 12 +++ 16 files changed, 237 insertions(+), 88 deletions(-) create mode 100644 pkg/apis/wireset.go create mode 100644 pkg/registry/apis/apis.go create mode 100644 pkg/registry/apis/wireset.go rename pkg/{apis => services/grafana-apiserver}/common.go (98%) create mode 100644 pkg/services/grafana-apiserver/wireset.go diff --git a/go.mod b/go.mod index 82f1f2a20de..e3b3ddf1375 100644 --- a/go.mod +++ b/go.mod @@ -282,6 +282,7 @@ require ( k8s.io/apiserver v0.27.1 // @grafana/grafana-app-platform-squad k8s.io/client-go v0.27.1 // @grafana/grafana-app-platform-squad k8s.io/klog/v2 v2.90.1 // @grafana/grafana-app-platform-squad + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // @grafana/grafana-app-platform-squad ) require ( @@ -300,7 +301,6 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/bmatcuk/doublestar v1.1.1 // indirect github.com/buildkite/yaml v2.1.0+incompatible // indirect - github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/centrifugal/protocol v0.10.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/cockroachdb/errors v1.9.1 // indirect @@ -412,8 +412,6 @@ require ( k8s.io/api v0.27.1 // indirect k8s.io/component-base v0.27.1 // indirect k8s.io/kms v0.27.1 // indirect - k8s.io/kube-aggregator v0.27.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect diff --git a/go.sum b/go.sum index d977ffb5214..559c8cd2b28 100644 --- a/go.sum +++ b/go.sum @@ -847,8 +847,6 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8= github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= -github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds= github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -1805,8 +1803,6 @@ github.com/grafana/grafana-google-sdk-go v0.1.0 h1:LKGY8z2DSxKjYfr2flZsWgTRTZ6HG github.com/grafana/grafana-google-sdk-go v0.1.0/go.mod h1:Vo2TKWfDVmNTELBUM+3lkrZvFtBws0qSZdXhQxRdJrE= github.com/grafana/grafana-plugin-sdk-go v0.94.0/go.mod h1:3VXz4nCv6wH5SfgB3mlW39s+c+LetqSCjFj7xxPC5+M= github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk= -github.com/grafana/grafana-plugin-sdk-go v0.176.0 h1:dayiqGR6uJyJBO8rRTp/E7oKsnEjNSS0p7vVkXy/Brw= -github.com/grafana/grafana-plugin-sdk-go v0.176.0/go.mod h1:9crAQqSzxvPe0VKC/T23cd+2I9TZb43yoOcUL/qZ5FU= github.com/grafana/grafana-plugin-sdk-go v0.177.0 h1:ERJ38b91Zw8tkz8MgHio5pVeK7ySaf5+i1I4F28YgWs= github.com/grafana/grafana-plugin-sdk-go v0.177.0/go.mod h1:9crAQqSzxvPe0VKC/T23cd+2I9TZb43yoOcUL/qZ5FU= github.com/grafana/kindsys v0.0.0-20230508162304-452481b63482 h1:1YNoeIhii4UIIQpCPU+EXidnqf449d0C3ZntAEt4KSo= @@ -4087,8 +4083,6 @@ k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kms v0.27.1 h1:JTSQbJb+mcobScQwF0bOmZhIwP17k8GvBsiLlA6SQqw= k8s.io/kms v0.27.1/go.mod h1:VuTsw0uHlSycKLCkypCGxfFCjLfzf/5YMeATECd/zJA= -k8s.io/kube-aggregator v0.27.1 h1:NYgl5PDV/oX1yqAZIkRnb+KtW+eLykzc6hHg81ECgiI= -k8s.io/kube-aggregator v0.27.1/go.mod h1:S1YUIr4mU0MjKm6kg2fUyIKK5fWgwoHFMgNjlI5JFpM= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= diff --git a/pkg/apis/playlist/v1/handler.go b/pkg/apis/playlist/v1/handler.go index 562f6a02fb8..0b688224174 100644 --- a/pkg/apis/playlist/v1/handler.go +++ b/pkg/apis/playlist/v1/handler.go @@ -4,13 +4,14 @@ import ( "context" "fmt" - "github.com/grafana/grafana/pkg/apis" - "github.com/grafana/grafana/pkg/services/playlist" "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" + + grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver" + "github.com/grafana/grafana/pkg/services/playlist" ) var _ rest.Scoper = (*handler)(nil) @@ -47,54 +48,83 @@ func (r *handler) ConvertToTable(ctx context.Context, object runtime.Object, tab func (r *handler) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { ns, ok := request.NamespaceFrom(ctx) - if ok && ns != "" { - orgId, err := apis.NamespaceToOrgID(ns) - if err != nil { - return nil, err - } - fmt.Printf("OrgID: %d\n", orgId) + if !ok || ns == "" { + return nil, fmt.Errorf("namespace required") } - // TODO: replace - return &PlaylistList{ + orgId, err := grafanaapiserver.NamespaceToOrgID(ns) + if err != nil { + return nil, err + } + + limit := 100 + if options.Limit > 0 { + limit = int(options.Limit) + } + res, err := r.service.Search(ctx, &playlist.GetPlaylistsQuery{ + OrgId: orgId, + Limit: limit, + }) + if err != nil { + return nil, err + } + + list := &PlaylistList{ TypeMeta: metav1.TypeMeta{ Kind: "PlaylistList", APIVersion: APIVersion, }, - Items: []Playlist{ - { - TypeMeta: metav1.TypeMeta{ - Kind: "Playlist", - APIVersion: APIVersion, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Name: "test", + } + for _, v := range res { + p := Playlist{ + TypeMeta: metav1.TypeMeta{ + Kind: "Playlist", + APIVersion: APIVersion, }, - }, - }, nil + ObjectMeta: metav1.ObjectMeta{ + Name: v.UID, + }, + } + p.Name = v.Name + " // " + v.Interval + list.Items = append(list.Items, p) + // TODO?? if table... we don't need the body of each, otherwise full lookup! + } + if len(list.Items) == limit { + list.Continue = "" // TODO? + } + return list, nil } func (r *handler) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { ns, ok := request.NamespaceFrom(ctx) - if ok && ns != "" { - orgId, err := apis.NamespaceToOrgID(ns) - if err != nil { - return nil, err - } - fmt.Printf("OrgID: %d\n", orgId) + if !ok || ns == "" { + return nil, fmt.Errorf("namespace required") + } + + orgId, err := grafanaapiserver.NamespaceToOrgID(ns) + if err != nil { + return nil, err + } + + p, err := r.service.Get(ctx, &playlist.GetPlaylistByUidQuery{ + UID: name, + OrgId: orgId, + }) + if err != nil { + return nil, err + } + if p == nil { + return nil, fmt.Errorf("not found?") } - // TODO: replace return &Playlist{ TypeMeta: metav1.TypeMeta{ Kind: "Playlist", APIVersion: APIVersion, }, ObjectMeta: metav1.ObjectMeta{ - Name: name, + Name: p.Uid, }, - Name: "test", + Name: p.Name + "//" + p.Interval, }, nil } diff --git a/pkg/apis/playlist/v1/register.go b/pkg/apis/playlist/v1/register.go index 1d908d8ead4..af780b68339 100644 --- a/pkg/apis/playlist/v1/register.go +++ b/pkg/apis/playlist/v1/register.go @@ -1,7 +1,6 @@ package v1 import ( - "github.com/grafana/grafana/pkg/apis" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -10,6 +9,9 @@ import ( genericapiserver "k8s.io/apiserver/pkg/server" common "k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/spec3" + + grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver" + "github.com/grafana/grafana/pkg/services/playlist" ) // GroupName is the group name for this API. @@ -17,14 +19,22 @@ const GroupName = "playlist.x.grafana.com" const VersionID = "v0-alpha" // const APIVersion = GroupName + "/" + VersionID -type builder struct{} +var _ grafanaapiserver.APIGroupBuilder = (*PlaylistAPIBuilder)(nil) -// TODO.. this will have wire dependencies -func GetAPIGroupBuilder() apis.APIGroupBuilder { - return &builder{} +// This is used just so wire has something unique to return +type PlaylistAPIBuilder struct { + service playlist.Service } -func (b *builder) InstallSchema(scheme *runtime.Scheme) error { +func RegisterAPIService(p playlist.Service, apiregistration grafanaapiserver.APIRegistrar) *PlaylistAPIBuilder { + builder := &PlaylistAPIBuilder{ + service: p, + } + apiregistration.RegisterAPI(builder) + return builder +} + +func (b *PlaylistAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { err := AddToScheme(scheme) if err != nil { return err @@ -32,26 +42,26 @@ func (b *builder) InstallSchema(scheme *runtime.Scheme) error { return scheme.SetVersionPriority(SchemeGroupVersion) } -func (b *builder) GetAPIGroupInfo( +func (b *PlaylistAPIBuilder) GetAPIGroupInfo( scheme *runtime.Scheme, codecs serializer.CodecFactory, // pointer? ) *genericapiserver.APIGroupInfo { apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(GroupName, scheme, metav1.ParameterCodec, codecs) storage := map[string]rest.Storage{} storage["playlists"] = &handler{ - service: nil, // TODO!!!!! + service: b.service, } apiGroupInfo.VersionedResourcesStorageMap[VersionID] = storage return &apiGroupInfo } -func (b *builder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions { +func (b *PlaylistAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions { return getOpenAPIDefinitions } // Register additional routes with the server -func (b *builder) GetOpenAPIPostProcessor() func(*spec3.OpenAPI) (*spec3.OpenAPI, error) { +func (b *PlaylistAPIBuilder) GetOpenAPIPostProcessor() func(*spec3.OpenAPI) (*spec3.OpenAPI, error) { return nil } diff --git a/pkg/apis/playlist/v1/types.go b/pkg/apis/playlist/v1/types.go index 80ba3dd76df..55922d347e6 100644 --- a/pkg/apis/playlist/v1/types.go +++ b/pkg/apis/playlist/v1/types.go @@ -18,10 +18,8 @@ type Playlist struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type PlaylistList struct { metav1.TypeMeta `json:",inline"` - // Standard object's metadata - // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` + metav1.ListMeta `json:"metadata,omitempty"` Items []Playlist `json:"playlists,omitempty"` } diff --git a/pkg/apis/playlist/v1/zz_generated.deepcopy.go b/pkg/apis/playlist/v1/zz_generated.deepcopy.go index 140203f1c72..a8281a47290 100644 --- a/pkg/apis/playlist/v1/zz_generated.deepcopy.go +++ b/pkg/apis/playlist/v1/zz_generated.deepcopy.go @@ -41,7 +41,7 @@ func (in *Playlist) DeepCopyObject() runtime.Object { func (in *PlaylistList) DeepCopyInto(out *PlaylistList) { *out = *in out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]Playlist, len(*in)) diff --git a/pkg/apis/wireset.go b/pkg/apis/wireset.go new file mode 100644 index 00000000000..6c6221c55ae --- /dev/null +++ b/pkg/apis/wireset.go @@ -0,0 +1,10 @@ +package apis + +import ( + "github.com/google/wire" + playlistv1 "github.com/grafana/grafana/pkg/apis/playlist/v1" +) + +var WireSet = wire.NewSet( + playlistv1.RegisterAPIService, +) diff --git a/pkg/registry/apis/apis.go b/pkg/registry/apis/apis.go new file mode 100644 index 00000000000..00023d06f4b --- /dev/null +++ b/pkg/registry/apis/apis.go @@ -0,0 +1,25 @@ +package apiregistry + +import ( + "context" + + playlistsv1 "github.com/grafana/grafana/pkg/apis/playlist/v1" + "github.com/grafana/grafana/pkg/registry" +) + +var ( + _ registry.BackgroundService = (*Service)(nil) +) + +type Service struct{} + +func ProvideService( + _ *playlistsv1.PlaylistAPIBuilder, +) *Service { + return &Service{} +} + +func (s *Service) Run(ctx context.Context) error { + <-ctx.Done() + return nil +} diff --git a/pkg/registry/apis/wireset.go b/pkg/registry/apis/wireset.go new file mode 100644 index 00000000000..d96125ebc42 --- /dev/null +++ b/pkg/registry/apis/wireset.go @@ -0,0 +1,12 @@ +package apiregistry + +import ( + "github.com/google/wire" + + "github.com/grafana/grafana/pkg/apis" +) + +var WireSet = wire.NewSet( + ProvideService, + apis.WireSet, +) diff --git a/pkg/registry/backgroundsvcs/background_services.go b/pkg/registry/backgroundsvcs/background_services.go index ec2425c8402..65bbe261615 100644 --- a/pkg/registry/backgroundsvcs/background_services.go +++ b/pkg/registry/backgroundsvcs/background_services.go @@ -8,11 +8,13 @@ import ( uss "github.com/grafana/grafana/pkg/infra/usagestats/service" "github.com/grafana/grafana/pkg/infra/usagestats/statscollector" "github.com/grafana/grafana/pkg/registry" + apiregistry "github.com/grafana/grafana/pkg/registry/apis" "github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/anonymous/anonimpl" "github.com/grafana/grafana/pkg/services/auth" "github.com/grafana/grafana/pkg/services/cleanup" "github.com/grafana/grafana/pkg/services/dashboardsnapshots" + grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver" "github.com/grafana/grafana/pkg/services/grpcserver" "github.com/grafana/grafana/pkg/services/guardian" ldapapi "github.com/grafana/grafana/pkg/services/ldap/api" @@ -51,16 +53,16 @@ func ProvideBackgroundServiceRegistry( secretsService *secretsManager.SecretsService, remoteCache *remotecache.RemoteCache, StorageService store.StorageService, searchService searchV2.SearchService, entityEventsService store.EntityEventsService, saService *samanager.ServiceAccountsService, authInfoService *authinfoservice.Implementation, grpcServerProvider grpcserver.Provider, secretMigrationProvider secretsMigrations.SecretMigrationProvider, loginAttemptService *loginattemptimpl.Service, - bundleService *supportbundlesimpl.Service, - publicDashboardsMetric *publicdashboardsmetric.Service, - keyRetriever *dynamic.KeyRetriever, - dynamicAngularDetectorsProvider *angulardetectorsprovider.Dynamic, + bundleService *supportbundlesimpl.Service, publicDashboardsMetric *publicdashboardsmetric.Service, + keyRetriever *dynamic.KeyRetriever, dynamicAngularDetectorsProvider *angulardetectorsprovider.Dynamic, + grafanaAPIServer grafanaapiserver.Service, anon *anonimpl.AnonDeviceService, // Need to make sure these are initialized, is there a better place to put them? _ dashboardsnapshots.Service, _ *alerting.AlertNotificationService, _ serviceaccounts.Service, _ *guardian.Provider, _ *plugindashboardsservice.DashboardUpdater, _ *sanitizer.Provider, _ *grpcserver.HealthService, _ entity.EntityStoreServer, _ *grpcserver.ReflectionService, _ *ldapapi.Service, + _ *apiregistry.Service, ) *BackgroundServiceRegistry { return NewBackgroundServiceRegistry( httpServer, @@ -94,6 +96,7 @@ func ProvideBackgroundServiceRegistry( publicDashboardsMetric, keyRetriever, dynamicAngularDetectorsProvider, + grafanaAPIServer, anon, ) } diff --git a/pkg/server/module_server.go b/pkg/server/module_server.go index 73fa373996b..8031a50567b 100644 --- a/pkg/server/module_server.go +++ b/pkg/server/module_server.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "os" - "path" "path/filepath" "strconv" "sync" @@ -16,7 +15,6 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/modules" "github.com/grafana/grafana/pkg/services/featuremgmt" - grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver" "github.com/grafana/grafana/pkg/setting" ) @@ -122,13 +120,14 @@ func (s *ModuleServer) Run() error { return NewService(s.cfg, s.opts, s.apiOpts) }) - if s.features.IsEnabled(featuremgmt.FlagGrafanaAPIServer) { - m.RegisterModule(modules.GrafanaAPIServer, func() (services.Service, error) { - return grafanaapiserver.New(path.Join(s.cfg.DataPath, "k8s")) - }) - } else { - s.log.Debug("apiserver feature is disabled") - } + // TODO: uncomment this once the apiserver is ready to be run as a standalone target + //if s.features.IsEnabled(featuremgmt.FlagGrafanaAPIServer) { + // m.RegisterModule(modules.GrafanaAPIServer, func() (services.Service, error) { + // return grafanaapiserver.New(path.Join(s.cfg.DataPath, "k8s")) + // }) + //} else { + // s.log.Debug("apiserver feature is disabled") + //} m.RegisterModule(modules.All, nil) diff --git a/pkg/server/wire.go b/pkg/server/wire.go index 75c4f8fbdd7..3321665bb16 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -33,6 +33,7 @@ import ( "github.com/grafana/grafana/pkg/login/social" "github.com/grafana/grafana/pkg/middleware/csrf" "github.com/grafana/grafana/pkg/middleware/loggermw" + apiregistry "github.com/grafana/grafana/pkg/registry/apis" "github.com/grafana/grafana/pkg/registry/corekind" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" @@ -63,6 +64,7 @@ import ( "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/folder/folderimpl" + grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver" "github.com/grafana/grafana/pkg/services/grpcserver" grpccontext "github.com/grafana/grafana/pkg/services/grpcserver/context" "github.com/grafana/grafana/pkg/services/grpcserver/interceptors" @@ -359,6 +361,8 @@ var wireBasicSet = wire.NewSet( loggermw.Provide, signingkeysimpl.ProvideEmbeddedSigningKeysService, wire.Bind(new(signingkeys.Service), new(*signingkeysimpl.Service)), + grafanaapiserver.WireSet, + apiregistry.WireSet, ) var wireSet = wire.NewSet( diff --git a/pkg/apis/common.go b/pkg/services/grafana-apiserver/common.go similarity index 98% rename from pkg/apis/common.go rename to pkg/services/grafana-apiserver/common.go index 867e9256bb6..54107924e58 100644 --- a/pkg/apis/common.go +++ b/pkg/services/grafana-apiserver/common.go @@ -1,4 +1,4 @@ -package apis +package grafanaapiserver import ( "fmt" diff --git a/pkg/services/grafana-apiserver/openapi.go b/pkg/services/grafana-apiserver/openapi.go index 33c974d3576..4e27fb03b7f 100644 --- a/pkg/services/grafana-apiserver/openapi.go +++ b/pkg/services/grafana-apiserver/openapi.go @@ -1,7 +1,6 @@ package grafanaapiserver import ( - "github.com/grafana/grafana/pkg/apis" "golang.org/x/exp/maps" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" common "k8s.io/kube-openapi/pkg/common" @@ -9,7 +8,7 @@ import ( ) // This should eventually live in grafana-app-sdk -func getOpenAPIDefinitions(builders []apis.APIGroupBuilder) common.GetOpenAPIDefinitions { +func getOpenAPIDefinitions(builders []APIGroupBuilder) common.GetOpenAPIDefinitions { return func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { defs := getStandardOpenAPIDefinitions(ref) for _, builder := range builders { diff --git a/pkg/services/grafana-apiserver/service.go b/pkg/services/grafana-apiserver/service.go index 838a4245561..29180f9b7f5 100644 --- a/pkg/services/grafana-apiserver/service.go +++ b/pkg/services/grafana-apiserver/service.go @@ -5,6 +5,8 @@ import ( "crypto/x509" "net" "path" + "strconv" + "strings" "github.com/go-logr/logr" "github.com/grafana/dskit/services" @@ -18,6 +20,7 @@ import ( "k8s.io/apiserver/pkg/authentication/request/headerrequest" "k8s.io/apiserver/pkg/authentication/user" 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" @@ -27,9 +30,15 @@ import ( clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/klog/v2" - "github.com/grafana/grafana/pkg/apis" - playlistv1 "github.com/grafana/grafana/pkg/apis/playlist/v1" + "github.com/grafana/grafana/pkg/api/routing" + "github.com/grafana/grafana/pkg/infra/appcontext" + "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/modules" + "github.com/grafana/grafana/pkg/registry" + contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" + "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/web" ) const ( @@ -37,15 +46,16 @@ const ( ) var ( - _ Service = (*service)(nil) - _ RestConfigProvider = (*service)(nil) + _ Service = (*service)(nil) + _ RestConfigProvider = (*service)(nil) + _ registry.BackgroundService = (*service)(nil) + _ registry.CanBeDisabled = (*service)(nil) ) var ( Scheme = runtime.NewScheme() Codecs = serializer.NewCodecFactory(Scheme) - // if you modify this, make sure you update the crEncoder unversionedVersion = schema.GroupVersion{Group: "", Version: "v1"} unversionedTypes = []runtime.Object{ &metav1.Status{}, @@ -65,6 +75,12 @@ func init() { type Service interface { services.NamedService + registry.BackgroundService + registry.CanBeDisabled +} + +type APIRegistrar interface { + RegisterAPI(builder APIGroupBuilder) } type RestConfigProvider interface { @@ -76,19 +92,50 @@ type service struct { restConfig *clientrest.Config + enabled bool dataPath string stopCh chan struct{} stoppedCh chan error + + rr routing.RouteRegister + handler web.Handler + builders []APIGroupBuilder } -func New(dataPath string) (*service, error) { +func ProvideService(cfg *setting.Cfg, + rr routing.RouteRegister, +) (*service, error) { s := &service{ - dataPath: dataPath, + enabled: cfg.IsFeatureToggleEnabled(featuremgmt.FlagGrafanaAPIServer), + rr: rr, + dataPath: path.Join(cfg.DataPath, "k8s"), stopCh: make(chan struct{}), + builders: []APIGroupBuilder{}, } + // This will be used when running as a dskit service s.BasicService = services.NewBasicService(s.start, s.running, nil).WithName(modules.GrafanaAPIServer) + // TODO: this is very hacky + // We need to register the routes in ProvideService to make sure + // the routes are registered before the Grafana HTTP server starts. + s.rr.Group("/k8s", func(k8sRoute routing.RouteRegister) { + handler := func(c *contextmodel.ReqContext) { + if s.handler == nil { + c.Resp.WriteHeader(404) + _, _ = c.Resp.Write([]byte("Not found")) + return + } + + if handle, ok := s.handler.(func(c *contextmodel.ReqContext)); ok { + handle(c) + return + } + } + k8sRoute.Any("/", middleware.ReqSignedIn, handler) + k8sRoute.Any("/*", middleware.ReqSignedIn, handler) + }) + return s, nil } @@ -96,6 +143,22 @@ func (s *service) GetRestConfig() *clientrest.Config { return s.restConfig } +func (s *service) IsDisabled() bool { + return !s.enabled +} + +// Run is an adapter for the BackgroundService interface. +func (s *service) Run(ctx context.Context) error { + if err := s.start(ctx); err != nil { + return err + } + return s.running(ctx) +} + +func (s *service) RegisterAPI(builder APIGroupBuilder) { + s.builders = append(s.builders, builder) +} + func (s *service) start(ctx context.Context) error { logger := logr.New(newLogAdapter()) logger.V(9) @@ -153,9 +216,7 @@ func (s *service) start(ctx context.Context) error { serverConfig.Authentication.Authenticator = authenticator // Get the list of groups the server will support - builders := []apis.APIGroupBuilder{ - playlistv1.GetAPIGroupBuilder(), - } + builders := s.builders // Install schemas for _, b := range builders { @@ -199,10 +260,8 @@ func (s *service) start(ctx context.Context) error { prepared := server.PrepareRun() - // TODO: not sure if we can still inject RouteRegister with the new module server setup - // Disabling the /k8s endpoint until we have a solution - - /* handler := func(c *contextmodel.ReqContext) { + // TODO: this is a hack. see note in ProvideService + s.handler = func(c *contextmodel.ReqContext) { req := c.Req req.URL.Path = strings.TrimPrefix(req.URL.Path, "/k8s") if req.URL.Path == "" { @@ -221,10 +280,6 @@ func (s *service) start(ctx context.Context) error { resp := responsewriter.WrapForHTTP1Or2(c.Resp) prepared.GenericAPIServer.Handler.ServeHTTP(resp, req) } - /* s.rr.Group("/k8s", func(k8sRoute routing.RouteRegister) { - k8sRoute.Any("/", middleware.ReqSignedIn, handler) - k8sRoute.Any("/*", middleware.ReqSignedIn, handler) - }) */ go func() { s.stoppedCh <- prepared.Run(s.stopCh) diff --git a/pkg/services/grafana-apiserver/wireset.go b/pkg/services/grafana-apiserver/wireset.go new file mode 100644 index 00000000000..8781d754183 --- /dev/null +++ b/pkg/services/grafana-apiserver/wireset.go @@ -0,0 +1,12 @@ +package grafanaapiserver + +import ( + "github.com/google/wire" +) + +var WireSet = wire.NewSet( + ProvideService, + wire.Bind(new(RestConfigProvider), new(*service)), + wire.Bind(new(Service), new(*service)), + wire.Bind(new(APIRegistrar), new(*service)), +)