Skip to content

Commit

Permalink
fix: Eventsource resourse label selector operators not working (#2795)
Browse files Browse the repository at this point in the history
Signed-off-by: gokulav137 <gokulav999@gmail.com>
  • Loading branch information
gokulav137 committed Sep 12, 2023
1 parent 8fa2b2f commit f5ce1ef
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 13 deletions.
8 changes: 6 additions & 2 deletions api/event-source.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions api/event-source.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions api/jsonschema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2573,7 +2573,7 @@
"type": "array"
},
"labels": {
"description": "Labels provide listing options to K8s API to watch resource/s. Refer https://kubernetes.io/docs/concepts/overview/working-with-objects/label-selectors/ for more info.",
"description": "Labels provide listing options to K8s API to watch resource/s. Refer https://kubernetes.io/docs/concepts/overview/working-with-objects/label-selectors/ for more info. Unlike K8s field selector, multiple values are passed as comma separated values instead of list of values. Eg: value: value1,value2. Same as K8s label selector, operator \"=\", \"==\", \"!=\", \"exists\", \"!\", \"notin\", \"in\", \"gt\" and \"lt\" are supported",
"items": {
"$ref": "#/definitions/io.argoproj.eventsource.v1alpha1.Selector"
},
Expand Down Expand Up @@ -2759,7 +2759,7 @@
"type": "string"
},
"operation": {
"description": "Supported operations like ==, !=, \u003c=, \u003e= etc. Defaults to ==. Refer https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors for more info.",
"description": "Supported operations like ==, != etc. Defaults to ==. Refer https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors for more info.",
"type": "string"
},
"value": {
Expand Down
4 changes: 2 additions & 2 deletions api/openapi-spec/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion eventsources/sources/resource/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,16 @@ func LabelReq(sel v1alpha1.Selector) (*labels.Requirement, error) {
if sel.Operation != "" {
op = selection.Operator(sel.Operation)
}
req, err := labels.NewRequirement(sel.Key, op, []string{sel.Value})
var values []string
switch {
case (op == selection.Exists || op == selection.DoesNotExist) && sel.Value == "":
values = []string{}
case op == selection.In || op == selection.NotIn:
values = strings.Split(sel.Value, ",")
default:
values = []string{sel.Value}
}
req, err := labels.NewRequirement(sel.Key, op, values)
if err != nil {
return nil, err
}
Expand Down
173 changes: 173 additions & 0 deletions eventsources/sources/resource/start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ package resource
import (
"context"
"encoding/json"
"fmt"
"testing"
"time"

"github.com/smartystreets/goconvey/convey"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes/fake"

"github.com/argoproj/argo-events/common/logging"
Expand Down Expand Up @@ -105,3 +107,174 @@ func TestFilter(t *testing.T) {
convey.So(pass, convey.ShouldBeTrue)
})
}

func TestLabelSelector(t *testing.T) {
// Test equality operators =, == and in
for _, op := range []string{"==", "=", "in"} {
t.Run(fmt.Sprintf("Test operator %v", op), func(t *testing.T) {
r, err := LabelSelector([]v1alpha1.Selector{{
Key: "key",
Operation: op,
Value: "1",
}})
if err != nil {
t.Fatal(err)
}
validL := &labels.Set{"key": "1"}
if !r.Matches(validL) {
t.Errorf("didnot match %v", validL)
}
invalidL := &labels.Set{"key": "2"}
if r.Matches(invalidL) {
t.Errorf("matched %v", invalidL)
}
})
}
// Test inequality operators != and notin
for _, op := range []string{"!=", "notin"} {
t.Run(fmt.Sprintf("Test operator %v", op), func(t *testing.T) {
r, err := LabelSelector([]v1alpha1.Selector{{
Key: "key",
Operation: op,
Value: "1",
}})
if err != nil {
t.Fatal(err)
}
validL := &labels.Set{"key": "2"}
if !r.Matches(validL) {
t.Errorf("didnot match %v", validL)
}
invalidL := &labels.Set{"key": "1"}
if r.Matches(invalidL) {
t.Errorf("matched %v", invalidL)
}
})
}
// Test greater than operator
t.Run("Test operator gt", func(t *testing.T) {
r, err := LabelSelector([]v1alpha1.Selector{{
Key: "key",
Operation: "gt",
Value: "1",
}})
if err != nil {
t.Fatal(err)
}
validL := &labels.Set{"key": "2"}
if !r.Matches(validL) {
t.Errorf("didnot match %v", validL)
}
invalidL := &labels.Set{"key": "1"}
if r.Matches(invalidL) {
t.Errorf("matched %v", invalidL)
}
})
// Test lower than operator
t.Run("Test operator lt", func(t *testing.T) {
r, err := LabelSelector([]v1alpha1.Selector{{
Key: "key",
Operation: "lt",
Value: "2",
}})
if err != nil {
t.Fatal(err)
}
validL := &labels.Set{"key": "1"}
if !r.Matches(validL) {
t.Errorf("didnot match %v", validL)
}
invalidL := &labels.Set{"key": "2"}
if r.Matches(invalidL) {
t.Errorf("matched %v", invalidL)
}
})
// Test exists operator
t.Run("Test operator exists", func(t *testing.T) {
r, err := LabelSelector([]v1alpha1.Selector{{
Key: "key",
Operation: "exists",
}})
if err != nil {
t.Fatal(err)
}
validL := &labels.Set{"key": "something"}
if !r.Matches(validL) {
t.Errorf("didnot match %v", validL)
}
invalidL := &labels.Set{"notkey": "something"}
if r.Matches(invalidL) {
t.Errorf("matched %v", invalidL)
}
})
// Test doesnot exist operator
t.Run("Test operator !", func(t *testing.T) {
r, err := LabelSelector([]v1alpha1.Selector{{
Key: "key",
Operation: "!",
}})
if err != nil {
t.Fatal(err)
}
validL := &labels.Set{"notkey": "something"}
if !r.Matches(validL) {
t.Errorf("didnot match %v", validL)
}
invalidL := &labels.Set{"key": "something"}
if r.Matches(invalidL) {
t.Errorf("matched %v", invalidL)
}
})
// Test default operator
t.Run("Test default operator", func(t *testing.T) {
r, err := LabelSelector([]v1alpha1.Selector{{
Key: "key",
Operation: "",
Value: "something",
}})
if err != nil {
t.Fatal(err)
}
validL := &labels.Set{"key": "something"}
if !r.Matches(validL) {
t.Errorf("didnot match %v", validL)
}
invalidL := &labels.Set{"key": "not something"}
if r.Matches(invalidL) {
t.Errorf("matched %v", invalidL)
}
})
// Test invalid operators <= and >=
for _, op := range []string{"<=", ">="} {
t.Run(fmt.Sprintf("Invalid operator %v", op), func(t *testing.T) {
_, err := LabelSelector([]v1alpha1.Selector{{
Key: "workflows.argoproj.io/phase",
Operation: op,
Value: "1",
}})
if err == nil {
t.Errorf("Invalid operator should throw error")
}
})
}
// Test comma separated values for in
t.Run("Comma separated values", func(t *testing.T) {
r, err := LabelSelector([]v1alpha1.Selector{{
Key: "key",
Operation: "in",
Value: "a,b,",
}})
if err != nil {
t.Fatal("valid value threw error, value %w", err)
}
for _, validL := range []labels.Set{{"key": "a"}, {"key": "b"}, {"key": ""}} {
if !r.Matches(validL) {
t.Errorf("didnot match %v", validL)
}
}
invalidL := &labels.Set{"key": "c"}
if r.Matches(invalidL) {
t.Errorf("matched %v", invalidL)
}
})
}
6 changes: 5 additions & 1 deletion pkg/apis/eventsource/v1alpha1/generated.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/apis/eventsource/v1alpha1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion pkg/apis/eventsource/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,10 @@ type ResourceFilter struct {
Prefix string `json:"prefix,omitempty" protobuf:"bytes,1,opt,name=prefix"`
// Labels provide listing options to K8s API to watch resource/s.
// Refer https://kubernetes.io/docs/concepts/overview/working-with-objects/label-selectors/ for more info.
// Unlike K8s field selector, multiple values are passed as comma separated values instead of list of values.
// Eg: value: value1,value2.
// Same as K8s label selector, operator "=", "==", "!=", "exists", "!", "notin", "in", "gt" and "lt"
// are supported
// +optional
Labels []Selector `json:"labels,omitempty" protobuf:"bytes,2,rep,name=labels"`
// Fields provide field filters similar to K8s field selector
Expand All @@ -371,7 +375,7 @@ type ResourceFilter struct {
type Selector struct {
// Key name
Key string `json:"key" protobuf:"bytes,1,opt,name=key"`
// Supported operations like ==, !=, <=, >= etc.
// Supported operations like ==, != etc.
// Defaults to ==.
// Refer https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors for more info.
// +optional
Expand Down

0 comments on commit f5ce1ef

Please sign in to comment.