diff --git a/pkg/query-service/app/dashboards/model.go b/pkg/query-service/app/dashboards/model.go index 39cf835bad..56adf9aae7 100644 --- a/pkg/query-service/app/dashboards/model.go +++ b/pkg/query-service/app/dashboards/model.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "reflect" + "regexp" "strconv" "strings" "time" @@ -20,6 +21,14 @@ import ( // This time the global variable is unexported. var db *sqlx.DB +// User for mapping job,instance from grafana +var instanceEQRE = regexp.MustCompile("instance(?s)=(?s)\\\"{{.instance}}\\\"") +var nodeEQRE = regexp.MustCompile("instance(?s)=(?s)\\\"{{.node}}\\\"") +var jobEQRE = regexp.MustCompile("job(?s)=(?s)\\\"{{.job}}\\\"") +var instanceRERE = regexp.MustCompile("instance(?s)=~(?s)\\\"{{.instance}}\\\"") +var nodeRERE = regexp.MustCompile("instance(?s)=~(?s)\\\"{{.node}}\\\"") +var jobRERE = regexp.MustCompile("job(?s)=~(?s)\\\"{{.job}}\\\"") + // InitDB sets up setting up the connection pool global variable. func InitDB(dataSourceName string) (*sqlx.DB, error) { var err error @@ -264,6 +273,78 @@ func SlugifyTitle(title string) string { return s } +func widgetFromPanel(panel model.Panels, idx int, variables map[string]model.Variable) *model.Widget { + widget := model.Widget{ + Description: panel.Description, + ID: strconv.Itoa(idx), + IsStacked: false, + NullZeroValues: "zero", + Opacity: "1", + PanelTypes: "TIME_SERIES", // TODO: Need to figure out how to get this + Query: model.Query{ + ClickHouse: []model.ClickHouseQueryDashboard{ + { + Disabled: false, + Legend: "", + Name: "A", + Query: "", + }, + }, + MetricsBuilder: model.MetricsBuilder{ + Formulas: []string{}, + QueryBuilder: []model.QueryBuilder{ + { + AggregateOperator: 1, + Disabled: false, + GroupBy: []string{}, + Legend: "", + MetricName: "", + Name: "A", + ReduceTo: 1, + }, + }, + }, + PromQL: []model.PromQueryDashboard{}, + QueryType: int(model.PROM), + }, + QueryData: model.QueryDataDashboard{ + Data: model.Data{ + QueryData: []interface{}{}, + }, + }, + Title: panel.Title, + YAxisUnit: panel.FieldConfig.Defaults.Unit, + QueryType: int(model.PROM), // TODO: Supprot for multiple query types + } + for _, target := range panel.Targets { + if target.Expr != "" { + for name := range variables { + target.Expr = strings.ReplaceAll(target.Expr, "$"+name, "{{"+"."+name+"}}") + target.Expr = strings.ReplaceAll(target.Expr, "$"+"__rate_interval", "5m") + } + + // prometheus receiver in collector maps job,instance as service_name,service_instance_id + target.Expr = instanceEQRE.ReplaceAllString(target.Expr, "service_instance_id=\"{{.instance}}\"") + target.Expr = nodeEQRE.ReplaceAllString(target.Expr, "service_instance_id=\"{{.node}}\"") + target.Expr = jobEQRE.ReplaceAllString(target.Expr, "service_name=\"{{.job}}\"") + target.Expr = instanceRERE.ReplaceAllString(target.Expr, "service_instance_id=~\"{{.instance}}\"") + target.Expr = nodeRERE.ReplaceAllString(target.Expr, "service_instance_id=~\"{{.node}}\"") + target.Expr = jobRERE.ReplaceAllString(target.Expr, "service_name=~\"{{.job}}\"") + + widget.Query.PromQL = append( + widget.Query.PromQL, + model.PromQueryDashboard{ + Disabled: false, + Legend: target.LegendFormat, + Name: target.RefID, + Query: target.Expr, + }, + ) + } + } + return &widget +} + func TransformGrafanaJSONToSignoz(grafanaJSON model.GrafanaJSON) model.DashboardData { var toReturn model.DashboardData toReturn.Title = grafanaJSON.Title @@ -350,7 +431,31 @@ func TransformGrafanaJSONToSignoz(grafanaJSON model.GrafanaJSON) model.Dashboard } row := 0 - for idx, panel := range grafanaJSON.Panels { + idx := 0 + for _, panel := range grafanaJSON.Panels { + if panel.Type == "row" { + if panel.Panels != nil && len(panel.Panels) > 0 { + for _, innerPanel := range panel.Panels { + if idx%3 == 0 { + row++ + } + toReturn.Layout = append( + toReturn.Layout, + model.Layout{ + X: idx % 3 * 4, + Y: row * 3, + W: 4, + H: 3, + I: strconv.Itoa(idx), + }, + ) + + toReturn.Widgets = append(toReturn.Widgets, *widgetFromPanel(innerPanel, idx, toReturn.Variables)) + idx++ + } + } + continue + } if panel.Datasource == nil { zap.S().Warnf("Skipping panel %d as it has no datasource", idx) continue @@ -395,67 +500,8 @@ func TransformGrafanaJSONToSignoz(grafanaJSON model.GrafanaJSON) model.Dashboard }, ) - widget := model.Widget{ - Description: panel.Description, - ID: strconv.Itoa(idx), - IsStacked: false, - NullZeroValues: "zero", - Opacity: "1", - PanelTypes: "TIME_SERIES", // TODO: Need to figure out how to get this - Query: model.Query{ - ClickHouse: []model.ClickHouseQueryDashboard{ - { - Disabled: false, - Legend: "", - Name: "A", - Query: "", - }, - }, - MetricsBuilder: model.MetricsBuilder{ - Formulas: []string{}, - QueryBuilder: []model.QueryBuilder{ - { - AggregateOperator: 1, - Disabled: false, - GroupBy: []string{}, - Legend: "", - MetricName: "", - Name: "A", - ReduceTo: 1, - }, - }, - }, - PromQL: []model.PromQueryDashboard{}, - QueryType: int(model.PROM), - }, - QueryData: model.QueryDataDashboard{ - Data: model.Data{ - QueryData: []interface{}{}, - }, - }, - Title: panel.Title, - YAxisUnit: panel.FieldConfig.Defaults.Unit, - QueryType: int(model.PROM), // TODO: Supprot for multiple query types - } - for _, target := range panel.Targets { - if target.Expr != "" { - for name := range toReturn.Variables { - target.Expr = strings.ReplaceAll(target.Expr, "$"+name, "{{"+"."+name+"}}") - target.Expr = strings.ReplaceAll(target.Expr, "$"+"__rate_interval", "5m") - } - widget.Query.PromQL = append( - widget.Query.PromQL, - model.PromQueryDashboard{ - Disabled: false, - Legend: target.LegendFormat, - Name: target.RefID, - Query: target.Expr, - }, - ) - } - } - - toReturn.Widgets = append(toReturn.Widgets, widget) + toReturn.Widgets = append(toReturn.Widgets, *widgetFromPanel(panel, idx, toReturn.Variables)) + idx++ } return toReturn } diff --git a/pkg/query-service/model/dashboards.go b/pkg/query-service/model/dashboards.go index 74e3a8c886..f897eeb7fb 100644 --- a/pkg/query-service/model/dashboards.go +++ b/pkg/query-service/model/dashboards.go @@ -5,6 +5,65 @@ type Datasource struct { UID string `json:"uid"` } +type Panels struct { + Datasource interface{} `json:"datasource"` + Description string `json:"description,omitempty"` + FieldConfig struct { + Defaults struct { + Color struct { + Mode string `json:"mode"` + } `json:"color"` + Max float64 `json:"max"` + Min float64 `json:"min"` + Thresholds struct { + Mode string `json:"mode"` + Steps []struct { + Color string `json:"color"` + Value interface{} `json:"value"` + } `json:"steps"` + } `json:"thresholds"` + Unit string `json:"unit"` + } `json:"defaults"` + Overrides []interface{} `json:"overrides"` + } `json:"fieldConfig,omitempty"` + GridPos struct { + H int `json:"h"` + W int `json:"w"` + X int `json:"x"` + Y int `json:"y"` + } `json:"gridPos"` + ID int `json:"id"` + Links []interface{} `json:"links,omitempty"` + Options struct { + Orientation string `json:"orientation"` + ReduceOptions struct { + Calcs []string `json:"calcs"` + Fields string `json:"fields"` + Values bool `json:"values"` + } `json:"reduceOptions"` + ShowThresholdLabels bool `json:"showThresholdLabels"` + ShowThresholdMarkers bool `json:"showThresholdMarkers"` + } `json:"options,omitempty"` + PluginVersion string `json:"pluginVersion,omitempty"` + Targets []struct { + Datasource interface{} `json:"datasource"` + EditorMode string `json:"editorMode"` + Expr string `json:"expr"` + Hide bool `json:"hide"` + IntervalFactor int `json:"intervalFactor"` + LegendFormat string `json:"legendFormat"` + Range bool `json:"range"` + RefID string `json:"refId"` + Step int `json:"step"` + } `json:"targets"` + Title string `json:"title"` + Type string `json:"type"` + HideTimeOverride bool `json:"hideTimeOverride,omitempty"` + MaxDataPoints int `json:"maxDataPoints,omitempty"` + Collapsed bool `json:"collapsed,omitempty"` + Panels []Panels `json:"panels,omitempty"` +} + type GrafanaJSON struct { Inputs []struct { Name string `json:"name"` @@ -51,65 +110,8 @@ type GrafanaJSON struct { Type string `json:"type"` URL string `json:"url"` } `json:"links"` - LiveNow bool `json:"liveNow"` - Panels []struct { - Datasource interface{} `json:"datasource"` - Description string `json:"description,omitempty"` - FieldConfig struct { - Defaults struct { - Color struct { - Mode string `json:"mode"` - } `json:"color"` - Max float64 `json:"max"` - Min float64 `json:"min"` - Thresholds struct { - Mode string `json:"mode"` - Steps []struct { - Color string `json:"color"` - Value interface{} `json:"value"` - } `json:"steps"` - } `json:"thresholds"` - Unit string `json:"unit"` - } `json:"defaults"` - Overrides []interface{} `json:"overrides"` - } `json:"fieldConfig,omitempty"` - GridPos struct { - H int `json:"h"` - W int `json:"w"` - X int `json:"x"` - Y int `json:"y"` - } `json:"gridPos"` - ID int `json:"id"` - Links []interface{} `json:"links,omitempty"` - Options struct { - Orientation string `json:"orientation"` - ReduceOptions struct { - Calcs []string `json:"calcs"` - Fields string `json:"fields"` - Values bool `json:"values"` - } `json:"reduceOptions"` - ShowThresholdLabels bool `json:"showThresholdLabels"` - ShowThresholdMarkers bool `json:"showThresholdMarkers"` - } `json:"options,omitempty"` - PluginVersion string `json:"pluginVersion,omitempty"` - Targets []struct { - Datasource interface{} `json:"datasource"` - EditorMode string `json:"editorMode"` - Expr string `json:"expr"` - Hide bool `json:"hide"` - IntervalFactor int `json:"intervalFactor"` - LegendFormat string `json:"legendFormat"` - Range bool `json:"range"` - RefID string `json:"refId"` - Step int `json:"step"` - } `json:"targets"` - Title string `json:"title"` - Type string `json:"type"` - HideTimeOverride bool `json:"hideTimeOverride,omitempty"` - MaxDataPoints int `json:"maxDataPoints,omitempty"` - Collapsed bool `json:"collapsed,omitempty"` - Panels []interface{} `json:"panels,omitempty"` - } `json:"panels"` + LiveNow bool `json:"liveNow"` + Panels []Panels `json:"panels"` SchemaVersion int `json:"schemaVersion"` Style string `json:"style"` Tags []string `json:"tags"`