signoz/pkg/transition/migrate_dashboard.go
Srikanth Chekuri bd02848623
chore: add sql migration for dashboards, alerts, and saved views (#8642)
## 📄 Summary

To reliably migrate the alerts and dashboards, we need access to the telemetrystore to fetch some metadata and while doing migration, I need to log some stuff to fix stuff later.

Key changes:
- Modified the migration to include telemetrystore and a logging provider (open to using a standard logger instead)
- To avoid the previous issues with imported dashboards failing during migration, I've ensured that imported JSON files are automatically transformed when migration is active
- Implemented detailed logic to handle dashboard migration cleanly and prevent unnecessary errors
- Separated the core migration logic from SQL migration code, as users from the dot metrics migration requested shareable code snippets for local migrations. This modular approach allows others to easily reuse the migration functionality.

Known: I didn't register the migration yet in this PR, and will not merge this yet, so please review with that in mid.
2025-08-06 23:05:39 +05:30

100 lines
2.3 KiB
Go

// nolint
package transition
import (
"context"
"log/slog"
"strings"
)
type dashboardMigrateV5 struct {
migrateCommon
}
func NewDashboardMigrateV5(logger *slog.Logger, logsDuplicateKeys []string, tracesDuplicateKeys []string) *dashboardMigrateV5 {
ambiguity := map[string][]string{
"logs": logsDuplicateKeys,
"traces": tracesDuplicateKeys,
}
return &dashboardMigrateV5{
migrateCommon: migrateCommon{
ambiguity: ambiguity,
logger: logger,
},
}
}
func (m *dashboardMigrateV5) Migrate(ctx context.Context, dashboardData map[string]any) bool {
updated := false
var version string
if _, ok := dashboardData["version"].(string); ok {
version = dashboardData["version"].(string)
}
if version == "v5" {
m.logger.InfoContext(ctx, "dashboard is already migrated to v5, skipping", "dashboard_name", dashboardData["title"])
return false
}
// if there is a white space in variable, replace it
if variables, ok := dashboardData["variables"].(map[string]any); ok {
for _, variable := range variables {
if varMap, ok := variable.(map[string]any); ok {
name, ok := varMap["name"].(string)
if ok {
if strings.Contains(name, " ") {
m.logger.InfoContext(ctx, "found a variable with space in map, replacing it", "name", name)
name = strings.ReplaceAll(name, " ", "")
updated = true
varMap["name"] = name
}
}
}
}
}
if widgets, ok := dashboardData["widgets"].([]any); ok {
for _, widget := range widgets {
if widgetMap, ok := widget.(map[string]any); ok {
if m.updateWidget(ctx, widgetMap, version) {
updated = true
}
}
}
}
dashboardData["version"] = "v5"
return updated
}
func (migration *dashboardMigrateV5) updateWidget(ctx context.Context, widget map[string]any, version string) bool {
query, ok := widget["query"].(map[string]any)
if !ok {
return false
}
builder, ok := query["builder"].(map[string]any)
if !ok {
return false
}
queryData, ok := builder["queryData"].([]any)
if !ok {
return false
}
widgetType := widget["panelTypes"].(string)
updated := false
for _, qd := range queryData {
if queryDataMap, ok := qd.(map[string]any); ok {
if migration.updateQueryData(ctx, queryDataMap, version, widgetType) {
updated = true
}
}
}
return updated
}