From a4181a2b49018de0f33e249f3cf0972dcd9e66bb Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Tue, 28 Jan 2025 13:13:11 +0545 Subject: [PATCH] ! or !* should return true if the label doesn't exist --- coll/coll.go | 23 +++++++++++------------ funcs/coll.go | 5 +++-- go.mod | 2 +- go.sum | 4 ++-- tests/cel_test.go | 5 +++++ 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/coll/coll.go b/coll/coll.go index ebc892157..bf8f3e69a 100644 --- a/coll/coll.go +++ b/coll/coll.go @@ -372,19 +372,18 @@ func KeyValToMap(s string) (map[string]string, error) { return m, nil } -func MatchLabel(labels map[string]any, key, valuePattern string) bool { - for k, v := range labels { - if k != key { - continue - } - - if vStr, ok := v.(string); ok { - if collections.MatchItems(vStr, valuePattern) { - return true - } - } +// Matchlabel returns true if the given map has a key that matches any of the given patterns. +// If all patterns are exclusions and the key doesn't exist, it's treated as a match. +func MatchLabel(labels map[string]any, key string, valuePatterns ...string) bool { + value, exists := labels[key] + if !exists { + return collections.IsExclusionOnlyPatterns(valuePatterns) + } + vStr, ok := value.(string) + if !ok { + return false } - return false + return collections.MatchItems(vStr, valuePatterns...) } diff --git a/funcs/coll.go b/funcs/coll.go index 356d9831c..b7e22663d 100644 --- a/funcs/coll.go +++ b/funcs/coll.go @@ -2,6 +2,7 @@ package funcs import ( "context" + "strings" "github.com/flanksource/gomplate/v3/conv" "github.com/google/cel-go/cel" @@ -203,14 +204,14 @@ var celLabelsMatch = cel.Function("matchLabel", cel.BoolType, cel.FunctionBinding(func(args ...ref.Val) ref.Val { key := conv.ToString(args[1]) - valuePattern := conv.ToString(args[2]) + valuePatterns := strings.Split(conv.ToString(args[2]), ",") labels, err := convertMap(args[0]) if err != nil { return types.WrapErr(errors.New("matchLabel expects the first argument to be a map[string]any")) } - result := coll.MatchLabel(labels, key, valuePattern) + result := coll.MatchLabel(labels, key, valuePatterns...) return types.DefaultTypeAdapter.NativeToValue(result) }), ), diff --git a/go.mod b/go.mod index a8017213d..acc9f1f0c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.4 require ( github.com/Masterminds/goutils v1.1.1 github.com/Masterminds/semver/v3 v3.3.1 - github.com/flanksource/commons v1.35.3 + github.com/flanksource/commons v1.36.1 github.com/flanksource/is-healthy v1.0.59 github.com/flanksource/kubectl-neat v1.0.4 github.com/google/cel-go v0.22.1 diff --git a/go.sum b/go.sum index 431b0c144..329d94fa4 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBeht github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/flanksource/commons v1.35.3 h1:EG46iWodmSQQbXywjvEAgK56ZH26jYtMv0RiPM3eKDE= -github.com/flanksource/commons v1.35.3/go.mod h1:cLZURmvF0dw3wR5VyPuibRl/7h+NEiyMdo70WhJFB9Y= +github.com/flanksource/commons v1.36.1 h1:SDppXOYO6vk06d12SPOYVZJX+8gV72FmK1l9ar5fkjc= +github.com/flanksource/commons v1.36.1/go.mod h1:nsYCn6JOhENXNLR5pz4zNco70DQV+bGObQ8NbOrUeuQ= github.com/flanksource/is-healthy v1.0.59 h1:/dObdgBEouYMX7eF2R4l20G8I+Equ0YGDrXOtRpar/s= github.com/flanksource/is-healthy v1.0.59/go.mod h1:5MUvkRbq78aPVIpwGjpn+k89n5+1thBLIRdhfcozUcQ= github.com/flanksource/kubectl-neat v1.0.4 h1:t5/9CqgE84oEtB0KitgJ2+WIeLfD+RhXSxYrqb4X8yI= diff --git a/tests/cel_test.go b/tests/cel_test.go index e2ad88d90..cfb469094 100644 --- a/tests/cel_test.go +++ b/tests/cel_test.go @@ -625,6 +625,11 @@ func TestMatchLabel(t *testing.T) { {map[string]any{"config": config}, "matchLabel(config.labels, 'environment', 'production')", "true"}, {map[string]any{"config": config}, "matchLabel(config.tags, 'cluster', 'aws-*')", "true"}, {map[string]any{"config": config}, "matchLabel(config.tags, 'cluster', '*-prod')", "true"}, + {map[string]any{"config": config}, "matchLabel(config.tags, 'cluster', '!aws-prod')", "false"}, + {map[string]any{"config": config}, "matchLabel(config.tags, 'cluster', '!aws-demo')", "true"}, + {map[string]any{"config": config}, "matchLabel(config.tags, 'nonExistingKey', 'aws-demo')", "false"}, + {map[string]any{"config": config}, "matchLabel(config.tags, 'nonExistingKey', '!aws-demo')", "true"}, + {map[string]any{"config": config}, "matchLabel(config.tags, 'nonExistingKey', '!*')", "true"}, }) }