Skip to content

Commit

Permalink
fix: Disable incorrect variables
Browse files Browse the repository at this point in the history
  • Loading branch information
yufeiminds committed Sep 8, 2023
1 parent 6fff6bb commit 4d0e2f9
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 26 deletions.
22 changes: 22 additions & 0 deletions internal/grafana/addons/variables/funcs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package variables

// PromQLFuncLabelNames is the label_names function in Grafana
type GrafanaFuncLabelNames struct {
MetricRegexp string
}

// PromQLFuncLabelValues is the label_values function in Grafana
type GrafanaFuncLabelValues struct {
Metric string
Label string
}

// PromQLFuncQueryResult is the query_result function in Grafana
type GrafanaFuncQueryResult struct {
Query string
}

// PromQLFuncMetrics is the metrics function in Grafana
type GrafanaFuncMetrics struct {
MetricRegexp string
}
131 changes: 131 additions & 0 deletions internal/grafana/addons/variables/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package variables

import (
"fmt"
"regexp"
)

var (
// PromQLLabelNamesFuncName is the label_names function in Grafana
PrometheusLabelNamesRegex = regexp.MustCompile(`^label_names\(\)\s*$`)

// PromQLLabelValuesFuncName is the label_values function in Grafana
PrometheusLabelValuesRegex = regexp.MustCompile(`^label_values\((?:(.+),\s*)?([a-zA-Z_$][a-zA-Z0-9_]*)\)\s*$`)

// PromQLMetricsFuncName is the metrics function in Grafana
PrometheusMetricNamesRegex = regexp.MustCompile(`^metrics\((.+)\)\s*$`)

// PromQLQueryResultFuncName is the query_result function in Grafana
PrometheusQueryResultRegex = regexp.MustCompile(`^query_result\((.+)\)\s*$`)

// PromQLLabelNamesFuncNameWithMatch is the label_names function in Grafana
PrometheusLabelNamesRegexWithMatch = regexp.MustCompile(`^label_names\((.+)\)\s*$`)
)

const (
// PrometheusLabelNamesFuncName is the label_names function name in Grafana
PrometheusLabelNamesFuncName = "label_names"

// PrometheusLabelValuesFuncName is the label_values function name in Grafana
PrometheusLabelValuesFuncName = "label_values"

// PrometheusMetricsFuncName is the metrics function name in Grafana
PrometheusMetricNamesFuncName = "metrics"

// PrometheusQueryResultFuncName is the query_result function name in Grafana
PrometheusQueryResultFuncName = "query_result"
)

type GrafanaVariable struct {
Expr string
FuncName string
Func interface{}
}

func ParseGrafanaVariable(expr string) (*GrafanaVariable, error) {
switch {
case PrometheusLabelValuesRegex.MatchString(expr):
match := PrometheusLabelValuesRegex.FindStringSubmatch(expr)
if len(match) != 3 {
return nil, fmt.Errorf("failed to get label from variable: %s", expr)
}
return &GrafanaVariable{
Expr: expr,
FuncName: PrometheusLabelValuesFuncName,
Func: &GrafanaFuncLabelValues{
Metric: match[1],
Label: match[2],
},
}, nil
case PrometheusQueryResultRegex.MatchString(expr):
match := PrometheusQueryResultRegex.FindStringSubmatch(expr)
if len(match) != 2 {
return nil, fmt.Errorf("failed to get query: %s", expr)
}
return &GrafanaVariable{
Expr: expr,
FuncName: PrometheusQueryResultFuncName,
Func: &GrafanaFuncQueryResult{
Query: match[1],
},
}, nil
case PrometheusMetricNamesRegex.MatchString(expr):
match := PrometheusMetricNamesRegex.FindStringSubmatch(expr)
if len(match) != 2 {
return nil, fmt.Errorf("failed to get metric: %s", expr)
}
return &GrafanaVariable{
Expr: expr,
FuncName: PrometheusMetricNamesFuncName,
Func: &GrafanaFuncMetrics{
MetricRegexp: match[1],
},
}, nil
case PrometheusLabelNamesRegexWithMatch.MatchString(expr):
match := PrometheusLabelNamesRegexWithMatch.FindStringSubmatch(expr)
if len(match) != 2 {
return nil, fmt.Errorf("failed to get metric: %s", expr)
}
return &GrafanaVariable{
Expr: expr,
FuncName: PrometheusLabelNamesFuncName,
Func: &GrafanaFuncLabelNames{
MetricRegexp: match[1],
},
}, nil
case PrometheusLabelNamesRegex.MatchString(expr):
return &GrafanaVariable{
Expr: expr,
FuncName: PrometheusLabelNamesFuncName,
Func: &GrafanaFuncLabelNames{
MetricRegexp: "",
},
}, nil
default:
return &GrafanaVariable{
Expr: expr,
FuncName: "",
Func: nil,
}, nil
}
}

