Compare commits

...

1 Commits

Author SHA1 Message Date
srikanthccv
515a1bda08 chore: fix panic from label set conversion 2025-10-11 20:36:55 +05:30
2 changed files with 106 additions and 4 deletions

View File

@@ -210,7 +210,9 @@ func (r *provider) Match(ctx context.Context, orgID string, ruleID string, set m
return matchedChannels, nil
}
func (r *provider) evaluateExpr(expression string, labelSet model.LabelSet) (bool, error) {
// convertLabelSetToEnv converts a flat label set with dotted keys into a nested map structure.
// For conflicting keys (e.g., "foo.bar" and "foo.bar.baz"), nested structures take precedence.
func convertLabelSetToEnv(labelSet model.LabelSet) map[string]interface{} {
env := make(map[string]interface{})
for k, v := range labelSet {
@@ -221,21 +223,55 @@ func (r *provider) evaluateExpr(expression string, labelSet model.LabelSet) (boo
parts := strings.Split(key, ".")
current := env
validPath := true
for i, part := range parts {
if i == len(parts)-1 {
current[part] = value
// if no conflict, set the value
if existing, exists := current[part]; exists {
// if there's already a map here, a longer path exists
if _, isMap := existing.(map[string]interface{}); isMap {
// TODO(srikanthccv): we need a better solution to handle this
validPath = false
break
}
}
if validPath {
current[part] = value
}
} else {
// ensure map
if current[part] == nil {
current[part] = make(map[string]interface{})
} else if _, ok := current[part].(map[string]interface{}); !ok {
// if this part is already a leaf value, not a map
// TODO(srikanthccv): we need a better solution to handle this
validPath = false
break
}
if validPath {
current = current[part].(map[string]interface{})
}
current = current[part].(map[string]interface{})
}
}
} else {
env[key] = value
// if no conflict, set value
if existing, exists := env[key]; exists {
if _, isMap := existing.(map[string]interface{}); !isMap {
env[key] = value
}
// already a map, skip
} else {
env[key] = value
}
}
}
return env
}
func (r *provider) evaluateExpr(expression string, labelSet model.LabelSet) (bool, error) {
env := convertLabelSetToEnv(labelSet)
program, err := expr.Compile(expression, expr.Env(env))
if err != nil {
return false, errors.NewInternalf(errors.CodeInternal, "error compiling route policy %s: %v", expression, err)

View File

@@ -907,3 +907,69 @@ func TestProvider_CreateRoutes(t *testing.T) {
})
}
}
func TestConvertLabelSetToEnv(t *testing.T) {
tests := []struct {
name string
labelSet model.LabelSet
expected map[string]interface{}
}{
{
name: "simple keys",
labelSet: model.LabelSet{
"key1": "value1",
"key2": "value2",
},
expected: map[string]interface{}{
"key1": "value1",
"key2": "value2",
},
},
{
name: "nested keys",
labelSet: model.LabelSet{
"foo.bar": "value1",
"foo.baz": "value2",
},
expected: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "value1",
"baz": "value2",
},
},
},
{
name: "conflict - nested structure wins",
labelSet: model.LabelSet{
"foo.bar.baz": "deep",
"foo.bar": "shallow",
},
expected: map[string]interface{}{
"foo": map[string]interface{}{
"bar": map[string]interface{}{
"baz": "deep",
},
},
},
},
{
name: "conflict - leaf value vs nested",
labelSet: model.LabelSet{
"foo.bar": "value",
"foo": "should_be_ignored",
},
expected: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "value",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := convertLabelSetToEnv(tt.labelSet)
assert.Equal(t, tt.expected, result)
})
}
}