diff --git a/pkg/registry/apis/provisioning/register.go b/pkg/registry/apis/provisioning/register.go index 8b22e97a2bd..944739b1689 100644 --- a/pkg/registry/apis/provisioning/register.go +++ b/pkg/registry/apis/provisioning/register.go @@ -37,6 +37,7 @@ import ( client "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" informers "github.com/grafana/grafana/pkg/generated/informers/externalversions" listers "github.com/grafana/grafana/pkg/generated/listers/provisioning/v0alpha1" + "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy" "github.com/grafana/grafana/pkg/registry/apis/provisioning/controller" @@ -75,6 +76,7 @@ var ( type APIBuilder struct { features featuremgmt.FeatureToggles + tracer tracing.Tracer getter rest.Getter localFileResolver *local.LocalFolderResolver parsers resources.ParserFactory @@ -115,6 +117,7 @@ func NewAPIBuilder( storageStatus dualwrite.Service, secrets secrets.Service, access authlib.AccessChecker, + tracer tracing.Tracer, extraBuilders []ExtraBuilder, ) *APIBuilder { clients := resources.NewClientFactory(configProvider) @@ -122,6 +125,7 @@ func NewAPIBuilder( resourceLister := resources.NewResourceLister(unified, unified, legacyMigrator, storageStatus) b := &APIBuilder{ + tracer: tracer, localFileResolver: local, features: features, ghFactory: ghFactory, @@ -162,6 +166,7 @@ func RegisterAPIService( usageStatsService usagestats.Service, // FIXME: use multi-tenant service when one exists. In this state, we can't make this a multi-tenant service! secretsSvc grafanasecrets.Service, + tracer tracing.Tracer, extraBuilders []ExtraBuilder, ) (*APIBuilder, error) { if !features.IsEnabledGlobally(featuremgmt.FlagProvisioning) { @@ -178,6 +183,7 @@ func RegisterAPIService( configProvider, ghFactory, legacyMigrator, storageStatus, secrets.NewSingleTenant(secretsSvc), access, + tracer, extraBuilders, ) apiregistration.RegisterAPI(builder) diff --git a/pkg/registry/apis/provisioning/usage.go b/pkg/registry/apis/provisioning/usage.go index fa45de200dd..e6cf749c6f8 100644 --- a/pkg/registry/apis/provisioning/usage.go +++ b/pkg/registry/apis/provisioning/usage.go @@ -4,34 +4,61 @@ import ( "context" "fmt" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apiserver/pkg/endpoints/request" + "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/storage/unified/resourcepb" ) -func (b *APIBuilder) collectProvisioningStats(ctx context.Context) (map[string]any, error) { +func (b *APIBuilder) collectProvisioningStats(ctx context.Context) (metrics map[string]any, err error) { + // TODO: tracer + ctx, span := b.tracer.Start(ctx, "Provisioning.Usage.collectProvisioningStats") + defer func() { + if err != nil { + // TODO: log it as warning + } + + span.SetStatus(codes.Error, fmt.Sprintf("failed to fetch provisioning usage stats: %v", err)) + span.End() + }() + m := map[string]any{} if b.unified == nil { + span.SetStatus(codes.Ok, "unified storage is not available") return m, nil } + // FIXME: hardcoded to "default" for now -- it works for single tenant deployments + // we could discover the set of valid namespaces, but that would count everything for + // each instance in cloud. + ns := "default" + ctx, _, err = identity.WithProvisioningIdentity(context.Background(), ns) + if err != nil { + return nil, err + } + ctx = request.WithNamespace(ctx, ns) + // FIXME: hardcoded to "default" for now -- it works for single tenant deployments // we could discover the set of valid namespaces, but that would count everything for // each instance in cloud. // // We could get namespaces from the list of repos below, but that could be zero // while we still have resources managed by terraform, etc - ns := "default" count, err := b.unified.CountManagedObjects(ctx, &resourcepb.CountManagedObjectsRequest{ Namespace: ns, }) if err != nil { - return m, err + return m, fmt.Errorf("count managed objects: %w", err) } counts := make(map[string]int, 10) for _, v := range count.Items { counts[v.Kind] = counts[v.Kind] + int(v.Count) } + + span.SetAttributes(attribute.Int("totalManagedObjectsCount", len(count.Items))) for k, v := range counts { m[fmt.Sprintf("stats.managed_by.%s.count", k)] = v } @@ -39,13 +66,14 @@ func (b *APIBuilder) collectProvisioningStats(ctx context.Context) (map[string]a // Inspect all configs repos, err := b.repositoryLister.List(labels.Everything()) if err != nil { - return m, err + return m, fmt.Errorf("list repositories: %w", err) } clear(counts) for _, repo := range repos { counts[string(repo.Spec.Type)] = counts[string(repo.Spec.Type)] + 1 } + span.SetAttributes(attribute.Int("repositoryCount", len(repos))) // Count how many items of each repository type for k, v := range counts { m[fmt.Sprintf("stats.repository.%s.count", k)] = v