Storage: Add blob storage interfaces (#90932)

Co-authored-by: Jean-Philippe Quémémer <jeanphilippe.quemener@grafana.com>
This commit is contained in:
Ryan McKinley
2024-10-17 12:18:29 +02:00
committed by GitHub
co-authored by Jean-Philippe Quémémer
parent 1b8b1d6c7a
commit 3457f219be
37 changed files with 1891 additions and 177 deletions
@@ -41,7 +41,7 @@ func TestAggregatorPostStartHooks(t *testing.T) {
cfg.GenericConfig.SharedInformerFactory = informers.NewSharedInformerFactory(fake.NewSimpleClientset(), 10*time.Minute)
// override the RESTOptionsGetter to use the in memory storage options
restOptionsGetter, err := apistore.NewRESTOptionsGetterMemory(*storagebackend.NewDefaultConfig("memory", nil))
restOptionsGetter, err := apistore.NewRESTOptionsGetterMemory(*storagebackend.NewDefaultConfig("memory", nil), make(map[string]any))
require.NoError(t, err)
cfg.GenericConfig.RESTOptionsGetter = restOptionsGetter
+2
View File
@@ -7,6 +7,7 @@ import (
"regexp"
"time"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/prometheus/client_golang/prometheus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -165,6 +166,7 @@ func InstallAPIs(
namespaceMapper request.NamespaceMapper,
kvStore grafanarest.NamespacedKVStore,
serverLock ServerLockService,
features featuremgmt.FeatureToggles,
) error {
// dual writing is only enabled when the storage type is not legacy.
// this is needed to support setting a default RESTOptionsGetter for new APIs that don't
+1
View File
@@ -53,6 +53,7 @@ func applyGrafanaConfig(cfg *setting.Cfg, features featuremgmt.FeatureToggles, o
o.StorageOptions.StorageType = options.StorageType(apiserverCfg.Key("storage_type").MustString(string(options.StorageTypeLegacy)))
o.StorageOptions.DataPath = apiserverCfg.Key("storage_path").MustString(filepath.Join(cfg.DataPath, "grafana-apiserver"))
o.StorageOptions.Address = apiserverCfg.Key("address").MustString(o.StorageOptions.Address)
o.StorageOptions.BlobStoreURL = apiserverCfg.Key("blob_url").MustString(o.StorageOptions.BlobStoreURL)
// unified storage configs look like
// [unified_storage.<group>.<resource>]
@@ -74,7 +74,7 @@ func (o *GrafanaAggregatorOptions) ApplyTo(aggregatorConfig *aggregatorapiserver
return err
}
// override the RESTOptionsGetter to use the in memory storage options
restOptionsGetter, err := apistore.NewRESTOptionsGetterMemory(etcdOptions.StorageConfig)
restOptionsGetter, err := apistore.NewRESTOptionsGetterMemory(etcdOptions.StorageConfig, make(map[string]any))
if err != nil {
return err
}
@@ -81,7 +81,7 @@ func (o *KubeAggregatorOptions) ApplyTo(aggregatorConfig *aggregatorapiserver.Co
return err
}
// override the RESTOptionsGetter to use the in memory storage options
restOptionsGetter, err := apistore.NewRESTOptionsGetterMemory(etcdOptions.StorageConfig)
restOptionsGetter, err := apistore.NewRESTOptionsGetterMemory(etcdOptions.StorageConfig, make(map[string]any))
if err != nil {
return err
}
+12
View File
@@ -30,6 +30,13 @@ type StorageOptions struct { // The desired storage type
// For file storage, this is the requested path
DataPath string
// Optional blob storage connection string
// file:///path/to/dir
// gs://my-bucket (using default credentials)
// s3://my-bucket?region=us-west-1 (using default credentials)
// azblob://my-container
BlobStoreURL string
// {resource}.{group} = 1|2|3|4
UnifiedStorageConfig map[string]setting.UnifiedStorageConfig
}
@@ -59,6 +66,11 @@ func (o *StorageOptions) Validate() []error {
if _, _, err := net.SplitHostPort(o.Address); err != nil {
errs = append(errs, fmt.Errorf("--grafana-apiserver-storage-address must be a valid network address: %v", err))
}
// Only works for single tenant grafana right now
if o.BlobStoreURL != "" && o.StorageType != StorageTypeUnified {
errs = append(errs, fmt.Errorf("blob storage is only valid with unified storage"))
}
return errs
}
+7 -3
View File
@@ -160,7 +160,6 @@ func ProvideService(
serverLockService: serverLockService,
unified: unified,
}
// This will be used when running as a dskit service
s.BasicService = services.NewBasicService(s.start, s.running, nil).WithName(modules.GrafanaAPIServer)
@@ -290,9 +289,14 @@ func (s *service) start(ctx context.Context) error {
return err
}
} else {
// This is needed as the apistore doesn't allow any core grafana dependencies.
features := make(map[string]any)
if s.features.IsEnabled(context.Background(), featuremgmt.FlagUnifiedStorageBigObjectsSupport) {
features[featuremgmt.FlagUnifiedStorageBigObjectsSupport] = struct{}{}
}
// Use unified storage client
serverConfig.Config.RESTOptionsGetter = apistore.NewRESTOptionsGetterForClient(
s.unified, o.RecommendedOptions.Etcd.StorageConfig)
s.unified, o.RecommendedOptions.Etcd.StorageConfig, features)
}
// Add OpenAPI specs for each group+version
@@ -319,7 +323,7 @@ func (s *service) start(ctx context.Context) error {
// Install the API group+version
err = builder.InstallAPIs(Scheme, Codecs, server, serverConfig.RESTOptionsGetter, builders, o.StorageOptions,
// Required for the dual writer initialization
s.metrics, request.GetNamespaceMapper(s.cfg), kvstore.WithNamespace(s.kvStore, 0, "storage.dualwriting"), s.serverLockService,
s.metrics, request.GetNamespaceMapper(s.cfg), kvstore.WithNamespace(s.kvStore, 0, "storage.dualwriting"), s.serverLockService, s.features,
)
if err != nil {
return err
+6
View File
@@ -1516,6 +1516,12 @@ var (
Stage: FeatureStageExperimental,
Owner: grafanaPluginsPlatformSquad,
},
{
Name: "unifiedStorageBigObjectsSupport",
Description: "Enables to save big objects in blob storage",
Stage: FeatureStageExperimental,
Owner: grafanaSearchAndStorageSquad,
},
}
)
+1
View File
@@ -201,3 +201,4 @@ useSessionStorageForRedirection,preview,@grafana/identity-access-team,false,fals
rolePickerDrawer,experimental,@grafana/identity-access-team,false,false,false
unifiedStorageSearch,experimental,@grafana/search-and-storage,false,false,false
pluginsSriChecks,experimental,@grafana/plugins-platform-backend,false,false,false
unifiedStorageBigObjectsSupport,experimental,@grafana/search-and-storage,false,false,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
201 rolePickerDrawer experimental @grafana/identity-access-team false false false
202 unifiedStorageSearch experimental @grafana/search-and-storage false false false
203 pluginsSriChecks experimental @grafana/plugins-platform-backend false false false
204 unifiedStorageBigObjectsSupport experimental @grafana/search-and-storage false false false
+4
View File
@@ -814,4 +814,8 @@ const (
// FlagPluginsSriChecks
// Enables SRI checks for plugin assets
FlagPluginsSriChecks = "pluginsSriChecks"
// FlagUnifiedStorageBigObjectsSupport
// Enables to save big objects in blob storage
FlagUnifiedStorageBigObjectsSupport = "unifiedStorageBigObjectsSupport"
)
+25
View File
@@ -3080,6 +3080,31 @@
"requiresRestart": true
}
},
{
"metadata": {
"name": "unifiedStorageBigObjectSupport",
"resourceVersion": "1728561321640",
"creationTimestamp": "2024-10-10T11:55:21Z",
"deletionTimestamp": "2024-10-15T12:09:18Z"
},
"spec": {
"description": "Enables to save big objects in blob storage",
"stage": "experimental",
"codeowner": "@grafana/search-and-storage"
}
},
{
"metadata": {
"name": "unifiedStorageBigObjectsSupport",
"resourceVersion": "1728994158474",
"creationTimestamp": "2024-10-15T12:09:18Z"
},
"spec": {
"description": "Enables to save big objects in blob storage",
"stage": "experimental",
"codeowner": "@grafana/search-and-storage"
}
},
{
"metadata": {
"name": "unifiedStorageSearch",