feat: add unified storage data migration step for playlists (#114582)

* fix: add type

* feat: register step

* feat: add playlist support

* test: add test case

* fix: gen mock

* fix: go gen

* fix: lint

* fix: lint

* fix: tests

* fix: add resource

* fix: readd

* fix: address comments

* fix: independent playlist query for migrations

* fix: remove lock logic for sqlite

* fix: handle creation and update datetimes

* fix: query templating

* fix: simply resources and address comments
This commit is contained in:
Mustafa Sencer Özcan
2025-12-04 15:15:00 +00:00
committed by GitHub
parent e5259c2ad4
commit 4c5d9cb95f
16 changed files with 641 additions and 105 deletions
+18 -64
View File
@@ -10,8 +10,6 @@ import (
authlib "github.com/grafana/authlib/types"
v1beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
)
@@ -35,39 +33,26 @@ type streamProvider interface {
createStream(ctx context.Context, opts legacy.MigrateOptions) (resourcepb.BulkStore_BulkProcessClient, error)
}
// resourceClientStreamProvider creates streams using resource.ResourceClient
type resourceClientStreamProvider struct {
client resource.ResourceClient
}
func (r *resourceClientStreamProvider) createStream(ctx context.Context, opts legacy.MigrateOptions) (resourcepb.BulkStore_BulkProcessClient, error) {
// Build collection settings for resource client
func buildCollectionSettings(opts legacy.MigrateOptions) resource.BulkSettings {
settings := resource.BulkSettings{
RebuildCollection: true,
SkipValidation: true,
}
for _, res := range opts.Resources {
switch fmt.Sprintf("%s/%s", res.Group, res.Resource) {
case "folder.grafana.app/folders":
settings.Collection = append(settings.Collection, &resourcepb.ResourceKey{
Namespace: opts.Namespace,
Group: folders.GROUP,
Resource: folders.RESOURCE,
})
case "dashboard.grafana.app/librarypanels":
settings.Collection = append(settings.Collection, &resourcepb.ResourceKey{
Namespace: opts.Namespace,
Group: v1beta1.GROUP,
Resource: v1beta1.LIBRARY_PANEL_RESOURCE,
})
case "dashboard.grafana.app/dashboards":
settings.Collection = append(settings.Collection, &resourcepb.ResourceKey{
Namespace: opts.Namespace,
Group: v1beta1.GROUP,
Resource: v1beta1.DASHBOARD_RESOURCE,
})
key := buildResourceKey(res.Group, res.Resource, opts.Namespace)
if key != nil {
settings.Collection = append(settings.Collection, key)
}
}
return settings
}
type resourceClientStreamProvider struct {
client resource.ResourceClient
}
func (r *resourceClientStreamProvider) createStream(ctx context.Context, opts legacy.MigrateOptions) (resourcepb.BulkStore_BulkProcessClient, error) {
settings := buildCollectionSettings(opts)
ctx = metadata.NewOutgoingContext(ctx, settings.ToMD())
return r.client.BulkProcess(ctx)
}
@@ -78,33 +63,7 @@ type bulkStoreClientStreamProvider struct {
}
func (b *bulkStoreClientStreamProvider) createStream(ctx context.Context, opts legacy.MigrateOptions) (resourcepb.BulkStore_BulkProcessClient, error) {
// Build collection settings for resource client
settings := resource.BulkSettings{
RebuildCollection: true,
SkipValidation: true,
}
for _, res := range opts.Resources {
switch fmt.Sprintf("%s/%s", res.Group, res.Resource) {
case "folder.grafana.app/folders":
settings.Collection = append(settings.Collection, &resourcepb.ResourceKey{
Namespace: opts.Namespace,
Group: folders.GROUP,
Resource: folders.RESOURCE,
})
case "dashboard.grafana.app/librarypanels":
settings.Collection = append(settings.Collection, &resourcepb.ResourceKey{
Namespace: opts.Namespace,
Group: v1beta1.GROUP,
Resource: v1beta1.LIBRARY_PANEL_RESOURCE,
})
case "dashboard.grafana.app/dashboards":
settings.Collection = append(settings.Collection, &resourcepb.ResourceKey{
Namespace: opts.Namespace,
Group: v1beta1.GROUP,
Resource: v1beta1.DASHBOARD_RESOURCE,
})
}
}
settings := buildCollectionSettings(opts)
ctx = metadata.NewOutgoingContext(ctx, settings.ToMD())
return b.client.BulkProcess(ctx)
}
@@ -170,16 +129,11 @@ func (m *unifiedMigration) Migrate(ctx context.Context, opts legacy.MigrateOptio
migratorFuncs := []migratorFunc{}
for _, res := range opts.Resources {
switch fmt.Sprintf("%s/%s", res.Group, res.Resource) {
case "folder.grafana.app/folders":
migratorFuncs = append(migratorFuncs, m.MigrateFolders)
case "dashboard.grafana.app/librarypanels":
migratorFuncs = append(migratorFuncs, m.MigrateLibraryPanels)
case "dashboard.grafana.app/dashboards":
migratorFuncs = append(migratorFuncs, m.MigrateDashboards)
default:
return nil, fmt.Errorf("unsupported resource: %s", res)
fn := getMigratorFunc(m.MigrationDashboardAccessor, res.Group, res.Resource)
if fn == nil {
return nil, fmt.Errorf("unsupported resource: %s/%s", res.Group, res.Resource)
}
migratorFuncs = append(migratorFuncs, fn)
}
// Execute migrations