diff --git a/config/core/resources/broker.yaml b/config/core/resources/broker.yaml index 0ac6c3b8a63..4c515555c72 100644 --- a/config/core/resources/broker.yaml +++ b/config/core/resources/broker.yaml @@ -66,8 +66,9 @@ spec: holding it if left out.' type: string delivery: - description: 'Delivery is the delivery specification for Events within - the Broker mesh. This includes things like retries, DLQ, etc.' + description: 'Delivery contains the delivery spec for each trigger + to this Broker. Each trigger delivery spec, if any, overrides this + global delivery spec.' type: object properties: backoffDelay: diff --git a/config/core/resources/trigger.yaml b/config/core/resources/trigger.yaml index d4f2c7c7a9f..30aaea1ec88 100644 --- a/config/core/resources/trigger.yaml +++ b/config/core/resources/trigger.yaml @@ -91,6 +91,59 @@ spec: uri: type: string description: 'the target URI or, if ref is provided, a relative URI reference that will be combined with ref to produce a target URI.' + delivery: + description: 'Delivery contains the delivery spec for this specific trigger.' + type: object + properties: + backoffDelay: + description: 'BackoffDelay is the delay before retrying. More + information on Duration format: - https://www.iso.org/iso-8601-date-and-time-format.html + - https://en.wikipedia.org/wiki/ISO_8601 For linear policy, + backoff delay is backoffDelay*. For + exponential policy, backoff delay is backoffDelay*2^.' + type: string + backoffPolicy: + description: ' BackoffPolicy is the retry backoff policy (linear, + exponential).' + type: string + deadLetterSink: + description: 'DeadLetterSink is the sink receiving event that + could not be sent to a destination.' + type: object + properties: + ref: + description: 'Ref points to an Addressable.' + type: object + properties: + apiVersion: + description: 'API version of the referent.' + type: string + kind: + description: 'Kind of the referent. More info: + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + This is optional field, it gets defaulted + to the object holding it if left out.' + type: string + uri: + description: 'URI can be an absolute URL(non-empty + scheme and non-empty host) pointing to the target + or a relative URI. Relative URIs will be resolved + using the base URI retrieved from Ref.' + type: string + retry: + description: 'Retry is the minimum number of retries the sender + should attempt when sending an event before moving it + to the dead letter sink.' + type: integer + format: int32 status: type: object x-kubernetes-preserve-unknown-fields: true diff --git a/docs/spec/broker.md b/docs/spec/broker.md index 34db7f9cb94..06f4971a23f 100644 --- a/docs/spec/broker.md +++ b/docs/spec/broker.md @@ -44,6 +44,18 @@ The attributes filter specifying a list of key-value pairs MUST be supported by Trigger. Events that pass the attributes filter MUST include context or extension attributes that match all key-value pairs exactly. +### Delivery Spec + +Both BrokerSpec and TriggerSpec have a `Delivery` field of type [`duck.DeliverySpec`](./spec.md#eventingduckv1deliveryspec). +This field, among the other features, allows the user to define the dead letter sink and retries. +The `BrokerSpec.Delivery` field is global across all the Triggers registered with that particular +Broker, while the `TriggerSpec.Delivery`, if configured, fully overrides `BrokerSpec.Delivery` for +that particular Trigger, hence: + +* When `BrokerSpec.Delivery` and `TriggerSpec.Delivery` are both not configured, no delivery spec MUST be used. +* When `BrokerSpec.Delivery` is configured, but not the specific `TriggerSpec.Delivery`, then the `BrokerSpec.Delivery` MUST be used. +* When `TriggerSpec.Delivery` is configured, then `TriggerSpec.Delivery` MUST be used. + ## Data Plane ### Ingress diff --git a/pkg/apis/eventing/v1/broker_types.go b/pkg/apis/eventing/v1/broker_types.go index 8574e628008..f968fdc7e49 100644 --- a/pkg/apis/eventing/v1/broker_types.go +++ b/pkg/apis/eventing/v1/broker_types.go @@ -74,8 +74,9 @@ type BrokerSpec struct { // +optional Config *duckv1.KReference `json:"config,omitempty"` - // Delivery is the delivery specification for Events within the Broker mesh. - // This includes things like retries, DLQ, etc. + // Delivery contains the delivery spec for each trigger + // to this Broker. Each trigger delivery spec, if any, overrides this + // global delivery spec. // +optional Delivery *eventingduckv1.DeliverySpec `json:"delivery,omitempty"` } diff --git a/pkg/apis/eventing/v1/trigger_types.go b/pkg/apis/eventing/v1/trigger_types.go index 1db26c2860e..7e27a9cbe51 100644 --- a/pkg/apis/eventing/v1/trigger_types.go +++ b/pkg/apis/eventing/v1/trigger_types.go @@ -22,6 +22,8 @@ import ( "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" "knative.dev/pkg/kmeta" + + eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1" ) const ( @@ -84,6 +86,10 @@ type TriggerSpec struct { // 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. + // +optional + Delivery *eventingduckv1.DeliverySpec `json:"delivery,omitempty"` } type TriggerFilter struct { diff --git a/pkg/apis/eventing/v1/trigger_validation.go b/pkg/apis/eventing/v1/trigger_validation.go index 895a1f7c24f..4b367340a6b 100644 --- a/pkg/apis/eventing/v1/trigger_validation.go +++ b/pkg/apis/eventing/v1/trigger_validation.go @@ -69,6 +69,12 @@ func (ts *TriggerSpec) Validate(ctx context.Context) *apis.FieldError { 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 } diff --git a/pkg/apis/eventing/v1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1/zz_generated.deepcopy.go index 41b8f75e6d9..2c78920e9ff 100644 --- a/pkg/apis/eventing/v1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1/zz_generated.deepcopy.go @@ -247,6 +247,11 @@ func (in *TriggerSpec) DeepCopyInto(out *TriggerSpec) { (*in).DeepCopyInto(*out) } in.Subscriber.DeepCopyInto(&out.Subscriber) + if in.Delivery != nil { + in, out := &in.Delivery, &out.Delivery + *out = new(apisduckv1.DeliverySpec) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/eventing/v1beta1/trigger_conversion.go b/pkg/apis/eventing/v1beta1/trigger_conversion.go index 5b03af79731..f311d2ef8ad 100644 --- a/pkg/apis/eventing/v1beta1/trigger_conversion.go +++ b/pkg/apis/eventing/v1beta1/trigger_conversion.go @@ -20,12 +20,14 @@ import ( "context" "fmt" + duckv1 "knative.dev/eventing/pkg/apis/duck/v1" + duckv1beta1 "knative.dev/eventing/pkg/apis/duck/v1beta1" v1 "knative.dev/eventing/pkg/apis/eventing/v1" "knative.dev/pkg/apis" ) // ConvertTo implements apis.Convertible -func (source *Trigger) ConvertTo(_ context.Context, to apis.Convertible) error { +func (source *Trigger) ConvertTo(ctx context.Context, to apis.Convertible) error { switch sink := to.(type) { case *v1.Trigger: sink.ObjectMeta = source.ObjectMeta @@ -39,6 +41,12 @@ func (source *Trigger) ConvertTo(_ context.Context, to apis.Convertible) error { sink.Spec.Filter.Attributes[k] = v } } + if source.Spec.Delivery != nil { + sink.Spec.Delivery = &duckv1.DeliverySpec{} + if err := source.Spec.Delivery.ConvertTo(ctx, sink.Spec.Delivery); err != nil { + return err + } + } sink.Status.Status = source.Status.Status sink.Status.SubscriberURI = source.Status.SubscriberURI return nil @@ -48,7 +56,7 @@ func (source *Trigger) ConvertTo(_ context.Context, to apis.Convertible) error { } // ConvertFrom implements apis.Convertible -func (sink *Trigger) ConvertFrom(_ context.Context, from apis.Convertible) error { +func (sink *Trigger) ConvertFrom(ctx context.Context, from apis.Convertible) error { switch source := from.(type) { case *v1.Trigger: sink.ObjectMeta = source.ObjectMeta @@ -63,6 +71,12 @@ func (sink *Trigger) ConvertFrom(_ context.Context, from apis.Convertible) error Attributes: attributes, } } + if source.Spec.Delivery != nil { + sink.Spec.Delivery = &duckv1beta1.DeliverySpec{} + if err := sink.Spec.Delivery.ConvertFrom(ctx, source.Spec.Delivery); err != nil { + return err + } + } sink.Status.Status = source.Status.Status sink.Status.SubscriberURI = source.Status.SubscriberURI return nil diff --git a/pkg/apis/eventing/v1beta1/trigger_types.go b/pkg/apis/eventing/v1beta1/trigger_types.go index 914dab27720..e4f0da375ee 100644 --- a/pkg/apis/eventing/v1beta1/trigger_types.go +++ b/pkg/apis/eventing/v1beta1/trigger_types.go @@ -22,6 +22,8 @@ import ( "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" "knative.dev/pkg/kmeta" + + eventingduckv1beta1 "knative.dev/eventing/pkg/apis/duck/v1beta1" ) const ( @@ -87,6 +89,10 @@ type TriggerSpec struct { // 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. + // +optional + Delivery *eventingduckv1beta1.DeliverySpec `json:"delivery,omitempty"` } type TriggerFilter struct { diff --git a/pkg/apis/eventing/v1beta1/trigger_validation.go b/pkg/apis/eventing/v1beta1/trigger_validation.go index 9c07ef72490..b29e87014fc 100644 --- a/pkg/apis/eventing/v1beta1/trigger_validation.go +++ b/pkg/apis/eventing/v1beta1/trigger_validation.go @@ -65,6 +65,12 @@ func (ts *TriggerSpec) Validate(ctx context.Context) *apis.FieldError { 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 } diff --git a/pkg/apis/eventing/v1beta1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1beta1/zz_generated.deepcopy.go index 38bdb083405..9d17b7c4cb4 100644 --- a/pkg/apis/eventing/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1beta1/zz_generated.deepcopy.go @@ -351,6 +351,11 @@ func (in *TriggerSpec) DeepCopyInto(out *TriggerSpec) { (*in).DeepCopyInto(*out) } in.Subscriber.DeepCopyInto(&out.Subscriber) + if in.Delivery != nil { + in, out := &in.Delivery, &out.Delivery + *out = new(duckv1beta1.DeliverySpec) + (*in).DeepCopyInto(*out) + } return }