func toDQL(promExpr string) (string, error) {
grafanaVariable, err := ParseGrafanaVariable(promExpr)
if err != nil {
return "", fmt.Errorf("failed to parse grafana variable: %w", err)
}

switch grafanaVariable.FuncName {
case PrometheusLabelNamesFuncName:
return "", nil
case PrometheusLabelValuesFuncName:
return "", nil
case PrometheusQueryResultFuncName:
return "", nil
case PrometheusMetricNamesFuncName:
return "", nil
default:
return "", nil
}
}
69 changes: 69 additions & 0 deletions internal/grafana/addons/variables/parser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package variables

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestParseGrafanaVariable(t *testing.T) {
tests := []struct {
expr string
wantErr bool
}{
{
expr: "label_names(cpu_seconds_total)",
wantErr: true,
},
{
expr: "label_names(cpu_seconds_.*)",
wantErr: true,
},
{
expr: "label_values(cpu_seconds_total)",
wantErr: true,
},
{
expr: "label_values(cpu_seconds_total, app)",
wantErr: true,
},
{
expr: `label_values(cpu_seconds_total{namespace="$namespace"})`,
wantErr: true,
},
{
expr: `label_values(cpu_seconds_total{namespace="$namespace"}, app)`,
wantErr: true,
},
{
expr: "metrics(cpu_seconds_total)",
wantErr: true,
},
{
expr: "metrics(cpu_seconds_.*)",
wantErr: true,
},
{
expr: `query_result(cpu_seconds_total{namespace="$namespace"})`,
wantErr: true,
},
{
expr: `cpu_seconds_total{namespace="$namespace"}`,
wantErr: true,
},
}

for _, tt := range tests {
dql, err := toDQL(tt.expr)
if err != nil {
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
continue
}
fmt.Println(dql)
}
}
41 changes: 15 additions & 26 deletions internal/grafana/addons/variables/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,40 @@ package variables

import (
"fmt"
"regexp"
"strings"

"github.com/hashicorp/go-multierror"

grafanaspec "github.com/GuanceCloud/guance-cli/internal/grafana/spec"
"github.com/GuanceCloud/guance-cli/internal/helpers/types"
)

// BuildVariables builds variables for Guance Cloud
func (addon *Addon) BuildVariables(variables []grafanaspec.VariableModel) ([]any, error) {
var mErr error

vars := make([]any, 0, len(variables))
for _, variable := range variables {
if variable.Type != "query" {
continue
}

dqlQuery, err := addon.toDQL(variable)
promExpr, err := getPromExpr(variable)
if err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("failed to get prometheus expression from variable: %w", err))
continue
}

dqlQuery, err := toDQL(promExpr)
if err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("failed to get label from variable: %w", err))
continue
}

name := types.StringValue(variable.Label)
if name == "" {
name = variable.Name
}

vars = append(vars, map[string]any{
"code": variable.Name,
"datasource": "dataflux",
Expand All @@ -41,7 +52,7 @@ func (addon *Addon) BuildVariables(variables []grafanaspec.VariableModel) ([]any
},
"hide": 0,
"isHiddenAsterisk": 0,
"name": variable.Label,
"name": name,
"seq": 2,
"type": "QUERY",
"valueSort": "asc",
Expand All @@ -53,28 +64,6 @@ func (addon *Addon) BuildVariables(variables []grafanaspec.VariableModel) ([]any
return vars, nil
}

var labelFuncPattern = regexp.MustCompile(`label_values\((.+),?\s*(.*)\)`)

func (addon *Addon) toDQL(variable grafanaspec.VariableModel) (string, error) {
queryString, err := getPromExpr(variable)
if err != nil {
return "", fmt.Errorf("failed to get prometheus expression: %w", err)
}

switch {
case strings.HasPrefix(queryString, "label_values("):
match := labelFuncPattern.FindStringSubmatch(queryString)
if len(match) != 3 {
return "", fmt.Errorf("failed to get label from variable: %s", variable.Name)
}
return fmt.Sprintf("SHOW_TAG_VALUE(from=['%s'], keyin=['%s'])", addon.Measurement, match[2]), nil
case strings.HasPrefix(queryString, "query_result("):
return "", nil
default:
return "", fmt.Errorf("failed to get label from variable: %s", variable.Name)
}
}

func getPromExpr(variable grafanaspec.VariableModel) (string, error) {
if variable.Query == nil {
return "", fmt.Errorf("query %s is empty", variable.Name)
Expand Down

0 comments on commit 4d0e2f9

Please sign in to comment.