mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Setup legacy search based on mode (#98908)
This commit is contained in:
parent
060182a3ba
commit
be8396cafa
@ -19,6 +19,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
dashboard "github.com/grafana/grafana/pkg/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacysearcher"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
@ -54,8 +55,9 @@ type dashboardSqlAccess struct {
|
||||
provisioning provisioning.ProvisioningService
|
||||
|
||||
// Use for writing (not reading)
|
||||
dashStore dashboards.Store
|
||||
softDelete bool
|
||||
dashStore dashboards.Store
|
||||
softDelete bool
|
||||
dashboardSearchClient legacysearcher.DashboardSearchClient
|
||||
|
||||
// Typically one... the server wrapper
|
||||
subscribers []chan *resource.WrittenEvent
|
||||
@ -68,12 +70,14 @@ func NewDashboardAccess(sql legacysql.LegacyDatabaseProvider,
|
||||
provisioning provisioning.ProvisioningService,
|
||||
softDelete bool,
|
||||
) DashboardAccess {
|
||||
dashboardSearchClient := legacysearcher.NewDashboardSearchClient(dashStore)
|
||||
return &dashboardSqlAccess{
|
||||
sql: sql,
|
||||
namespacer: namespacer,
|
||||
dashStore: dashStore,
|
||||
provisioning: provisioning,
|
||||
softDelete: softDelete,
|
||||
sql: sql,
|
||||
namespacer: namespacer,
|
||||
dashStore: dashStore,
|
||||
provisioning: provisioning,
|
||||
softDelete: softDelete,
|
||||
dashboardSearchClient: *dashboardSearchClient,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
@ -255,9 +254,8 @@ func (a *dashboardSqlAccess) Read(ctx context.Context, req *resource.ReadRequest
|
||||
return a.ReadResource(ctx, req), nil
|
||||
}
|
||||
|
||||
// TODO: this needs to be implemented
|
||||
func (a *dashboardSqlAccess) Search(ctx context.Context, req *resource.ResourceSearchRequest) (*resource.ResourceSearchResponse, error) {
|
||||
return nil, fmt.Errorf("not yet (filter)")
|
||||
return a.dashboardSearchClient.Search(ctx, req)
|
||||
}
|
||||
|
||||
func (a *dashboardSqlAccess) ListRepositoryObjects(ctx context.Context, req *resource.ListRepositoryObjectsRequest) (*resource.ListRepositoryObjectsResponse, error) {
|
||||
@ -270,35 +268,5 @@ func (a *dashboardSqlAccess) CountRepositoryObjects(context.Context, *resource.C
|
||||
|
||||
// GetStats implements ResourceServer.
|
||||
func (a *dashboardSqlAccess) GetStats(ctx context.Context, req *resource.ResourceStatsRequest) (*resource.ResourceStatsResponse, error) {
|
||||
info, err := claims.ParseNamespace(req.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read namespace")
|
||||
}
|
||||
if info.OrgID == 0 {
|
||||
return nil, fmt.Errorf("invalid OrgID found in namespace")
|
||||
}
|
||||
|
||||
if len(req.Kinds) != 1 {
|
||||
return nil, fmt.Errorf("only can query for dashboard kind in legacy fallback")
|
||||
}
|
||||
|
||||
parts := strings.SplitN(req.Kinds[0], "/", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid kind")
|
||||
}
|
||||
|
||||
count, err := a.dashStore.CountInOrg(ctx, info.OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &resource.ResourceStatsResponse{
|
||||
Stats: []*resource.ResourceStatsResponse_Stats{
|
||||
{
|
||||
Group: parts[0],
|
||||
Resource: parts[1],
|
||||
Count: count,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
return a.dashboardSearchClient.GetStats(ctx, req)
|
||||
}
|
||||
|
128
pkg/registry/apis/dashboard/legacysearcher/search_client.go
Normal file
128
pkg/registry/apis/dashboard/legacysearcher/search_client.go
Normal file
@ -0,0 +1,128 @@
|
||||
package legacysearcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type DashboardSearchClient struct {
|
||||
resource.ResourceIndexClient
|
||||
dashboardStore dashboards.Store
|
||||
}
|
||||
|
||||
func NewDashboardSearchClient(dashboardStore dashboards.Store) *DashboardSearchClient {
|
||||
return &DashboardSearchClient{dashboardStore: dashboardStore}
|
||||
}
|
||||
|
||||
func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.ResourceSearchRequest, opts ...grpc.CallOption) (*resource.ResourceSearchResponse, error) {
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.Query == "*" {
|
||||
req.Query = ""
|
||||
}
|
||||
|
||||
// TODO add missing support for the following query params:
|
||||
// - tag
|
||||
// - starred (won't support)
|
||||
// - page (check)
|
||||
// - type
|
||||
// - sort
|
||||
// - deleted
|
||||
// - permission
|
||||
// - dashboardIds
|
||||
// - dashboardUIDs
|
||||
// - folderIds
|
||||
// - folderUIDs
|
||||
// - sort (default by title)
|
||||
query := &dashboards.FindPersistedDashboardsQuery{
|
||||
Title: req.Query,
|
||||
Limit: req.Limit,
|
||||
// FolderUIDs: req.FolderUIDs,
|
||||
SignedInUser: user,
|
||||
}
|
||||
|
||||
// TODO need to test this
|
||||
// emptyResponse, err := a.dashService.GetSharedDashboardUIDsQuery(ctx, query)
|
||||
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// } else if emptyResponse {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
res, err := c.dashboardStore.FindDashboards(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO sort if query.Sort == "" see sortedHits in services/search/service.go
|
||||
|
||||
searchFields := resource.StandardSearchFields()
|
||||
list := &resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{
|
||||
Columns: []*resource.ResourceTableColumnDefinition{
|
||||
searchFields.Field(resource.SEARCH_FIELD_TITLE),
|
||||
searchFields.Field(resource.SEARCH_FIELD_FOLDER),
|
||||
// searchFields.Field(resource.SEARCH_FIELD_TAGS),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dashboard := range res {
|
||||
list.Results.Rows = append(list.Results.Rows, &resource.ResourceTableRow{
|
||||
Key: &resource.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Name: dashboard.UID,
|
||||
},
|
||||
Cells: [][]byte{[]byte(dashboard.Title), []byte(dashboard.FolderUID)}, // TODO add tag
|
||||
})
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (c *DashboardSearchClient) GetStats(ctx context.Context, req *resource.ResourceStatsRequest, opts ...grpc.CallOption) (*resource.ResourceStatsResponse, error) {
|
||||
info, err := claims.ParseNamespace(req.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read namespace")
|
||||
}
|
||||
if info.OrgID == 0 {
|
||||
return nil, fmt.Errorf("invalid OrgID found in namespace")
|
||||
}
|
||||
|
||||
if len(req.Kinds) != 1 {
|
||||
return nil, fmt.Errorf("only can query for dashboard kind in legacy fallback")
|
||||
}
|
||||
|
||||
parts := strings.SplitN(req.Kinds[0], "/", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid kind")
|
||||
}
|
||||
|
||||
count, err := c.dashboardStore.CountInOrg(ctx, info.OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &resource.ResourceStatsResponse{
|
||||
Stats: []*resource.ResourceStatsResponse_Stats{
|
||||
{
|
||||
Group: parts[0],
|
||||
Resource: parts[1],
|
||||
Count: count,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
dashboardsearch "github.com/grafana/grafana/pkg/services/dashboards/service/search"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/util/errhttp"
|
||||
)
|
||||
@ -32,9 +33,10 @@ type SearchHandler struct {
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
func NewSearchHandler(client resource.ResourceIndexClient, tracer trace.Tracer) *SearchHandler {
|
||||
func NewSearchHandler(client resource.ResourceIndexClient, tracer trace.Tracer, cfg *setting.Cfg, legacyDashboardSearcher resource.ResourceIndexClient) *SearchHandler {
|
||||
searchClient := resource.NewSearchClient(cfg, setting.UnifiedStorageConfigKeyDashboard, client, legacyDashboardSearcher)
|
||||
return &SearchHandler{
|
||||
client: client,
|
||||
client: searchClient,
|
||||
log: log.New("grafana-apiserver.dashboards.search"),
|
||||
tracer: tracer,
|
||||
}
|
||||
@ -332,7 +334,6 @@ func (s *SearchHandler) DoSearch(w http.ResponseWriter, r *http.Request) {
|
||||
searchRequest.Options.Fields = append(searchRequest.Options.Fields, namesFilter...)
|
||||
}
|
||||
|
||||
// Run the query
|
||||
result, err := s.client.Search(ctx, searchRequest)
|
||||
if err != nil {
|
||||
errhttp.Write(ctx, err, w)
|
||||
|
@ -7,13 +7,173 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func TestSearchFallback(t *testing.T) {
|
||||
t.Run("should hit legacy search handler on mode 0", func(t *testing.T) {
|
||||
mockClient := &MockClient{}
|
||||
mockLegacyClient := &MockClient{}
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {DualWriterMode: rest.Mode0},
|
||||
},
|
||||
}
|
||||
searchHandler := NewSearchHandler(mockClient, tracing.NewNoopTracerService(), cfg, mockLegacyClient)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/search", nil)
|
||||
req.Header.Add("content-type", "application/json")
|
||||
req = req.WithContext(identity.WithRequester(req.Context(), &user.SignedInUser{Namespace: "test"}))
|
||||
|
||||
searchHandler.DoSearch(rr, req)
|
||||
|
||||
if mockClient.LastSearchRequest != nil {
|
||||
t.Fatalf("expected Search NOT to be called, but it was")
|
||||
}
|
||||
if mockLegacyClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should hit legacy search handler on mode 1", func(t *testing.T) {
|
||||
mockClient := &MockClient{}
|
||||
mockLegacyClient := &MockClient{}
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {DualWriterMode: rest.Mode1},
|
||||
},
|
||||
}
|
||||
searchHandler := NewSearchHandler(mockClient, tracing.NewNoopTracerService(), cfg, mockLegacyClient)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/search", nil)
|
||||
req.Header.Add("content-type", "application/json")
|
||||
req = req.WithContext(identity.WithRequester(req.Context(), &user.SignedInUser{Namespace: "test"}))
|
||||
|
||||
searchHandler.DoSearch(rr, req)
|
||||
|
||||
if mockClient.LastSearchRequest != nil {
|
||||
t.Fatalf("expected Search NOT to be called, but it was")
|
||||
}
|
||||
if mockLegacyClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should hit legacy search handler on mode 2", func(t *testing.T) {
|
||||
mockClient := &MockClient{}
|
||||
mockLegacyClient := &MockClient{}
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {DualWriterMode: rest.Mode2},
|
||||
},
|
||||
}
|
||||
searchHandler := NewSearchHandler(mockClient, tracing.NewNoopTracerService(), cfg, mockLegacyClient)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/search", nil)
|
||||
req.Header.Add("content-type", "application/json")
|
||||
req = req.WithContext(identity.WithRequester(req.Context(), &user.SignedInUser{Namespace: "test"}))
|
||||
|
||||
searchHandler.DoSearch(rr, req)
|
||||
|
||||
if mockClient.LastSearchRequest != nil {
|
||||
t.Fatalf("expected Search NOT to be called, but it was")
|
||||
}
|
||||
if mockLegacyClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should hit unified storage search handler on mode 3", func(t *testing.T) {
|
||||
mockClient := &MockClient{}
|
||||
mockLegacyClient := &MockClient{}
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {DualWriterMode: rest.Mode3},
|
||||
},
|
||||
}
|
||||
searchHandler := NewSearchHandler(mockClient, tracing.NewNoopTracerService(), cfg, mockLegacyClient)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/search", nil)
|
||||
req.Header.Add("content-type", "application/json")
|
||||
req = req.WithContext(identity.WithRequester(req.Context(), &user.SignedInUser{Namespace: "test"}))
|
||||
|
||||
searchHandler.DoSearch(rr, req)
|
||||
|
||||
if mockClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
if mockLegacyClient.LastSearchRequest != nil {
|
||||
t.Fatalf("expected Search NOT to be called, but it was")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should hit unified storage search handler on mode 4", func(t *testing.T) {
|
||||
mockClient := &MockClient{}
|
||||
mockLegacyClient := &MockClient{}
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {DualWriterMode: rest.Mode4},
|
||||
},
|
||||
}
|
||||
searchHandler := NewSearchHandler(mockClient, tracing.NewNoopTracerService(), cfg, mockLegacyClient)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/search", nil)
|
||||
req.Header.Add("content-type", "application/json")
|
||||
req = req.WithContext(identity.WithRequester(req.Context(), &user.SignedInUser{Namespace: "test"}))
|
||||
|
||||
searchHandler.DoSearch(rr, req)
|
||||
|
||||
if mockClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
if mockLegacyClient.LastSearchRequest != nil {
|
||||
t.Fatalf("expected Search NOT to be called, but it was")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should hit unified storage search handler on mode 5", func(t *testing.T) {
|
||||
mockClient := &MockClient{}
|
||||
mockLegacyClient := &MockClient{}
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
UnifiedStorage: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {DualWriterMode: rest.Mode5},
|
||||
},
|
||||
}
|
||||
searchHandler := NewSearchHandler(mockClient, tracing.NewNoopTracerService(), cfg, mockLegacyClient)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/search", nil)
|
||||
req.Header.Add("content-type", "application/json")
|
||||
req = req.WithContext(identity.WithRequester(req.Context(), &user.SignedInUser{Namespace: "test"}))
|
||||
|
||||
searchHandler.DoSearch(rr, req)
|
||||
|
||||
if mockClient.LastSearchRequest == nil {
|
||||
t.Fatalf("expected Search to be called, but it was not")
|
||||
}
|
||||
if mockLegacyClient.LastSearchRequest != nil {
|
||||
t.Fatalf("expected Search NOT to be called, but it was")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSearchHandlerFields(t *testing.T) {
|
||||
// Create a mock client
|
||||
mockClient := &MockClient{}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacysearcher"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
@ -71,6 +72,7 @@ func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles,
|
||||
softDelete := features.IsEnabledGlobally(featuremgmt.FlagDashboardRestore)
|
||||
dbp := legacysql.NewDatabaseProvider(sql)
|
||||
namespacer := request.GetNamespaceMapper(cfg)
|
||||
legacyDashboardSearcher := legacysearcher.NewDashboardSearchClient(dashStore)
|
||||
builder := &DashboardsAPIBuilder{
|
||||
log: log.New("grafana-apiserver.dashboards.v0alpha1"),
|
||||
DashboardsAPIBuilder: dashboard.DashboardsAPIBuilder{
|
||||
@ -80,7 +82,7 @@ func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles,
|
||||
features: features,
|
||||
accessControl: accessControl,
|
||||
unified: unified,
|
||||
search: dashboard.NewSearchHandler(unified, tracing),
|
||||
search: dashboard.NewSearchHandler(unified, tracing, cfg, legacyDashboardSearcher),
|
||||
|
||||
legacy: &dashboard.DashboardStorage{
|
||||
Resource: dashboardv0alpha1.DashboardResourceInfo,
|
||||
|
@ -12,8 +12,11 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacysearcher"
|
||||
"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/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
k8sUser "k8s.io/apiserver/pkg/authentication/user"
|
||||
k8sRequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
@ -40,12 +43,14 @@ type k8sHandler struct {
|
||||
searcher resource.ResourceIndexClient
|
||||
}
|
||||
|
||||
func NewK8sHandler(namespacer request.NamespaceMapper, gvr schema.GroupVersionResource, restConfigProvider apiserver.RestConfigProvider, searcher resource.ResourceIndexClient) K8sHandler {
|
||||
func NewK8sHandler(cfg *setting.Cfg, namespacer request.NamespaceMapper, gvr schema.GroupVersionResource, restConfigProvider apiserver.RestConfigProvider, searcher resource.ResourceIndexClient, dashStore dashboards.Store) K8sHandler {
|
||||
legacySearcher := legacysearcher.NewDashboardSearchClient(dashStore)
|
||||
searchClient := resource.NewSearchClient(cfg, setting.UnifiedStorageConfigKeyDashboard, searcher, legacySearcher)
|
||||
return &k8sHandler{
|
||||
namespacer: namespacer,
|
||||
gvr: gvr,
|
||||
restConfigProvider: restConfigProvider,
|
||||
searcher: searcher,
|
||||
searcher: searchClient,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ func ProvideDashboardServiceImpl(
|
||||
restConfigProvider apiserver.RestConfigProvider, userService user.Service, unified resource.ResourceClient,
|
||||
quotaService quota.Service, orgService org.Service, publicDashboardService publicdashboards.ServiceWrapper,
|
||||
) (*DashboardServiceImpl, error) {
|
||||
k8sHandler := client.NewK8sHandler(request.GetNamespaceMapper(cfg), v0alpha1.DashboardResourceInfo.GroupVersionResource(), restConfigProvider, unified)
|
||||
k8sHandler := client.NewK8sHandler(cfg, request.GetNamespaceMapper(cfg), v0alpha1.DashboardResourceInfo.GroupVersionResource(), restConfigProvider, unified, dashboardStore)
|
||||
|
||||
dashSvc := &DashboardServiceImpl{
|
||||
cfg: cfg,
|
||||
|
@ -540,6 +540,8 @@ type Cfg struct {
|
||||
HttpsSkipVerify bool
|
||||
}
|
||||
|
||||
const UnifiedStorageConfigKeyDashboard = "dashboards.dashboard.grafana.app"
|
||||
|
||||
type UnifiedStorageConfig struct {
|
||||
DualWriterMode rest.DualWriterMode
|
||||
DualWriterPeriodicDataSyncJobEnabled bool
|
||||
|
@ -17,6 +17,7 @@ require (
|
||||
github.com/grafana/grafana v11.4.0-00010101000000-000000000000+incompatible
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.263.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d
|
||||
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250121113133-e747350fee2d
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
@ -120,7 +121,6 @@ require (
|
||||
github.com/grafana/grafana-app-sdk/logging v0.29.0 // indirect
|
||||
github.com/grafana/grafana-aws-sdk v0.31.5 // indirect
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect
|
||||
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250121113133-e747350fee2d // indirect
|
||||
github.com/grafana/otel-profiling-go v0.5.1 // indirect
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
|
||||
github.com/grafana/sqlds/v4 v4.1.3 // indirect
|
||||
|
20
pkg/storage/unified/resource/search_client.go
Normal file
20
pkg/storage/unified/resource/search_client.go
Normal file
@ -0,0 +1,20 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func NewSearchClient(cfg *setting.Cfg, unifiedStorageConfigKey string, unifiedClient ResourceIndexClient, legacyClient ResourceIndexClient) ResourceIndexClient {
|
||||
config, ok := cfg.UnifiedStorage[unifiedStorageConfigKey]
|
||||
if !ok {
|
||||
return legacyClient
|
||||
}
|
||||
|
||||
switch config.DualWriterMode {
|
||||
case rest.Mode0, rest.Mode1, rest.Mode2:
|
||||
return legacyClient
|
||||
default:
|
||||
return unifiedClient
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user