Unified Storage: Pass ns, group, resource to GetResourceStats instead of just namespace (#114050)

* passes nsr to GetResourceStats instead of just namespace

* removes ns check

* fixes failing tests

* make update-workspace

* pass group and resource from rebuild request when getting resource stats
This commit is contained in:
owensmallwood
2025-11-18 13:05:21 -06:00
committed by GitHub
parent bf3de07147
commit 8dddff3ce4
12 changed files with 57 additions and 26 deletions
+1 -1
View File
@@ -403,7 +403,7 @@ require (
github.com/diegoholiveira/jsonlogic/v3 v3.7.4 // indirect github.com/diegoholiveira/jsonlogic/v3 v3.7.4 // indirect
github.com/distribution/reference v0.6.0 // indirect github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v28.4.0+incompatible // indirect github.com/docker/docker v28.4.0+incompatible // indirect
github.com/docker/go-connections v0.6.0 // @grafana/grafana-app-platform-squad github.com/docker/go-connections v0.6.0 // indirect; @grafana/grafana-app-platform-squad
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 // indirect github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 // indirect
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 // indirect github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 // indirect
+3 -3
View File
@@ -15,7 +15,6 @@ import (
_ "github.com/blugelabs/bluge" _ "github.com/blugelabs/bluge"
_ "github.com/blugelabs/bluge_segment_api" _ "github.com/blugelabs/bluge_segment_api"
_ "github.com/crewjam/saml" _ "github.com/crewjam/saml"
_ "github.com/docker/go-connections/nat"
_ "github.com/go-jose/go-jose/v4" _ "github.com/go-jose/go-jose/v4"
_ "github.com/gobwas/glob" _ "github.com/gobwas/glob"
_ "github.com/googleapis/gax-go/v2" _ "github.com/googleapis/gax-go/v2"
@@ -31,7 +30,6 @@ import (
_ "github.com/spf13/cobra" // used by the standalone apiserver cli _ "github.com/spf13/cobra" // used by the standalone apiserver cli
_ "github.com/spyzhov/ajson" _ "github.com/spyzhov/ajson"
_ "github.com/stretchr/testify/require" _ "github.com/stretchr/testify/require"
_ "github.com/testcontainers/testcontainers-go"
_ "gocloud.dev/secrets/awskms" _ "gocloud.dev/secrets/awskms"
_ "gocloud.dev/secrets/azurekeyvault" _ "gocloud.dev/secrets/azurekeyvault"
_ "gocloud.dev/secrets/gcpkms" _ "gocloud.dev/secrets/gcpkms"
@@ -56,7 +54,9 @@ import (
_ "github.com/grafana/e2e" _ "github.com/grafana/e2e"
_ "github.com/grafana/gofpdf" _ "github.com/grafana/gofpdf"
_ "github.com/grafana/gomemcache/memcache" _ "github.com/grafana/gomemcache/memcache"
_ "github.com/grafana/tempo/pkg/traceql"
_ "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1" _ "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1"
_ "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1" _ "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1"
_ "github.com/grafana/tempo/pkg/traceql" _ "github.com/testcontainers/testcontainers-go"
) )
@@ -165,7 +165,7 @@ type rowsWrapper struct {
rejected []dashboardRow rejected []dashboardRow
} }
func (a *dashboardSqlAccess) GetResourceStats(ctx context.Context, namespace string, minCount int) ([]resource.ResourceStats, error) { func (a *dashboardSqlAccess) GetResourceStats(ctx context.Context, nsr resource.NamespacedResource, minCount int) ([]resource.ResourceStats, error) {
return nil, fmt.Errorf("not implemented") return nil, fmt.Errorf("not implemented")
} }
@@ -23,7 +23,7 @@ func ProvideStorageBackend() *StorageBackendImpl {
} }
// GetResourceStats implements resource.StorageBackend. // GetResourceStats implements resource.StorageBackend.
func (c *StorageBackendImpl) GetResourceStats(ctx context.Context, namespace string, minCount int) ([]resource.ResourceStats, error) { func (c *StorageBackendImpl) GetResourceStats(ctx context.Context, nsr resource.NamespacedResource, minCount int) ([]resource.ResourceStats, error) {
return []resource.ResourceStats{}, errNoopStorage return []resource.ResourceStats{}, errNoopStorage
} }
@@ -60,7 +60,7 @@ func ProvideStorageBackend(dbProvider legacysql.LegacyDatabaseProvider) *Resourc
} }
} }
func (s *ResourcePermSqlBackend) GetResourceStats(ctx context.Context, namespace string, minCount int) ([]resource.ResourceStats, error) { func (s *ResourcePermSqlBackend) GetResourceStats(ctx context.Context, nsr resource.NamespacedResource, minCount int) ([]resource.ResourceStats, error) {
return []resource.ResourceStats{}, errNotImplemented return []resource.ResourceStats{}, errNotImplemented
} }
+23 -6
View File
@@ -245,7 +245,10 @@ func (s *searchSupport) ListManagedObjects(ctx context.Context, req *resourcepb.
} }
rsp := &resourcepb.ListManagedObjectsResponse{} rsp := &resourcepb.ListManagedObjectsResponse{}
stats, err := s.storage.GetResourceStats(ctx, req.Namespace, 0) nsr := NamespacedResource{
Namespace: req.Namespace,
}
stats, err := s.storage.GetResourceStats(ctx, nsr, 0)
if err != nil { if err != nil {
rsp.Error = AsErrorResult(err) rsp.Error = AsErrorResult(err)
return rsp, nil return rsp, nil
@@ -286,7 +289,10 @@ func (s *searchSupport) ListManagedObjects(ctx context.Context, req *resourcepb.
func (s *searchSupport) CountManagedObjects(ctx context.Context, req *resourcepb.CountManagedObjectsRequest) (*resourcepb.CountManagedObjectsResponse, error) { func (s *searchSupport) CountManagedObjects(ctx context.Context, req *resourcepb.CountManagedObjectsRequest) (*resourcepb.CountManagedObjectsResponse, error) {
rsp := &resourcepb.CountManagedObjectsResponse{} rsp := &resourcepb.CountManagedObjectsResponse{}
stats, err := s.storage.GetResourceStats(ctx, req.Namespace, 0) nsr := NamespacedResource{
Namespace: req.Namespace,
}
stats, err := s.storage.GetResourceStats(ctx, nsr, 0)
if err != nil { if err != nil {
rsp.Error = AsErrorResult(err) rsp.Error = AsErrorResult(err)
return rsp, nil return rsp, nil
@@ -408,7 +414,10 @@ func (s *searchSupport) GetStats(ctx context.Context, req *resourcepb.ResourceSt
return rsp, nil return rsp, nil
} }
stats, err := s.storage.GetResourceStats(ctx, req.Namespace, 0) nsr := NamespacedResource{
Namespace: req.Namespace,
}
stats, err := s.storage.GetResourceStats(ctx, nsr, 0)
if err != nil { if err != nil {
return &resourcepb.ResourceStatsResponse{ return &resourcepb.ResourceStatsResponse{
Error: AsErrorResult(err), Error: AsErrorResult(err),
@@ -457,7 +466,7 @@ func (s *searchSupport) buildIndexes(ctx context.Context) (int, error) {
group := errgroup.Group{} group := errgroup.Group{}
group.SetLimit(s.initWorkers) group.SetLimit(s.initWorkers)
stats, err := s.storage.GetResourceStats(ctx, "", s.initMinSize) stats, err := s.storage.GetResourceStats(ctx, NamespacedResource{}, s.initMinSize)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -657,7 +666,12 @@ func (s *searchSupport) rebuildIndex(ctx context.Context, req rebuildRequest) {
// Get the correct value of size + RV for building the index. This is important for our Bleve // Get the correct value of size + RV for building the index. This is important for our Bleve
// backend to decide whether to build index in-memory or as file-based. // backend to decide whether to build index in-memory or as file-based.
stats, err := s.storage.GetResourceStats(ctx, req.Namespace, 0) nsr := NamespacedResource{
Namespace: req.Namespace,
Group: req.Group,
Resource: req.Resource,
}
stats, err := s.storage.GetResourceStats(ctx, nsr, 0)
if err != nil { if err != nil {
span.RecordError(fmt.Errorf("failed to get resource stats: %w", err)) span.RecordError(fmt.Errorf("failed to get resource stats: %w", err))
l.Error("failed to get resource stats", "error", err) l.Error("failed to get resource stats", "error", err)
@@ -750,7 +764,10 @@ func (s *searchSupport) getOrCreateIndex(ctx context.Context, key NamespacedReso
// Get correct value of size + RV for building the index. This is important for our Bleve // Get correct value of size + RV for building the index. This is important for our Bleve
// backend to decide whether to build index in-memory or as file-based. // backend to decide whether to build index in-memory or as file-based.
stats, err := s.storage.GetResourceStats(ctx, key.Namespace, 0) nsr := NamespacedResource{
Namespace: key.Namespace,
}
stats, err := s.storage.GetResourceStats(ctx, nsr, 0)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get resource stats: %w", err) return nil, fmt.Errorf("failed to get resource stats: %w", err)
} }
+1 -1
View File
@@ -88,7 +88,7 @@ type mockStorageBackend struct {
resourceStats []ResourceStats resourceStats []ResourceStats
} }
func (m *mockStorageBackend) GetResourceStats(ctx context.Context, namespace string, minCount int) ([]ResourceStats, error) { func (m *mockStorageBackend) GetResourceStats(ctx context.Context, nsr NamespacedResource, minCount int) ([]ResourceStats, error) {
var result []ResourceStats var result []ResourceStats
for _, stat := range m.resourceStats { for _, stat := range m.resourceStats {
// Apply the minCount filter like the real implementation does // Apply the minCount filter like the real implementation does
+1 -1
View File
@@ -123,7 +123,7 @@ type StorageBackend interface {
WatchWriteEvents(ctx context.Context) (<-chan *WrittenEvent, error) WatchWriteEvents(ctx context.Context) (<-chan *WrittenEvent, error)
// Get resource stats within the storage backend. When namespace is empty, it will apply to all // Get resource stats within the storage backend. When namespace is empty, it will apply to all
GetResourceStats(ctx context.Context, namespace string, minCount int) ([]ResourceStats, error) GetResourceStats(ctx context.Context, nsr NamespacedResource, minCount int) ([]ResourceStats, error)
// GetResourceLastImportTimes returns import times for all namespaced resources in the backend. // GetResourceLastImportTimes returns import times for all namespaced resources in the backend.
GetResourceLastImportTimes(ctx context.Context) iter.Seq2[ResourceLastImportTime, error] GetResourceLastImportTimes(ctx context.Context) iter.Seq2[ResourceLastImportTime, error]
@@ -1223,8 +1223,8 @@ func (k *kvStorageBackend) WatchWriteEvents(ctx context.Context) (<-chan *Writte
} }
// GetResourceStats returns resource stats within the storage backend. // GetResourceStats returns resource stats within the storage backend.
func (k *kvStorageBackend) GetResourceStats(ctx context.Context, namespace string, minCount int) ([]ResourceStats, error) { func (k *kvStorageBackend) GetResourceStats(ctx context.Context, nsr NamespacedResource, minCount int) ([]ResourceStats, error) {
return k.dataStore.GetResourceStats(ctx, namespace, minCount) return k.dataStore.GetResourceStats(ctx, nsr.Namespace, minCount)
} }
func (k *kvStorageBackend) GetResourceLastImportTimes(ctx context.Context) iter.Seq2[ResourceLastImportTime, error] { func (k *kvStorageBackend) GetResourceLastImportTimes(ctx context.Context) iter.Seq2[ResourceLastImportTime, error] {
@@ -1397,7 +1397,7 @@ func TestKvStorageBackend_GetResourceStats_Success(t *testing.T) {
} }
// Get stats for default namespace // Get stats for default namespace
stats, err := backend.GetResourceStats(ctx, "default", 0) stats, err := backend.GetResourceStats(ctx, NamespacedResource{Namespace: "default"}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, stats, 2) // Should have stats for 2 resource types in default namespace require.Len(t, stats, 2) // Should have stats for 2 resource types in default namespace
@@ -1413,12 +1413,12 @@ func TestKvStorageBackend_GetResourceStats_Success(t *testing.T) {
require.Equal(t, int64(1), resourceTypes["default/core/services"]) require.Equal(t, int64(1), resourceTypes["default/core/services"])
// Get stats for all namespaces (empty string) // Get stats for all namespaces (empty string)
allStats, err := backend.GetResourceStats(ctx, "", 0) allStats, err := backend.GetResourceStats(ctx, NamespacedResource{Namespace: ""}, 0)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, allStats, 4) // Should have stats for all 4 resource types across namespaces require.Len(t, allStats, 4) // Should have stats for all 4 resource types across namespaces
// Get stats with minCount filter // Get stats with minCount filter
filteredStats, err := backend.GetResourceStats(ctx, "", 1) filteredStats, err := backend.GetResourceStats(ctx, NamespacedResource{Namespace: ""}, 1)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, filteredStats, 1) // Only resources in default namespace has count > 1 require.Len(t, filteredStats, 1) // Only resources in default namespace has count > 1
+4 -2
View File
@@ -262,13 +262,15 @@ func (b *backend) Stop(_ context.Context) error {
} }
// GetResourceStats implements Backend. // GetResourceStats implements Backend.
func (b *backend) GetResourceStats(ctx context.Context, namespace string, minCount int) ([]resource.ResourceStats, error) { func (b *backend) GetResourceStats(ctx context.Context, nsr resource.NamespacedResource, minCount int) ([]resource.ResourceStats, error) {
ctx, span := b.tracer.Start(ctx, tracePrefix+"GetResourceStats") ctx, span := b.tracer.Start(ctx, tracePrefix+"GetResourceStats")
defer span.End() defer span.End()
req := &sqlStatsRequest{ req := &sqlStatsRequest{
SQLTemplate: sqltemplate.New(b.dialect), SQLTemplate: sqltemplate.New(b.dialect),
Namespace: namespace, Namespace: nsr.Namespace,
Group: nsr.Group,
Resource: nsr.Resource,
MinCount: minCount, // not used in query... yet? MinCount: minCount, // not used in query... yet?
} }
+16 -4
View File
@@ -262,7 +262,10 @@ func runTestIntegrationBackendGetResourceStats(t *testing.T, backend resource.St
require.NoError(t, err) require.NoError(t, err)
t.Run("Get stats for ns1", func(t *testing.T) { t.Run("Get stats for ns1", func(t *testing.T) {
stats, err := backend.GetResourceStats(ctx, nsPrefix+"-stats-ns1", 0) nsr := resource.NamespacedResource{
Namespace: nsPrefix + "-stats-ns1",
}
stats, err := backend.GetResourceStats(ctx, nsr, 0)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, stats, 2) require.Len(t, stats, 2)
@@ -285,7 +288,10 @@ func runTestIntegrationBackendGetResourceStats(t *testing.T, backend resource.St
}) })
t.Run("Get stats for ns2", func(t *testing.T) { t.Run("Get stats for ns2", func(t *testing.T) {
stats, err := backend.GetResourceStats(ctx, nsPrefix+"-stats-ns2", 0) nsr := resource.NamespacedResource{
Namespace: nsPrefix + "-stats-ns2",
}
stats, err := backend.GetResourceStats(ctx, nsr, 0)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, stats, 1) require.Len(t, stats, 1)
@@ -297,7 +303,10 @@ func runTestIntegrationBackendGetResourceStats(t *testing.T, backend resource.St
}) })
t.Run("Get stats with minimum count", func(t *testing.T) { t.Run("Get stats with minimum count", func(t *testing.T) {
stats, err := backend.GetResourceStats(ctx, nsPrefix+"-stats-ns1", 1) nsr := resource.NamespacedResource{
Namespace: nsPrefix + "-stats-ns1",
}
stats, err := backend.GetResourceStats(ctx, nsr, 1)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, stats, 1) require.Len(t, stats, 1)
@@ -308,7 +317,10 @@ func runTestIntegrationBackendGetResourceStats(t *testing.T, backend resource.St
}) })
t.Run("Get stats for non-existent namespace", func(t *testing.T) { t.Run("Get stats for non-existent namespace", func(t *testing.T) {
stats, err := backend.GetResourceStats(ctx, "non-existent", 0) nsr := resource.NamespacedResource{
Namespace: "non-existent",
}
stats, err := backend.GetResourceStats(ctx, nsr, 0)
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, stats) require.Empty(t, stats)
}) })