diff --git a/cmd/webhook/experimental.go b/cmd/webhook/experimental.go new file mode 100644 index 00000000000..37fc5194997 --- /dev/null +++ b/cmd/webhook/experimental.go @@ -0,0 +1,40 @@ +// +build js_trigger_filter + +package main + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/webhook/resourcesemantics" + "knative.dev/pkg/webhook/resourcesemantics/conversion" + + "knative.dev/eventing/pkg/apis/eventing" + eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" + eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1" + eventingv1experimental "knative.dev/eventing/pkg/apis/eventing/v1experimental" +) + +func AdditionalCRD() map[schema.GroupVersionKind]resourcesemantics.GenericCRD { + return map[schema.GroupVersionKind]resourcesemantics.GenericCRD{ + eventingv1experimental.SchemeGroupVersion.WithKind("Trigger"): &eventingv1experimental.Trigger{}, + } +} + +func AdditionalConversions() map[schema.GroupKind]conversion.GroupKindConversion { + var ( + eventingv1beta1_ = eventingv1beta1.SchemeGroupVersion.Version + eventingv1_ = eventingv1.SchemeGroupVersion.Version + eventingv1experimental_ = eventingv1experimental.SchemeGroupVersion.Version + ) + + return map[schema.GroupKind]conversion.GroupKindConversion{ + eventingv1.Kind("Trigger"): { + DefinitionName: eventing.TriggersResource.String(), + HubVersion: eventingv1experimental_, + Zygotes: map[string]conversion.ConvertibleObject{ + eventingv1beta1_: &eventingv1beta1.Trigger{}, + eventingv1_: &eventingv1.Trigger{}, + eventingv1experimental_: &eventingv1experimental.Trigger{}, + }, + }, + } +} diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index f1bb226d32f..c9d7912d7b9 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -114,6 +114,12 @@ var ourTypes = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{ var callbacks = map[schema.GroupVersionKind]validation.Callback{} +func init() { + for k, v := range AdditionalCRD() { + ourTypes[k] = v + } +} + func NewDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl { // Decorate contexts with the current state of the config. store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store")) @@ -248,113 +254,119 @@ func NewConversionController(ctx context.Context, cmw configmap.Watcher) *contro sourcesv1_ = sourcesv1.SchemeGroupVersion.Version ) - return conversion.NewConversionController(ctx, - // The path on which to serve the webhook - "/resource-conversion", - - // Specify the types of custom resource definitions that should be converted - map[schema.GroupKind]conversion.GroupKindConversion{ - // Eventing - eventingv1.Kind("Trigger"): { - DefinitionName: eventing.TriggersResource.String(), - HubVersion: eventingv1beta1_, - Zygotes: map[string]conversion.ConvertibleObject{ - eventingv1beta1_: &eventingv1beta1.Trigger{}, - eventingv1_: &eventingv1.Trigger{}, - }, + conversions := map[schema.GroupKind]conversion.GroupKindConversion{ + // Eventing + eventingv1.Kind("Trigger"): { + DefinitionName: eventing.TriggersResource.String(), + HubVersion: eventingv1beta1_, + Zygotes: map[string]conversion.ConvertibleObject{ + eventingv1beta1_: &eventingv1beta1.Trigger{}, + eventingv1_: &eventingv1.Trigger{}, }, - eventingv1.Kind("Broker"): { - DefinitionName: eventing.BrokersResource.String(), - HubVersion: eventingv1beta1_, - Zygotes: map[string]conversion.ConvertibleObject{ - eventingv1beta1_: &eventingv1beta1.Broker{}, - eventingv1_: &eventingv1.Broker{}, - }, + }, + eventingv1.Kind("Broker"): { + DefinitionName: eventing.BrokersResource.String(), + HubVersion: eventingv1beta1_, + Zygotes: map[string]conversion.ConvertibleObject{ + eventingv1beta1_: &eventingv1beta1.Broker{}, + eventingv1_: &eventingv1.Broker{}, }, + }, - // Messaging - messagingv1.Kind("Channel"): { - DefinitionName: messaging.ChannelsResource.String(), - HubVersion: messagingv1beta1_, - Zygotes: map[string]conversion.ConvertibleObject{ - messagingv1beta1_: &messagingv1beta1.Channel{}, - messagingv1_: &messagingv1.Channel{}, - }, + // Messaging + messagingv1.Kind("Channel"): { + DefinitionName: messaging.ChannelsResource.String(), + HubVersion: messagingv1beta1_, + Zygotes: map[string]conversion.ConvertibleObject{ + messagingv1beta1_: &messagingv1beta1.Channel{}, + messagingv1_: &messagingv1.Channel{}, }, - messagingv1.Kind("InMemoryChannel"): { - DefinitionName: messaging.InMemoryChannelsResource.String(), - HubVersion: messagingv1beta1_, - Zygotes: map[string]conversion.ConvertibleObject{ - messagingv1beta1_: &messagingv1beta1.InMemoryChannel{}, - messagingv1_: &messagingv1.InMemoryChannel{}, - }, + }, + messagingv1.Kind("InMemoryChannel"): { + DefinitionName: messaging.InMemoryChannelsResource.String(), + HubVersion: messagingv1beta1_, + Zygotes: map[string]conversion.ConvertibleObject{ + messagingv1beta1_: &messagingv1beta1.InMemoryChannel{}, + messagingv1_: &messagingv1.InMemoryChannel{}, }, - messagingv1.Kind("Subscription"): { - DefinitionName: messaging.SubscriptionsResource.String(), - HubVersion: messagingv1beta1_, - Zygotes: map[string]conversion.ConvertibleObject{ - messagingv1beta1_: &messagingv1beta1.Subscription{}, - messagingv1_: &messagingv1.Subscription{}, - }, + }, + messagingv1.Kind("Subscription"): { + DefinitionName: messaging.SubscriptionsResource.String(), + HubVersion: messagingv1beta1_, + Zygotes: map[string]conversion.ConvertibleObject{ + messagingv1beta1_: &messagingv1beta1.Subscription{}, + messagingv1_: &messagingv1.Subscription{}, }, + }, - // flows - flowsv1.Kind("Sequence"): { - DefinitionName: flows.SequenceResource.String(), - HubVersion: flowsv1beta1_, - Zygotes: map[string]conversion.ConvertibleObject{ - flowsv1beta1_: &flowsv1beta1.Sequence{}, - flowsv1_: &flowsv1.Sequence{}, - }, + // flows + flowsv1.Kind("Sequence"): { + DefinitionName: flows.SequenceResource.String(), + HubVersion: flowsv1beta1_, + Zygotes: map[string]conversion.ConvertibleObject{ + flowsv1beta1_: &flowsv1beta1.Sequence{}, + flowsv1_: &flowsv1.Sequence{}, }, - flowsv1.Kind("Parallel"): { - DefinitionName: flows.ParallelResource.String(), - HubVersion: flowsv1beta1_, - Zygotes: map[string]conversion.ConvertibleObject{ - flowsv1beta1_: &flowsv1beta1.Parallel{}, - flowsv1_: &flowsv1.Parallel{}, - }, + }, + flowsv1.Kind("Parallel"): { + DefinitionName: flows.ParallelResource.String(), + HubVersion: flowsv1beta1_, + Zygotes: map[string]conversion.ConvertibleObject{ + flowsv1beta1_: &flowsv1beta1.Parallel{}, + flowsv1_: &flowsv1.Parallel{}, }, + }, - // Sources - sourcesv1.Kind("ApiServerSource"): { - DefinitionName: sources.ApiServerSourceResource.String(), - HubVersion: sourcesv1alpha1_, - Zygotes: map[string]conversion.ConvertibleObject{ - sourcesv1alpha1_: &sourcesv1alpha1.ApiServerSource{}, - sourcesv1alpha2_: &sourcesv1alpha2.ApiServerSource{}, - sourcesv1beta1_: &sourcesv1beta1.ApiServerSource{}, - sourcesv1_: &sourcesv1.ApiServerSource{}, - }, + // Sources + sourcesv1.Kind("ApiServerSource"): { + DefinitionName: sources.ApiServerSourceResource.String(), + HubVersion: sourcesv1alpha1_, + Zygotes: map[string]conversion.ConvertibleObject{ + sourcesv1alpha1_: &sourcesv1alpha1.ApiServerSource{}, + sourcesv1alpha2_: &sourcesv1alpha2.ApiServerSource{}, + sourcesv1beta1_: &sourcesv1beta1.ApiServerSource{}, + sourcesv1_: &sourcesv1.ApiServerSource{}, }, - sourcesv1beta1.Kind("PingSource"): { - DefinitionName: sources.PingSourceResource.String(), - HubVersion: sourcesv1alpha2_, - Zygotes: map[string]conversion.ConvertibleObject{ - sourcesv1alpha2_: &sourcesv1alpha2.PingSource{}, - sourcesv1beta1_: &sourcesv1beta1.PingSource{}, - }, + }, + sourcesv1beta1.Kind("PingSource"): { + DefinitionName: sources.PingSourceResource.String(), + HubVersion: sourcesv1alpha2_, + Zygotes: map[string]conversion.ConvertibleObject{ + sourcesv1alpha2_: &sourcesv1alpha2.PingSource{}, + sourcesv1beta1_: &sourcesv1beta1.PingSource{}, }, - sourcesv1.Kind("SinkBinding"): { - DefinitionName: sources.SinkBindingResource.String(), - HubVersion: sourcesv1alpha1_, - Zygotes: map[string]conversion.ConvertibleObject{ - sourcesv1alpha1_: &sourcesv1alpha1.SinkBinding{}, - sourcesv1alpha2_: &sourcesv1alpha2.SinkBinding{}, - sourcesv1beta1_: &sourcesv1beta1.SinkBinding{}, - sourcesv1_: &sourcesv1.SinkBinding{}, - }, + }, + sourcesv1.Kind("SinkBinding"): { + DefinitionName: sources.SinkBindingResource.String(), + HubVersion: sourcesv1alpha1_, + Zygotes: map[string]conversion.ConvertibleObject{ + sourcesv1alpha1_: &sourcesv1alpha1.SinkBinding{}, + sourcesv1alpha2_: &sourcesv1alpha2.SinkBinding{}, + sourcesv1beta1_: &sourcesv1beta1.SinkBinding{}, + sourcesv1_: &sourcesv1.SinkBinding{}, }, - sourcesv1.Kind("ContainerSource"): { - DefinitionName: sources.ContainerSourceResource.String(), - HubVersion: sourcesv1alpha2_, - Zygotes: map[string]conversion.ConvertibleObject{ - sourcesv1alpha2_: &sourcesv1alpha2.ContainerSource{}, - sourcesv1beta1_: &sourcesv1beta1.ContainerSource{}, - sourcesv1_: &sourcesv1.ContainerSource{}, - }, + }, + sourcesv1.Kind("ContainerSource"): { + DefinitionName: sources.ContainerSourceResource.String(), + HubVersion: sourcesv1alpha2_, + Zygotes: map[string]conversion.ConvertibleObject{ + sourcesv1alpha2_: &sourcesv1alpha2.ContainerSource{}, + sourcesv1beta1_: &sourcesv1beta1.ContainerSource{}, + sourcesv1_: &sourcesv1.ContainerSource{}, }, }, + } + + for k, v := range AdditionalConversions() { + conversions[k] = v + } + + return conversion.NewConversionController(ctx, + // The path on which to serve the webhook + "/resource-conversion", + + // Specify the types of custom resource definitions that should be converted + conversions, // A function that infuses the context passed to ConvertTo/ConvertFrom/SetDefaults with custom metadata. ctxFunc, diff --git a/cmd/webhook/stable.go b/cmd/webhook/stable.go new file mode 100644 index 00000000000..a94eb6f0bd5 --- /dev/null +++ b/cmd/webhook/stable.go @@ -0,0 +1,17 @@ +// +build !js_trigger_filter + +package main + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/webhook/resourcesemantics" + "knative.dev/pkg/webhook/resourcesemantics/conversion" +) + +func AdditionalCRD() map[schema.GroupVersionKind]resourcesemantics.GenericCRD { + return map[schema.GroupVersionKind]resourcesemantics.GenericCRD{} +} + +func AdditionalConversions() map[schema.GroupKind]conversion.GroupKindConversion { + return map[schema.GroupKind]conversion.GroupKindConversion{} +} diff --git a/config/core/resources/trigger.yaml b/config/core/resources/trigger.yaml index d4f2c7c7a9f..c483537a220 100644 --- a/config/core/resources/trigger.yaml +++ b/config/core/resources/trigger.yaml @@ -156,6 +156,71 @@ spec: description: 'Status represents the current state of the Trigger. This data may be out of date.' type: object x-kubernetes-preserve-unknown-fields: true + - <<: *version + name: v1experimental + served: true + storage: false + schema: + openAPIV3Schema: + type: object + description: 'Trigger represents a request to have events delivered to a subscriber from a Broker''s event pool.' + properties: + spec: + description: 'Spec defines the desired state of the Trigger.' + required: + - subscriber + - broker + type: object + properties: + broker: + type: string + description: 'Broker that this trigger receives events from.' + filter: + type: object + description: 'Filter 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.' + properties: + attributes: + type: object + description: 'Map of CloudEvents attributes used for filtering events. If not specified, will default to all events' + additionalProperties: + type: string + expression: + type: string + description: 'JS expression' + subscriber: + type: object + description: 'the destination that should receive events.' + properties: + ref: + type: object + description: 'a reference to a Kubernetes object from which to retrieve the target URI.' + required: + - apiVersion + - kind + - name + properties: + apiVersion: + type: string + minLength: 1 + kind: + type: string + minLength: 1 + namespace: + type: string + minLength: 1 + name: + type: string + minLength: 1 + 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.' + status: + description: 'Status represents the current state of the Trigger. This data may be out of date.' + type: object + x-kubernetes-preserve-unknown-fields: true names: kind: Trigger plural: triggers @@ -168,7 +233,7 @@ spec: conversion: strategy: Webhook webhook: - conversionReviewVersions: ["v1", "v1beta1"] + conversionReviewVersions: ["v1experimental", "v1", "v1beta1"] clientConfig: service: name: eventing-webhook diff --git a/experimental_trigger.yaml b/experimental_trigger.yaml new file mode 100644 index 00000000000..b0530d7ec30 --- /dev/null +++ b/experimental_trigger.yaml @@ -0,0 +1,13 @@ +apiVersion: eventing.knative.dev/v1experimental +kind: Trigger +metadata: + name: my-service-trigger +spec: + broker: default + filter: + expression: "helloworld" + subscriber: + ref: + apiVersion: serving.knative.dev/v1 + kind: Service + name: my-service \ No newline at end of file diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index b787fd06124..4e36ea22c5d 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -47,6 +47,12 @@ ${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \ "eventing:v1beta1 eventing:v1 messaging:v1beta1 messaging:v1 flows:v1beta1 flows:v1 sources:v1alpha1 sources:v1alpha2 sources:v1beta1 sources:v1 configs:v1alpha1" \ --go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt +${CODEGEN_PKG}/generate-groups.sh "deepcopy" \ + knative.dev/eventing/pkg/client knative.dev/eventing/pkg/apis \ + "eventing:v1experimental" \ + --go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \ + --build-tag js_trigger_filter + # Deep copy config ${GOPATH}/bin/deepcopy-gen \ -O zz_generated.deepcopy \ diff --git a/pkg/apis/eventing/v1experimental/doc.go b/pkg/apis/eventing/v1experimental/doc.go new file mode 100644 index 00000000000..63aa8b55375 --- /dev/null +++ b/pkg/apis/eventing/v1experimental/doc.go @@ -0,0 +1,23 @@ +// +build js_trigger_filter + +/* + * Copyright 2020 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package v1 is the v1 version of the API. +// +k8s:deepcopy-gen=package +// +groupName=eventing.knative.dev + +package v1experimental diff --git a/pkg/apis/eventing/v1experimental/fuzzer.go b/pkg/apis/eventing/v1experimental/fuzzer.go new file mode 100644 index 00000000000..0998b2cc1df --- /dev/null +++ b/pkg/apis/eventing/v1experimental/fuzzer.go @@ -0,0 +1,47 @@ +// +build js_trigger_filter + +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1experimental + +import ( + fuzz "github.com/google/gofuzz" + "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" + "k8s.io/apimachinery/pkg/runtime/serializer" + pkgfuzzer "knative.dev/pkg/apis/testing/fuzzer" +) + +// FuzzerFuncs includes fuzzing funcs for knative.dev/eventing v1 types +// +// For other examples see +// https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/fuzzer/fuzzer.go +var FuzzerFuncs = fuzzer.MergeFuzzerFuncs( + func(codecs serializer.CodecFactory) []interface{} { + return []interface{}{ + func(s *TriggerStatus, c fuzz.Continue) { + c.FuzzNoCustom(s) // fuzz the status object + + // Clear the random fuzzed condition + s.Status.SetConditions(nil) + + // Fuzz the known conditions except their type value + s.InitializeConditions() + pkgfuzzer.FuzzConditions(&s.Status, c) + }, + } + }, +) diff --git a/pkg/apis/eventing/v1experimental/register.go b/pkg/apis/eventing/v1experimental/register.go new file mode 100644 index 00000000000..9a35540e94b --- /dev/null +++ b/pkg/apis/eventing/v1experimental/register.go @@ -0,0 +1,55 @@ +// +build js_trigger_filter + +/* + * Copyright 2020 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package v1experimental + +import ( + "knative.dev/eventing/pkg/apis/eventing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: eventing.GroupName, Version: "v1experimental"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Trigger{}, + &TriggerList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/eventing/v1experimental/trigger_conversion.go b/pkg/apis/eventing/v1experimental/trigger_conversion.go new file mode 100644 index 00000000000..182e19a97d6 --- /dev/null +++ b/pkg/apis/eventing/v1experimental/trigger_conversion.go @@ -0,0 +1,126 @@ +// +build js_trigger_filter + +/* +Copyright 2020 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1experimental + +import ( + "context" + "fmt" + + "knative.dev/pkg/apis" + + v1 "knative.dev/eventing/pkg/apis/eventing/v1" + v1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1" +) + +// ConvertTo implements apis.Convertible +func (source *Trigger) ConvertTo(_ context.Context, to apis.Convertible) error { + switch sink := to.(type) { + case *v1beta1.Trigger: + sink.ObjectMeta = source.ObjectMeta + sink.Spec.Broker = source.Spec.Broker + sink.Spec.Subscriber = source.Spec.Subscriber + if source.Spec.Filter != nil { + sink.Spec.Filter = &v1beta1.TriggerFilter{ + Attributes: make(v1beta1.TriggerFilterAttributes), + } + for k, v := range source.Spec.Filter.Attributes { + sink.Spec.Filter.Attributes[k] = v + } + + if source.Spec.Filter.Expression != "" { + sink.Annotations["v1experimental/expression"] = source.Spec.Filter.Expression + } + } + sink.Status.Status = source.Status.Status + sink.Status.SubscriberURI = source.Status.SubscriberURI + return nil + case *v1.Trigger: + sink.ObjectMeta = source.ObjectMeta + sink.Spec.Broker = source.Spec.Broker + sink.Spec.Subscriber = source.Spec.Subscriber + if source.Spec.Filter != nil { + sink.Spec.Filter = &v1.TriggerFilter{ + Attributes: make(v1.TriggerFilterAttributes), + } + for k, v := range source.Spec.Filter.Attributes { + sink.Spec.Filter.Attributes[k] = v + } + + if source.Spec.Filter.Expression != "" { + sink.Annotations["v1experimental/expression"] = source.Spec.Filter.Expression + } + } + sink.Status.Status = source.Status.Status + sink.Status.SubscriberURI = source.Status.SubscriberURI + return nil + default: + return fmt.Errorf("unknown version, got: %T", sink) + } +} + +// ConvertFrom implements apis.Convertible +func (sink *Trigger) ConvertFrom(_ context.Context, from apis.Convertible) error { + switch source := from.(type) { + case *v1.Trigger: + sink.ObjectMeta = source.ObjectMeta + sink.Spec.Broker = source.Spec.Broker + sink.Spec.Subscriber = source.Spec.Subscriber + if source.Spec.Filter != nil { + attributes := TriggerFilterAttributes{} + for k, v := range source.Spec.Filter.Attributes { + attributes[k] = v + } + sink.Spec.Filter = &TriggerFilter{ + Attributes: attributes, + } + } + sink.Status.Status = source.Status.Status + sink.Status.SubscriberURI = source.Status.SubscriberURI + sink.fromAnnotations(source.Annotations) + return nil + case *v1beta1.Trigger: + sink.ObjectMeta = source.ObjectMeta + sink.Spec.Broker = source.Spec.Broker + sink.Spec.Subscriber = source.Spec.Subscriber + if source.Spec.Filter != nil { + attributes := TriggerFilterAttributes{} + for k, v := range source.Spec.Filter.Attributes { + attributes[k] = v + } + sink.Spec.Filter = &TriggerFilter{ + Attributes: attributes, + } + } + sink.Status.Status = source.Status.Status + sink.Status.SubscriberURI = source.Status.SubscriberURI + sink.fromAnnotations(source.Annotations) + return nil + default: + return fmt.Errorf("unknown version, got: %T", source) + } +} + +func (sink *Trigger) fromAnnotations(annotations map[string]string) { + if expression, ok := annotations["v1experimental/expression"]; ok { + if sink.Spec.Filter == nil { + sink.Spec.Filter = &TriggerFilter{} + } + sink.Spec.Filter.Expression = expression + } +} diff --git a/pkg/apis/eventing/v1experimental/trigger_defaults.go b/pkg/apis/eventing/v1experimental/trigger_defaults.go new file mode 100644 index 00000000000..ed1811822bf --- /dev/null +++ b/pkg/apis/eventing/v1experimental/trigger_defaults.go @@ -0,0 +1,53 @@ +// +build js_trigger_filter + +/* + * Copyright 2020 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package v1experimental + +import ( + "context" + + "knative.dev/pkg/apis" +) + +const ( + brokerLabel = "eventing.knative.dev/broker" +) + +func (t *Trigger) SetDefaults(ctx context.Context) { + withNS := apis.WithinParent(ctx, t.ObjectMeta) + t.Spec.SetDefaults(withNS) + setLabels(t) +} + +func (ts *TriggerSpec) SetDefaults(ctx context.Context) { + // Make a default filter that allows anything. + if ts.Filter == nil { + ts.Filter = &TriggerFilter{} + } + // Default the Subscriber namespace + ts.Subscriber.SetDefaults(ctx) +} + +func setLabels(t *Trigger) { + if t.Spec.Broker != "" { + if len(t.Labels) == 0 { + t.Labels = map[string]string{} + } + t.Labels[brokerLabel] = t.Spec.Broker + } +} diff --git a/pkg/apis/eventing/v1experimental/trigger_lifecycle.go b/pkg/apis/eventing/v1experimental/trigger_lifecycle.go new file mode 100644 index 00000000000..a772aa37649 --- /dev/null +++ b/pkg/apis/eventing/v1experimental/trigger_lifecycle.go @@ -0,0 +1,189 @@ +// +build js_trigger_filter + +/* + * Copyright 2020 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package v1experimental + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +var triggerCondSet = apis.NewLivingConditionSet(TriggerConditionBroker, TriggerConditionSubscribed, TriggerConditionDependency, TriggerConditionSubscriberResolved) + +const ( + // TriggerConditionReady has status True when all subconditions below have been set to True. + TriggerConditionReady = apis.ConditionReady + + TriggerConditionBroker apis.ConditionType = "BrokerReady" + + TriggerConditionSubscribed apis.ConditionType = "SubscriptionReady" + + TriggerConditionDependency apis.ConditionType = "DependencyReady" + + TriggerConditionSubscriberResolved apis.ConditionType = "SubscriberResolved" + + // TriggerAnyFilter Constant to represent that we should allow anything. + TriggerAnyFilter = "" +) + +// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface. +func (*Trigger) GetConditionSet() apis.ConditionSet { + return triggerCondSet +} + +// GetGroupVersionKind returns GroupVersionKind for Triggers +func (t *Trigger) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Trigger") +} + +// GetUntypedSpec returns the spec of the Trigger. +func (t *Trigger) GetUntypedSpec() interface{} { + return t.Spec +} + +// GetCondition returns the condition currently associated with the given type, or nil. +func (ts *TriggerStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return triggerCondSet.Manage(ts).GetCondition(t) +} + +// GetTopLevelCondition returns the top level Condition. +func (ts *TriggerStatus) GetTopLevelCondition() *apis.Condition { + return triggerCondSet.Manage(ts).GetTopLevelCondition() +} + +// IsReady returns true if the resource is ready overall. +func (ts *TriggerStatus) IsReady() bool { + return triggerCondSet.Manage(ts).IsHappy() +} + +// InitializeConditions sets relevant unset conditions to Unknown state. +func (ts *TriggerStatus) InitializeConditions() { + triggerCondSet.Manage(ts).InitializeConditions() +} + +func (ts *TriggerStatus) PropagateBrokerCondition(bc *apis.Condition) { + if bc == nil { + ts.MarkBrokerNotConfigured() + return + } + + switch { + case bc.Status == corev1.ConditionUnknown: + ts.MarkBrokerUnknown(bc.Reason, bc.Message) + case bc.Status == corev1.ConditionTrue: + triggerCondSet.Manage(ts).MarkTrue(TriggerConditionBroker) + case bc.Status == corev1.ConditionFalse: + ts.MarkBrokerFailed(bc.Reason, bc.Message) + default: + ts.MarkBrokerUnknown("BrokerUnknown", "The status of Broker is invalid: %v", bc.Status) + } +} + +func (ts *TriggerStatus) MarkBrokerFailed(reason, messageFormat string, messageA ...interface{}) { + triggerCondSet.Manage(ts).MarkFalse(TriggerConditionBroker, reason, messageFormat, messageA...) +} + +func (ts *TriggerStatus) MarkBrokerUnknown(reason, messageFormat string, messageA ...interface{}) { + triggerCondSet.Manage(ts).MarkUnknown(TriggerConditionBroker, reason, messageFormat, messageA...) +} + +func (ts *TriggerStatus) MarkBrokerNotConfigured() { + triggerCondSet.Manage(ts).MarkUnknown(TriggerConditionBroker, + "BrokerNotConfigured", "Broker has not yet been reconciled.") +} + +func (ts *TriggerStatus) PropagateSubscriptionCondition(sc *apis.Condition) { + if sc == nil { + ts.MarkSubscriptionNotConfigured() + return + } + + switch { + case sc.Status == corev1.ConditionUnknown: + ts.MarkSubscribedUnknown(sc.Reason, sc.Message) + case sc.Status == corev1.ConditionTrue: + triggerCondSet.Manage(ts).MarkTrue(TriggerConditionSubscribed) + case sc.Status == corev1.ConditionFalse: + ts.MarkNotSubscribed(sc.Reason, sc.Message) + default: + ts.MarkSubscribedUnknown("SubscriptionUnknown", "The status of Subscription is invalid: %v", sc.Status) + } +} + +func (ts *TriggerStatus) MarkNotSubscribed(reason, messageFormat string, messageA ...interface{}) { + triggerCondSet.Manage(ts).MarkFalse(TriggerConditionSubscribed, reason, messageFormat, messageA...) +} + +func (ts *TriggerStatus) MarkSubscribedUnknown(reason, messageFormat string, messageA ...interface{}) { + triggerCondSet.Manage(ts).MarkUnknown(TriggerConditionSubscribed, reason, messageFormat, messageA...) +} + +func (ts *TriggerStatus) MarkSubscriptionNotConfigured() { + triggerCondSet.Manage(ts).MarkUnknown(TriggerConditionSubscribed, + "SubscriptionNotConfigured", "Subscription has not yet been reconciled.") +} + +func (ts *TriggerStatus) MarkSubscriberResolvedSucceeded() { + triggerCondSet.Manage(ts).MarkTrue(TriggerConditionSubscriberResolved) +} + +func (ts *TriggerStatus) MarkSubscriberResolvedFailed(reason, messageFormat string, messageA ...interface{}) { + triggerCondSet.Manage(ts).MarkFalse(TriggerConditionSubscriberResolved, reason, messageFormat, messageA...) +} + +func (ts *TriggerStatus) MarkSubscriberResolvedUnknown(reason, messageFormat string, messageA ...interface{}) { + triggerCondSet.Manage(ts).MarkUnknown(TriggerConditionSubscriberResolved, reason, messageFormat, messageA...) +} + +func (ts *TriggerStatus) MarkDependencySucceeded() { + triggerCondSet.Manage(ts).MarkTrue(TriggerConditionDependency) +} + +func (ts *TriggerStatus) MarkDependencyFailed(reason, messageFormat string, messageA ...interface{}) { + triggerCondSet.Manage(ts).MarkFalse(TriggerConditionDependency, reason, messageFormat, messageA...) +} + +func (ts *TriggerStatus) MarkDependencyUnknown(reason, messageFormat string, messageA ...interface{}) { + triggerCondSet.Manage(ts).MarkUnknown(TriggerConditionDependency, reason, messageFormat, messageA...) +} + +func (ts *TriggerStatus) MarkDependencyNotConfigured() { + triggerCondSet.Manage(ts).MarkUnknown(TriggerConditionDependency, + "DependencyNotConfigured", "Dependency has not yet been reconciled.") +} + +func (ts *TriggerStatus) PropagateDependencyStatus(ks *duckv1.KResource) { + kc := ks.Status.GetCondition(apis.ConditionReady) + if kc == nil { + ts.MarkDependencyNotConfigured() + return + } + + switch { + case kc.Status == corev1.ConditionUnknown: + ts.MarkDependencyUnknown(kc.Reason, kc.Message) + case kc.Status == corev1.ConditionTrue: + ts.MarkDependencySucceeded() + case kc.Status == corev1.ConditionFalse: + ts.MarkDependencyFailed(kc.Reason, kc.Message) + default: + ts.MarkDependencyUnknown("DependencyUnknown", "The status of Dependency is invalid: %v", kc.Status) + } +} diff --git a/pkg/apis/eventing/v1experimental/trigger_types.go b/pkg/apis/eventing/v1experimental/trigger_types.go new file mode 100644 index 00000000000..4a20cae41ff --- /dev/null +++ b/pkg/apis/eventing/v1experimental/trigger_types.go @@ -0,0 +1,135 @@ +// +build js_trigger_filter + +/* + * Copyright 2020 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package v1experimental + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/pkg/kmeta" +) + +const ( + // DependencyAnnotation is the annotation key used to mark the sources that the Trigger depends on. + // This will be used when the kn client creates a source and trigger pair for the user such that the trigger only receives events produced by the paired source. + DependencyAnnotation = "knative.dev/dependency" + + // InjectionAnnotation is the annotation key used to enable knative eventing + // injection for a namespace to automatically create a broker. + InjectionAnnotation = "eventing.knative.dev/injection" +) + +// +genclient +// +genreconciler +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Trigger represents a request to have events delivered to a subscriber from a +// Broker's event pool. +type Trigger struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of the Trigger. + Spec TriggerSpec `json:"spec,omitempty"` + + // Status represents the current state of the Trigger. This data may be out of + // date. + // +optional + Status TriggerStatus `json:"status,omitempty"` +} + +var ( + // Check that Trigger can be validated, can be defaulted, and has immutable fields. + _ apis.Validatable = (*Trigger)(nil) + _ apis.Defaultable = (*Trigger)(nil) + + // Check that Trigger can return its spec untyped. + _ apis.HasSpec = (*Trigger)(nil) + + _ runtime.Object = (*Trigger)(nil) + + // Check that we can create OwnerReferences to a Trigger. + _ kmeta.OwnerRefable = (*Trigger)(nil) + + // Check that the type conforms to the duck Knative Resource shape. + _ duckv1.KRShaped = (*Trigger)(nil) +) + +type TriggerSpec struct { + // Broker is the broker that this trigger receives events from. + Broker string `json:"broker,omitempty"` + + // Filter 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. + // + // +optional + Filter *TriggerFilter `json:"filter,omitempty"` + + // Subscriber is the addressable that receives events from the Broker that pass the Filter. It + // is required. + Subscriber duckv1.Destination `json:"subscriber"` +} + +type TriggerFilter 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. + // + // Nested context attributes are not supported as keys. Only string values are supported. + // + // +optional + Attributes TriggerFilterAttributes `json:"attributes,omitempty"` + + // +optional + Expression string `json:"expression,omitempty"` +} + +// 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 + +// TriggerStatus represents the current state of a Trigger. +type TriggerStatus struct { + // inherits duck/v1 Status, which currently provides: + // * ObservedGeneration - the 'Generation' of the Trigger that was last processed by the controller. + // * Conditions - the latest available observations of a resource's current state. + duckv1.Status `json:",inline"` + + // SubscriberURI is the resolved URI of the receiver for this Trigger. + SubscriberURI *apis.URL `json:"subscriberUri,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TriggerList is a collection of Triggers. +type TriggerList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Trigger `json:"items"` +} + +// GetStatus retrieves the status of the Trigger. Implements the KRShaped interface. +func (t *Trigger) GetStatus() *duckv1.Status { + return &t.Status.Status +} diff --git a/pkg/apis/eventing/v1experimental/trigger_validation.go b/pkg/apis/eventing/v1experimental/trigger_validation.go new file mode 100644 index 00000000000..ade2da95511 --- /dev/null +++ b/pkg/apis/eventing/v1experimental/trigger_validation.go @@ -0,0 +1,161 @@ +// +build js_trigger_filter + +/* + * Copyright 2020 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package v1experimental + +import ( + "context" + "encoding/json" + "fmt" + "regexp" + + "knative.dev/pkg/apis" + "knative.dev/pkg/kmp" + + corev1 "k8s.io/api/core/v1" +) + +var ( + // Only allow lowercase alphanumeric, starting with letters. + validAttributeName = regexp.MustCompile(`^[a-z][a-z0-9]*$`) +) + +// Validate the Trigger. +func (t *Trigger) Validate(ctx context.Context) *apis.FieldError { + errs := t.Spec.Validate(ctx).ViaField("spec") + errs = t.validateAnnotation(errs, DependencyAnnotation, t.validateDependencyAnnotation) + errs = t.validateAnnotation(errs, InjectionAnnotation, t.validateInjectionAnnotation) + if apis.IsInUpdate(ctx) { + original := apis.GetBaseline(ctx).(*Trigger) + errs = errs.Also(t.CheckImmutableFields(ctx, original)) + } + return errs +} + +// Validate the TriggerSpec. +func (ts *TriggerSpec) Validate(ctx context.Context) *apis.FieldError { + var 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")) + } + + return errs +} + +// CheckImmutableFields checks that any immutable fields were not changed. +func (t *Trigger) CheckImmutableFields(ctx context.Context, original *Trigger) *apis.FieldError { + if original == nil { + return nil + } + + if diff, err := kmp.ShortDiff(original.Spec.Broker, t.Spec.Broker); err != nil { + return &apis.FieldError{ + Message: "Failed to diff Trigger", + Paths: []string{"spec"}, + Details: err.Error(), + } + } else if diff != "" { + return &apis.FieldError{ + Message: "Immutable fields changed (-old +new)", + Paths: []string{"spec", "broker"}, + Details: diff, + } + } + return nil +} + +func GetObjRefFromDependencyAnnotation(dependencyAnnotation string) (corev1.ObjectReference, error) { + var objectRef corev1.ObjectReference + if err := json.Unmarshal([]byte(dependencyAnnotation), &objectRef); err != nil { + return objectRef, err + } + return objectRef, nil +} + +func (t *Trigger) validateAnnotation(errs *apis.FieldError, annotation string, function func(string) *apis.FieldError) *apis.FieldError { + if annotationValue, ok := t.GetAnnotations()[annotation]; ok { + annotationPrefix := fmt.Sprintf("metadata.annotations[%s]", annotation) + errs = errs.Also(function(annotationValue).ViaField(annotationPrefix)) + } + return errs +} + +func (t *Trigger) validateDependencyAnnotation(dependencyAnnotation string) *apis.FieldError { + depObjRef, err := GetObjRefFromDependencyAnnotation(dependencyAnnotation) + if err != nil { + return &apis.FieldError{ + Message: fmt.Sprintf("The provided annotation was not a corev1.ObjectReference: %q", dependencyAnnotation), + Details: err.Error(), + Paths: []string{""}, + } + } + var errs *apis.FieldError + if depObjRef.Namespace != "" && depObjRef.Namespace != t.GetNamespace() { + fe := &apis.FieldError{ + Message: fmt.Sprintf("Namespace must be empty or equal to the trigger namespace %q", t.GetNamespace()), + Paths: []string{"namespace"}, + } + errs = errs.Also(fe) + } + if depObjRef.Kind == "" { + fe := apis.ErrMissingField("kind") + errs = errs.Also(fe) + } + if depObjRef.Name == "" { + fe := apis.ErrMissingField("name") + errs = errs.Also(fe) + } + if depObjRef.APIVersion == "" { + fe := apis.ErrMissingField("apiVersion") + errs = errs.Also(fe) + } + return errs +} + +func (t *Trigger) validateInjectionAnnotation(injectionAnnotation string) *apis.FieldError { + if injectionAnnotation != "enabled" && injectionAnnotation != "disabled" { + return &apis.FieldError{ + Message: fmt.Sprintf(`The provided injection annotation value can only be "enabled" or "disabled", not %q`, injectionAnnotation), + Paths: []string{""}, + } + } + if t.Spec.Broker != "default" { + return &apis.FieldError{ + Message: fmt.Sprintf("The provided injection annotation is only used for default broker, but non-default broker specified here: %q", t.Spec.Broker), + Paths: []string{""}, + } + } + return nil +} diff --git a/pkg/apis/eventing/v1experimental/zz_generated.deepcopy.go b/pkg/apis/eventing/v1experimental/zz_generated.deepcopy.go new file mode 100644 index 00000000000..5acace6086b --- /dev/null +++ b/pkg/apis/eventing/v1experimental/zz_generated.deepcopy.go @@ -0,0 +1,176 @@ +// +build js_trigger_filter + +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1experimental + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" + "knative.dev/pkg/apis" +) + +// 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 *TriggerFilter) DeepCopyInto(out *TriggerFilter) { + *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 + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerFilter. +func (in *TriggerFilter) DeepCopy() *TriggerFilter { + if in == nil { + return nil + } + out := new(TriggerFilter) + 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) { + { + in := &in + *out = make(TriggerFilterAttributes, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerFilterAttributes. +func (in TriggerFilterAttributes) DeepCopy() TriggerFilterAttributes { + if in == nil { + return nil + } + out := new(TriggerFilterAttributes) + in.DeepCopyInto(out) + return *out +} + +// 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 + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Trigger, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerList. +func (in *TriggerList) DeepCopy() *TriggerList { + if in == nil { + return nil + } + out := new(TriggerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TriggerList) 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 *TriggerSpec) DeepCopyInto(out *TriggerSpec) { + *out = *in + if in.Filter != nil { + in, out := &in.Filter, &out.Filter + *out = new(TriggerFilter) + (*in).DeepCopyInto(*out) + } + in.Subscriber.DeepCopyInto(&out.Subscriber) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerSpec. +func (in *TriggerSpec) DeepCopy() *TriggerSpec { + if in == nil { + return nil + } + out := new(TriggerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TriggerStatus) DeepCopyInto(out *TriggerStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + if in.SubscriberURI != nil { + in, out := &in.SubscriberURI, &out.SubscriberURI + *out = new(apis.URL) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerStatus. +func (in *TriggerStatus) DeepCopy() *TriggerStatus { + if in == nil { + return nil + } + out := new(TriggerStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/features/js_trigger_filter.go b/pkg/features/js_trigger_filter.go new file mode 100644 index 00000000000..fa10f31b053 --- /dev/null +++ b/pkg/features/js_trigger_filter.go @@ -0,0 +1,7 @@ +//+build js_trigger_filter + +package features + +func IsJSTriggerFilterEnabled() bool { + return true +} diff --git a/pkg/features/stable.go b/pkg/features/stable.go new file mode 100644 index 00000000000..d582a326ca8 --- /dev/null +++ b/pkg/features/stable.go @@ -0,0 +1,7 @@ +//+build !js_trigger_filter + +package features + +func IsJSTriggerFilterEnabled() bool { + return false +} diff --git a/pkg/reconciler/mtbroker/trigger/expression_js_trigger_filter.go b/pkg/reconciler/mtbroker/trigger/expression_js_trigger_filter.go new file mode 100644 index 00000000000..8a03d5e6112 --- /dev/null +++ b/pkg/reconciler/mtbroker/trigger/expression_js_trigger_filter.go @@ -0,0 +1,23 @@ +//+build js_trigger_filter + +package mttrigger + +import ( + "context" + + "knative.dev/pkg/logging" + + v1 "knative.dev/eventing/pkg/apis/eventing/v1" + "knative.dev/eventing/pkg/apis/eventing/v1experimental" +) + +func LogTriggerFilterExpression(ctx context.Context, trigger *v1.Trigger) { + triggerExperimental := &v1experimental.Trigger{} + + err := triggerExperimental.ConvertFrom(ctx, trigger) + if err != nil { + logging.FromContext(ctx).Error(err) + return + } + logging.FromContext(ctx).Infof("Expression: %s", triggerExperimental.Spec.Filter.Expression) +} diff --git a/pkg/reconciler/mtbroker/trigger/expression_stable.go b/pkg/reconciler/mtbroker/trigger/expression_stable.go new file mode 100644 index 00000000000..fc4e0a3ea5f --- /dev/null +++ b/pkg/reconciler/mtbroker/trigger/expression_stable.go @@ -0,0 +1,15 @@ +//+build !js_trigger_filter + +package mttrigger + +import ( + "context" + + "knative.dev/pkg/logging" + + v1 "knative.dev/eventing/pkg/apis/eventing/v1" +) + +func LogTriggerFilterExpression(ctx context.Context, trigger *v1.Trigger) { + logging.FromContext(ctx).Info("Compiled controller doesn't contain js_trigger_filter feature") +} diff --git a/pkg/reconciler/mtbroker/trigger/trigger.go b/pkg/reconciler/mtbroker/trigger/trigger.go index 17b6e2aea63..462450a2d97 100644 --- a/pkg/reconciler/mtbroker/trigger/trigger.go +++ b/pkg/reconciler/mtbroker/trigger/trigger.go @@ -77,6 +77,7 @@ type Reconciler struct { func (r *Reconciler) ReconcileKind(ctx context.Context, t *eventingv1.Trigger) pkgreconciler.Event { logging.FromContext(ctx).Infow("Reconciling", zap.Any("Trigger", t)) + LogTriggerFilterExpression(ctx, t) t.Status.InitializeConditions() if t.DeletionTimestamp != nil {