diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go index 1267631aa..242d90263 100644 --- a/cmd/promtool/main.go +++ b/cmd/promtool/main.go @@ -24,7 +24,7 @@ import ( "net/url" "os" "path/filepath" - "reflect" + "sort" "strconv" "strings" "time" @@ -48,6 +48,7 @@ import ( _ "github.com/prometheus/prometheus/discovery/install" // Register service discovery implementations. "github.com/prometheus/prometheus/discovery/kubernetes" "github.com/prometheus/prometheus/discovery/targetgroup" + "github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/rulefmt" "github.com/prometheus/prometheus/promql" ) @@ -471,8 +472,8 @@ func checkRules(filename string) (int, []error) { fmt.Printf("%d duplicate rule(s) found.\n", len(dRules)) for _, n := range dRules { fmt.Printf("Metric: %s\nLabel(s):\n", n.metric) - for i, l := range n.label { - fmt.Printf("\t%s: %s\n", i, l) + for _, l := range n.label { + fmt.Printf("\t%s: %s\n", l.Name, l.Value) } } fmt.Println("Might cause inconsistency while recording expressions.") @@ -483,29 +484,52 @@ func checkRules(filename string) (int, []error) { type compareRuleType struct { metric string - label map[string]string + label labels.Labels +} + +type compareRuleTypes []compareRuleType + +func (c compareRuleTypes) Len() int { return len(c) } +func (c compareRuleTypes) Swap(i, j int) { c[i], c[j] = c[j], c[i] } +func (c compareRuleTypes) Less(i, j int) bool { return compare(c[i], c[j]) < 0 } + +func compare(a, b compareRuleType) int { + if res := strings.Compare(a.metric, b.metric); res != 0 { + return res + } + + return labels.Compare(a.label, b.label) } func checkDuplicates(groups []rulefmt.RuleGroup) []compareRuleType { var duplicates []compareRuleType + var rules compareRuleTypes for _, group := range groups { - for index, rule := range group.Rules { - inst := compareRuleType{ + + for _, rule := range group.Rules { + rules = append(rules, compareRuleType{ metric: ruleMetric(rule), - label: rule.Labels, - } - for i := 0; i < index; i++ { - t := compareRuleType{ - metric: ruleMetric(group.Rules[i]), - label: group.Rules[i].Labels, - } - if reflect.DeepEqual(t, inst) { - duplicates = append(duplicates, t) - } - } + label: labels.FromMap(rule.Labels), + }) } } + if len(rules) < 2 { + return duplicates + } + sort.Sort(rules) + + last := rules[0] + for i := 1; i < len(rules); i++ { + if compare(last, rules[i]) == 0 { + // Don't add a duplicated rule multiple times. + if len(duplicates) == 0 || compare(last, duplicates[len(duplicates)-1]) != 0 { + duplicates = append(duplicates, rules[i]) + } + } + last = rules[i] + } + return duplicates } diff --git a/cmd/promtool/main_test.go b/cmd/promtool/main_test.go index 6af9befee..87ac13e76 100644 --- a/cmd/promtool/main_test.go +++ b/cmd/promtool/main_test.go @@ -21,6 +21,7 @@ import ( "testing" "time" + "github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/rulefmt" "github.com/stretchr/testify/require" ) @@ -136,6 +137,7 @@ func TestCheckDuplicates(t *testing.T) { expectedDups: []compareRuleType{ { metric: "job:test:count_over_time1m", + label: labels.New(), }, }, },