diff --git a/pkg/services/apiserver/service.go b/pkg/services/apiserver/service.go index c0e416f3bc39b..9246d08439a14 100644 --- a/pkg/services/apiserver/service.go +++ b/pkg/services/apiserver/service.go @@ -71,6 +71,10 @@ var ( &metav1.PartialObjectMetadata{}, &metav1.PartialObjectMetadataList{}, } + + // internal provider of the package level client Config + restConfig RestConfigProvider + ready = make(chan struct{}) ) func init() { @@ -79,6 +83,17 @@ func init() { Scheme.AddUnversionedTypes(unversionedVersion, unversionedTypes...) } +// GetRestConfig return a client Config mounted at package level +// This resolves circular dependency issues between apiserver, authz, +// and Folder Service. +// The client Config gets initialized during the first call to +// ProvideService. +// Any call to GetRestConfig will block until we have a restConfig available +func GetRestConfig(ctx context.Context) *clientrest.Config { + <-ready + return restConfig.GetRestConfig(ctx) +} + type Service interface { services.NamedService registry.BackgroundService @@ -210,6 +225,12 @@ func ProvideService( s.rr.Group("/openapi", proxyHandler) s.rr.Group("/version", proxyHandler) + // only set the package level restConfig once + if restConfig == nil { + restConfig = s + close(ready) + } + return s, nil } diff --git a/pkg/services/folder/folderimpl/folder.go b/pkg/services/folder/folderimpl/folder.go index 091e8c83e9ad7..e05b6f896aa1c 100644 --- a/pkg/services/folder/folderimpl/folder.go +++ b/pkg/services/folder/folderimpl/folder.go @@ -25,6 +25,7 @@ import ( "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/apiserver" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" @@ -76,12 +77,13 @@ func ProvideService( tracer tracing.Tracer, ) *Service { k8sHandler := &foldk8sHandler{ - gvr: v0alpha1.FolderResourceInfo.GroupVersionResource(), - namespacer: request.GetNamespaceMapper(cfg), - cfg: cfg, + gvr: v0alpha1.FolderResourceInfo.GroupVersionResource(), + namespacer: request.GetNamespaceMapper(cfg), + cfg: cfg, + restConfigProvider: apiserver.GetRestConfig, } - unifiedStore := ProvideUnifiedStore(cfg) + unifiedStore := ProvideUnifiedStore(k8sHandler) srv := &Service{ log: slog.Default().With("logger", "folder-service"), diff --git a/pkg/services/folder/folderimpl/folder_unifiedstorage.go b/pkg/services/folder/folderimpl/folder_unifiedstorage.go index d8f9a5b8b763b..77a94d4268802 100644 --- a/pkg/services/folder/folderimpl/folder_unifiedstorage.go +++ b/pkg/services/folder/folderimpl/folder_unifiedstorage.go @@ -10,7 +10,7 @@ import ( "golang.org/x/exp/slices" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" + clientrest "k8s.io/client-go/rest" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/events" @@ -36,9 +36,10 @@ type folderK8sHandler interface { var _ folderK8sHandler = (*foldk8sHandler)(nil) type foldk8sHandler struct { - cfg *setting.Cfg - namespacer request.NamespaceMapper - gvr schema.GroupVersionResource + cfg *setting.Cfg + namespacer request.NamespaceMapper + gvr schema.GroupVersionResource + restConfigProvider func(ctx context.Context) *clientrest.Config } func (s *Service) getFoldersFromApiServer(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) { @@ -680,20 +681,16 @@ func (s *Service) getDescendantCountsFromApiServer(ctx context.Context, q *folde // ----------------------------------------------------------------------------------------- func (fk8s *foldk8sHandler) getClient(ctx context.Context, orgID int64) (dynamic.ResourceInterface, bool) { - cfg := &rest.Config{ - Host: fk8s.cfg.AppURL, - APIPath: "/apis", - TLSClientConfig: rest.TLSClientConfig{ - Insecure: true, // Skip TLS verification - }, - Username: fk8s.cfg.AdminUser, - Password: fk8s.cfg.AdminPassword, + cfg := fk8s.restConfigProvider(ctx) + if cfg == nil { + return nil, false } dyn, err := dynamic.NewForConfig(cfg) if err != nil { return nil, false } + return dyn.Resource(fk8s.gvr).Namespace(fk8s.getNamespace(orgID)), true } diff --git a/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go b/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go index 4b18053cb3e1d..fa95434170f3a 100644 --- a/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go +++ b/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/stretchr/testify/require" + clientrest "k8s.io/client-go/rest" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" @@ -22,6 +23,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" "github.com/grafana/grafana/pkg/services/accesscontrol/actest" + "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/folder" @@ -31,6 +33,16 @@ import ( "github.com/grafana/grafana/pkg/services/user" ) +type rcp struct { + Host string +} + +func (r rcp) GetRestConfig(ctx context.Context) *clientrest.Config { + return &clientrest.Config{ + Host: r.Host, + } +} + func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") @@ -147,7 +159,18 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) { db, cfg := sqlstore.InitTestDB(t) cfg.AppURL = folderApiServerMock.URL - unifiedStore := ProvideUnifiedStore(cfg) + restCfgProvider := rcp{ + Host: folderApiServerMock.URL, + } + + k8sHandler := &foldk8sHandler{ + gvr: v0alpha1.FolderResourceInfo.GroupVersionResource(), + namespacer: request.GetNamespaceMapper(cfg), + cfg: cfg, + restConfigProvider: restCfgProvider.GetRestConfig, + } + + unifiedStore := ProvideUnifiedStore(k8sHandler) ctx := context.Background() usr := &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{ diff --git a/pkg/services/folder/folderimpl/unifiedstore.go b/pkg/services/folder/folderimpl/unifiedstore.go index db05b701b2374..b3ebe609d47ea 100644 --- a/pkg/services/folder/folderimpl/unifiedstore.go +++ b/pkg/services/folder/folderimpl/unifiedstore.go @@ -15,9 +15,7 @@ import ( "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" "github.com/grafana/grafana/pkg/infra/log" internalfolders "github.com/grafana/grafana/pkg/registry/apis/folders" - "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/folder" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" ) @@ -29,13 +27,7 @@ type FolderUnifiedStoreImpl struct { // sqlStore implements the store interface. var _ folder.Store = (*FolderUnifiedStoreImpl)(nil) -func ProvideUnifiedStore(cfg *setting.Cfg) *FolderUnifiedStoreImpl { - k8sHandler := &foldk8sHandler{ - gvr: v0alpha1.FolderResourceInfo.GroupVersionResource(), - namespacer: request.GetNamespaceMapper(cfg), - cfg: cfg, - } - +func ProvideUnifiedStore(k8sHandler *foldk8sHandler) *FolderUnifiedStoreImpl { return &FolderUnifiedStoreImpl{ k8sclient: k8sHandler, log: log.New("folder-store"), diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 527a3ccdac529..6d532145c3db7 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -292,6 +292,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o= github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= +github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= @@ -567,12 +569,16 @@ github.com/grafana/grafana-plugin-sdk-go v0.262.0 h1:R2DV6lwBQE5zaogxX3PorD9Seo8 github.com/grafana/grafana-plugin-sdk-go v0.262.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw= github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d h1:aBD5kzsIAh50vjNqUkWK9mNpLGIBYAnKkWtUepGNAiQ= github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d/go.mod h1:1sq0guad+G4SUTlBgx7SXfhnzy7D86K/LcVOtiQCiMA= +github.com/grafana/grafana/pkg/promlib v0.0.7 h1:BdpanKOKnID/l1BJZLhE7TRNtmq7aOVdou1LBFWaMmU= +github.com/grafana/grafana/pkg/promlib v0.0.7/go.mod h1:rnwJXCA2xRwb7F27NB35iO/JsLL/H/+eVXECk/hrEhQ= github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d h1:cYBjuhb3m5oC6Z00Kw8DdySFaNhwb38SMxx0oXnz5vQ= github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d/go.mod h1:tfLnBpPYgwrBMRz4EXqPCZJyCjEG4Ev37FSlXnocJ2c= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= @@ -912,6 +918,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/prometheus v0.301.0 h1:0z8dgegmILivNomCd79RKvVkIols8vBGPKmcIBc7OyY= +github.com/prometheus/prometheus v0.301.0/go.mod h1:BJLjWCKNfRfjp7Q48DrAjARnCi7GhfUVvUFEAWTssZM= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=