Skip to content

Commit 6866c57

Browse files
authored
feat: matchLabels (#122)
* feat: matchLabels * ! or !* should return true if the label doesn't exist
1 parent cf120e2 commit 6866c57

File tree

8 files changed

+74
-10
lines changed

8 files changed

+74
-10
lines changed

coll/coll.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"sort"
1414
"strings"
1515

16+
"github.com/flanksource/commons/collections"
1617
"github.com/flanksource/gomplate/v3/conv"
1718
iconv "github.com/flanksource/gomplate/v3/internal/conv"
1819
)
@@ -370,3 +371,19 @@ func KeyValToMap(s string) (map[string]string, error) {
370371
}
371372
return m, nil
372373
}
374+
375+
// MatchLabel returns true if the given map has a key that matches any of the given patterns.
376+
// If all patterns are exclusions and the key doesn't exist, it's treated as a match.
377+
func MatchLabel(labels map[string]any, key string, valuePatterns ...string) bool {
378+
value, exists := labels[key]
379+
if !exists {
380+
return collections.IsExclusionOnlyPatterns(valuePatterns)
381+
}
382+
383+
vStr, ok := value.(string)
384+
if !ok {
385+
return false
386+
}
387+
388+
return collections.MatchItems(vStr, valuePatterns...)
389+
}

funcs/cel_exports.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

funcs/coll.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package funcs
22

33
import (
44
"context"
5+
"strings"
56

67
"github.com/flanksource/gomplate/v3/conv"
8+
"github.com/google/cel-go/cel"
9+
"github.com/google/cel-go/common/types"
10+
"github.com/google/cel-go/common/types/ref"
711

812
"github.com/flanksource/gomplate/v3/coll"
913
"github.com/pkg/errors"
@@ -43,6 +47,8 @@ func CreateCollFuncs(ctx context.Context) map[string]interface{} {
4347
f["sort"] = ns.Sort
4448
f["jq"] = ns.JQ
4549
f["flatten"] = ns.Flatten
50+
51+
f["matchLabel"] = coll.MatchLabel
4652
f["mapToKeyVal"] = coll.MapToKeyVal[any]
4753
f["keyValToMap"] = coll.KeyValToMap
4854
f["jsonpath"] = coll.JSONPath
@@ -189,3 +195,24 @@ func (CollFuncs) Omit(args ...interface{}) (map[string]interface{}, error) {
189195
}
190196
return coll.Omit(m, keys...), nil
191197
}
198+
199+
var celLabelsMatch = cel.Function("matchLabel",
200+
cel.Overload("matchLabel_map_string_string",
201+
[]*cel.Type{
202+
cel.MapType(cel.StringType, cel.DynType), cel.StringType, cel.StringType,
203+
},
204+
cel.BoolType,
205+
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
206+
key := conv.ToString(args[1])
207+
valuePatterns := strings.Split(conv.ToString(args[2]), ",")
208+
209+
labels, err := convertMap(args[0])
210+
if err != nil {
211+
return types.WrapErr(errors.New("matchLabel expects the first argument to be a map[string]any"))
212+
}
213+
214+
result := coll.MatchLabel(labels, key, valuePatterns...)
215+
return types.DefaultTypeAdapter.NativeToValue(result)
216+
}),
217+
),
218+
)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ toolchain go1.23.4
77
require (
88
github.com/Masterminds/goutils v1.1.1
99
github.com/Masterminds/semver/v3 v3.3.1
10-
github.com/flanksource/commons v1.35.3
10+
github.com/flanksource/commons v1.36.1
1111
github.com/flanksource/is-healthy v1.0.59
1212
github.com/flanksource/kubectl-neat v1.0.4
1313
github.com/google/cel-go v0.22.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBeht
2828
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
2929
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
3030
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
31-
github.com/flanksource/commons v1.35.3 h1:EG46iWodmSQQbXywjvEAgK56ZH26jYtMv0RiPM3eKDE=
32-
github.com/flanksource/commons v1.35.3/go.mod h1:cLZURmvF0dw3wR5VyPuibRl/7h+NEiyMdo70WhJFB9Y=
31+
github.com/flanksource/commons v1.36.1 h1:SDppXOYO6vk06d12SPOYVZJX+8gV72FmK1l9ar5fkjc=
32+
github.com/flanksource/commons v1.36.1/go.mod h1:nsYCn6JOhENXNLR5pz4zNco70DQV+bGObQ8NbOrUeuQ=
3333
github.com/flanksource/is-healthy v1.0.59 h1:/dObdgBEouYMX7eF2R4l20G8I+Equ0YGDrXOtRpar/s=
3434
github.com/flanksource/is-healthy v1.0.59/go.mod h1:5MUvkRbq78aPVIpwGjpn+k89n5+1thBLIRdhfcozUcQ=
3535
github.com/flanksource/kubectl-neat v1.0.4 h1:t5/9CqgE84oEtB0KitgJ2+WIeLfD+RhXSxYrqb4X8yI=

metrics.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,3 @@ type MetricsType struct {
2020
TemplatesProcessed int
2121
Errors int
2222
}
23-
24-
func newMetrics() *MetricsType {
25-
return &MetricsType{
26-
RenderDuration: make(map[string]time.Duration),
27-
}
28-
}

template.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ func RunExpressionContext(ctx commonsContext.Context, _environment map[string]an
188188

189189
ast, issues := env.Compile(strings.ReplaceAll(template.Expression, "\n", " "))
190190
if issues != nil && issues.Err() != nil {
191-
return "", oops.With("template", template.Expression).Errorf(issues.String())
191+
return "", oops.With("template", template.Expression).Errorf("issues: %s", issues.String())
192192
}
193193

194194
prg, err = env.Program(ast, cel.Globals(data))

tests/cel_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,31 @@ func TestCelK8sMemoryResourceUnits(t *testing.T) {
608608
}
609609
}
610610

611+
func TestMatchLabel(t *testing.T) {
612+
config := map[string]any{
613+
"labels": map[string]string{
614+
"environment": "production",
615+
"region": "us-east-1",
616+
},
617+
"tags": map[string]string{
618+
"cluster": "aws-prod",
619+
},
620+
}
621+
622+
runTests(t, []Test{
623+
{map[string]any{"config": config}, "matchLabel(config.labels, 'region', 'us-*')", "true"},
624+
{map[string]any{"config": config}, "matchLabel(config.labels, 'region', 'eu-*')", "false"},
625+
{map[string]any{"config": config}, "matchLabel(config.labels, 'environment', 'production')", "true"},
626+
{map[string]any{"config": config}, "matchLabel(config.tags, 'cluster', 'aws-*')", "true"},
627+
{map[string]any{"config": config}, "matchLabel(config.tags, 'cluster', '*-prod')", "true"},
628+
{map[string]any{"config": config}, "matchLabel(config.tags, 'cluster', '!aws-prod')", "false"},
629+
{map[string]any{"config": config}, "matchLabel(config.tags, 'cluster', '!aws-demo')", "true"},
630+
{map[string]any{"config": config}, "matchLabel(config.tags, 'nonExistingKey', 'aws-demo')", "false"},
631+
{map[string]any{"config": config}, "matchLabel(config.tags, 'nonExistingKey', '!aws-demo')", "true"},
632+
{map[string]any{"config": config}, "matchLabel(config.tags, 'nonExistingKey', '!*')", "true"},
633+
})
634+
}
635+
611636
func TestCelYAML(t *testing.T) {
612637
person := Person{
613638
Name: "Aditya",

0 commit comments

Comments
 (0)