From cf97e31fbf700480f747bfb059458c7398c4b85e Mon Sep 17 00:00:00 2001
From: Ahmed Abdalla
Date: Tue, 7 Sep 2021 08:31:30 +0200
Subject: [PATCH] Implement CE Subscriptions filters
Signed-off-by: Ahmed Abdalla
---
config/core/resources/trigger.yaml | 40 +++
docs/eventing-api.md | 240 +++++++++++--
pkg/apis/eventing/v1/trigger_defaults.go | 5 +-
pkg/apis/eventing/v1/trigger_defaults_test.go | 6 +-
pkg/apis/eventing/v1/trigger_types.go | 102 +++++-
pkg/apis/eventing/v1/trigger_validation.go | 138 ++++++--
.../eventing/v1/trigger_validation_test.go | 333 ++++++++++++++++--
pkg/apis/eventing/v1/zz_generated.deepcopy.go | 204 +++++++++--
pkg/apis/feature/api_validation_test.go | 8 +-
pkg/broker/filter/filter_handler.go | 4 +-
pkg/broker/filter/filter_handler_test.go | 16 +-
pkg/reconciler/broker/trigger/trigger_test.go | 2 +-
test/e2e/helpers/broker_test_helper.go | 2 +-
test/lib/resources/eventing.go | 6 +-
test/rekt/features/broker/control_plane.go | 2 +-
15 files changed, 946 insertions(+), 162 deletions(-)
diff --git a/config/core/resources/trigger.yaml b/config/core/resources/trigger.yaml
index 281ad42ed72..87b4351b366 100644
--- a/config/core/resources/trigger.yaml
+++ b/config/core/resources/trigger.yaml
@@ -101,6 +101,46 @@ spec:
description: 'Attributes filters events by exact match on event context attributes. Each key in the map is compared with the equivalent key in the event context. An event passes the filter if all values are equal to the specified values. Nested context attributes are not supported as keys. Only string values are supported. '
type: object
x-kubernetes-preserve-unknown-fields: true
+ filters:
+ description: 'Filters is the filter to apply against all events from the Broker. Only events that pass this filter will be sent to the Subscriber. If not specified, will default to allowing all events. '
+ type: object
+ properties:
+ exact:
+ description: 'Exact evaluates to true if the value of the matching CloudEvents attribute is matches exactly the String value specified (case sensitive). Exact must contain exactly one property, where the key is the name of the CloudEvents attribute to be matched, and its value is the String value to use in the comparison. The attribute name and value specified in the filter express cannot be be empty strings.'
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ prefix:
+ description: 'Prefix evaluates to true if the value of the matching CloudEvents attribute starts with the String value specified (case sensitive). Prefix must contain exactly one property, where the key is the name of the CloudEvents attribute to be matched, and its value is the String value to use in the comparison. The attribute name and value specified in the filter express cannot be be empty strings.'
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ suffix:
+ description: 'Suffix evaluates to true if the value of the matching CloudEvents attribute ends with the String value specified (case sensitive). | Suffix must contain exactly one property, where the key is the name of the CloudEvents attribute to be matched, and its value is the String value to use in the comparison. he attribute name and value specified in the filter express cannot be be empty strings.'
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ not:
+ description: 'Not evaluates to true if the nested expression evaluates to false.'
+ type: object
+ # Because kube doesn't allow to use $ref, we can't recursively define this schema.
+ x-kubernetes-preserve-unknown-fields: true
+ all:
+ description: 'All evaluates to true if all the nested expressions evaluate to true. All must contain at least one filter expression.'
+ type: array
+ items:
+ # Because kube doesn't allow to use $ref, we can't recursively reference to the filter schema.
+ type: object
+ description: "Sub schema"
+ x-kubernetes-preserve-unknown-fields: true
+ any:
+ description: 'Any evaluates to true if at least one of the nested expressions evaluate to true. Any must contain at least one filter expression.'
+ type: array
+ items:
+ # Because kube doesn't allow to use $ref, we can't recursively reference to the filter schema.
+ type: object
+ description: "Sub schema"
+ x-kubernetes-preserve-unknown-fields: true
+ # This allows extension filter dialects
+ additionalProperties: true
+ x-kubernetes-preserve-unknown-fields: true
subscriber:
description: Subscriber is the addressable that receives events from the Broker that pass the Filter. It is required.
type: object
diff --git a/docs/eventing-api.md b/docs/eventing-api.md
index 1e2cb9cfe3f..fbfa9d2ce6f 100644
--- a/docs/eventing-api.md
+++ b/docs/eventing-api.md
@@ -1564,8 +1564,8 @@ string
filter
-
-TriggerFilter
+
+AttributesFilter
|
@@ -1577,6 +1577,29 @@ filter will be sent to the Subscriber. If not specified, will default to allowin
+filters
+
+
+SubscriptionsAPIFiltersList
+
+
+ |
+
+(Optional)
+ Filters is an experimental field that conforms to CNCF CloudEvents Subscriptions
+API. It’s An array of filter expressions that evaluates to true or false.
+If any filter expression in the array evaluates to false, the event MUST
+NOT be sent to the Subscriber. If all the filter expressions in the array
+evaluates to true, the event MUST be attempted to be delivered. Absence of
+a filter or empty array implies a value of true. In the event of users
+specifying both Filter and Filters, then the later will override the first.
+This will allow our users to try out the effect of the new filters field
+without compromising existing Filter objects and try it out on existing
+Trigger objects.
+ |
+
+
+
subscriber
@@ -1585,8 +1608,8 @@ knative.dev/pkg/apis/duck/v1.Destination
|
- Subscriber is the addressable that receives events from the Broker that pass the Filter. It
-is required.
+Subscriber is the addressable that receives events from the Broker that pass
+the Filter. It is required.
|
@@ -1623,6 +1646,45 @@ date.
+AttributesFilter
+
+
+(Appears on:TriggerSpec)
+
+
+
AttributesFilter allows to define a filter expression. If multiple filters
+are specified, then the same semantics of AttributesFilter.All is applied.
+If no filter dialect or empty object is specified, then the filter always
+accept the events.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+attributes
+
+
+CloudEventsAttributes
+
+
+ |
+
+(Optional)
+ Attributes filters events by exact match on event context attributes.
+Each key in the map is compared with the equivalent key in the event
+context. An event passes the filter if all values are equal to the
+specified values. Nested context attributes are not supported as keys. Only
+string values are supported.
+ |
+
+
+
BrokerSpec
@@ -1723,12 +1785,26 @@ delivered into the Broker mesh.
-TriggerFilter
+CloudEventsAttributes
+(map[string]string
alias)
+
+(Appears on:AttributesFilter)
+
+
+
CloudEventsAttributes is a map of context attribute names to values for
+filtering by equality. Only exact matches will pass the filter. You can use
+the value “ to indicate all strings match.
+
+SubscriptionsAPIFilter
-(Appears on:TriggerSpec)
+(Appears on:SubscriptionsAPIFilter)
+
SubscriptionsAPIFilter allows defining a filter expression using CloudEvents
+Subscriptions API. If multiple filters are specified, then the same semantics
+of SubscriptionsAPIFilter.All is applied. If no filter dialect or empty
+object is specified, then the filter always accept the events.
@@ -1740,34 +1816,119 @@ delivered into the Broker mesh.
-attributes
+all
-
-TriggerFilterAttributes
+
+SubscriptionsAPIFiltersList
|
(Optional)
- Attributes filters events by exact match on event context attributes.
-Each key in the map is compared with the equivalent key in the event
-context. An event passes the filter if all values are equal to the
-specified values.
-Nested context attributes are not supported as keys. Only string values are supported.
+All evaluates to true if all the nested expressions evaluate to true.
+It must contain at least one filter expression.
|
-
-
-TriggerFilterAttributes
-(map[string]string
alias)
-
-(Appears on:TriggerFilter)
-
+
+
+any
+
+
+SubscriptionsAPIFiltersList
+
+
+ |
+
+(Optional)
+ Any evaluates to true if at least one of the nested expressions evaluates
+to true. It must contain at least one filter expression.
+ |
+
+
+
+not
+
+
+SubscriptionsAPIFilter
+
+
+ |
+
+(Optional)
+ Not evaluates to true if the nested expression evaluates to false.
+ |
+
+
+
+exact
+
+map[string]string
+
+ |
+
+(Optional)
+ Exact evaluates to true if the value of the matching CloudEvents
+attribute matches exactly the String value specified (case-sensitive).
+Exact must contain exactly one property, where the key is the name of the
+CloudEvents attribute to be matched, and its value is the String value to
+use in the comparison. The attribute name and value specified in the filter
+expression cannot be empty strings.
+ |
+
+
+
+prefix
+
+map[string]string
+
+ |
+
+(Optional)
+ Prefix evaluates to true if the value of the matching CloudEvents
+attribute starts with the String value specified (case-sensitive). Prefix
+must contain exactly one property, where the key is the name of the
+CloudEvents attribute to be matched, and its value is the String value to
+use in the comparison. The attribute name and value specified in the filter
+expression cannot be empty strings.
+ |
+
+
+
+suffix
+
+map[string]string
+
+ |
+
+(Optional)
+ Suffix evaluates to true if the value of the matching CloudEvents
+attribute ends with the String value specified (case-sensitive). Suffix
+must contain exactly one property, where the key is the name of the
+CloudEvents attribute to be matched, and its value is the String value to
+use in the comparison. The attribute name and value specified in the filter
+expression cannot be empty strings.
+ |
+
+
+
+Extensions
+
+map[string]*k8s.io/apimachinery/pkg/runtime.RawExtension
+
+ |
+
- TriggerFilterAttributes is a map of context attribute names to values for
-filtering by equality. Only exact matches will pass the filter. You can use the value “
-to indicate all strings match.
+(Members of Extensions are embedded into this type.)
+(Optional)
+Extensions includes the list of additional filter dialects supported by
+specific broker implementations. Check out the documentation of the
+broker implementation you’re using to know about what additional filters
+are supported.
+ |
+
+
+
TriggerSpec
@@ -1798,8 +1959,8 @@ string
filter
-
-TriggerFilter
+
+AttributesFilter
|
@@ -1811,6 +1972,29 @@ filter will be sent to the Subscriber. If not specified, will default to allowin
+filters
+
+
+SubscriptionsAPIFiltersList
+
+
+ |
+
+(Optional)
+ Filters is an experimental field that conforms to CNCF CloudEvents Subscriptions
+API. It’s An array of filter expressions that evaluates to true or false.
+If any filter expression in the array evaluates to false, the event MUST
+NOT be sent to the Subscriber. If all the filter expressions in the array
+evaluates to true, the event MUST be attempted to be delivered. Absence of
+a filter or empty array implies a value of true. In the event of users
+specifying both Filter and Filters, then the later will override the first.
+This will allow our users to try out the effect of the new filters field
+without compromising existing Filter objects and try it out on existing
+Trigger objects.
+ |
+
+
+
subscriber
@@ -1819,8 +2003,8 @@ knative.dev/pkg/apis/duck/v1.Destination
|
- Subscriber is the addressable that receives events from the Broker that pass the Filter. It
-is required.
+Subscriber is the addressable that receives events from the Broker that pass
+the Filter. It is required.
|
diff --git a/pkg/apis/eventing/v1/trigger_defaults.go b/pkg/apis/eventing/v1/trigger_defaults.go
index 4c90811f355..8a5e0598413 100644
--- a/pkg/apis/eventing/v1/trigger_defaults.go
+++ b/pkg/apis/eventing/v1/trigger_defaults.go
@@ -35,8 +35,11 @@ func (t *Trigger) SetDefaults(ctx context.Context) {
func (ts *TriggerSpec) SetDefaults(ctx context.Context) {
// Make a default filter that allows anything.
if ts.Filter == nil {
- ts.Filter = &TriggerFilter{}
+ ts.Filter = &AttributesFilter{
+ Attributes: &CloudEventsAttributes{},
+ }
}
+
// Default the Subscriber namespace
ts.Subscriber.SetDefaults(ctx)
}
diff --git a/pkg/apis/eventing/v1/trigger_defaults_test.go b/pkg/apis/eventing/v1/trigger_defaults_test.go
index 0d01bc10ea1..718059ce5e0 100644
--- a/pkg/apis/eventing/v1/trigger_defaults_test.go
+++ b/pkg/apis/eventing/v1/trigger_defaults_test.go
@@ -31,8 +31,10 @@ var (
defaultBroker = "default"
otherBroker = "other_broker"
namespace = "testnamespace"
- emptyTriggerFilter = &TriggerFilter{}
- defaultTrigger = Trigger{Spec: TriggerSpec{Filter: emptyTriggerFilter}}
+ emptyTriggerFilter = &AttributesFilter{
+ Attributes: &CloudEventsAttributes{},
+ }
+ defaultTrigger = Trigger{Spec: TriggerSpec{Filter: emptyTriggerFilter}}
)
func TestTriggerDefaults(t *testing.T) {
diff --git a/pkg/apis/eventing/v1/trigger_types.go b/pkg/apis/eventing/v1/trigger_types.go
index d7b876c868e..587aa3fff48 100644
--- a/pkg/apis/eventing/v1/trigger_types.go
+++ b/pkg/apis/eventing/v1/trigger_types.go
@@ -81,10 +81,24 @@ type TriggerSpec struct {
// filter will be sent to the Subscriber. If not specified, will default to allowing all events.
//
// +optional
- Filter *TriggerFilter `json:"filter,omitempty"`
+ Filter *AttributesFilter `json:"filter,omitempty"`
+
+ // Filters is an experimental field that conforms to CNCF CloudEvents Subscriptions
+ // API. It's An array of filter expressions that evaluates to true or false.
+ // If any filter expression in the array evaluates to false, the event MUST
+ // NOT be sent to the Subscriber. If all the filter expressions in the array
+ // evaluates to true, the event MUST be attempted to be delivered. Absence of
+ // a filter or empty array implies a value of true. In the event of users
+ // specifying both Filter and Filters, then the later will override the first.
+ // This will allow our users to try out the effect of the new filters field
+ // without compromising existing Filter objects and try it out on existing
+ // Trigger objects.
+ //
+ // +optional
+ Filters *SubscriptionsAPIFiltersList `json:"filters,omitempty"`
- // Subscriber is the addressable that receives events from the Broker that pass the Filter. It
- // is required.
+ // Subscriber is the addressable that receives events from the Broker that pass
+ // the Filter. It is required.
Subscriber duckv1.Destination `json:"subscriber"`
// Delivery contains the delivery spec for this specific trigger.
@@ -92,22 +106,88 @@ type TriggerSpec struct {
Delivery *eventingduckv1.DeliverySpec `json:"delivery,omitempty"`
}
-type TriggerFilter struct {
+// AttributesFilter allows to define a filter expression. If multiple filters
+// are specified, then the same semantics of AttributesFilter.All is applied.
+// If no filter dialect or empty object is specified, then the filter always
+// accept the events.
+type AttributesFilter struct {
// Attributes filters events by exact match on event context attributes.
// Each key in the map is compared with the equivalent key in the event
// context. An event passes the filter if all values are equal to the
- // specified values.
+ // specified values. Nested context attributes are not supported as keys. Only
+ // string values are supported.
//
- // Nested context attributes are not supported as keys. Only string values are supported.
+ // +optional
+ Attributes *CloudEventsAttributes `json:"attributes,omitempty"`
+}
+
+type SubscriptionsAPIFiltersList []SubscriptionsAPIFilter
+
+// SubscriptionsAPIFilter allows defining a filter expression using CloudEvents
+// Subscriptions API. If multiple filters are specified, then the same semantics
+// of SubscriptionsAPIFilter.All is applied. If no filter dialect or empty
+// object is specified, then the filter always accept the events.
+type SubscriptionsAPIFilter struct {
+ // All evaluates to true if all the nested expressions evaluate to true.
+ // It must contain at least one filter expression.
+ //
+ // +optional
+ All *SubscriptionsAPIFiltersList `json:"all,omitempty"`
+
+ // Any evaluates to true if at least one of the nested expressions evaluates
+ // to true. It must contain at least one filter expression.
+ //
+ // +optional
+ Any *SubscriptionsAPIFiltersList `json:"any,omitempty"`
+
+ // Not evaluates to true if the nested expression evaluates to false.
+ //
+ // +optional
+ Not *SubscriptionsAPIFilter `json:"not,omitempty"`
+
+ // Exact evaluates to true if the value of the matching CloudEvents
+ // attribute matches exactly the String value specified (case-sensitive).
+ // Exact must contain exactly one property, where the key is the name of the
+ // CloudEvents attribute to be matched, and its value is the String value to
+ // use in the comparison. The attribute name and value specified in the filter
+ // expression cannot be empty strings.
+ //
+ // +optional
+ Exact *map[string]string `json:"exact,omitempty"`
+
+ // Prefix evaluates to true if the value of the matching CloudEvents
+ // attribute starts with the String value specified (case-sensitive). Prefix
+ // must contain exactly one property, where the key is the name of the
+ // CloudEvents attribute to be matched, and its value is the String value to
+ // use in the comparison. The attribute name and value specified in the filter
+ // expression cannot be empty strings.
+ //
+ // +optional
+ Prefix *map[string]string `json:"prefix,omitempty"`
+
+ // Suffix evaluates to true if the value of the matching CloudEvents
+ // attribute ends with the String value specified (case-sensitive). Suffix
+ // must contain exactly one property, where the key is the name of the
+ // CloudEvents attribute to be matched, and its value is the String value to
+ // use in the comparison. The attribute name and value specified in the filter
+ // expression cannot be empty strings.
+ //
+ // +optional
+ Suffix *map[string]string `json:"suffix,omitempty"`
+
+ // Extensions includes the list of additional filter dialects supported by
+ // specific broker implementations. Check out the documentation of the
+ // broker implementation you're using to know about what additional filters
+ // are supported.
//
// +optional
- Attributes TriggerFilterAttributes `json:"attributes,omitempty"`
+ Extensions *map[string]*runtime.RawExtension `json:",inline"`
}
-// TriggerFilterAttributes is a map of context attribute names to values for
-// filtering by equality. Only exact matches will pass the filter. You can use the value ''
-// to indicate all strings match.
-type TriggerFilterAttributes map[string]string
+// CloudEventsAttributes is a map of context attribute names to values for
+// filtering by equality. Only exact matches will pass the filter. You can use
+// the value '' to indicate all strings match.
+type CloudEventsAttributes map[string]string
// TriggerStatus represents the current state of a Trigger.
type TriggerStatus struct {
diff --git a/pkg/apis/eventing/v1/trigger_validation.go b/pkg/apis/eventing/v1/trigger_validation.go
index 4b367340a6b..63e04880fad 100644
--- a/pkg/apis/eventing/v1/trigger_validation.go
+++ b/pkg/apis/eventing/v1/trigger_validation.go
@@ -22,10 +22,9 @@ import (
"fmt"
"regexp"
+ corev1 "k8s.io/api/core/v1"
"knative.dev/pkg/apis"
"knative.dev/pkg/kmp"
-
- corev1 "k8s.io/api/core/v1"
)
var (
@@ -35,7 +34,7 @@ var (
// Validate the Trigger.
func (t *Trigger) Validate(ctx context.Context) *apis.FieldError {
- errs := t.Spec.Validate(ctx).ViaField("spec")
+ errs := t.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec")
errs = t.validateAnnotation(errs, DependencyAnnotation, t.validateDependencyAnnotation)
errs = t.validateAnnotation(errs, InjectionAnnotation, t.validateInjectionAnnotation)
if apis.IsInUpdate(ctx) {
@@ -46,36 +45,20 @@ func (t *Trigger) Validate(ctx context.Context) *apis.FieldError {
}
// Validate the TriggerSpec.
-func (ts *TriggerSpec) Validate(ctx context.Context) *apis.FieldError {
- var errs *apis.FieldError
+func (ts *TriggerSpec) Validate(ctx context.Context) (errs *apis.FieldError) {
if ts.Broker == "" {
- fe := apis.ErrMissingField("broker")
- errs = errs.Also(fe)
- }
-
- if ts.Filter != nil {
- for attr := range map[string]string(ts.Filter.Attributes) {
- if !validAttributeName.MatchString(attr) {
- fe := &apis.FieldError{
- Message: fmt.Sprintf("Invalid attribute name: %q", attr),
- Paths: []string{"filter.attributes"},
- }
- errs = errs.Also(fe)
- }
- }
- }
-
- if fe := ts.Subscriber.Validate(ctx); fe != nil {
- errs = errs.Also(fe.ViaField("subscriber"))
- }
-
- if ts.Delivery != nil {
- if de := ts.Delivery.Validate(ctx); de != nil {
- errs = errs.Also(de.ViaField("delivery"))
- }
- }
-
- return errs
+ errs = errs.Also(apis.ErrMissingField("broker"))
+ }
+
+ return errs.Also(
+ ValidateAttributeFilters(ts.Filter).ViaField("filter"),
+ ).Also(
+ ValidateSubscriptionAPIFilters(ts.Filters, "filters").ViaField("filters"),
+ ).Also(
+ ts.Subscriber.Validate(ctx).ViaField("subscriber"),
+ ).Also(
+ ts.Delivery.Validate(ctx).ViaField("delivery"),
+ )
}
// CheckImmutableFields checks that any immutable fields were not changed.
@@ -163,3 +146,94 @@ func (t *Trigger) validateInjectionAnnotation(injectionAnnotation string) *apis.
}
return nil
}
+
+func ValidateAttributeFilters(filter *AttributesFilter) (errs *apis.FieldError) {
+ if filter == nil {
+ return nil
+ }
+ // Validate Attributes
+ for attr := range *filter.Attributes {
+ if !validAttributeName.MatchString(attr) {
+ errs = errs.Also(apis.ErrInvalidKeyName(attr, apis.CurrentField, "Attribute name must start with a letter and can only contain lowercase alphanumeric").ViaFieldKey("attributes", attr))
+ }
+ }
+ return errs
+}
+
+func ValidateExactExpression(expr *map[string]string) (errs *apis.FieldError) {
+ if expr == nil {
+ return nil
+ }
+ if len(*expr) != 1 {
+ return apis.ErrGeneric("Exact can have only one key-value", apis.CurrentField)
+ }
+ for attr := range *expr {
+ if !validAttributeName.MatchString(attr) {
+ errs = errs.Also(apis.ErrInvalidKeyName(attr, apis.CurrentField, "Attribute name must start with a letter and can only contain lowercase alphanumeric").ViaKey(attr))
+ }
+ }
+ return errs
+}
+
+func ValidatePrefixExpression(expr *map[string]string) (errs *apis.FieldError) {
+ if expr == nil {
+ return nil
+ }
+ if len(*expr) != 1 {
+ return apis.ErrGeneric("Prefix can have only one key-value", apis.CurrentField)
+ }
+ for attr := range *expr {
+ if !validAttributeName.MatchString(attr) {
+ errs = errs.Also(apis.ErrInvalidKeyName(attr, apis.CurrentField, "Attribute name must start with a letter and can only contain lowercase alphanumeric").ViaKey(attr))
+ }
+ }
+ return errs
+}
+
+func ValidateSuffixExpression(expr *map[string]string) (errs *apis.FieldError) {
+ if expr == nil {
+ return nil
+ }
+ if len(*expr) != 1 {
+ return apis.ErrGeneric("Suffix can have only one key-value", apis.CurrentField)
+ }
+ for attr := range *expr {
+ if !validAttributeName.MatchString(attr) {
+ errs = errs.Also(apis.ErrInvalidKeyName(attr, apis.CurrentField, "Attribute name must start with a letter and can only contain lowercase alphanumeric").ViaKey(attr))
+ }
+ }
+ return errs
+}
+
+func ValidateSubscriptionAPIFilters(filters *SubscriptionsAPIFiltersList, field string) (errs *apis.FieldError) {
+ if filters == nil {
+ return nil
+ }
+ if len(*filters) < 1 {
+ return apis.ErrGeneric(fmt.Sprintf("%s must contain at least one nested filter", field), apis.CurrentField)
+ }
+
+ for i, f := range *filters {
+ f := f
+ errs = errs.Also(ValidateSubscriptionAPIFilter(&f)).ViaIndex(i)
+ }
+ return errs
+}
+
+func ValidateSubscriptionAPIFilter(filter *SubscriptionsAPIFilter) (errs *apis.FieldError) {
+ if filter == nil {
+ return nil
+ }
+ errs = errs.Also(
+ ValidateExactExpression(filter.Exact).ViaField("exact"),
+ ).Also(
+ ValidatePrefixExpression(filter.Prefix).ViaField("prefix"),
+ ).Also(
+ ValidateSuffixExpression(filter.Suffix).ViaField("suffix"),
+ ).Also(
+ ValidateSubscriptionAPIFilters(filter.All, "all").ViaField("all"),
+ ).Also(
+ ValidateSubscriptionAPIFilters(filter.Any, "any").ViaField("any"),
+ ).Also(ValidateSubscriptionAPIFilter(filter.Not).ViaField("not"))
+ return errs
+}
diff --git a/pkg/apis/eventing/v1/trigger_validation_test.go b/pkg/apis/eventing/v1/trigger_validation_test.go
index 629164c479d..b9c714b2748 100644
--- a/pkg/apis/eventing/v1/trigger_validation_test.go
+++ b/pkg/apis/eventing/v1/trigger_validation_test.go
@@ -23,19 +23,28 @@ import (
"github.com/google/go-cmp/cmp"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1"
+ "k8s.io/apimachinery/pkg/runtime"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
+
+ eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1"
)
var (
- validEmptyFilter = &TriggerFilter{}
- validAttributesFilter = &TriggerFilter{
- Attributes: TriggerFilterAttributes{
+ validEmptyAttributesFilter = &AttributesFilter{
+ Attributes: &CloudEventsAttributes{},
+ }
+ validAttributesFilter = &AttributesFilter{
+ Attributes: &CloudEventsAttributes{
"type": "other_type",
"source": "other_source",
},
}
+ validSubscriptionAPIFilter = &SubscriptionsAPIFilter{
+ Exact: &map[string]string{
+ "type": "other_type",
+ },
+ }
validSubscriber = duckv1.Destination{
Ref: &duckv1.KReference{
Namespace: "namespace",
@@ -76,7 +85,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "default",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: nil,
@@ -100,7 +109,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -118,7 +127,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -136,7 +145,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -153,7 +162,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -170,7 +179,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -187,7 +196,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -225,7 +234,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "default",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -266,7 +275,7 @@ func TestTriggerValidation(t *testing.T) {
}},
Spec: TriggerSpec{
Broker: "test-broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -282,7 +291,7 @@ func TestTriggerValidation(t *testing.T) {
},
Spec: TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
Delivery: &eventingduckv1.DeliverySpec{
BackoffDelay: &invalidString,
@@ -315,7 +324,7 @@ func TestTriggerUpdateValidation(t *testing.T) {
},
Spec: TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
tNew: &Trigger{
@@ -324,7 +333,7 @@ func TestTriggerUpdateValidation(t *testing.T) {
},
Spec: TriggerSpec{
Broker: "anotherBroker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
}},
want: &apis.FieldError{
@@ -381,8 +390,8 @@ func TestTriggerSpecValidation(t *testing.T) {
name: "missing attributes keys, match all",
ts: &TriggerSpec{
Broker: "test_broker",
- Filter: &TriggerFilter{
- Attributes: TriggerFilterAttributes{},
+ Filter: &AttributesFilter{
+ Attributes: &CloudEventsAttributes{},
},
Subscriber: validSubscriber,
},
@@ -391,32 +400,30 @@ func TestTriggerSpecValidation(t *testing.T) {
name: "invalid attribute name, start with number",
ts: &TriggerSpec{
Broker: "test_broker",
- Filter: &TriggerFilter{
- Attributes: TriggerFilterAttributes{
+ Filter: &AttributesFilter{
+ Attributes: &CloudEventsAttributes{
"0invalid": "my-value",
},
},
Subscriber: validSubscriber,
},
- want: &apis.FieldError{
- Message: `Invalid attribute name: "0invalid"`,
- Paths: []string{"filter.attributes"},
- },
+ want: apis.ErrInvalidKeyName("0invalid", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("attributes", "0invalid").ViaField("filter"),
}, {
name: "invalid attribute name, capital letters",
ts: &TriggerSpec{
Broker: "test_broker",
- Filter: &TriggerFilter{
- Attributes: TriggerFilterAttributes{
+ Filter: &AttributesFilter{
+ Attributes: &CloudEventsAttributes{
"invALID": "my-value",
},
},
Subscriber: validSubscriber,
},
- want: &apis.FieldError{
- Message: `Invalid attribute name: "invALID"`,
- Paths: []string{"filter.attributes"},
- },
+ want: apis.ErrInvalidKeyName("invALID", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("attributes", "invALID").ViaField("filter"),
}, {
name: "missing subscriber",
ts: &TriggerSpec{
@@ -444,7 +451,7 @@ func TestTriggerSpecValidation(t *testing.T) {
name: "valid empty filter",
ts: &TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
},
want: &apis.FieldError{},
@@ -468,7 +475,7 @@ func TestTriggerSpecValidation(t *testing.T) {
name: "invalid delivery, invalid delay string",
ts: &TriggerSpec{
Broker: "test_broker",
- Filter: validEmptyFilter,
+ Filter: validEmptyAttributesFilter,
Subscriber: validSubscriber,
Delivery: &eventingduckv1.DeliverySpec{
BackoffDelay: &invalidString,
@@ -487,6 +494,268 @@ func TestTriggerSpecValidation(t *testing.T) {
}
}
+func TestFilterSpecValidation(t *testing.T) {
+ tests := []struct {
+ name string
+ filter *AttributesFilter
+ filters *SubscriptionsAPIFiltersList
+ want *apis.FieldError
+ }{{
+ name: "missing filters, match all",
+ filters: &SubscriptionsAPIFiltersList{},
+ want: apis.ErrGeneric("filters must contain at least one nested filter", "filters"),
+ }, {
+ name: "invalid exact filter attribute name, start with number",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Exact: &map[string]string{
+ "0invalid": "some-value",
+ },
+ },
+ },
+ want: apis.ErrInvalidKeyName("0invalid", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("exact", "0invalid").ViaFieldIndex("filters", 0),
+ }, {
+ name: "invalid exact filter attribute name, capital letters",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Exact: &map[string]string{
+ "invALID": "some-value",
+ },
+ },
+ },
+ want: apis.ErrInvalidKeyName("invALID", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("exact", "invALID").ViaFieldIndex("filters", 0),
+ }, {
+ name: "valid empty filter",
+ filter: validEmptyAttributesFilter,
+ want: &apis.FieldError{},
+ }, {
+ name: "valid SourceAndType filter",
+ filter: validAttributesFilter,
+ want: &apis.FieldError{},
+ }, {
+ name: "valid Attributes filter",
+ filter: validAttributesFilter,
+ want: &apis.FieldError{},
+ }, {
+ name: "exact filter contains more than one attribute",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Exact: &map[string]string{
+ "myext": "abc",
+ "anotherext": "xyz",
+ },
+ },
+ },
+ want: apis.ErrGeneric("Exact can have only one key-value", "exact").ViaFieldIndex("filters", 0),
+ }, {
+ name: "valid exact filter",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Exact: &map[string]string{
+ "valid": "abc",
+ },
+ },
+ },
+ want: &apis.FieldError{},
+ }, {
+ name: "suffix filter contains more than one attribute",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Suffix: &map[string]string{
+ "myext": "abc",
+ "anotherext": "xyz",
+ },
+ },
+ },
+ want: apis.ErrGeneric("Suffix can have only one key-value", "suffix").ViaFieldIndex("filters", 0),
+ }, {
+ name: "suffix filter contains invalid attribute name",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Suffix: &map[string]string{
+ "invALID": "abc",
+ },
+ },
+ },
+ want: apis.ErrInvalidKeyName("invALID", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("suffix", "invALID").ViaFieldIndex("filters", 0),
+ }, {
+ name: "valid suffix filter",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Suffix: &map[string]string{
+ "valid": "abc",
+ },
+ },
+ },
+ want: &apis.FieldError{},
+ }, {
+ name: "prefix filter contains more than one attribute",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Prefix: &map[string]string{
+ "myext": "abc",
+ "anotherext": "xyz",
+ },
+ },
+ },
+ want: apis.ErrGeneric("Prefix can have only one key-value", "prefix").ViaFieldIndex("filters", 0),
+ }, {
+ name: "prefix filter contains invalid attribute name",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Prefix: &map[string]string{
+ "invALID": "abc",
+ },
+ },
+ },
+ want: apis.ErrInvalidKeyName("invALID", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("prefix", "invALID").ViaFieldIndex("filters", 0),
+ }, {
+ name: "valid prefix filter",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Prefix: &map[string]string{
+ "valid": "abc",
+ },
+ },
+ },
+ want: &apis.FieldError{},
+ }, {
+ name: "not nested expression is valid",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Not: validSubscriptionAPIFilter,
+ },
+ },
+ want: &apis.FieldError{},
+ }, {
+ name: "not nested expression is invalid",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Not: &SubscriptionsAPIFilter{
+ Prefix: &map[string]string{
+ "invALID": "abc",
+ },
+ },
+ },
+ },
+ want: apis.ErrInvalidKeyName("invALID", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("prefix", "invALID").ViaField("not").ViaFieldIndex("filters", 0),
+ }, {
+ name: "all filter is empty",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ All: &SubscriptionsAPIFiltersList{},
+ },
+ },
+ want: apis.ErrGeneric("all must contain at least one nested filter", "all").ViaFieldIndex("filters", 0),
+ }, {
+ name: "all filter is valid",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ All: &SubscriptionsAPIFiltersList{
+ *validSubscriptionAPIFilter,
+ SubscriptionsAPIFilter{
+ Exact: &map[string]string{"myattr": "myval"},
+ },
+ },
+ },
+ },
+ want: &apis.FieldError{},
+ }, {
+ name: "all filter sub expression is invalid",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ All: &SubscriptionsAPIFiltersList{
+ *validSubscriptionAPIFilter,
+ SubscriptionsAPIFilter{
+ Exact: &map[string]string{"myattr": "myval"},
+ },
+ SubscriptionsAPIFilter{
+ Prefix: &map[string]string{
+ "invALID": "abc",
+ },
+ },
+ },
+ },
+ },
+ want: apis.ErrInvalidKeyName("invALID", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("prefix", "invALID").ViaFieldIndex("all", 2).ViaFieldIndex("filters", 0),
+ }, {
+ name: "any filter is empty",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Any: &SubscriptionsAPIFiltersList{},
+ },
+ },
+ want: apis.ErrGeneric("any must contain at least one nested filter", "any").ViaFieldIndex("filters", 0),
+ }, {
+ name: "any filter is valid",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Any: &SubscriptionsAPIFiltersList{
+ *validSubscriptionAPIFilter,
+ SubscriptionsAPIFilter{
+ Exact: &map[string]string{"myattr": "myval"},
+ },
+ },
+ },
+ },
+ want: &apis.FieldError{},
+ }, {
+ name: "any filter sub expression is invalid",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Any: &SubscriptionsAPIFiltersList{
+ *validSubscriptionAPIFilter,
+ SubscriptionsAPIFilter{
+ Exact: &map[string]string{"myattr": "myval"},
+ },
+ SubscriptionsAPIFilter{
+ Prefix: &map[string]string{"invALID": "abc"},
+ },
+ },
+ },
+ },
+ want: apis.ErrInvalidKeyName("invALID", apis.CurrentField,
+ "Attribute name must start with a letter and can only contain "+
+ "lowercase alphanumeric").ViaFieldKey("prefix", "invALID").ViaFieldIndex("any", 2).ViaFieldIndex("filters", 0)}, {
+ name: "raw extension expression is valid",
+ filters: &SubscriptionsAPIFiltersList{
+ SubscriptionsAPIFilter{
+ Extensions: &map[string]*runtime.RawExtension{
+ "juel": {Raw: []byte("\"myExpressionUsingJUEL\"")},
+ },
+ },
+ },
+ want: &apis.FieldError{},
+ }}
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ ts := &TriggerSpec{
+ Broker: "test_broker",
+ Filter: test.filter,
+ Filters: test.filters,
+ Subscriber: validSubscriber,
+ }
+ got := ts.Validate(context.TODO())
+ if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" {
+ t.Errorf("Validate TriggerSpec (-want, +got) =\n%s", diff)
+ }
+ })
+ }
+}
+
func TestTriggerImmutableFields(t *testing.T) {
tests := []struct {
name string
diff --git a/pkg/apis/eventing/v1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1/zz_generated.deepcopy.go
index 908b4e354a8..28e01388c4d 100644
--- a/pkg/apis/eventing/v1/zz_generated.deepcopy.go
+++ b/pkg/apis/eventing/v1/zz_generated.deepcopy.go
@@ -27,6 +27,33 @@ import (
duckv1 "knative.dev/pkg/apis/duck/v1"
)
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AttributesFilter) DeepCopyInto(out *AttributesFilter) {
+ *out = *in
+ if in.Attributes != nil {
+ in, out := &in.Attributes, &out.Attributes
+ *out = new(CloudEventsAttributes)
+ if **in != nil {
+ in, out := *in, *out
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AttributesFilter.
+func (in *AttributesFilter) DeepCopy() *AttributesFilter {
+ if in == nil {
+ return nil
+ }
+ out := new(AttributesFilter)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Broker) DeepCopyInto(out *Broker) {
*out = *in
@@ -133,78 +160,172 @@ func (in *BrokerStatus) DeepCopy() *BrokerStatus {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *Trigger) DeepCopyInto(out *Trigger) {
- *out = *in
- out.TypeMeta = in.TypeMeta
- in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
- in.Spec.DeepCopyInto(&out.Spec)
- in.Status.DeepCopyInto(&out.Status)
- return
+func (in CloudEventsAttributes) DeepCopyInto(out *CloudEventsAttributes) {
+ {
+ in := &in
+ *out = make(CloudEventsAttributes, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ return
+ }
}
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trigger.
-func (in *Trigger) DeepCopy() *Trigger {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudEventsAttributes.
+func (in CloudEventsAttributes) DeepCopy() CloudEventsAttributes {
if in == nil {
return nil
}
- out := new(Trigger)
+ out := new(CloudEventsAttributes)
in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *Trigger) DeepCopyObject() runtime.Object {
- if c := in.DeepCopy(); c != nil {
- return c
- }
- return nil
+ return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *TriggerFilter) DeepCopyInto(out *TriggerFilter) {
+func (in *SubscriptionsAPIFilter) DeepCopyInto(out *SubscriptionsAPIFilter) {
*out = *in
- if in.Attributes != nil {
- in, out := &in.Attributes, &out.Attributes
- *out = make(TriggerFilterAttributes, len(*in))
- for key, val := range *in {
- (*out)[key] = val
+ if in.All != nil {
+ in, out := &in.All, &out.All
+ *out = new(SubscriptionsAPIFiltersList)
+ if **in != nil {
+ in, out := *in, *out
+ *out = make([]SubscriptionsAPIFilter, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ }
+ if in.Any != nil {
+ in, out := &in.Any, &out.Any
+ *out = new(SubscriptionsAPIFiltersList)
+ if **in != nil {
+ in, out := *in, *out
+ *out = make([]SubscriptionsAPIFilter, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ }
+ if in.Not != nil {
+ in, out := &in.Not, &out.Not
+ *out = new(SubscriptionsAPIFilter)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Exact != nil {
+ in, out := &in.Exact, &out.Exact
+ *out = new(map[string]string)
+ if **in != nil {
+ in, out := *in, *out
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ }
+ if in.Prefix != nil {
+ in, out := &in.Prefix, &out.Prefix
+ *out = new(map[string]string)
+ if **in != nil {
+ in, out := *in, *out
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ }
+ if in.Suffix != nil {
+ in, out := &in.Suffix, &out.Suffix
+ *out = new(map[string]string)
+ if **in != nil {
+ in, out := *in, *out
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ }
+ if in.Extensions != nil {
+ in, out := &in.Extensions, &out.Extensions
+ *out = new(map[string]*runtime.RawExtension)
+ if **in != nil {
+ in, out := *in, *out
+ *out = make(map[string]*runtime.RawExtension, len(*in))
+ for key, val := range *in {
+ var outVal *runtime.RawExtension
+ if val == nil {
+ (*out)[key] = nil
+ } else {
+ in, out := &val, &outVal
+ *out = new(runtime.RawExtension)
+ (*in).DeepCopyInto(*out)
+ }
+ (*out)[key] = outVal
+ }
}
}
return
}
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerFilter.
-func (in *TriggerFilter) DeepCopy() *TriggerFilter {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionsAPIFilter.
+func (in *SubscriptionsAPIFilter) DeepCopy() *SubscriptionsAPIFilter {
if in == nil {
return nil
}
- out := new(TriggerFilter)
+ out := new(SubscriptionsAPIFilter)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in TriggerFilterAttributes) DeepCopyInto(out *TriggerFilterAttributes) {
+func (in SubscriptionsAPIFiltersList) DeepCopyInto(out *SubscriptionsAPIFiltersList) {
{
in := &in
- *out = make(TriggerFilterAttributes, len(*in))
- for key, val := range *in {
- (*out)[key] = val
+ *out = make(SubscriptionsAPIFiltersList, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
}
return
}
}
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerFilterAttributes.
-func (in TriggerFilterAttributes) DeepCopy() TriggerFilterAttributes {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionsAPIFiltersList.
+func (in SubscriptionsAPIFiltersList) DeepCopy() SubscriptionsAPIFiltersList {
if in == nil {
return nil
}
- out := new(TriggerFilterAttributes)
+ out := new(SubscriptionsAPIFiltersList)
in.DeepCopyInto(out)
return *out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Trigger) DeepCopyInto(out *Trigger) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+ in.Status.DeepCopyInto(&out.Status)
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trigger.
+func (in *Trigger) DeepCopy() *Trigger {
+ if in == nil {
+ return nil
+ }
+ out := new(Trigger)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Trigger) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TriggerList) DeepCopyInto(out *TriggerList) {
*out = *in
@@ -243,9 +364,20 @@ func (in *TriggerSpec) DeepCopyInto(out *TriggerSpec) {
*out = *in
if in.Filter != nil {
in, out := &in.Filter, &out.Filter
- *out = new(TriggerFilter)
+ *out = new(AttributesFilter)
(*in).DeepCopyInto(*out)
}
+ if in.Filters != nil {
+ in, out := &in.Filters, &out.Filters
+ *out = new(SubscriptionsAPIFiltersList)
+ if **in != nil {
+ in, out := *in, *out
+ *out = make([]SubscriptionsAPIFilter, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ }
in.Subscriber.DeepCopyInto(&out.Subscriber)
if in.Delivery != nil {
in, out := &in.Delivery, &out.Delivery
diff --git a/pkg/apis/feature/api_validation_test.go b/pkg/apis/feature/api_validation_test.go
index 86e7958ada4..32fbaf23d4d 100644
--- a/pkg/apis/feature/api_validation_test.go
+++ b/pkg/apis/feature/api_validation_test.go
@@ -62,7 +62,7 @@ func TestValidateAPIFields(t *testing.T) {
Subscriber: duckv1.Destination{
URI: apis.HTTP("example.com"),
},
- Filter: &eventingv1.TriggerFilter{},
+ Filter: &eventingv1.AttributesFilter{},
},
experimentalFields: []string{"Filter"},
},
@@ -77,7 +77,7 @@ func TestValidateAPIFields(t *testing.T) {
Subscriber: duckv1.Destination{
URI: apis.HTTP("example.com"),
},
- Filter: &eventingv1.TriggerFilter{},
+ Filter: &eventingv1.AttributesFilter{},
},
experimentalFields: []string{"Filter"},
wantErrs: &apis.FieldError{
@@ -127,8 +127,8 @@ func TestValidateAPIFields(t *testing.T) {
flags: map[string]Flag{
flagName: Disabled,
},
- object: &eventingv1.TriggerFilter{
- Attributes: map[string]string{},
+ object: &eventingv1.AttributesFilter{
+ Attributes: (*eventingv1.CloudEventsAttributes)(&map[string]string{}),
},
experimentalFields: []string{"Attributes"},
wantErrs: &apis.FieldError{
diff --git a/pkg/broker/filter/filter_handler.go b/pkg/broker/filter/filter_handler.go
index 215c0d3f498..ce3b13f7c2c 100644
--- a/pkg/broker/filter/filter_handler.go
+++ b/pkg/broker/filter/filter_handler.go
@@ -325,7 +325,7 @@ func (h *Handler) getTrigger(ref path.NamespacedNameUID) (*eventingv1.Trigger, e
return t, nil
}
-func filterEvent(ctx context.Context, filter *eventingv1.TriggerFilter, event cloudevents.Event) eventfilter.FilterResult {
+func filterEvent(ctx context.Context, filter *eventingv1.AttributesFilter, event cloudevents.Event) eventfilter.FilterResult {
if filter == nil {
return eventfilter.NoFilter
}
@@ -338,7 +338,7 @@ func filterEvent(ctx context.Context, filter *eventingv1.TriggerFilter, event cl
// triggerFilterAttribute returns the filter attribute value for a given `attributeName`. If it doesn't not exist,
// returns the any value filter.
-func triggerFilterAttribute(filter *eventingv1.TriggerFilter, attributeName string) string {
+func triggerFilterAttribute(filter *eventingv1.AttributesFilter, attributeName string) string {
attributeValue := eventingv1.TriggerAnyFilter
if filter != nil {
attrs := map[string]string(filter.Attributes)
diff --git a/pkg/broker/filter/filter_handler_test.go b/pkg/broker/filter/filter_handler_test.go
index 15236d99d1f..a8f0d4db380 100644
--- a/pkg/broker/filter/filter_handler_test.go
+++ b/pkg/broker/filter/filter_handler_test.go
@@ -549,18 +549,18 @@ func (h *fakeHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
}
}
-func makeTriggerFilterWithAttributes(t, s string) *eventingv1.TriggerFilter {
- return &eventingv1.TriggerFilter{
- Attributes: eventingv1.TriggerFilterAttributes{
+func makeTriggerFilterWithAttributes(t, s string) *eventingv1.AttributesFilter {
+ return &eventingv1.AttributesFilter{
+ Attributes: eventingv1.CloudEventsAttributes{
"type": t,
"source": s,
},
}
}
-func makeTriggerFilterWithAttributesAndExtension(t, s, e string) *eventingv1.TriggerFilter {
- return &eventingv1.TriggerFilter{
- Attributes: eventingv1.TriggerFilterAttributes{
+func makeTriggerFilterWithAttributesAndExtension(t, s, e string) *eventingv1.AttributesFilter {
+ return &eventingv1.AttributesFilter{
+ Attributes: eventingv1.CloudEventsAttributes{
"type": t,
"source": s,
extensionName: e,
@@ -568,13 +568,13 @@ func makeTriggerFilterWithAttributesAndExtension(t, s, e string) *eventingv1.Tri
}
}
-func makeTriggerWithDifferentUID(filter *eventingv1.TriggerFilter) *eventingv1.Trigger {
+func makeTriggerWithDifferentUID(filter *eventingv1.AttributesFilter) *eventingv1.Trigger {
t := makeTrigger(filter)
t.ObjectMeta.UID = "wrongone"
return t
}
-func makeTrigger(filter *eventingv1.TriggerFilter) *eventingv1.Trigger {
+func makeTrigger(filter *eventingv1.AttributesFilter) *eventingv1.Trigger {
return &eventingv1.Trigger{
TypeMeta: metav1.TypeMeta{
APIVersion: "eventing.knative.dev/v1",
diff --git a/pkg/reconciler/broker/trigger/trigger_test.go b/pkg/reconciler/broker/trigger/trigger_test.go
index e7867a2f284..12e83e7f1b2 100644
--- a/pkg/reconciler/broker/trigger/trigger_test.go
+++ b/pkg/reconciler/broker/trigger/trigger_test.go
@@ -1072,7 +1072,7 @@ func makeTrigger(subscriberNamespace string) *eventingv1.Trigger {
},
Spec: eventingv1.TriggerSpec{
Broker: brokerName,
- Filter: &eventingv1.TriggerFilter{
+ Filter: &eventingv1.AttributesFilter{
Attributes: map[string]string{"Source": "Any", "Type": "Any"},
},
Subscriber: duckv1.Destination{
diff --git a/test/e2e/helpers/broker_test_helper.go b/test/e2e/helpers/broker_test_helper.go
index de6a8ef4212..27a4e67f742 100644
--- a/test/e2e/helpers/broker_test_helper.go
+++ b/test/e2e/helpers/broker_test_helper.go
@@ -148,7 +148,7 @@ func TestBrokerWithManyTriggers(ctx context.Context, t *testing.T, brokerCreator
// to set in the subscriber and services pod
// The attributes in these test cases will be used as assertions on the receivers
eventFilters []eventTestCase
- // TriggerFilter with DeprecatedSourceAndType or not
+ // AttributesFilter with DeprecatedSourceAndType or not
deprecatedTriggerFilter bool
}{
{
diff --git a/test/lib/resources/eventing.go b/test/lib/resources/eventing.go
index d4720d2b8f5..251e01e49af 100644
--- a/test/lib/resources/eventing.go
+++ b/test/lib/resources/eventing.go
@@ -207,7 +207,7 @@ func Broker(name string, options ...BrokerOption) *eventingv1.Broker {
return broker
}
-// WithAttributesTriggerFilter returns an option that adds a TriggerFilter with Attributes for the given Trigger.
+// WithAttributesTriggerFilter returns an option that adds a AttributesFilter with Attributes for the given Trigger.
func WithAttributesTriggerFilter(eventSource, eventType string, extensions map[string]interface{}) TriggerOption {
attrs := make(map[string]string)
if eventType != "" {
@@ -224,8 +224,8 @@ func WithAttributesTriggerFilter(eventSource, eventType string, extensions map[s
attrs[k] = fmt.Sprintf("%v", v)
}
return func(t *eventingv1.Trigger) {
- t.Spec.Filter = &eventingv1.TriggerFilter{
- Attributes: eventingv1.TriggerFilterAttributes(attrs),
+ t.Spec.Filter = &eventingv1.AttributesFilter{
+ Attributes: eventingv1.CloudEventsAttributes(attrs),
}
}
}
diff --git a/test/rekt/features/broker/control_plane.go b/test/rekt/features/broker/control_plane.go
index 76404ee4f8b..071a0c155bb 100644
--- a/test/rekt/features/broker/control_plane.go
+++ b/test/rekt/features/broker/control_plane.go
@@ -258,7 +258,7 @@ func ControlPlaneTrigger_WithInvalidFilters(brokerName string) *feature.Feature
trigger := triggerfeatures.GetTrigger(ctx, t)
if trigger.Spec.Filter == nil {
- trigger.Spec.Filter = &eventingv1.TriggerFilter{
+ trigger.Spec.Filter = &eventingv1.AttributesFilter{
Attributes: map[string]string{},
}
} else if trigger.Spec.Filter.Attributes == nil {