| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | package unified | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2024-09-19 22:16:48 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							|  |  |  | 	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" | 
					
						
							|  |  |  | 	"gocloud.dev/blob/fileblob" | 
					
						
							|  |  |  | 	"google.golang.org/grpc" | 
					
						
							|  |  |  | 	"google.golang.org/grpc/credentials/insecure" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	authnlib "github.com/grafana/authlib/authn" | 
					
						
							| 
									
										
										
										
											2025-01-21 17:06:55 +08:00
										 |  |  | 	"github.com/grafana/authlib/types" | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 	infraDB "github.com/grafana/grafana/pkg/infra/db" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/infra/tracing" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/apiserver/options" | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/authn/grpcutils" | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/featuremgmt" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2024-12-11 19:46:31 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/storage/legacysql" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/storage/unified/federated" | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/storage/unified/resource" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/storage/unified/sql" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | const resourceStoreAudience = "resourceStore" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-22 12:45:59 +08:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	// internal provider of the package level resource client
 | 
					
						
							|  |  |  | 	pkgResourceClient resource.ResourceClient | 
					
						
							|  |  |  | 	ready             = make(chan struct{}) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func GetResourceClient(ctx context.Context) resource.ResourceClient { | 
					
						
							|  |  |  | 	<-ready | 
					
						
							|  |  |  | 	return pkgResourceClient | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | // This adds a UnifiedStorage client into the wire dependency tree
 | 
					
						
							|  |  |  | func ProvideUnifiedStorageClient( | 
					
						
							|  |  |  | 	cfg *setting.Cfg, | 
					
						
							|  |  |  | 	features featuremgmt.FeatureToggles, | 
					
						
							|  |  |  | 	db infraDB.DB, | 
					
						
							|  |  |  | 	tracer tracing.Tracer, | 
					
						
							| 
									
										
										
										
											2024-10-17 18:18:29 +08:00
										 |  |  | 	reg prometheus.Registerer, | 
					
						
							| 
									
										
										
										
											2025-01-21 17:06:55 +08:00
										 |  |  | 	authzc types.AccessClient, | 
					
						
							| 
									
										
										
										
											2024-11-21 13:53:25 +08:00
										 |  |  | 	docs resource.DocumentBuilderSupplier, | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | ) (resource.ResourceClient, error) { | 
					
						
							|  |  |  | 	// See: apiserver.ApplyGrafanaConfig(cfg, features, o)
 | 
					
						
							|  |  |  | 	apiserverCfg := cfg.SectionWithEnvOverrides("grafana-apiserver") | 
					
						
							| 
									
										
										
										
											2024-12-11 19:46:31 +08:00
										 |  |  | 	client, err := newClient(options.StorageOptions{ | 
					
						
							| 
									
										
										
										
											2024-12-17 22:00:35 +08:00
										 |  |  | 		StorageType:  options.StorageType(apiserverCfg.Key("storage_type").MustString(string(options.StorageTypeUnified))), | 
					
						
							| 
									
										
										
										
											2024-10-17 18:18:29 +08:00
										 |  |  | 		DataPath:     apiserverCfg.Key("storage_path").MustString(filepath.Join(cfg.DataPath, "grafana-apiserver")), | 
					
						
							|  |  |  | 		Address:      apiserverCfg.Key("address").MustString(""), // client address
 | 
					
						
							|  |  |  | 		BlobStoreURL: apiserverCfg.Key("blob_url").MustString(""), | 
					
						
							| 
									
										
										
										
											2024-12-11 19:46:31 +08:00
										 |  |  | 	}, cfg, features, db, tracer, reg, authzc, docs) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		// Used to get the folder stats
 | 
					
						
							|  |  |  | 		client = federated.NewFederatedClient( | 
					
						
							|  |  |  | 			client, // The original
 | 
					
						
							|  |  |  | 			legacysql.NewDatabaseProvider(db), | 
					
						
							|  |  |  | 		) | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-01-22 12:45:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// only set the package level restConfig once
 | 
					
						
							|  |  |  | 	if pkgResourceClient == nil { | 
					
						
							|  |  |  | 		pkgResourceClient = client | 
					
						
							|  |  |  | 		close(ready) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-11 19:46:31 +08:00
										 |  |  | 	return client, err | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-11 19:46:31 +08:00
										 |  |  | func newClient(opts options.StorageOptions, | 
					
						
							|  |  |  | 	cfg *setting.Cfg, | 
					
						
							|  |  |  | 	features featuremgmt.FeatureToggles, | 
					
						
							|  |  |  | 	db infraDB.DB, | 
					
						
							|  |  |  | 	tracer tracing.Tracer, | 
					
						
							|  |  |  | 	reg prometheus.Registerer, | 
					
						
							| 
									
										
										
										
											2025-01-21 17:06:55 +08:00
										 |  |  | 	authzc types.AccessClient, | 
					
						
							| 
									
										
										
										
											2024-12-11 19:46:31 +08:00
										 |  |  | 	docs resource.DocumentBuilderSupplier, | 
					
						
							|  |  |  | ) (resource.ResourceClient, error) { | 
					
						
							|  |  |  | 	ctx := context.Background() | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 	switch opts.StorageType { | 
					
						
							|  |  |  | 	case options.StorageTypeFile: | 
					
						
							|  |  |  | 		if opts.DataPath == "" { | 
					
						
							|  |  |  | 			opts.DataPath = filepath.Join(cfg.DataPath, "grafana-apiserver") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		bucket, err := fileblob.OpenBucket(filepath.Join(opts.DataPath, "resource"), &fileblob.Options{ | 
					
						
							|  |  |  | 			CreateDir: true, | 
					
						
							|  |  |  | 			Metadata:  fileblob.MetadataDontWrite, // skip
 | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-10-08 21:43:23 +08:00
										 |  |  | 		backend, err := resource.NewCDKBackend(ctx, resource.CDKBackendOptions{ | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 			Bucket: bucket, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		server, err := resource.NewResourceServer(resource.ResourceServerOptions{ | 
					
						
							|  |  |  | 			Backend: backend, | 
					
						
							| 
									
										
										
										
											2024-10-17 18:18:29 +08:00
										 |  |  | 			Blob: resource.BlobConfig{ | 
					
						
							|  |  |  | 				URL: opts.BlobStoreURL, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return resource.NewLocalResourceClient(server), nil | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case options.StorageTypeUnifiedGrpc: | 
					
						
							| 
									
										
										
										
											2024-09-19 22:16:48 +08:00
										 |  |  | 		if opts.Address == "" { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("expecting address for storage_type: %s", opts.StorageType) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 		// Create a connection to the gRPC server
 | 
					
						
							|  |  |  | 		conn, err := grpc.NewClient(opts.Address, | 
					
						
							|  |  |  | 			grpc.WithStatsHandler(otelgrpc.NewClientHandler()), | 
					
						
							|  |  |  | 			grpc.WithTransportCredentials(insecure.NewCredentials()), | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Create a client instance
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:35:30 +08:00
										 |  |  | 		client, err := newResourceClient(conn, cfg, features, tracer) | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return client, nil | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Use the local SQL
 | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2024-11-21 13:53:25 +08:00
										 |  |  | 		server, err := sql.NewResourceServer(ctx, db, cfg, features, docs, tracer, reg, authzc) | 
					
						
							| 
									
										
										
										
											2024-09-12 22:22:27 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return resource.NewLocalResourceClient(server), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func clientCfgMapping(clientCfg *grpcutils.GrpcClientConfig) authnlib.GrpcClientConfig { | 
					
						
							|  |  |  | 	return authnlib.GrpcClientConfig{ | 
					
						
							|  |  |  | 		TokenClientConfig: &authnlib.TokenExchangeConfig{ | 
					
						
							|  |  |  | 			Token:            clientCfg.Token, | 
					
						
							|  |  |  | 			TokenExchangeURL: clientCfg.TokenExchangeURL, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		TokenRequest: &authnlib.TokenExchangeRequest{ | 
					
						
							|  |  |  | 			Namespace: clientCfg.TokenNamespace, | 
					
						
							|  |  |  | 			Audiences: []string{resourceStoreAudience}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:35:30 +08:00
										 |  |  | func newResourceClient(conn *grpc.ClientConn, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer) (resource.ResourceClient, error) { | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | 	if !features.IsEnabledGlobally(featuremgmt.FlagAppPlatformGrpcClientAuth) { | 
					
						
							|  |  |  | 		return resource.NewLegacyResourceClient(conn), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if cfg.StackID == "" { | 
					
						
							| 
									
										
										
										
											2024-10-28 20:35:30 +08:00
										 |  |  | 		return resource.NewGRPCResourceClient(tracer, conn) | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	grpcClientCfg := grpcutils.ReadGrpcClientConfig(cfg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:35:30 +08:00
										 |  |  | 	return resource.NewCloudResourceClient(tracer, conn, clientCfgMapping(grpcClientCfg), cfg.Env == setting.Dev) | 
					
						
							| 
									
										
										
										
											2024-10-24 15:12:37 +08:00
										 |  |  | } |