feat: inject unified data migrations in dual writer (#114138)

* feat: draft changes for on-prem unified migration

* feat: further draft changes for on-prem unified migration

* fix: remove some tbis

* refactor: rename

* fix: another approach

* fix: background service related issues

* fix: address comments

* fix: make gen-go

* fix: background service related issues

* feat: refactor dual writer and legacy migrator

* fix: minor issues

* feat: working version in oss

* fix: wire

* fix: revert test data override

* fix: enterprise related issues

* chore: add todo

* fix: revert dual writer method

* fix: lint

* chore: logger format

* fix: reduce log level

* fix: log change

* fix: disable

* fix: address comments

* fix: return error on dual writer service

* fix: merge conflict

---------

Co-authored-by: Rafael Paulovic <rafael.paulovic@grafana.com>
This commit is contained in:
Mustafa Sencer Özcan
2025-11-20 16:40:20 +01:00
committed by GitHub
co-authored by Rafael Paulovic
parent cb06bba243
commit 30c04ab3fc
40 changed files with 1779 additions and 1348 deletions
@@ -107,7 +107,7 @@ func NewDualWriter(
if currentMode != mode {
klog.Warningf("Requested DualWrite mode: %d, but using %d for %+v", mode, currentMode, gr)
}
return dualwrite.NewDualWriter(gr, currentMode, legacy, storage)
return dualwrite.NewStaticStorage(gr, currentMode, legacy, storage)
}
func getRequestInfo(gr schema.GroupResource, namespaceMapper request.NamespaceMapper) *k8srequest.RequestInfo {
+1 -1
View File
@@ -379,7 +379,7 @@ func InstallAPIs(
case grafanarest.Mode4, grafanarest.Mode5:
return storage, nil
default:
return dualwrite.NewDualWriter(gr, currentMode, legacy, storage)
return dualwrite.NewStaticStorage(gr, currentMode, legacy, storage)
}
}
}
+11 -6
View File
@@ -97,7 +97,7 @@ type service struct {
authorizer *authorizer.GrafanaAuthorizer
serverLockService builder.ServerLockService
storageStatus dualwrite.Service
dualWriter dualwrite.Service
kvStore kvstore.KVStore
pluginClient plugins.Client
@@ -127,7 +127,7 @@ func ProvideService(
datasources datasource.ScopedPluginDatasourceProvider,
contextProvider datasource.PluginContextWrapper,
pluginStore pluginstore.Store,
storageStatus dualwrite.Service,
dualWriter dualwrite.Service,
unified resource.ResourceClient,
secrets secret.InlineSecureValueSupport,
restConfigProvider RestConfigProvider,
@@ -158,7 +158,7 @@ func ProvideService(
contextProvider: contextProvider,
pluginStore: pluginStore,
serverLockService: serverLockService,
storageStatus: storageStatus,
dualWriter: dualWriter,
unified: unified,
secrets: secrets,
restConfigProvider: restConfigProvider,
@@ -385,12 +385,17 @@ func (s *service) start(ctx context.Context) error {
}
// Install the API group+version for existing builders
err = builder.InstallAPIs(s.scheme, s.codecs, server, serverConfig.RESTOptionsGetter, builders, o.StorageOptions,
err = builder.InstallAPIs(s.scheme,
s.codecs,
server,
serverConfig.RESTOptionsGetter,
builders,
o.StorageOptions,
s.metrics,
request.GetNamespaceMapper(s.cfg),
kvstore.WithNamespace(s.kvStore, 0, "storage.dualwriting"),
s.serverLockService,
s.storageStatus,
s.dualWriter,
optsregister,
s.features,
s.dualWriterMetrics,
@@ -409,7 +414,7 @@ func (s *service) start(ctx context.Context) error {
kvstore.WithNamespace(s.kvStore, 0, "storage.dualwriting"),
s.serverLockService,
request.GetNamespaceMapper(s.cfg),
s.storageStatus,
s.dualWriter,
s.dualWriterMetrics,
s.builderMetrics,
serverConfig.MergedResourceConfig,
+69
View File
@@ -0,0 +1,69 @@
package provisioning
import (
"os"
"path/filepath"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/provisioning/dashboards"
"github.com/grafana/grafana/pkg/setting"
)
type StubProvisioningService interface {
GetDashboardProvisionerResolvedPath(name string) string
GetAllowUIUpdatesFromConfig(name string) bool
}
func ProvideStubProvisioningService(cfg *setting.Cfg) (StubProvisioningService, error) {
return NewStubProvisioning(cfg.ProvisioningPath)
}
func NewStubProvisioning(path string) (StubProvisioningService, error) {
cfgs, err := dashboards.ReadDashboardConfig(filepath.Join(path, "dashboards"))
if err != nil {
return nil, err
}
stub := &stubProvisioning{
path: make(map[string]string),
allowUIUpdates: make(map[string]bool),
log: log.New("provisioning.stub"),
}
for _, cfg := range cfgs {
stub.path[cfg.Name] = cfg.Options["path"].(string)
stub.allowUIUpdates[cfg.Name] = cfg.AllowUIUpdates
}
return stub, nil
}
type stubProvisioning struct {
path map[string]string // name > options.path
allowUIUpdates map[string]bool
log log.Logger
}
func (s *stubProvisioning) GetAllowUIUpdatesFromConfig(name string) bool {
return s.allowUIUpdates[name]
}
func (s *stubProvisioning) GetDashboardProvisionerResolvedPath(name string) string {
path := s.path[name]
if _, err := os.Stat(path); os.IsNotExist(err) {
s.log.Warn("Cannot read directory", "error", err)
}
path, err := filepath.Abs(path)
if err != nil {
s.log.Warn("Could not create absolute path", "path", path, "error", err)
}
path, err = filepath.EvalSymlinks(path)
if err != nil {
s.log.Warn("Failed to read content of symlinked path", "path", path, "error", err)
}
if path == "" {
path = s.path[name]
s.log.Info("falling back to original path due to EvalSymlink/Abs failure")
}
return path
}