Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions central/metrics/aggregator/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package common
import (
"context"
"iter"
"maps"
"regexp"
"slices"

Expand Down Expand Up @@ -41,6 +42,26 @@ func (mcfg MetricsConfiguration) HasAnyLabelOf(labels []Label) bool {
return false
}

func (mcfg MetricsConfiguration) DiffLabels(another MetricsConfiguration) ([]MetricName, []MetricName, []MetricName) {
if mcfg == nil && another == nil {
return nil, nil, nil
}
var toAdd, toDelete, changed []MetricName
for metric, labels := range mcfg {
if anotherLabels, ok := another[metric]; !ok {
toDelete = append(toDelete, metric)
} else if !maps.EqualFunc(labels, anotherLabels, Expression.Equals) {
changed = append(changed, metric)
}
}
for metric := range another {
if _, ok := mcfg[metric]; !ok {
toAdd = append(toAdd, metric)
}
}
return toAdd, toDelete, changed
}

type OneOrMore int

func (o OneOrMore) Count() int {
Expand Down
222 changes: 222 additions & 0 deletions central/metrics/aggregator/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ func makeTestMetricLabels(t *testing.T) map[string]*storage.PrometheusMetricsCon
},
"Cluster": nil,
},
Exposure: storage.PrometheusMetricsConfig_Labels_BOTH,
RegistryName: "registry1",
},
pfx + "_metric2": {
Labels: map[string]*storage.PrometheusMetricsConfig_Labels_Expression{
Expand Down Expand Up @@ -126,3 +128,223 @@ func TestOneOrMore(t *testing.T) {
assert.Equal(t, 1, OneOrMore(1).Count())
assert.Equal(t, 2, OneOrMore(2).Count())
}

/*
func TestEquals(t *testing.T) {
t.Run("empty", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
assert.False(t, one.Equals(MetricsConfiguration{}))
assert.False(t, MetricsConfiguration{}.Equals(one))
})

t.Run("equals", func(t *testing.T) {
var m MetricsConfiguration
assert.True(t, m.Equals(m))
assert.True(t, MetricsConfiguration{}.Equals(MetricsConfiguration{}))
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)
assert.True(t, one.Equals(one))
assert.True(t, one.Equals(another))
assert.True(t, another.Equals(one))
})

t.Run("changed condition", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)

loop:
for _, labels := range another {
for _, expr := range labels {
for _, cond := range expr {
cond.arg = "changed"
break loop
}
}
}
assert.False(t, one.Equals(another))
assert.False(t, another.Equals(one))
})

t.Run("extra condition", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)

loop:
for _, labels := range another {
for label, expr := range labels {
labels[label] = append(expr, &Condition{"=", "extra"})
break loop
}
}
assert.False(t, one.Equals(another))
assert.False(t, another.Equals(one))
})

t.Run("extra label", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)

for _, labels := range another {
labels["extra"] = Expression{}
break
}
assert.False(t, one.Equals(another))
assert.False(t, another.Equals(one))
})

t.Run("extra metric", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)
another["extra"] = map[Label]Expression{}
assert.False(t, one.Equals(another))
assert.False(t, another.Equals(one))
})

}

func TestEqualLabels(t *testing.T) {
t.Run("empty", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
assert.False(t, one.Equals(MetricsConfiguration{}))
assert.False(t, MetricsConfiguration{}.Equals(one))
})

t.Run("equals", func(t *testing.T) {
var m MetricsConfiguration
assert.True(t, m.Equals(m))
assert.True(t, MetricsConfiguration{}.Equals(MetricsConfiguration{}))
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)
assert.True(t, one.Equals(one))
assert.True(t, one.Equals(another))
assert.True(t, another.Equals(one))
})

t.Run("changed condition", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)

loop:
for _, labels := range another {
for _, expr := range labels {
for _, cond := range expr {
cond.arg = "changed"
break loop
}
}
}
assert.False(t, one.Equals(another))
assert.False(t, another.Equals(one))
})

t.Run("extra condition", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)

loop:
for _, labels := range another {
for label, expr := range labels {
labels[label] = append(expr, &Condition{"=", "extra"})
break loop
}
}
assert.False(t, one.Equals(another))
assert.False(t, another.Equals(one))
})

t.Run("extra label", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)

for _, labels := range another {
labels["extra"] = Expression{}
break
}
assert.False(t, one.Equals(another))
assert.False(t, another.Equals(one))
})

t.Run("extra metric", func(t *testing.T) {
one := makeTestMetricLabelExpression(t)
another := makeTestMetricLabelExpression(t)
another["extra"] = map[Label]Expression{}
assert.False(t, one.Equals(another))
assert.False(t, another.Equals(one))
})

}
*/

func TestDiffLabels(t *testing.T) {
tests := []struct {
name string
a, b MetricsConfiguration
wantToAdd []MetricName
wantToDelete []MetricName
wantChanged []MetricName
}{
{
name: "both nil",
a: nil,
b: nil,
wantToAdd: nil,
wantToDelete: nil,
},
{
name: "a empty, b has one",
a: MetricsConfiguration{},
b: MetricsConfiguration{"metric1": {"label1": nil}},
wantToAdd: []MetricName{"metric1"},
wantToDelete: nil,
},
{
name: "a has one, b empty",
a: MetricsConfiguration{"metric1": {"label1": nil}},
b: MetricsConfiguration{},
wantToAdd: nil,
wantToDelete: []MetricName{"metric1"},
},
{
name: "a has one, b has another",
a: MetricsConfiguration{"metric1": {"label1": nil}},
b: MetricsConfiguration{"metric2": {"label2": nil}},
wantToAdd: []MetricName{"metric2"},
wantToDelete: []MetricName{"metric1"},
},
{
name: "a and b have overlap",
a: MetricsConfiguration{
"metric1": {"label1": nil},
"metric2": {"label2": nil},
},
b: MetricsConfiguration{
"metric2": {"label2": nil},
"metric3": {"label3": nil},
},
wantToAdd: []MetricName{"metric3"},
wantToDelete: []MetricName{"metric1"},
},
{
name: "identical",
a: MetricsConfiguration{
"metric1": {"label1": nil},
"metric2": {"label2": nil},
},
b: MetricsConfiguration{
"metric1": {"label1": nil},
"metric2": {"label2": nil},
},
wantToAdd: nil,
wantToDelete: nil,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotToAdd, gotToDelete, gotChanged := tt.a.DiffLabels(tt.b)
assert.ElementsMatch(t, tt.wantToAdd, gotToAdd)
assert.ElementsMatch(t, tt.wantToDelete, gotToDelete)
assert.ElementsMatch(t, tt.wantChanged, gotChanged)
})
}
}
4 changes: 4 additions & 0 deletions central/metrics/aggregator/common/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ func (c *Condition) validate() error {
return nil
}

func (c *Condition) Equals(b *Condition) bool {
return c == b || c != nil && b != nil && c.op == b.op && c.arg == b.arg
}

func (c *Condition) String() string {
return string(c.op) + c.arg
}
Expand Down
13 changes: 12 additions & 1 deletion central/metrics/aggregator/common/config_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ package common

import (
"maps"
"regexp"
"slices"

"github.com/stackrox/rox/generated/storage"
"github.com/stackrox/rox/pkg/errox"
)

var errInvalidConfiguration = errox.InvalidArgs.New("invalid configuration")
var (
errInvalidConfiguration = errox.InvalidArgs.New("invalid configuration")

registryNamePattern = regexp.MustCompile("^[a-zA-Z0-9-_]*$")
)

func isKnownLabel(label string, labelOrder map[Label]int) bool {
_, ok := labelOrder[Label(label)]
Expand All @@ -19,6 +24,12 @@ func isKnownLabel(label string, labelOrder map[Label]int) bool {
func parseMetricLabels(config map[string]*storage.PrometheusMetricsConfig_Labels, labelOrder map[Label]int) (MetricsConfiguration, error) {
result := make(MetricsConfiguration, len(config))
for metric, labels := range config {

if !registryNamePattern.MatchString(labels.GetRegistryName()) {
return nil, errInvalidConfiguration.CausedByf(
`registry name for metric %s doesn't match "`+registryNamePattern.String()+`"`, metric)
}

if err := validateMetricName(metric); err != nil {
return nil, errInvalidConfiguration.CausedByf(
"invalid metric name %q: %v", metric, err)
Expand Down
6 changes: 6 additions & 0 deletions central/metrics/aggregator/common/expression.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package common

import "slices"

type Expression []*Condition

func (expr Expression) Equals(another Expression) bool {
return slices.EqualFunc(expr, another, (*Condition).Equals)
}

func (expr Expression) match(value string) bool {
if len(expr) == 0 {
return true
Expand Down
Loading
Loading