From 18025784cf6098a7dcf4b94eaffba696c5cc4852 Mon Sep 17 00:00:00 2001 From: nachocano Date: Mon, 22 Jul 2019 11:12:30 -0700 Subject: [PATCH 01/39] adding messaging.channel resource --- config/300-channel-messaging.yaml | 99 ++++++++++ config/400-default-channel-config.yaml | 10 + .../messaging/v1alpha1/channel_defaults.go | 27 +++ .../messaging/v1alpha1/channel_lifecycle.go | 124 +++++++++++++ pkg/apis/messaging/v1alpha1/channel_types.go | 92 +++++++++ .../v1alpha1/zz_generated.deepcopy.go | 101 ++++++++++ .../typed/messaging/v1alpha1/channel.go | 174 ++++++++++++++++++ .../messaging/v1alpha1/fake/fake_channel.go | 140 ++++++++++++++ .../v1alpha1/fake/fake_messaging_client.go | 4 + .../messaging/v1alpha1/generated_expansion.go | 2 + .../messaging/v1alpha1/messaging_client.go | 5 + .../informers/externalversions/generic.go | 2 + .../messaging/v1alpha1/channel.go | 89 +++++++++ .../messaging/v1alpha1/interface.go | 7 + .../messaging/v1alpha1/channel/channel.go | 52 ++++++ .../messaging/v1alpha1/channel/fake/fake.go | 40 ++++ .../listers/messaging/v1alpha1/channel.go | 94 ++++++++++ .../messaging/v1alpha1/expansion_generated.go | 8 + 18 files changed, 1070 insertions(+) create mode 100644 config/300-channel-messaging.yaml create mode 100644 pkg/apis/messaging/v1alpha1/channel_defaults.go create mode 100644 pkg/apis/messaging/v1alpha1/channel_lifecycle.go create mode 100644 pkg/apis/messaging/v1alpha1/channel_types.go create mode 100644 pkg/client/clientset/versioned/typed/messaging/v1alpha1/channel.go create mode 100644 pkg/client/clientset/versioned/typed/messaging/v1alpha1/fake/fake_channel.go create mode 100644 pkg/client/informers/externalversions/messaging/v1alpha1/channel.go create mode 100644 pkg/client/injection/informers/messaging/v1alpha1/channel/channel.go create mode 100644 pkg/client/injection/informers/messaging/v1alpha1/channel/fake/fake.go create mode 100644 pkg/client/listers/messaging/v1alpha1/channel.go diff --git a/config/300-channel-messaging.yaml b/config/300-channel-messaging.yaml new file mode 100644 index 00000000000..637abff18d2 --- /dev/null +++ b/config/300-channel-messaging.yaml @@ -0,0 +1,99 @@ +# Copyright 2019 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. + +# TODO rename file to 300-channel.yaml once we remove the eventing Channel. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: channels.messaging.knative.dev + labels: + eventing.knative.dev/release: devel + knative.dev/crd-install: "true" + messaging.knative.dev/subscribable: "true" +spec: + group: messaging.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + names: + kind: Channel + plural: channels + singular: channel + categories: + - all + - knative + - messaging + - channel + shortNames: + - ch + scope: Namespaced + subresources: + status: {} + additionalPrinterColumns: + - name: Ready + type: string + JSONPath: ".status.conditions[?(@.type==\"Ready\")].status" + - name: Reason + type: string + JSONPath: ".status.conditions[?(@.type==\"Ready\")].reason" + - name: Hostname + type: string + JSONPath: .status.address.hostname + - name: Age + type: date + JSONPath: .metadata.creationTimestamp + validation: + openAPIV3Schema: + properties: + spec: + properties: + subscribable: + type: object + properties: + subscribers: + type: array + items: + required: + - uid + properties: + ref: + type: object + required: + - namespace + - name + - uid + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + minLength: 1 + namespace: + type: string + minLength: 1 + uid: + type: string + minLength: 1 + uid: + type: string + minLength: 1 + subscriberURI: + type: string + minLength: 1 + replyURI: + type: string + minLength: 1 diff --git a/config/400-default-channel-config.yaml b/config/400-default-channel-config.yaml index 9d6bd5aa519..ebad9c23cfd 100644 --- a/config/400-default-channel-config.yaml +++ b/config/400-default-channel-config.yaml @@ -30,3 +30,13 @@ data: apiversion: eventing.knative.dev/v1alpha1 kind: ClusterChannelProvisioner name: some-other-provisioner + # Configuration for defaulting channels that do not specify the channel CRD. All field names are + # lowercase. + default-ch-config: | + clusterdefault: + apiversion: messaging.knative.dev/v1alpha1 + kind: InMemoryChannel + namespacedefaults: + some-namespace: + apiversion: messaging.knative.dev/v1alpha1 + kind: InMemoryChannel diff --git a/pkg/apis/messaging/v1alpha1/channel_defaults.go b/pkg/apis/messaging/v1alpha1/channel_defaults.go new file mode 100644 index 00000000000..fc07e161dec --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/channel_defaults.go @@ -0,0 +1,27 @@ +/* +Copyright 2019 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 v1alpha1 + +import "context" + +func (c *Channel) SetDefaults(ctx context.Context) { + c.Spec.SetDefaults(ctx) +} + +func (dcs *ChannelSpec) SetDefaults(ctx context.Context) { + // TODO: Nothing to default here... +} diff --git a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go new file mode 100644 index 00000000000..14747ce161f --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go @@ -0,0 +1,124 @@ +/* + * Copyright 2019 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 v1alpha1 + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "knative.dev/pkg/apis" + "knative.dev/pkg/apis/duck/v1alpha1" +) + +var chCondSet = apis.NewLivingConditionSet(ChannelConditionDispatcherReady, ChannelConditionServiceReady, ChannelConditionEndpointsReady, ChannelConditionAddressable, ChannelConditionChannelServiceReady) + +const ( + // ChannelConditionReady has status True when all subconditions below have been set to True. + ChannelConditionReady = apis.ConditionReady + + // ChannelConditionDispatcherReady has status True when a Dispatcher deployment is ready + // Keyed off appsv1.DeploymentAvailable, which means minimum available replicas required are up + // and running for at least minReadySeconds. + ChannelConditionDispatcherReady apis.ConditionType = "DispatcherReady" + + // ChannelConditionServiceReady has status True when a k8s Service is ready. This + // basically just means it exists because there's no meaningful status in Service. See Endpoints + // below. + ChannelConditionServiceReady apis.ConditionType = "ServiceReady" + + // ChannelConditionEndpointsReady has status True when a k8s Service Endpoints are backed + // by at least one endpoint. + ChannelConditionEndpointsReady apis.ConditionType = "EndpointsReady" + + // ChannelConditionAddressable has status true when this Channel meets + // the Addressable contract and has a non-empty hostname. + ChannelConditionAddressable apis.ConditionType = "Addressable" + + // ChannelConditionServiceReady has status True when a k8s Service representing the channel is ready. + // Because this uses ExternalName, there are no endpoints to check. + ChannelConditionChannelServiceReady apis.ConditionType = "ChannelServiceReady" +) + +// GetCondition returns the condition currently associated with the given type, or nil. +func (cs *ChannelStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return chCondSet.Manage(cs).GetCondition(t) +} + +// IsReady returns true if the resource is ready overall. +func (cs *ChannelStatus) IsReady() bool { + return chCondSet.Manage(cs).IsHappy() +} + +// InitializeConditions sets relevant unset conditions to Unknown state. +func (cs *ChannelStatus) InitializeConditions() { + chCondSet.Manage(cs).InitializeConditions() +} + +func (cs *ChannelStatus) SetAddress(url *apis.URL) { + if cs.Address == nil { + cs.Address = &v1alpha1.Addressable{} + } + if url != nil { + cs.Address.Hostname = url.Host + cs.Address.URL = url + chCondSet.Manage(cs).MarkTrue(ChannelConditionAddressable) + } else { + cs.Address.Hostname = "" + cs.Address.URL = nil + chCondSet.Manage(cs).MarkFalse(ChannelConditionAddressable, "EmptyHostname", "hostname is the empty string") + } +} + +func (cs *ChannelStatus) MarkDispatcherFailed(reason, messageFormat string, messageA ...interface{}) { + chCondSet.Manage(cs).MarkFalse(ChannelConditionDispatcherReady, reason, messageFormat, messageA...) +} + +// TODO: Unify this with the ones from Eventing. Say: Broker, Trigger. +func (cs *ChannelStatus) PropagateDispatcherStatus(ds *appsv1.DeploymentStatus) { + for _, cond := range ds.Conditions { + if cond.Type == appsv1.DeploymentAvailable { + if cond.Status != corev1.ConditionTrue { + cs.MarkDispatcherFailed("DispatcherNotReady", "Dispatcher Deployment is not ready: %s : %s", cond.Reason, cond.Message) + } else { + chCondSet.Manage(cs).MarkTrue(ChannelConditionDispatcherReady) + } + } + } +} + +func (cs *ChannelStatus) MarkServiceFailed(reason, messageFormat string, messageA ...interface{}) { + chCondSet.Manage(cs).MarkFalse(ChannelConditionServiceReady, reason, messageFormat, messageA...) +} + +func (cs *ChannelStatus) MarkServiceTrue() { + chCondSet.Manage(cs).MarkTrue(ChannelConditionServiceReady) +} + +func (cs *ChannelStatus) MarkChannelServiceFailed(reason, messageFormat string, messageA ...interface{}) { + chCondSet.Manage(cs).MarkFalse(ChannelConditionChannelServiceReady, reason, messageFormat, messageA...) +} + +func (cs *ChannelStatus) MarkChannelServiceTrue() { + chCondSet.Manage(cs).MarkTrue(ChannelConditionChannelServiceReady) +} + +func (cs *ChannelStatus) MarkEndpointsFailed(reason, messageFormat string, messageA ...interface{}) { + chCondSet.Manage(cs).MarkFalse(ChannelConditionEndpointsReady, reason, messageFormat, messageA...) +} + +func (cs *ChannelStatus) MarkEndpointsTrue() { + chCondSet.Manage(cs).MarkTrue(ChannelConditionEndpointsReady) +} diff --git a/pkg/apis/messaging/v1alpha1/channel_types.go b/pkg/apis/messaging/v1alpha1/channel_types.go new file mode 100644 index 00000000000..b8bda0e97e8 --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/channel_types.go @@ -0,0 +1,92 @@ +/* + * Copyright 2019 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 v1alpha1 + +import ( + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/apis" + duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1" + duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1" + "knative.dev/pkg/webhook" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Channel is a resource representing a Channel. +type Channel struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of the Channel. + Spec ChannelSpec `json:"spec,omitempty"` + + // Status represents the current state of the Channel. This data may be out of + // date. + // +optional + Status ChannelStatus `json:"status,omitempty"` +} + +// Check that Channel can be validated, can be defaulted, and has immutable fields. +var _ apis.Validatable = (*Channel)(nil) +var _ apis.Defaultable = (*Channel)(nil) +var _ runtime.Object = (*Channel)(nil) +var _ webhook.GenericCRD = (*Channel)(nil) + +// ChannelSpec defines which subscribers have expressed interest in +// receiving events from this Channel. +type ChannelSpec struct { + // Channel conforms to Duck type Subscribable. + Subscribable *eventingduck.Subscribable `json:"subscribable,omitempty"` +} + +// ChannelStatus represents the current state of a Channel. +type ChannelStatus struct { + // inherits duck/v1beta1 Status, which currently provides: + // * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller. + // * Conditions - the latest available observations of a resource's current state. + duckv1beta1.Status `json:",inline"` + + // Channel is Addressable. It currently exposes the endpoint as a + // fully-qualified DNS name which will distribute traffic over the + // provided targets from inside the cluster. + // + // It generally has the form {channel}.{namespace}.svc.{cluster domain name} + duckv1alpha1.AddressStatus `json:",inline"` + + // Subscribers is populated with the statuses of each of the Channelable's subscribers. + eventingduck.SubscribableTypeStatus `json:",inline"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ChannelList is a collection of Channels. +type ChannelList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Channel `json:"items"` +} + +// GetGroupVersionKind returns GroupVersionKind for Channels. +func (dc *Channel) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Channel") +} diff --git a/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go index 7df53c750bf..224c9623021 100644 --- a/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go @@ -27,6 +27,107 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Channel) DeepCopyInto(out *Channel) { + *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 Channel. +func (in *Channel) DeepCopy() *Channel { + if in == nil { + return nil + } + out := new(Channel) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Channel) 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 *ChannelList) DeepCopyInto(out *ChannelList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Channel, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelList. +func (in *ChannelList) DeepCopy() *ChannelList { + if in == nil { + return nil + } + out := new(ChannelList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ChannelList) 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 *ChannelSpec) DeepCopyInto(out *ChannelSpec) { + *out = *in + if in.Subscribable != nil { + in, out := &in.Subscribable, &out.Subscribable + *out = new(duckv1alpha1.Subscribable) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelSpec. +func (in *ChannelSpec) DeepCopy() *ChannelSpec { + if in == nil { + return nil + } + out := new(ChannelSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChannelStatus) DeepCopyInto(out *ChannelStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.AddressStatus.DeepCopyInto(&out.AddressStatus) + in.SubscribableTypeStatus.DeepCopyInto(&out.SubscribableTypeStatus) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelStatus. +func (in *ChannelStatus) DeepCopy() *ChannelStatus { + if in == nil { + return nil + } + out := new(ChannelStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ChannelTemplateSpec) DeepCopyInto(out *ChannelTemplateSpec) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/messaging/v1alpha1/channel.go b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/channel.go new file mode 100644 index 00000000000..55341f51721 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/channel.go @@ -0,0 +1,174 @@ +/* +Copyright 2019 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 client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ChannelsGetter has a method to return a ChannelInterface. +// A group's client should implement this interface. +type ChannelsGetter interface { + Channels(namespace string) ChannelInterface +} + +// ChannelInterface has methods to work with Channel resources. +type ChannelInterface interface { + Create(*v1alpha1.Channel) (*v1alpha1.Channel, error) + Update(*v1alpha1.Channel) (*v1alpha1.Channel, error) + UpdateStatus(*v1alpha1.Channel) (*v1alpha1.Channel, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Channel, error) + List(opts v1.ListOptions) (*v1alpha1.ChannelList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Channel, err error) + ChannelExpansion +} + +// channels implements ChannelInterface +type channels struct { + client rest.Interface + ns string +} + +// newChannels returns a Channels +func newChannels(c *MessagingV1alpha1Client, namespace string) *channels { + return &channels{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the channel, and returns the corresponding channel object, and an error if there is any. +func (c *channels) Get(name string, options v1.GetOptions) (result *v1alpha1.Channel, err error) { + result = &v1alpha1.Channel{} + err = c.client.Get(). + Namespace(c.ns). + Resource("channels"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Channels that match those selectors. +func (c *channels) List(opts v1.ListOptions) (result *v1alpha1.ChannelList, err error) { + result = &v1alpha1.ChannelList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("channels"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested channels. +func (c *channels) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("channels"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a channel and creates it. Returns the server's representation of the channel, and an error, if there is any. +func (c *channels) Create(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { + result = &v1alpha1.Channel{} + err = c.client.Post(). + Namespace(c.ns). + Resource("channels"). + Body(channel). + Do(). + Into(result) + return +} + +// Update takes the representation of a channel and updates it. Returns the server's representation of the channel, and an error, if there is any. +func (c *channels) Update(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { + result = &v1alpha1.Channel{} + err = c.client.Put(). + Namespace(c.ns). + Resource("channels"). + Name(channel.Name). + Body(channel). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *channels) UpdateStatus(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { + result = &v1alpha1.Channel{} + err = c.client.Put(). + Namespace(c.ns). + Resource("channels"). + Name(channel.Name). + SubResource("status"). + Body(channel). + Do(). + Into(result) + return +} + +// Delete takes name of the channel and deletes it. Returns an error if one occurs. +func (c *channels) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("channels"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *channels) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("channels"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched channel. +func (c *channels) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Channel, err error) { + result = &v1alpha1.Channel{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("channels"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/messaging/v1alpha1/fake/fake_channel.go b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/fake/fake_channel.go new file mode 100644 index 00000000000..72813b3a9d1 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/fake/fake_channel.go @@ -0,0 +1,140 @@ +/* +Copyright 2019 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 client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeChannels implements ChannelInterface +type FakeChannels struct { + Fake *FakeMessagingV1alpha1 + ns string +} + +var channelsResource = schema.GroupVersionResource{Group: "messaging.knative.dev", Version: "v1alpha1", Resource: "channels"} + +var channelsKind = schema.GroupVersionKind{Group: "messaging.knative.dev", Version: "v1alpha1", Kind: "Channel"} + +// Get takes name of the channel, and returns the corresponding channel object, and an error if there is any. +func (c *FakeChannels) Get(name string, options v1.GetOptions) (result *v1alpha1.Channel, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(channelsResource, c.ns, name), &v1alpha1.Channel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Channel), err +} + +// List takes label and field selectors, and returns the list of Channels that match those selectors. +func (c *FakeChannels) List(opts v1.ListOptions) (result *v1alpha1.ChannelList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(channelsResource, channelsKind, c.ns, opts), &v1alpha1.ChannelList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ChannelList{ListMeta: obj.(*v1alpha1.ChannelList).ListMeta} + for _, item := range obj.(*v1alpha1.ChannelList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested channels. +func (c *FakeChannels) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(channelsResource, c.ns, opts)) + +} + +// Create takes the representation of a channel and creates it. Returns the server's representation of the channel, and an error, if there is any. +func (c *FakeChannels) Create(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(channelsResource, c.ns, channel), &v1alpha1.Channel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Channel), err +} + +// Update takes the representation of a channel and updates it. Returns the server's representation of the channel, and an error, if there is any. +func (c *FakeChannels) Update(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(channelsResource, c.ns, channel), &v1alpha1.Channel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Channel), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeChannels) UpdateStatus(channel *v1alpha1.Channel) (*v1alpha1.Channel, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(channelsResource, "status", c.ns, channel), &v1alpha1.Channel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Channel), err +} + +// Delete takes name of the channel and deletes it. Returns an error if one occurs. +func (c *FakeChannels) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(channelsResource, c.ns, name), &v1alpha1.Channel{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeChannels) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(channelsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.ChannelList{}) + return err +} + +// Patch applies the patch and returns the patched channel. +func (c *FakeChannels) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Channel, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(channelsResource, c.ns, name, data, subresources...), &v1alpha1.Channel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Channel), err +} diff --git a/pkg/client/clientset/versioned/typed/messaging/v1alpha1/fake/fake_messaging_client.go b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/fake/fake_messaging_client.go index f0317d0cfa1..90a70c27b96 100644 --- a/pkg/client/clientset/versioned/typed/messaging/v1alpha1/fake/fake_messaging_client.go +++ b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/fake/fake_messaging_client.go @@ -28,6 +28,10 @@ type FakeMessagingV1alpha1 struct { *testing.Fake } +func (c *FakeMessagingV1alpha1) Channels(namespace string) v1alpha1.ChannelInterface { + return &FakeChannels{c, namespace} +} + func (c *FakeMessagingV1alpha1) InMemoryChannels(namespace string) v1alpha1.InMemoryChannelInterface { return &FakeInMemoryChannels{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/messaging/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/generated_expansion.go index 107a069c119..c3eadee3c40 100644 --- a/pkg/client/clientset/versioned/typed/messaging/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/generated_expansion.go @@ -18,6 +18,8 @@ limitations under the License. package v1alpha1 +type ChannelExpansion interface{} + type InMemoryChannelExpansion interface{} type SequenceExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/messaging/v1alpha1/messaging_client.go b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/messaging_client.go index 762dcbf29e3..6ede09a4b2b 100644 --- a/pkg/client/clientset/versioned/typed/messaging/v1alpha1/messaging_client.go +++ b/pkg/client/clientset/versioned/typed/messaging/v1alpha1/messaging_client.go @@ -27,6 +27,7 @@ import ( type MessagingV1alpha1Interface interface { RESTClient() rest.Interface + ChannelsGetter InMemoryChannelsGetter SequencesGetter } @@ -36,6 +37,10 @@ type MessagingV1alpha1Client struct { restClient rest.Interface } +func (c *MessagingV1alpha1Client) Channels(namespace string) ChannelInterface { + return newChannels(c, namespace) +} + func (c *MessagingV1alpha1Client) InMemoryChannels(namespace string) InMemoryChannelInterface { return newInMemoryChannels(c, namespace) } diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 003f68c8e3f..ad007df212f 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -69,6 +69,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Triggers().Informer()}, nil // Group=messaging.knative.dev, Version=v1alpha1 + case messagingv1alpha1.SchemeGroupVersion.WithResource("channels"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Messaging().V1alpha1().Channels().Informer()}, nil case messagingv1alpha1.SchemeGroupVersion.WithResource("inmemorychannels"): return &genericInformer{resource: resource.GroupResource(), informer: f.Messaging().V1alpha1().InMemoryChannels().Informer()}, nil case messagingv1alpha1.SchemeGroupVersion.WithResource("sequences"): diff --git a/pkg/client/informers/externalversions/messaging/v1alpha1/channel.go b/pkg/client/informers/externalversions/messaging/v1alpha1/channel.go new file mode 100644 index 00000000000..3fb450a747c --- /dev/null +++ b/pkg/client/informers/externalversions/messaging/v1alpha1/channel.go @@ -0,0 +1,89 @@ +/* +Copyright 2019 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 informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + versioned "github.com/knative/eventing/pkg/client/clientset/versioned" + internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/knative/eventing/pkg/client/listers/messaging/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ChannelInformer provides access to a shared informer and lister for +// Channels. +type ChannelInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ChannelLister +} + +type channelInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewChannelInformer constructs a new informer for Channel type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewChannelInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredChannelInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredChannelInformer constructs a new informer for Channel type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredChannelInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.MessagingV1alpha1().Channels(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.MessagingV1alpha1().Channels(namespace).Watch(options) + }, + }, + &messagingv1alpha1.Channel{}, + resyncPeriod, + indexers, + ) +} + +func (f *channelInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredChannelInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *channelInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&messagingv1alpha1.Channel{}, f.defaultInformer) +} + +func (f *channelInformer) Lister() v1alpha1.ChannelLister { + return v1alpha1.NewChannelLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/messaging/v1alpha1/interface.go b/pkg/client/informers/externalversions/messaging/v1alpha1/interface.go index 4e66545178e..6a61f938e30 100644 --- a/pkg/client/informers/externalversions/messaging/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/messaging/v1alpha1/interface.go @@ -24,6 +24,8 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { + // Channels returns a ChannelInformer. + Channels() ChannelInformer // InMemoryChannels returns a InMemoryChannelInformer. InMemoryChannels() InMemoryChannelInformer // Sequences returns a SequenceInformer. @@ -41,6 +43,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } +// Channels returns a ChannelInformer. +func (v *version) Channels() ChannelInformer { + return &channelInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // InMemoryChannels returns a InMemoryChannelInformer. func (v *version) InMemoryChannels() InMemoryChannelInformer { return &inMemoryChannelInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/injection/informers/messaging/v1alpha1/channel/channel.go b/pkg/client/injection/informers/messaging/v1alpha1/channel/channel.go new file mode 100644 index 00000000000..5e720aedfe6 --- /dev/null +++ b/pkg/client/injection/informers/messaging/v1alpha1/channel/channel.go @@ -0,0 +1,52 @@ +/* +Copyright 2019 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 injection-gen. DO NOT EDIT. + +package channel + +import ( + "context" + + v1alpha1 "github.com/knative/eventing/pkg/client/informers/externalversions/messaging/v1alpha1" + factory "github.com/knative/eventing/pkg/client/injection/informers/messaging/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct{} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := factory.Get(ctx) + inf := f.Messaging().V1alpha1().Channels() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1alpha1.ChannelInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Fatalf( + "Unable to fetch %T from context.", (v1alpha1.ChannelInformer)(nil)) + } + return untyped.(v1alpha1.ChannelInformer) +} diff --git a/pkg/client/injection/informers/messaging/v1alpha1/channel/fake/fake.go b/pkg/client/injection/informers/messaging/v1alpha1/channel/fake/fake.go new file mode 100644 index 00000000000..d21e31ba8a7 --- /dev/null +++ b/pkg/client/injection/informers/messaging/v1alpha1/channel/fake/fake.go @@ -0,0 +1,40 @@ +/* +Copyright 2019 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 injection-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + fake "github.com/knative/eventing/pkg/client/injection/informers/messaging/factory/fake" + channel "github.com/knative/eventing/pkg/client/injection/informers/messaging/v1alpha1/channel" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = channel.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Messaging().V1alpha1().Channels() + return context.WithValue(ctx, channel.Key{}, inf), inf.Informer() +} diff --git a/pkg/client/listers/messaging/v1alpha1/channel.go b/pkg/client/listers/messaging/v1alpha1/channel.go new file mode 100644 index 00000000000..a900780ef87 --- /dev/null +++ b/pkg/client/listers/messaging/v1alpha1/channel.go @@ -0,0 +1,94 @@ +/* +Copyright 2019 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 lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ChannelLister helps list Channels. +type ChannelLister interface { + // List lists all Channels in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Channel, err error) + // Channels returns an object that can list and get Channels. + Channels(namespace string) ChannelNamespaceLister + ChannelListerExpansion +} + +// channelLister implements the ChannelLister interface. +type channelLister struct { + indexer cache.Indexer +} + +// NewChannelLister returns a new ChannelLister. +func NewChannelLister(indexer cache.Indexer) ChannelLister { + return &channelLister{indexer: indexer} +} + +// List lists all Channels in the indexer. +func (s *channelLister) List(selector labels.Selector) (ret []*v1alpha1.Channel, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Channel)) + }) + return ret, err +} + +// Channels returns an object that can list and get Channels. +func (s *channelLister) Channels(namespace string) ChannelNamespaceLister { + return channelNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ChannelNamespaceLister helps list and get Channels. +type ChannelNamespaceLister interface { + // List lists all Channels in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.Channel, err error) + // Get retrieves the Channel from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.Channel, error) + ChannelNamespaceListerExpansion +} + +// channelNamespaceLister implements the ChannelNamespaceLister +// interface. +type channelNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Channels in the indexer for a given namespace. +func (s channelNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Channel, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Channel)) + }) + return ret, err +} + +// Get retrieves the Channel from the indexer for a given namespace and name. +func (s channelNamespaceLister) Get(name string) (*v1alpha1.Channel, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("channel"), name) + } + return obj.(*v1alpha1.Channel), nil +} diff --git a/pkg/client/listers/messaging/v1alpha1/expansion_generated.go b/pkg/client/listers/messaging/v1alpha1/expansion_generated.go index e19acc3311b..2d242f716bb 100644 --- a/pkg/client/listers/messaging/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/messaging/v1alpha1/expansion_generated.go @@ -18,6 +18,14 @@ limitations under the License. package v1alpha1 +// ChannelListerExpansion allows custom methods to be added to +// ChannelLister. +type ChannelListerExpansion interface{} + +// ChannelNamespaceListerExpansion allows custom methods to be added to +// ChannelNamespaceLister. +type ChannelNamespaceListerExpansion interface{} + // InMemoryChannelListerExpansion allows custom methods to be added to // InMemoryChannelLister. type InMemoryChannelListerExpansion interface{} From 755ac4ad052a928ef293b2e30652cf78781be087 Mon Sep 17 00:00:00 2001 From: nachocano Date: Mon, 22 Jul 2019 12:26:08 -0700 Subject: [PATCH 02/39] channel messaging --- cmd/controller/main.go | 2 + config/200-controller-clusterrole.yaml | 2 + ...ssaging.yaml => 300-channel-eventing.yaml} | 47 ++++-- config/300-channel.yaml | 46 ++---- .../messaging/v1alpha1/channel_validation.go | 44 +++++ pkg/reconciler/channel/channel.go | 30 +--- pkg/reconciler/channel/controller.go | 4 +- pkg/reconciler/channel/controller_test.go | 2 +- pkg/reconciler/channeleventing/channel.go | 155 ++++++++++++++++++ .../channel_test.go | 2 +- pkg/reconciler/channeleventing/controller.go | 56 +++++++ .../channeleventing/controller_test.go | 40 +++++ 12 files changed, 354 insertions(+), 76 deletions(-) rename config/{300-channel-messaging.yaml => 300-channel-eventing.yaml} (77%) create mode 100644 pkg/apis/messaging/v1alpha1/channel_validation.go create mode 100644 pkg/reconciler/channeleventing/channel.go rename pkg/reconciler/{channel => channeleventing}/channel_test.go (99%) create mode 100644 pkg/reconciler/channeleventing/controller.go create mode 100644 pkg/reconciler/channeleventing/controller_test.go diff --git a/cmd/controller/main.go b/cmd/controller/main.go index dba2fde4b77..6aed771b2f2 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -24,6 +24,7 @@ import ( "github.com/knative/eventing/pkg/reconciler/broker" "github.com/knative/eventing/pkg/reconciler/channel" + "github.com/knative/eventing/pkg/reconciler/channeleventing" "github.com/knative/eventing/pkg/reconciler/eventtype" "github.com/knative/eventing/pkg/reconciler/namespace" "github.com/knative/eventing/pkg/reconciler/sequence" @@ -35,6 +36,7 @@ func main() { sharedmain.Main("controller", subscription.NewController, namespace.NewController, + channeleventing.NewController, channel.NewController, trigger.NewController, broker.NewController, diff --git a/config/200-controller-clusterrole.yaml b/config/200-controller-clusterrole.yaml index 72a5a69ddfd..d08a231b870 100644 --- a/config/200-controller-clusterrole.yaml +++ b/config/200-controller-clusterrole.yaml @@ -84,6 +84,8 @@ rules: resources: - "sequences" - "sequences/status" + - "channels" + - "channels/status" verbs: *everything # Messaging resources and finalizers we care about. diff --git a/config/300-channel-messaging.yaml b/config/300-channel-eventing.yaml similarity index 77% rename from config/300-channel-messaging.yaml rename to config/300-channel-eventing.yaml index 637abff18d2..9bbcc3ae485 100644 --- a/config/300-channel-messaging.yaml +++ b/config/300-channel-eventing.yaml @@ -1,4 +1,4 @@ -# Copyright 2019 The Knative Authors +# Copyright 2018 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. @@ -11,33 +11,29 @@ # 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. - -# TODO rename file to 300-channel.yaml once we remove the eventing Channel. apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: channels.messaging.knative.dev + name: channels.eventing.knative.dev labels: eventing.knative.dev/release: devel knative.dev/crd-install: "true" - messaging.knative.dev/subscribable: "true" spec: - group: messaging.knative.dev + group: eventing.knative.dev versions: - - name: v1alpha1 - served: true - storage: true + - name: v1alpha1 + served: true + storage: true names: kind: Channel plural: channels singular: channel categories: - - all - - knative - - messaging - - channel + - all + - knative + - eventing shortNames: - - ch + - chan scope: Namespaced subresources: status: {} @@ -48,9 +44,6 @@ spec: - name: Reason type: string JSONPath: ".status.conditions[?(@.type==\"Ready\")].reason" - - name: Hostname - type: string - JSONPath: .status.address.hostname - name: Age type: date JSONPath: .metadata.creationTimestamp @@ -59,6 +52,24 @@ spec: properties: spec: properties: + provisioner: + type: object + required: + - apiVersion + - kind + - name + properties: + apiVersion: + type: string + minLength: 1 + kind: + type: string + minLength: 1 + name: + type: string + minLength: 1 + arguments: + type: object subscribable: type: object properties: @@ -88,6 +99,8 @@ spec: uid: type: string minLength: 1 + generation: + type: integer uid: type: string minLength: 1 diff --git a/config/300-channel.yaml b/config/300-channel.yaml index 9bbcc3ae485..8450c63e828 100644 --- a/config/300-channel.yaml +++ b/config/300-channel.yaml @@ -1,4 +1,4 @@ -# Copyright 2018 The Knative Authors +# Copyright 2019 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. @@ -11,29 +11,32 @@ # 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. + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: channels.eventing.knative.dev + name: channels.messaging.knative.dev labels: eventing.knative.dev/release: devel knative.dev/crd-install: "true" + messaging.knative.dev/subscribable: "true" spec: - group: eventing.knative.dev + group: messaging.knative.dev versions: - - name: v1alpha1 - served: true - storage: true + - name: v1alpha1 + served: true + storage: true names: kind: Channel plural: channels singular: channel categories: - - all - - knative - - eventing + - all + - knative + - messaging + - channel shortNames: - - chan + - ch scope: Namespaced subresources: status: {} @@ -44,6 +47,9 @@ spec: - name: Reason type: string JSONPath: ".status.conditions[?(@.type==\"Ready\")].reason" + - name: Hostname + type: string + JSONPath: .status.address.hostname - name: Age type: date JSONPath: .metadata.creationTimestamp @@ -52,24 +58,6 @@ spec: properties: spec: properties: - provisioner: - type: object - required: - - apiVersion - - kind - - name - properties: - apiVersion: - type: string - minLength: 1 - kind: - type: string - minLength: 1 - name: - type: string - minLength: 1 - arguments: - type: object subscribable: type: object properties: @@ -99,8 +87,6 @@ spec: uid: type: string minLength: 1 - generation: - type: integer uid: type: string minLength: 1 diff --git a/pkg/apis/messaging/v1alpha1/channel_validation.go b/pkg/apis/messaging/v1alpha1/channel_validation.go new file mode 100644 index 00000000000..289a5928efa --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/channel_validation.go @@ -0,0 +1,44 @@ +/* +Copyright 2019 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 v1alpha1 + +import ( + "context" + "fmt" + + "knative.dev/pkg/apis" +) + +func (c *Channel) Validate(ctx context.Context) *apis.FieldError { + return c.Spec.Validate(ctx).ViaField("spec") +} + +func (cs *ChannelSpec) Validate(ctx context.Context) *apis.FieldError { + var errs *apis.FieldError + + if cs.Subscribable != nil { + for i, subscriber := range cs.Subscribable.Subscribers { + if subscriber.ReplyURI == "" && subscriber.SubscriberURI == "" { + fe := apis.ErrMissingField("replyURI", "subscriberURI") + fe.Details = "expected at least one of, got none" + errs = errs.Also(fe.ViaField(fmt.Sprintf("subscriber[%d]", i)).ViaField("subscribable")) + } + } + } + + return errs +} diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index 1c285f02b58..9e18ec1c965 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -26,8 +26,8 @@ import ( apierrs "k8s.io/apimachinery/pkg/api/errors" "k8s.io/client-go/tools/cache" - "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - listers "github.com/knative/eventing/pkg/client/listers/eventing/v1alpha1" + "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + listers "github.com/knative/eventing/pkg/client/listers/messaging/v1alpha1" "github.com/knative/eventing/pkg/logging" "github.com/knative/eventing/pkg/reconciler" "go.uber.org/zap" @@ -50,8 +50,6 @@ type Reconciler struct { // Check that our Reconciler implements controller.Reconciler var _ controller.Reconciler = (*Reconciler)(nil) -// Reconcile will check if the channel is being watched by provisioner's channel controller -// This will improve UX. See https://github.com/knative/eventing/issues/779 func (r *Reconciler) Reconcile(ctx context.Context, key string) error { // Convert the namespace/name string into a distinct namespace and name namespace, name, err := cache.SplitMetaNamespaceKey(key) @@ -99,27 +97,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, key string) error { } func (r *Reconciler) reconcile(ctx context.Context, ch *v1alpha1.Channel) error { - // Do not Initialize() Status in channel-default-controller. It will set ChannelConditionProvisionerInstalled=True - // Directly call GetCondition(). If the Status was never initialized then GetCondition() will return nil and - // IsUnknown() will return true - c := ch.Status.GetCondition(v1alpha1.ChannelConditionProvisionerInstalled) - - if c == nil || c.IsUnknown() { - - var proName string - var proKind string - if ch.Spec.Provisioner != nil { - proName = ch.Spec.Provisioner.Name - proKind = ch.Spec.Provisioner.Kind - } - ch.Status.MarkProvisionerNotInstalled( - "Provisioner not found.", - "Specified provisioner [Name:%s Kind:%s] is not installed or not controlling the channel.", - proName, - proKind, - ) - } + // TODO reconcile the channel + logging.FromContext(ctx).Sugar().Infof("Reconciling Channel: %s", ch.Name) return nil } @@ -140,7 +120,7 @@ func (r *Reconciler) updateStatus(ctx context.Context, desired *v1alpha1.Channel existing := channel.DeepCopy() existing.Status = desired.Status - c, err := r.EventingClientSet.EventingV1alpha1().Channels(desired.Namespace).UpdateStatus(existing) + c, err := r.EventingClientSet.MessagingV1alpha1().Channels(desired.Namespace).UpdateStatus(existing) if err == nil && becomesReady { duration := time.Since(c.ObjectMeta.CreationTimestamp.Time) diff --git a/pkg/reconciler/channel/controller.go b/pkg/reconciler/channel/controller.go index 310df88bb3d..448dd25f424 100644 --- a/pkg/reconciler/channel/controller.go +++ b/pkg/reconciler/channel/controller.go @@ -23,7 +23,7 @@ import ( "knative.dev/pkg/configmap" "knative.dev/pkg/controller" - channelinformer "github.com/knative/eventing/pkg/client/injection/informers/eventing/v1alpha1/channel" + channelinformer "github.com/knative/eventing/pkg/client/injection/informers/messaging/v1alpha1/channel" ) const ( @@ -31,7 +31,7 @@ const ( ReconcilerName = "Channels" // controllerAgentName is the string used by this controller to identify // itself when creating events. - controllerAgentName = "channel-default-controller" + controllerAgentName = "ch-default-controller" ) // NewController initializes the controller and is called by the generated code diff --git a/pkg/reconciler/channel/controller_test.go b/pkg/reconciler/channel/controller_test.go index b96a7d12e1c..263f690ae6e 100644 --- a/pkg/reconciler/channel/controller_test.go +++ b/pkg/reconciler/channel/controller_test.go @@ -25,7 +25,7 @@ import ( . "knative.dev/pkg/reconciler/testing" // Fake injection informers - _ "github.com/knative/eventing/pkg/client/injection/informers/eventing/v1alpha1/channel/fake" + _ "github.com/knative/eventing/pkg/client/injection/informers/messaging/v1alpha1/channel/fake" ) func TestNew(t *testing.T) { diff --git a/pkg/reconciler/channeleventing/channel.go b/pkg/reconciler/channeleventing/channel.go new file mode 100644 index 00000000000..13442d60e6a --- /dev/null +++ b/pkg/reconciler/channeleventing/channel.go @@ -0,0 +1,155 @@ +/* +Copyright 2019 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 channeleventing + +import ( + "context" + "fmt" + "reflect" + "time" + + corev1 "k8s.io/api/core/v1" + apierrs "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/tools/cache" + + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + listers "github.com/knative/eventing/pkg/client/listers/eventing/v1alpha1" + "github.com/knative/eventing/pkg/logging" + "github.com/knative/eventing/pkg/reconciler" + "go.uber.org/zap" + "knative.dev/pkg/controller" +) + +const ( + channelReadinessChanged = "ChannelReadinessChanged" + channelReconciled = "ChannelReconciled" + channelUpdateStatusFailed = "ChannelUpdateStatusFailed" +) + +type Reconciler struct { + *reconciler.Base + + // listers index properties about resources + channelLister listers.ChannelLister +} + +// Check that our Reconciler implements controller.Reconciler +var _ controller.Reconciler = (*Reconciler)(nil) + +// Reconcile will check if the channel is being watched by provisioner's channel controller +// This will improve UX. See https://github.com/knative/eventing/issues/779 +func (r *Reconciler) Reconcile(ctx context.Context, key string) error { + // Convert the namespace/name string into a distinct namespace and name + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + logging.FromContext(ctx).Error("invalid resource key") + return nil + } + + // Get the Channel resource with this namespace/name + original, err := r.channelLister.Channels(namespace).Get(name) + if apierrs.IsNotFound(err) { + // The resource may no longer exist, in which case we stop processing. + logging.FromContext(ctx).Error("Channel key in work queue no longer exists") + return nil + } else if err != nil { + return err + } + + // Delete is a no-op. + if original.DeletionTimestamp != nil { + return nil + } + + // Don't modify the informers copy + channel := original.DeepCopy() + + // Reconcile this copy of the Channel and then write back any status + // updates regardless of whether the reconcile error out. + reconcileErr := r.reconcile(ctx, channel) + if reconcileErr != nil { + logging.FromContext(ctx).Error("Error reconciling Channel", zap.Error(reconcileErr)) + } else { + logging.FromContext(ctx).Debug("Successfully reconciled Channel") + r.Recorder.Eventf(channel, corev1.EventTypeNormal, channelReconciled, "Channel reconciled: %s", key) + } + + if _, updateStatusErr := r.updateStatus(ctx, channel.DeepCopy()); updateStatusErr != nil { + logging.FromContext(ctx).Warn("Error updating Channel status", zap.Error(updateStatusErr)) + r.Recorder.Eventf(channel, corev1.EventTypeWarning, channelUpdateStatusFailed, "Failed to update channel status: %s", key) + return updateStatusErr + } + + // Requeue if the resource is not ready: + return reconcileErr +} + +func (r *Reconciler) reconcile(ctx context.Context, ch *v1alpha1.Channel) error { + // Do not Initialize() Status in channel-default-controller. It will set ChannelConditionProvisionerInstalled=True + // Directly call GetCondition(). If the Status was never initialized then GetCondition() will return nil and + // IsUnknown() will return true + c := ch.Status.GetCondition(v1alpha1.ChannelConditionProvisionerInstalled) + + if c == nil || c.IsUnknown() { + + var proName string + var proKind string + if ch.Spec.Provisioner != nil { + proName = ch.Spec.Provisioner.Name + proKind = ch.Spec.Provisioner.Kind + } + + ch.Status.MarkProvisionerNotInstalled( + "Provisioner not found.", + "Specified provisioner [Name:%s Kind:%s] is not installed or not controlling the channel.", + proName, + proKind, + ) + } + return nil +} + +func (r *Reconciler) updateStatus(ctx context.Context, desired *v1alpha1.Channel) (*v1alpha1.Channel, error) { + channel, err := r.channelLister.Channels(desired.Namespace).Get(desired.Name) + if err != nil { + return nil, err + } + + // If there's nothing to update, just return. + if reflect.DeepEqual(channel.Status, desired.Status) { + return channel, nil + } + + becomesReady := desired.Status.IsReady() && !channel.Status.IsReady() + + // Don't modify the informers copy. + existing := channel.DeepCopy() + existing.Status = desired.Status + + c, err := r.EventingClientSet.EventingV1alpha1().Channels(desired.Namespace).UpdateStatus(existing) + + if err == nil && becomesReady { + duration := time.Since(c.ObjectMeta.CreationTimestamp.Time) + logging.FromContext(ctx).Sugar().Infof("Channel %q became ready after %v", channel.Name, duration) + r.Recorder.Event(channel, corev1.EventTypeNormal, channelReadinessChanged, fmt.Sprintf("Channel %q became ready", channel.Name)) + if err := r.StatsReporter.ReportReady("Channel", channel.Namespace, channel.Name, duration); err != nil { + logging.FromContext(ctx).Sugar().Infof("failed to record ready for Channel, %v", err) + } + } + + return c, err +} diff --git a/pkg/reconciler/channel/channel_test.go b/pkg/reconciler/channeleventing/channel_test.go similarity index 99% rename from pkg/reconciler/channel/channel_test.go rename to pkg/reconciler/channeleventing/channel_test.go index fea0deea4c0..b7527d0af5e 100644 --- a/pkg/reconciler/channel/channel_test.go +++ b/pkg/reconciler/channeleventing/channel_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package channel +package channeleventing import ( "context" diff --git a/pkg/reconciler/channeleventing/controller.go b/pkg/reconciler/channeleventing/controller.go new file mode 100644 index 00000000000..baee4bb5ddc --- /dev/null +++ b/pkg/reconciler/channeleventing/controller.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 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 channeleventing + +import ( + "context" + + "github.com/knative/eventing/pkg/reconciler" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + + channelinformer "github.com/knative/eventing/pkg/client/injection/informers/eventing/v1alpha1/channel" +) + +const ( + // ReconcilerName is the name of the reconciler + ReconcilerName = "Channels" + // controllerAgentName is the string used by this controller to identify + // itself when creating events. + controllerAgentName = "channel-default-controller" +) + +// NewController initializes the controller and is called by the generated code +// Registers event handlers to enqueue events +func NewController( + ctx context.Context, + cmw configmap.Watcher, +) *controller.Impl { + + channelInformer := channelinformer.Get(ctx) + + r := &Reconciler{ + Base: reconciler.NewBase(ctx, controllerAgentName, cmw), + channelLister: channelInformer.Lister(), + } + impl := controller.NewImpl(r, r.Logger, ReconcilerName) + + r.Logger.Info("Setting up event handlers") + channelInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + + return impl +} diff --git a/pkg/reconciler/channeleventing/controller_test.go b/pkg/reconciler/channeleventing/controller_test.go new file mode 100644 index 00000000000..35b7017abb7 --- /dev/null +++ b/pkg/reconciler/channeleventing/controller_test.go @@ -0,0 +1,40 @@ +/* +Copyright 2019 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 channeleventing + +import ( + "testing" + + "knative.dev/pkg/configmap" + + logtesting "knative.dev/pkg/logging/testing" + . "knative.dev/pkg/reconciler/testing" + + // Fake injection informers + _ "github.com/knative/eventing/pkg/client/injection/informers/eventing/v1alpha1/channel/fake" +) + +func TestNew(t *testing.T) { + defer logtesting.ClearAll() + ctx, _ := SetupFakeContext(t) + + c := NewController(ctx, configmap.NewFixedWatcher()) + + if c == nil { + t.Fatal("Expected NewController to return a non-nil value") + } +} From b1f91d87499cca729af0ad790c3f6966e09cf573 Mon Sep 17 00:00:00 2001 From: nachocano Date: Mon, 22 Jul 2019 15:24:16 -0700 Subject: [PATCH 03/39] using ChannelTemplateSpec --- cmd/webhook/main.go | 8 ++ config/400-default-ch-config.yaml | 30 ++++ config/400-default-channel-config.yaml | 10 -- pkg/apis/duck/v1alpha1/channel_types.go | 45 ++++++ .../duck/v1alpha1/zz_generated.deepcopy.go | 61 ++++++++ pkg/apis/eventing/v1alpha1/broker_types.go | 29 +--- .../eventing/v1alpha1/broker_validation.go | 7 +- .../v1alpha1/broker_validation_test.go | 9 +- .../v1alpha1/zz_generated.deepcopy.go | 53 ------- .../messaging/v1alpha1/channel_defaults.go | 31 +++- pkg/apis/messaging/v1alpha1/channel_types.go | 10 +- pkg/apis/messaging/v1alpha1/sequence_types.go | 29 +--- .../messaging/v1alpha1/sequence_validation.go | 4 +- .../v1alpha1/sequence_validation_test.go | 11 +- .../v1alpha1/zz_generated.deepcopy.go | 66 +-------- pkg/defaultchannel/channel_defaulter.go | 136 ++++++++++++++++++ pkg/reconciler/broker/resources/channel.go | 9 +- pkg/reconciler/sequence/resources/channel.go | 9 +- 18 files changed, 351 insertions(+), 206 deletions(-) create mode 100644 config/400-default-ch-config.yaml create mode 100644 pkg/apis/duck/v1alpha1/channel_types.go create mode 100644 pkg/defaultchannel/channel_defaulter.go diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 89e1608f30c..4bacdd5de46 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -20,6 +20,7 @@ import ( "log" "github.com/knative/eventing/pkg/channeldefaulter" + "github.com/knative/eventing/pkg/defaultchannel" "go.uber.org/zap" @@ -78,6 +79,12 @@ func main() { eventingv1alpha1.ChannelDefaulterSingleton = channelDefaulter configMapWatcher.Watch(channeldefaulter.ConfigMapName, channelDefaulter.UpdateConfigMap) + // Watch the default-ch-webhook ConfigMap and dynamically update the default + // Channel CRD. + chDefaulter := defaultchannel.New(logger.Desugar()) + messagingv1alpha1.ChannelDefaulterSingleton = chDefaulter + configMapWatcher.Watch(defaultchannel.ConfigMapName, chDefaulter.UpdateConfigMap) + if err = configMapWatcher.Start(stopCh); err != nil { logger.Fatalf("failed to start webhook configmap watcher: %v", err) } @@ -104,6 +111,7 @@ func main() { // For group messaging.knative.dev. messagingv1alpha1.SchemeGroupVersion.WithKind("InMemoryChannel"): &messagingv1alpha1.InMemoryChannel{}, messagingv1alpha1.SchemeGroupVersion.WithKind("Sequence"): &messagingv1alpha1.Sequence{}, + messagingv1alpha1.SchemeGroupVersion.WithKind("Channel"): &messagingv1alpha1.Channel{}, }, Logger: logger, } diff --git a/config/400-default-ch-config.yaml b/config/400-default-ch-config.yaml new file mode 100644 index 00000000000..3bed8e47d4c --- /dev/null +++ b/config/400-default-ch-config.yaml @@ -0,0 +1,30 @@ +# Copyright 2018 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: default-ch-webhook + namespace: knative-eventing +data: + # Configuration for defaulting channels that do not specify CRD implementations. All field names are + # lowercase. + default-channel-config: | + clusterdefault: + apiversion: messaging.knative.dev/v1alpha1 + kind: InMemoryChannel + namespacedefaults: + some-namespace: + apiversion: messaging.knative.dev/v1alpha1 + kind: InMemoryChannel diff --git a/config/400-default-channel-config.yaml b/config/400-default-channel-config.yaml index ebad9c23cfd..9d6bd5aa519 100644 --- a/config/400-default-channel-config.yaml +++ b/config/400-default-channel-config.yaml @@ -30,13 +30,3 @@ data: apiversion: eventing.knative.dev/v1alpha1 kind: ClusterChannelProvisioner name: some-other-provisioner - # Configuration for defaulting channels that do not specify the channel CRD. All field names are - # lowercase. - default-ch-config: | - clusterdefault: - apiversion: messaging.knative.dev/v1alpha1 - kind: InMemoryChannel - namespacedefaults: - some-namespace: - apiversion: messaging.knative.dev/v1alpha1 - kind: InMemoryChannel diff --git a/pkg/apis/duck/v1alpha1/channel_types.go b/pkg/apis/duck/v1alpha1/channel_types.go new file mode 100644 index 00000000000..80f0b09961e --- /dev/null +++ b/pkg/apis/duck/v1alpha1/channel_types.go @@ -0,0 +1,45 @@ +/* +Copyright 2019 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 v1alpha1 + +import "k8s.io/apimachinery/pkg/runtime" +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type ChannelTemplateSpec struct { + metav1.TypeMeta `json:",inline"` + + // Spec defines the Spec to use for each channel created. Passed + // in verbatim to the Channel CRD as Spec section. + // +optional + Spec *runtime.RawExtension `json:"spec,omitempty"` +} + +// Internal version of ChannelTemplateSpec that includes ObjectMeta so that +// we can easily create new Channels off of it. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type ChannelTemplateSpecInternal struct { + metav1.TypeMeta `json:",inline"` + + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the Spec to use for each channel created. Passed + // in verbatim to the Channel CRD as Spec section. + // +optional + Spec *runtime.RawExtension `json:"spec,omitempty"` +} diff --git a/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go index 4867db71b5c..1cf97201ca4 100644 --- a/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go @@ -25,6 +25,67 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChannelTemplateSpec) DeepCopyInto(out *ChannelTemplateSpec) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelTemplateSpec. +func (in *ChannelTemplateSpec) DeepCopy() *ChannelTemplateSpec { + if in == nil { + return nil + } + out := new(ChannelTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ChannelTemplateSpec) 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 *ChannelTemplateSpecInternal) DeepCopyInto(out *ChannelTemplateSpecInternal) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelTemplateSpecInternal. +func (in *ChannelTemplateSpecInternal) DeepCopy() *ChannelTemplateSpecInternal { + if in == nil { + return nil + } + out := new(ChannelTemplateSpecInternal) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ChannelTemplateSpecInternal) 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 *Channelable) DeepCopyInto(out *Channelable) { *out = *in diff --git a/pkg/apis/eventing/v1alpha1/broker_types.go b/pkg/apis/eventing/v1alpha1/broker_types.go index 8ef08030dff..5ac3828ce4d 100644 --- a/pkg/apis/eventing/v1alpha1/broker_types.go +++ b/pkg/apis/eventing/v1alpha1/broker_types.go @@ -17,6 +17,7 @@ package v1alpha1 import ( + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -62,32 +63,6 @@ var ( _ kmeta.OwnerRefable = (*Broker)(nil) ) -// This should be duck so that Broker can also use this -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type ChannelTemplateSpec struct { - metav1.TypeMeta `json:",inline"` - - // Spec defines the Spec to use for each channel created. Passed - // in verbatim to the Channel CRD as Spec section. - // +optional - Spec runtime.RawExtension `json:"spec"` -} - -// Internal version of ChannelTemplateSpec that includes ObjectMeta so that -// we can easily create new Channels off of it. -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type ChannelTemplateSpecInternal struct { - metav1.TypeMeta `json:",inline"` - - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the Spec to use for each channel created. Passed - // in verbatim to the Channel CRD as Spec section. - // +optional - Spec runtime.RawExtension `json:"spec"` -} - type BrokerSpec struct { // DeprecatedChannelTemplate, if specified will be used to create all the Channels used internally by the // Broker. Only Provisioner and Arguments may be specified. If left unspecified, the default @@ -98,7 +73,7 @@ type BrokerSpec struct { // ChannelTemplate specifies which Channel CRD to use to create all the Channels used internally by the // Broker. - ChannelTemplate ChannelTemplateSpec `json:"channelTemplateSpec"` + ChannelTemplate eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplateSpec"` } // BrokerStatus represents the current state of a Broker. diff --git a/pkg/apis/eventing/v1alpha1/broker_validation.go b/pkg/apis/eventing/v1alpha1/broker_validation.go index a56ca993081..b68b66b6bb5 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation.go @@ -19,6 +19,7 @@ package v1alpha1 import ( "context" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "k8s.io/apimachinery/pkg/api/equality" "knative.dev/pkg/apis" ) @@ -30,7 +31,7 @@ func (b *Broker) Validate(ctx context.Context) *apis.FieldError { func (bs *BrokerSpec) Validate(ctx context.Context) *apis.FieldError { var errs *apis.FieldError - if bs.DeprecatedChannelTemplate != nil && !equality.Semantic.DeepEqual(bs.ChannelTemplate, ChannelTemplateSpec{}) { + if bs.DeprecatedChannelTemplate != nil && !equality.Semantic.DeepEqual(bs.ChannelTemplate, eventingduckv1alpha1.ChannelTemplateSpec{}) { errs = errs.Also(apis.ErrMultipleOneOf("channelTemplate", "channelTemplateSpec")) return errs } @@ -39,7 +40,7 @@ func (bs *BrokerSpec) Validate(ctx context.Context) *apis.FieldError { errs = errs.Also(dcte.ViaField("channelTemplate")) } - if !equality.Semantic.DeepEqual(bs.ChannelTemplate, ChannelTemplateSpec{}) { + if !equality.Semantic.DeepEqual(bs.ChannelTemplate, eventingduckv1alpha1.ChannelTemplateSpec{}) { if cte := isValidChannelTemplate(bs.ChannelTemplate); cte != nil { errs = errs.Also(cte.ViaField("channelTemplateSpec")) } @@ -64,7 +65,7 @@ func isValidDeprecatedChannelTemplate(dct *ChannelSpec) *apis.FieldError { return errs } -func isValidChannelTemplate(dct ChannelTemplateSpec) *apis.FieldError { +func isValidChannelTemplate(dct eventingduckv1alpha1.ChannelTemplateSpec) *apis.FieldError { var errs *apis.FieldError if dct.Kind == "" { errs = errs.Also(apis.ErrMissingField("kind")) diff --git a/pkg/apis/eventing/v1alpha1/broker_validation_test.go b/pkg/apis/eventing/v1alpha1/broker_validation_test.go index bc1a2944f44..be2eb4fa3a9 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation_test.go @@ -22,6 +22,7 @@ import ( "github.com/google/go-cmp/cmp" eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/pkg/apis" @@ -101,7 +102,7 @@ func TestValidSpec(t *testing.T) { DeprecatedChannelTemplate: &ChannelSpec{ Provisioner: &corev1.ObjectReference{}, }, - ChannelTemplate: ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}}, + ChannelTemplate: eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}}, }, want: func() *apis.FieldError { var errs *apis.FieldError @@ -112,7 +113,7 @@ func TestValidSpec(t *testing.T) { }, { name: "invalid templatespec, missing kind", spec: BrokerSpec{ - ChannelTemplate: ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{APIVersion: "myapiversion"}}, + ChannelTemplate: eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{APIVersion: "myapiversion"}}, }, want: func() *apis.FieldError { var errs *apis.FieldError @@ -123,7 +124,7 @@ func TestValidSpec(t *testing.T) { }, { name: "invalid templatespec, missing apiVersion", spec: BrokerSpec{ - ChannelTemplate: ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}}, + ChannelTemplate: eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}}, }, want: func() *apis.FieldError { var errs *apis.FieldError @@ -134,7 +135,7 @@ func TestValidSpec(t *testing.T) { }, { name: "valid templatespec", spec: BrokerSpec{ - ChannelTemplate: ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind", APIVersion: "myapiversion"}}, + ChannelTemplate: eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind", APIVersion: "myapiversion"}}, }, want: nil, }} diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index c79033073fd..2a24b86d7b4 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -253,59 +253,6 @@ func (in *ChannelStatus) DeepCopy() *ChannelStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelTemplateSpec) DeepCopyInto(out *ChannelTemplateSpec) { - *out = *in - out.TypeMeta = in.TypeMeta - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelTemplateSpec. -func (in *ChannelTemplateSpec) DeepCopy() *ChannelTemplateSpec { - if in == nil { - return nil - } - out := new(ChannelTemplateSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ChannelTemplateSpec) 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 *ChannelTemplateSpecInternal) DeepCopyInto(out *ChannelTemplateSpecInternal) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelTemplateSpecInternal. -func (in *ChannelTemplateSpecInternal) DeepCopy() *ChannelTemplateSpecInternal { - if in == nil { - return nil - } - out := new(ChannelTemplateSpecInternal) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ChannelTemplateSpecInternal) 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 *ClusterChannelProvisioner) DeepCopyInto(out *ClusterChannelProvisioner) { *out = *in diff --git a/pkg/apis/messaging/v1alpha1/channel_defaults.go b/pkg/apis/messaging/v1alpha1/channel_defaults.go index fc07e161dec..dbb39ee2657 100644 --- a/pkg/apis/messaging/v1alpha1/channel_defaults.go +++ b/pkg/apis/messaging/v1alpha1/channel_defaults.go @@ -16,12 +16,35 @@ limitations under the License. package v1alpha1 -import "context" +import ( + "context" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" +) + +// ChannelDefaulter sets the default Channel CRD and Arguments on Channels that do not +// specify any implementation. +type ChannelDefaulter interface { + // GetDefault determines the default Channel CRD for the given Channel. It does + // not modify the given Channel. + GetDefault(c *Channel) *eventingduckv1alpha1.ChannelTemplateSpec +} + +var ( + // ChannelDefaulterSingleton is the global singleton used to default Channels that do not + // specify a Channel CRD. + ChannelDefaulterSingleton ChannelDefaulter +) func (c *Channel) SetDefaults(ctx context.Context) { + if c != nil && c.Spec.ChannelTemplate == nil { + // The singleton may not have been set, if so ignore it and validation will reject the + // Channel. + if cd := ChannelDefaulterSingleton; cd != nil { + channelTemplate := cd.GetDefault(c.DeepCopy()) + c.Spec.ChannelTemplate = channelTemplate + } + } c.Spec.SetDefaults(ctx) } -func (dcs *ChannelSpec) SetDefaults(ctx context.Context) { - // TODO: Nothing to default here... -} +func (cs *ChannelSpec) SetDefaults(ctx context.Context) {} diff --git a/pkg/apis/messaging/v1alpha1/channel_types.go b/pkg/apis/messaging/v1alpha1/channel_types.go index b8bda0e97e8..d52380e6c23 100644 --- a/pkg/apis/messaging/v1alpha1/channel_types.go +++ b/pkg/apis/messaging/v1alpha1/channel_types.go @@ -51,9 +51,13 @@ var _ apis.Defaultable = (*Channel)(nil) var _ runtime.Object = (*Channel)(nil) var _ webhook.GenericCRD = (*Channel)(nil) -// ChannelSpec defines which subscribers have expressed interest in -// receiving events from this Channel. +// ChannelSpec defines which subscribers have expressed interest in receiving events from this Channel. +// It also defines the ChannelTemplate to use in order to create the CRD Channel backing this Channel. type ChannelSpec struct { + + // ChannelTemplate specifies which Channel CRD to use to create the CRD Channel backing this Channel. + ChannelTemplate *eventingduck.ChannelTemplateSpec `json:"channelTemplateSpec"` + // Channel conforms to Duck type Subscribable. Subscribable *eventingduck.Subscribable `json:"subscribable,omitempty"` } @@ -74,6 +78,8 @@ type ChannelStatus struct { // Subscribers is populated with the statuses of each of the Channelable's subscribers. eventingduck.SubscribableTypeStatus `json:",inline"` + + // TODO add backing channel object ref. } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/messaging/v1alpha1/sequence_types.go b/pkg/apis/messaging/v1alpha1/sequence_types.go index 41f3dccc87e..6899e9d9a59 100644 --- a/pkg/apis/messaging/v1alpha1/sequence_types.go +++ b/pkg/apis/messaging/v1alpha1/sequence_types.go @@ -17,6 +17,7 @@ package v1alpha1 import ( + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -55,39 +56,13 @@ var _ apis.Defaultable = (*Sequence)(nil) var _ runtime.Object = (*Sequence)(nil) var _ webhook.GenericCRD = (*Sequence)(nil) -// This should be duck so that Broker can also use this -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type ChannelTemplateSpec struct { - metav1.TypeMeta `json:",inline"` - - // Spec defines the Spec to use for each channel created. Passed - // in verbatim to the Channel CRD as Spec section. - // +optional - Spec *runtime.RawExtension `json:"spec,omitempty"` -} - -// Internal version of ChannelTemplateSpec that includes ObjectMeta so that -// we can easily create new Channels off of it. -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type ChannelTemplateSpecInternal struct { - metav1.TypeMeta `json:",inline"` - - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the Spec to use for each channel created. Passed - // in verbatim to the Channel CRD as Spec section. - // +optional - Spec *runtime.RawExtension `json:"spec,omitempty"` -} - type SequenceSpec struct { // Steps is the list of Subscribers (processors / functions) that will be called in the order // provided. Steps []eventingv1alpha1.SubscriberSpec `json:"steps"` // ChannelTemplate specifies which Channel CRD to use - ChannelTemplate ChannelTemplateSpec `json:"channelTemplate"` + ChannelTemplate eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplate"` // Reply is a Reference to where the result of the last Subscriber gets sent to. // diff --git a/pkg/apis/messaging/v1alpha1/sequence_validation.go b/pkg/apis/messaging/v1alpha1/sequence_validation.go index 76db1d99ceb..80095758113 100644 --- a/pkg/apis/messaging/v1alpha1/sequence_validation.go +++ b/pkg/apis/messaging/v1alpha1/sequence_validation.go @@ -18,7 +18,7 @@ package v1alpha1 import ( "context" - + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "k8s.io/apimachinery/pkg/api/equality" "knative.dev/pkg/apis" @@ -41,7 +41,7 @@ func (ps *SequenceSpec) Validate(ctx context.Context) *apis.FieldError { } } - if equality.Semantic.DeepEqual(ps.ChannelTemplate, ChannelTemplateSpec{}) { + if equality.Semantic.DeepEqual(ps.ChannelTemplate, eventingduck.ChannelTemplateSpec{}) { errs = errs.Also(apis.ErrMissingField("channelTemplate")) return errs } diff --git a/pkg/apis/messaging/v1alpha1/sequence_validation_test.go b/pkg/apis/messaging/v1alpha1/sequence_validation_test.go index 2d496fa94a2..49fb630710e 100644 --- a/pkg/apis/messaging/v1alpha1/sequence_validation_test.go +++ b/pkg/apis/messaging/v1alpha1/sequence_validation_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -64,12 +65,12 @@ func makeInvalidReply(channelName string) *corev1.ObjectReference { func TestSequenceSpecValidation(t *testing.T) { subscriberURI := "http://example.com" - validChannelTemplate := ChannelTemplateSpec{ - metav1.TypeMeta{ + validChannelTemplate := eventingduck.ChannelTemplateSpec{ + TypeMeta: metav1.TypeMeta{ Kind: "mykind", APIVersion: "myapiversion", }, - &runtime.RawExtension{}, + Spec: &runtime.RawExtension{}, } tests := []struct { name string @@ -103,7 +104,7 @@ func TestSequenceSpecValidation(t *testing.T) { }, { name: "invalid channeltemplatespec missing APIVersion", ts: &SequenceSpec{ - ChannelTemplate: ChannelTemplateSpec{metav1.TypeMeta{Kind: "mykind"}, &runtime.RawExtension{}}, + ChannelTemplate: eventingduck.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}, Spec: &runtime.RawExtension{}}, Steps: []eventingv1alpha1.SubscriberSpec{{URI: &subscriberURI}}, }, want: func() *apis.FieldError { @@ -113,7 +114,7 @@ func TestSequenceSpecValidation(t *testing.T) { }, { name: "invalid channeltemplatespec missing Kind", ts: &SequenceSpec{ - ChannelTemplate: ChannelTemplateSpec{metav1.TypeMeta{APIVersion: "myapiversion"}, &runtime.RawExtension{}}, + ChannelTemplate: eventingduck.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{APIVersion: "myapiversion"}, Spec: &runtime.RawExtension{}}, Steps: []eventingv1alpha1.SubscriberSpec{{URI: &subscriberURI}}, }, want: func() *apis.FieldError { diff --git a/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go index 224c9623021..a6eb1c08fd2 100644 --- a/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go @@ -91,6 +91,11 @@ func (in *ChannelList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ChannelSpec) DeepCopyInto(out *ChannelSpec) { *out = *in + if in.ChannelTemplate != nil { + in, out := &in.ChannelTemplate, &out.ChannelTemplate + *out = new(duckv1alpha1.ChannelTemplateSpec) + (*in).DeepCopyInto(*out) + } if in.Subscribable != nil { in, out := &in.Subscribable, &out.Subscribable *out = new(duckv1alpha1.Subscribable) @@ -128,67 +133,6 @@ func (in *ChannelStatus) DeepCopy() *ChannelStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelTemplateSpec) DeepCopyInto(out *ChannelTemplateSpec) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Spec != nil { - in, out := &in.Spec, &out.Spec - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelTemplateSpec. -func (in *ChannelTemplateSpec) DeepCopy() *ChannelTemplateSpec { - if in == nil { - return nil - } - out := new(ChannelTemplateSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ChannelTemplateSpec) 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 *ChannelTemplateSpecInternal) DeepCopyInto(out *ChannelTemplateSpecInternal) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - if in.Spec != nil { - in, out := &in.Spec, &out.Spec - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelTemplateSpecInternal. -func (in *ChannelTemplateSpecInternal) DeepCopy() *ChannelTemplateSpecInternal { - if in == nil { - return nil - } - out := new(ChannelTemplateSpecInternal) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ChannelTemplateSpecInternal) 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 *InMemoryChannel) DeepCopyInto(out *InMemoryChannel) { *out = *in diff --git a/pkg/defaultchannel/channel_defaulter.go b/pkg/defaultchannel/channel_defaulter.go new file mode 100644 index 00000000000..79d7b54dc5f --- /dev/null +++ b/pkg/defaultchannel/channel_defaulter.go @@ -0,0 +1,136 @@ +/* +Copyright 2018 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 defaultchannel + +import ( + "sync/atomic" + + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + "go.uber.org/zap" + "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" +) + +const ( + // ConfigMapName is the name of the ConfigMap that contains the configuration for the default + // channel CRD. + ConfigMapName = "default-ch-webhook" + + // channelDefaulterKey is the key in the ConfigMap to get the name of the default + // Channel CRD. + channelDefaulterKey = "default-ch-config" +) + +// Configuration is the data structure serialized to YAML in the config map. When a Channel needs to be +// defaulted, the Channel's namespace will be used as a key into NamespaceDefaults, if there is +// something present, then that is used. If not, then the ClusterDefault is used. +type Config struct { + // NamespaceDefaultChannels are the default Channels CRDs for each namespace. namespace is the + // key, the value is the default ChannelTemplate to use. + NamespaceDefaults map[string]*eventingduckv1alpha1.ChannelTemplateSpec `json:"namespaceDefaults,omitempty"` + // ClusterDefaultChannel is the default Channel CRD for all namespaces that are not in + // NamespaceDefaultChannels. + ClusterDefault *eventingduckv1alpha1.ChannelTemplateSpec `json:"clusterDefault,omitempty"` +} + +// ChannelDefaulter adds a default Channel CRD to Channels that do not have any +// CRD specified. The default is stored in a ConfigMap and can be updated at runtime. +type ChannelDefaulter struct { + // The current default Channel CRD to set. This should only be accessed via + // getConfig() and setConfig(), as they correctly enforce the type we require (*Config). + config atomic.Value + logger *zap.Logger +} + +var _ messagingv1alpha1.ChannelDefaulter = &ChannelDefaulter{} + +// New creates a new ChannelDefaulter. The caller is expected to set this as the global singleton. +// +// channelDefaulter := channeldefaulter.New(logger) +// messagingv1alpha1.ChannelDefaulterSingleton = channelDefaulter +// configMapWatcher.Watch(channelDefaulter.ConfigMapName, channelDefaulter.UpdateConfigMap) +func New(logger *zap.Logger) *ChannelDefaulter { + return &ChannelDefaulter{ + logger: logger.With(zap.String("role", "channelDefaulter")), + } +} + +// UpdateConfigMap reads in a ConfigMap and updates the internal default Channel CRD to use. +func (cd *ChannelDefaulter) UpdateConfigMap(cm *corev1.ConfigMap) { + if cm == nil { + cd.logger.Info("UpdateConfigMap on a nil map") + return + } + defaultChannelConfig, present := cm.Data[channelDefaulterKey] + if !present { + cd.logger.Info("ConfigMap is missing key", zap.String("key", channelDefaulterKey), zap.Any("configMap", cm)) + return + } + + if defaultChannelConfig == "" { + cd.logger.Info("ConfigMap's value was the empty string, ignoring it.", zap.Any("configMap", cm)) + return + } + + config := Config{} + if err := yaml.UnmarshalStrict([]byte(defaultChannelConfig), &config); err != nil { + cd.logger.Error("ConfigMap's value could not be unmarshaled.", zap.Error(err), zap.Any("configMap", cm)) + return + } + + cd.logger.Info("Updated channelDefaulter config", zap.Any("config", config)) + cd.setConfig(&config) +} + +// setConfig is a typed wrapper around config. +func (cd *ChannelDefaulter) setConfig(config *Config) { + cd.config.Store(config) +} + +// getConfig is a typed wrapper around config. +func (cd *ChannelDefaulter) getConfig() *Config { + if config, ok := cd.config.Load().(*Config); ok { + return config + } + return nil +} + +// GetDefault determines the default Channel CRD and arguments for the provided Channel. +func (cd *ChannelDefaulter) GetDefault(c *messagingv1alpha1.Channel) *eventingduckv1alpha1.ChannelTemplateSpec { + if c == nil { + return nil + } + // Because we are treating this as a singleton, be tolerant to it having not been setup at all. + if cd == nil { + return nil + } + config := cd.getConfig() + if config == nil { + return nil + } + channelTemplate := getDefaultChannelTemplate(config, c.Namespace) + cd.logger.Info("Defaulting the Channel", zap.Any("defaultChannelTemplate", channelTemplate)) + return channelTemplate +} + +func getDefaultChannelTemplate(config *Config, namespace string) *eventingduckv1alpha1.ChannelTemplateSpec { + if template, ok := config.NamespaceDefaults[namespace]; ok { + return template + } + return config.ClusterDefault +} diff --git a/pkg/reconciler/broker/resources/channel.go b/pkg/reconciler/broker/resources/channel.go index db7323256cd..f5a8ec14945 100644 --- a/pkg/reconciler/broker/resources/channel.go +++ b/pkg/reconciler/broker/resources/channel.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "knative.dev/pkg/kmeta" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -37,12 +38,12 @@ func BrokerChannelName(brokerName, channelType string) string { // for a given Broker. func NewChannel(channelType string, b *v1alpha1.Broker, l map[string]string) (*unstructured.Unstructured, error) { // Set the name of the resource we're creating as well as the namespace, etc. - template := v1alpha1.ChannelTemplateSpecInternal{ - metav1.TypeMeta{ + template := eventingduck.ChannelTemplateSpecInternal{ + TypeMeta: metav1.TypeMeta{ Kind: b.Spec.ChannelTemplate.Kind, APIVersion: b.Spec.ChannelTemplate.APIVersion, }, - metav1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ OwnerReferences: []metav1.OwnerReference{ *kmeta.NewControllerRef(b), }, @@ -50,7 +51,7 @@ func NewChannel(channelType string, b *v1alpha1.Broker, l map[string]string) (*u Namespace: b.Namespace, Labels: l, }, - b.Spec.ChannelTemplate.Spec, + Spec: b.Spec.ChannelTemplate.Spec, } raw, err := json.Marshal(template) if err != nil { diff --git a/pkg/reconciler/sequence/resources/channel.go b/pkg/reconciler/sequence/resources/channel.go index 693b6a48364..f9b4c8eb56e 100644 --- a/pkg/reconciler/sequence/resources/channel.go +++ b/pkg/reconciler/sequence/resources/channel.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "knative.dev/pkg/kmeta" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" v1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -36,19 +37,19 @@ func SequenceChannelName(sequenceName string, step int) string { // for a given sequence. func NewChannel(name string, p *v1alpha1.Sequence) (*unstructured.Unstructured, error) { // Set the name of the resource we're creating as well as the namespace, etc. - template := v1alpha1.ChannelTemplateSpecInternal{ - metav1.TypeMeta{ + template := eventingduck.ChannelTemplateSpecInternal{ + TypeMeta: metav1.TypeMeta{ Kind: p.Spec.ChannelTemplate.Kind, APIVersion: p.Spec.ChannelTemplate.APIVersion, }, - metav1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ OwnerReferences: []metav1.OwnerReference{ *kmeta.NewControllerRef(p), }, Name: name, Namespace: p.Namespace, }, - p.Spec.ChannelTemplate.Spec, + Spec: p.Spec.ChannelTemplate.Spec, } raw, err := json.Marshal(template) if err != nil { From 7d34bcf7ec3c9f1a203db9fd23984c8be466aa00 Mon Sep 17 00:00:00 2001 From: nachocano Date: Mon, 22 Jul 2019 16:39:34 -0700 Subject: [PATCH 04/39] updating channel controller --- .../messaging/v1alpha1/channel_lifecycle.go | 69 +++------------ pkg/apis/messaging/v1alpha1/channel_types.go | 4 +- pkg/reconciler/channel/channel.go | 87 +++++++++++++++++-- pkg/reconciler/channel/controller.go | 4 + pkg/reconciler/channel/resources/channel.go | 57 ++++++++++++ 5 files changed, 159 insertions(+), 62 deletions(-) create mode 100644 pkg/reconciler/channel/resources/channel.go diff --git a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go index 14747ce161f..a43f321d6f9 100644 --- a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go +++ b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go @@ -17,39 +17,23 @@ package v1alpha1 import ( - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "knative.dev/pkg/apis" "knative.dev/pkg/apis/duck/v1alpha1" ) -var chCondSet = apis.NewLivingConditionSet(ChannelConditionDispatcherReady, ChannelConditionServiceReady, ChannelConditionEndpointsReady, ChannelConditionAddressable, ChannelConditionChannelServiceReady) +var chCondSet = apis.NewLivingConditionSet(ChannelConditionBackingChannelReady, ChannelConditionAddressable) const ( // ChannelConditionReady has status True when all subconditions below have been set to True. ChannelConditionReady = apis.ConditionReady - // ChannelConditionDispatcherReady has status True when a Dispatcher deployment is ready - // Keyed off appsv1.DeploymentAvailable, which means minimum available replicas required are up - // and running for at least minReadySeconds. - ChannelConditionDispatcherReady apis.ConditionType = "DispatcherReady" - - // ChannelConditionServiceReady has status True when a k8s Service is ready. This - // basically just means it exists because there's no meaningful status in Service. See Endpoints - // below. - ChannelConditionServiceReady apis.ConditionType = "ServiceReady" - - // ChannelConditionEndpointsReady has status True when a k8s Service Endpoints are backed - // by at least one endpoint. - ChannelConditionEndpointsReady apis.ConditionType = "EndpointsReady" - // ChannelConditionAddressable has status true when this Channel meets // the Addressable contract and has a non-empty hostname. ChannelConditionAddressable apis.ConditionType = "Addressable" - // ChannelConditionServiceReady has status True when a k8s Service representing the channel is ready. - // Because this uses ExternalName, there are no endpoints to check. - ChannelConditionChannelServiceReady apis.ConditionType = "ChannelServiceReady" + // ChannelConditionBackingChannelReady has status True when the backing Channel CRD is ready. + ChannelConditionBackingChannelReady apis.ConditionType = "BackingChannelReady" ) // GetCondition returns the condition currently associated with the given type, or nil. @@ -82,43 +66,16 @@ func (cs *ChannelStatus) SetAddress(url *apis.URL) { } } -func (cs *ChannelStatus) MarkDispatcherFailed(reason, messageFormat string, messageA ...interface{}) { - chCondSet.Manage(cs).MarkFalse(ChannelConditionDispatcherReady, reason, messageFormat, messageA...) +func (cs *ChannelStatus) MarkBackingChannelFailed(reason, messageFormat string, messageA ...interface{}) { + chCondSet.Manage(cs).MarkFalse(ChannelConditionBackingChannelReady, reason, messageFormat, messageA...) } -// TODO: Unify this with the ones from Eventing. Say: Broker, Trigger. -func (cs *ChannelStatus) PropagateDispatcherStatus(ds *appsv1.DeploymentStatus) { - for _, cond := range ds.Conditions { - if cond.Type == appsv1.DeploymentAvailable { - if cond.Status != corev1.ConditionTrue { - cs.MarkDispatcherFailed("DispatcherNotReady", "Dispatcher Deployment is not ready: %s : %s", cond.Reason, cond.Message) - } else { - chCondSet.Manage(cs).MarkTrue(ChannelConditionDispatcherReady) - } - } +func (cs *ChannelStatus) PropagateChannelReadiness(chs *eventingduck.ChannelableStatus) { + // TODO: Once you can get a Ready status from Channelable in a generic way, use it here... + address := cs.AddressStatus.Address + if address != nil { + chCondSet.Manage(cs).MarkTrue(ChannelConditionBackingChannelReady) + } else { + cs.MarkBackingChannelFailed("ChannelNotReady", "Channel is not ready: not addressable") } } - -func (cs *ChannelStatus) MarkServiceFailed(reason, messageFormat string, messageA ...interface{}) { - chCondSet.Manage(cs).MarkFalse(ChannelConditionServiceReady, reason, messageFormat, messageA...) -} - -func (cs *ChannelStatus) MarkServiceTrue() { - chCondSet.Manage(cs).MarkTrue(ChannelConditionServiceReady) -} - -func (cs *ChannelStatus) MarkChannelServiceFailed(reason, messageFormat string, messageA ...interface{}) { - chCondSet.Manage(cs).MarkFalse(ChannelConditionChannelServiceReady, reason, messageFormat, messageA...) -} - -func (cs *ChannelStatus) MarkChannelServiceTrue() { - chCondSet.Manage(cs).MarkTrue(ChannelConditionChannelServiceReady) -} - -func (cs *ChannelStatus) MarkEndpointsFailed(reason, messageFormat string, messageA ...interface{}) { - chCondSet.Manage(cs).MarkFalse(ChannelConditionEndpointsReady, reason, messageFormat, messageA...) -} - -func (cs *ChannelStatus) MarkEndpointsTrue() { - chCondSet.Manage(cs).MarkTrue(ChannelConditionEndpointsReady) -} diff --git a/pkg/apis/messaging/v1alpha1/channel_types.go b/pkg/apis/messaging/v1alpha1/channel_types.go index d52380e6c23..12447637217 100644 --- a/pkg/apis/messaging/v1alpha1/channel_types.go +++ b/pkg/apis/messaging/v1alpha1/channel_types.go @@ -18,6 +18,7 @@ package v1alpha1 import ( eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -79,7 +80,8 @@ type ChannelStatus struct { // Subscribers is populated with the statuses of each of the Channelable's subscribers. eventingduck.SubscribableTypeStatus `json:",inline"` - // TODO add backing channel object ref. + // Channel is an ObjectReference to the object for the Channel CRD. + Channel *corev1.ObjectReference `json:"channel,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index 9e18ec1c965..f39e466f679 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -19,12 +19,19 @@ package channel import ( "context" "fmt" + duckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "github.com/knative/eventing/pkg/duck" + "github.com/knative/eventing/pkg/reconciler/channel/resources" + "github.com/knative/eventing/pkg/utils" + duckapis "knative.dev/pkg/apis/duck" "reflect" "time" corev1 "k8s.io/api/core/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/cache" + duckroot "knative.dev/pkg/apis" "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" listers "github.com/knative/eventing/pkg/client/listers/messaging/v1alpha1" @@ -44,7 +51,8 @@ type Reconciler struct { *reconciler.Base // listers index properties about resources - channelLister listers.ChannelLister + channelLister listers.ChannelLister + resourceTracker duck.ResourceTracker } // Check that our Reconciler implements controller.Reconciler @@ -96,11 +104,42 @@ func (r *Reconciler) Reconcile(ctx context.Context, key string) error { return reconcileErr } -func (r *Reconciler) reconcile(ctx context.Context, ch *v1alpha1.Channel) error { +func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { + c.Status.InitializeConditions() + + // 1. Reconcile the backing Channel CRD and propagate its status. + + if c.DeletionTimestamp != nil { + // Everything is cleaned up by the garbage collector. + return nil + } + + logging.FromContext(ctx).Info("Reconciling the backing channel CRD") + backingChannel, err := r.reconcileBackingChannel(ctx, c) + if err != nil { + logging.FromContext(ctx).Error("Problem reconciling the backing channel", zap.Error(err)) + c.Status.MarkBackingChannelFailed("ChannelFailure", "%v", err) + return err + } + + // Tell tracker to reconcile this Channel whenever the backing Channel CRD changes. + track := r.resourceTracker.TrackInNamespace(c) + + // Start tracking the Channel CRD... + if err = track(utils.ObjectRef(backingChannel, backingChannel.GroupVersionKind())); err != nil { + logging.FromContext(ctx).Error("Unable to track changes to Channel", zap.Error(err)) + return err + } + + c.Status.Channel = &corev1.ObjectReference{ + Kind: backingChannel.Kind, + APIVersion: backingChannel.APIVersion, + Name: backingChannel.Name, + Namespace: backingChannel.Namespace, + } + + c.Status.PropagateChannelReadiness(&backingChannel.Status) - // TODO reconcile the channel - logging.FromContext(ctx).Sugar().Infof("Reconciling Channel: %s", ch.Name) - return nil } func (r *Reconciler) updateStatus(ctx context.Context, desired *v1alpha1.Channel) (*v1alpha1.Channel, error) { @@ -133,3 +172,41 @@ func (r *Reconciler) updateStatus(ctx context.Context, desired *v1alpha1.Channel return c, err } + +// reconcileBackingChannel reconciles Channel's 'c' underlying CRD channel. +func (r *Reconciler) reconcileBackingChannel(ctx context.Context, c *v1alpha1.Channel) (*duckv1alpha1.Channelable, error) { + + channelResourceInterface := r.DynamicClientSet.Resource(duckroot.KindToResource(c.Spec.ChannelTemplate.GetObjectKind().GroupVersionKind())).Namespace(c.Namespace) + channel, err := channelResourceInterface.Get(c.Name, metav1.GetOptions{}) + channelable := &duckv1alpha1.Channelable{} + + // If the resource doesn't exist, we'll create it + if err != nil { + if apierrs.IsNotFound(err) { + newChannel, err := resources.NewChannel(c) + if err != nil { + logging.FromContext(ctx).Error("Failed to create Channel from ChannelTemplate", zap.Any("channelTemplate", c.Spec.ChannelTemplate), zap.Error(err)) + return nil, err + } + logging.FromContext(ctx).Debug(fmt.Sprintf("Creating Channel CRD Object: %+v", newChannel)) + channel, err = channelResourceInterface.Create(newChannel, metav1.CreateOptions{}) + if err != nil { + logging.FromContext(ctx).Error(fmt.Sprintf("Failed to create Channel: %s/%s", c.Namespace, c.Name), zap.Error(err)) + return nil, err + } + logging.FromContext(ctx).Info(fmt.Sprintf("Created Channel: %s/%s", c.Namespace, c.Name), zap.Any("channel", newChannel)) + } else { + logging.FromContext(ctx).Error(fmt.Sprintf("Failed to get Channel: %s/%s", c.Namespace, c.Name), zap.Error(err)) + return nil, err + } + } else { + logging.FromContext(ctx).Debug(fmt.Sprintf("Found Channel: %s/%s", c.Namespace, c.Name), zap.Any("channel", c)) + } + + err = duckapis.FromUnstructured(channel, channelable) + if err != nil { + logging.FromContext(ctx).Error(fmt.Sprintf("Failed to convert to Channelable Object: %s/%s", c.Namespace, c.Name), zap.Error(err)) + return nil, err + } + return channelable, nil +} diff --git a/pkg/reconciler/channel/controller.go b/pkg/reconciler/channel/controller.go index 448dd25f424..3c827c68cef 100644 --- a/pkg/reconciler/channel/controller.go +++ b/pkg/reconciler/channel/controller.go @@ -18,6 +18,7 @@ package channel import ( "context" + "github.com/knative/eventing/pkg/duck" "github.com/knative/eventing/pkg/reconciler" "knative.dev/pkg/configmap" @@ -42,6 +43,7 @@ func NewController( ) *controller.Impl { channelInformer := channelinformer.Get(ctx) + resourceInformer := duck.NewResourceInformer(ctx) r := &Reconciler{ Base: reconciler.NewBase(ctx, controllerAgentName, cmw), @@ -49,6 +51,8 @@ func NewController( } impl := controller.NewImpl(r, r.Logger, ReconcilerName) + r.resourceTracker = resourceInformer.NewTracker(impl.EnqueueKey, controller.GetTrackerLease(ctx)) + r.Logger.Info("Setting up event handlers") channelInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) diff --git a/pkg/reconciler/channel/resources/channel.go b/pkg/reconciler/channel/resources/channel.go new file mode 100644 index 00000000000..768c832459d --- /dev/null +++ b/pkg/reconciler/channel/resources/channel.go @@ -0,0 +1,57 @@ +/* +Copyright 2019 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 resources + +import ( + "encoding/json" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "knative.dev/pkg/kmeta" + + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NewChannel returns an unstructured.Unstructured based on the ChannelTemplateSpec for a given Channel. +func NewChannel(c *v1alpha1.Channel) (*unstructured.Unstructured, error) { + // Set the name of the resource we're creating as well as the namespace, etc. + template := eventingduck.ChannelTemplateSpecInternal{ + TypeMeta: metav1.TypeMeta{ + Kind: c.Spec.ChannelTemplate.Kind, + APIVersion: c.Spec.ChannelTemplate.APIVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{ + *kmeta.NewControllerRef(c), + }, + Name: c.Name, + Namespace: c.Namespace, + }, + Spec: c.Spec.ChannelTemplate.Spec, + } + raw, err := json.Marshal(template) + if err != nil { + return nil, err + } + u := &unstructured.Unstructured{} + err = json.Unmarshal(raw, u) + if err != nil { + return nil, err + } + return u, nil +} From 71187baba8ea78fef45a53e2030a61fc8e3ac70a Mon Sep 17 00:00:00 2001 From: nachocano Date: Mon, 22 Jul 2019 16:49:04 -0700 Subject: [PATCH 05/39] channel validation --- .../messaging/v1alpha1/channel_validation.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/apis/messaging/v1alpha1/channel_validation.go b/pkg/apis/messaging/v1alpha1/channel_validation.go index 289a5928efa..408b4936c57 100644 --- a/pkg/apis/messaging/v1alpha1/channel_validation.go +++ b/pkg/apis/messaging/v1alpha1/channel_validation.go @@ -20,6 +20,7 @@ import ( "context" "fmt" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "knative.dev/pkg/apis" ) @@ -30,6 +31,14 @@ func (c *Channel) Validate(ctx context.Context) *apis.FieldError { func (cs *ChannelSpec) Validate(ctx context.Context) *apis.FieldError { var errs *apis.FieldError + if cs.ChannelTemplate == nil { + errs = errs.Also(apis.ErrMissingField("channelTemplate")) + } else { + if cte := isValidChannelTemplate(cs.ChannelTemplate); cte != nil { + errs = errs.Also(cte.ViaField("channelTemplate")) + } + } + if cs.Subscribable != nil { for i, subscriber := range cs.Subscribable.Subscribers { if subscriber.ReplyURI == "" && subscriber.SubscriberURI == "" { @@ -42,3 +51,14 @@ func (cs *ChannelSpec) Validate(ctx context.Context) *apis.FieldError { return errs } + +func isValidChannelTemplate(ct *eventingduck.ChannelTemplateSpec) *apis.FieldError { + var errs *apis.FieldError + if ct.Kind == "" { + errs = errs.Also(apis.ErrMissingField("kind")) + } + if ct.APIVersion == "" { + errs = errs.Also(apis.ErrMissingField("apiVersion")) + } + return errs +} From 109e8c611a12efb3877e225eab2f7ca7c5fec67e Mon Sep 17 00:00:00 2001 From: nachocano Date: Mon, 22 Jul 2019 17:01:53 -0700 Subject: [PATCH 06/39] setting to channelTemplate --- config/300-channel.yaml | 14 ++++++++++++++ pkg/apis/messaging/v1alpha1/channel_types.go | 2 +- .../messaging/v1alpha1/zz_generated.deepcopy.go | 5 +++++ pkg/reconciler/channel/channel.go | 2 ++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/config/300-channel.yaml b/config/300-channel.yaml index 8450c63e828..e01862da38a 100644 --- a/config/300-channel.yaml +++ b/config/300-channel.yaml @@ -58,6 +58,20 @@ spec: properties: spec: properties: + channelTemplate: + type: object + properties: + apiVersion: + type: string + minLength: 1 + kind: + type: string + minLength: 1 + spec: + type: object + required: + - apiVersion + - kind subscribable: type: object properties: diff --git a/pkg/apis/messaging/v1alpha1/channel_types.go b/pkg/apis/messaging/v1alpha1/channel_types.go index 12447637217..dfe3d53ed58 100644 --- a/pkg/apis/messaging/v1alpha1/channel_types.go +++ b/pkg/apis/messaging/v1alpha1/channel_types.go @@ -57,7 +57,7 @@ var _ webhook.GenericCRD = (*Channel)(nil) type ChannelSpec struct { // ChannelTemplate specifies which Channel CRD to use to create the CRD Channel backing this Channel. - ChannelTemplate *eventingduck.ChannelTemplateSpec `json:"channelTemplateSpec"` + ChannelTemplate *eventingduck.ChannelTemplateSpec `json:"channelTemplate"` // Channel conforms to Duck type Subscribable. Subscribable *eventingduck.Subscribable `json:"subscribable,omitempty"` diff --git a/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go index a6eb1c08fd2..876ea568f7c 100644 --- a/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go @@ -120,6 +120,11 @@ func (in *ChannelStatus) DeepCopyInto(out *ChannelStatus) { in.Status.DeepCopyInto(&out.Status) in.AddressStatus.DeepCopyInto(&out.AddressStatus) in.SubscribableTypeStatus.DeepCopyInto(&out.SubscribableTypeStatus) + if in.Channel != nil { + in, out := &in.Channel, &out.Channel + *out = new(v1.ObjectReference) + **out = **in + } return } diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index f39e466f679..ccbdceb35c9 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -140,6 +140,8 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { c.Status.PropagateChannelReadiness(&backingChannel.Status) + return nil + } func (r *Reconciler) updateStatus(ctx context.Context, desired *v1alpha1.Channel) (*v1alpha1.Channel, error) { From 2b54e41137d334b64fa493e057eae4fdb44eec88 Mon Sep 17 00:00:00 2001 From: nachocano Date: Mon, 22 Jul 2019 17:50:08 -0700 Subject: [PATCH 07/39] to json --- config/400-default-ch-config.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/config/400-default-ch-config.yaml b/config/400-default-ch-config.yaml index 3bed8e47d4c..a8c9374d03b 100644 --- a/config/400-default-ch-config.yaml +++ b/config/400-default-ch-config.yaml @@ -18,13 +18,12 @@ metadata: name: default-ch-webhook namespace: knative-eventing data: - # Configuration for defaulting channels that do not specify CRD implementations. All field names are - # lowercase. - default-channel-config: | + # Configuration for defaulting channels that do not specify CRD implementations. + default-ch-config: | clusterdefault: - apiversion: messaging.knative.dev/v1alpha1 + apiVersion: messaging.knative.dev/v1alpha1 kind: InMemoryChannel namespacedefaults: some-namespace: - apiversion: messaging.knative.dev/v1alpha1 + apiVersion: messaging.knative.dev/v1alpha1 kind: InMemoryChannel From eac06fea6729ffedbbafaddb1f3fef49f137cdb3 Mon Sep 17 00:00:00 2001 From: nachocano Date: Mon, 22 Jul 2019 17:50:26 -0700 Subject: [PATCH 08/39] channel defaulter --- pkg/apis/messaging/v1alpha1/channel_defaults.go | 5 +++++ pkg/defaultchannel/channel_defaulter.go | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/apis/messaging/v1alpha1/channel_defaults.go b/pkg/apis/messaging/v1alpha1/channel_defaults.go index dbb39ee2657..0a5af956a6d 100644 --- a/pkg/apis/messaging/v1alpha1/channel_defaults.go +++ b/pkg/apis/messaging/v1alpha1/channel_defaults.go @@ -19,6 +19,8 @@ package v1alpha1 import ( "context" eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "github.com/knative/eventing/pkg/logging" + "go.uber.org/zap" ) // ChannelDefaulter sets the default Channel CRD and Arguments on Channels that do not @@ -39,8 +41,11 @@ func (c *Channel) SetDefaults(ctx context.Context) { if c != nil && c.Spec.ChannelTemplate == nil { // The singleton may not have been set, if so ignore it and validation will reject the // Channel. + logging.FromContext(ctx).Info("Hereeee template == nil") if cd := ChannelDefaulterSingleton; cd != nil { + logging.FromContext(ctx).Info("Hereeee got channelDefaulter") channelTemplate := cd.GetDefault(c.DeepCopy()) + logging.FromContext(ctx).Sugar().Info("ChannelTemplate retrieved", zap.Any("channelTemplate", channelTemplate)) c.Spec.ChannelTemplate = channelTemplate } } diff --git a/pkg/defaultchannel/channel_defaulter.go b/pkg/defaultchannel/channel_defaulter.go index 79d7b54dc5f..983f49b20a8 100644 --- a/pkg/defaultchannel/channel_defaulter.go +++ b/pkg/defaultchannel/channel_defaulter.go @@ -17,12 +17,12 @@ limitations under the License. package defaultchannel import ( + "encoding/json" "sync/atomic" eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" "go.uber.org/zap" - "gopkg.in/yaml.v2" corev1 "k8s.io/api/core/v1" ) @@ -87,8 +87,10 @@ func (cd *ChannelDefaulter) UpdateConfigMap(cm *corev1.ConfigMap) { return } + cd.logger.Info("ConfigMap's value", zap.String("value", defaultChannelConfig)) + config := Config{} - if err := yaml.UnmarshalStrict([]byte(defaultChannelConfig), &config); err != nil { + if err := json.Unmarshal([]byte(defaultChannelConfig), &config); err != nil { cd.logger.Error("ConfigMap's value could not be unmarshaled.", zap.Error(err), zap.Any("configMap", cm)) return } @@ -112,6 +114,7 @@ func (cd *ChannelDefaulter) getConfig() *Config { // GetDefault determines the default Channel CRD and arguments for the provided Channel. func (cd *ChannelDefaulter) GetDefault(c *messagingv1alpha1.Channel) *eventingduckv1alpha1.ChannelTemplateSpec { + cd.logger.Info("Calling this guyyyyy", zap.Any("channel", c)) if c == nil { return nil } From a7b0c638cb1cf0b49933c6678419b2818818301d Mon Sep 17 00:00:00 2001 From: Nacho Cano Date: Mon, 22 Jul 2019 19:46:45 -0700 Subject: [PATCH 09/39] working --- pkg/defaultchannel/channel_defaulter.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/defaultchannel/channel_defaulter.go b/pkg/defaultchannel/channel_defaulter.go index 983f49b20a8..e44c6381deb 100644 --- a/pkg/defaultchannel/channel_defaulter.go +++ b/pkg/defaultchannel/channel_defaulter.go @@ -18,6 +18,7 @@ package defaultchannel import ( "encoding/json" + "github.com/ghodss/yaml" "sync/atomic" eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" @@ -87,16 +88,20 @@ func (cd *ChannelDefaulter) UpdateConfigMap(cm *corev1.ConfigMap) { return } - cd.logger.Info("ConfigMap's value", zap.String("value", defaultChannelConfig)) + defaultChannelConfigJson, err := yaml.YAMLToJSON([]byte(defaultChannelConfig)) + if err != nil { + cd.logger.Error("ConfigMap's value could not be converted to JSON.", zap.Error(err), zap.String("defaultChannelConfig", defaultChannelConfig)) + return + } - config := Config{} - if err := json.Unmarshal([]byte(defaultChannelConfig), &config); err != nil { + config := &Config{} + if err := json.Unmarshal([]byte(defaultChannelConfigJson), config); err != nil { cd.logger.Error("ConfigMap's value could not be unmarshaled.", zap.Error(err), zap.Any("configMap", cm)) return } cd.logger.Info("Updated channelDefaulter config", zap.Any("config", config)) - cd.setConfig(&config) + cd.setConfig(config) } // setConfig is a typed wrapper around config. @@ -114,7 +119,6 @@ func (cd *ChannelDefaulter) getConfig() *Config { // GetDefault determines the default Channel CRD and arguments for the provided Channel. func (cd *ChannelDefaulter) GetDefault(c *messagingv1alpha1.Channel) *eventingduckv1alpha1.ChannelTemplateSpec { - cd.logger.Info("Calling this guyyyyy", zap.Any("channel", c)) if c == nil { return nil } From b419077541336eefd7251d92a24e0e72d6cdfff5 Mon Sep 17 00:00:00 2001 From: Nacho Cano Date: Mon, 22 Jul 2019 19:49:27 -0700 Subject: [PATCH 10/39] removing logs --- pkg/apis/messaging/v1alpha1/channel_defaults.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/apis/messaging/v1alpha1/channel_defaults.go b/pkg/apis/messaging/v1alpha1/channel_defaults.go index 0a5af956a6d..dbb39ee2657 100644 --- a/pkg/apis/messaging/v1alpha1/channel_defaults.go +++ b/pkg/apis/messaging/v1alpha1/channel_defaults.go @@ -19,8 +19,6 @@ package v1alpha1 import ( "context" eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" - "github.com/knative/eventing/pkg/logging" - "go.uber.org/zap" ) // ChannelDefaulter sets the default Channel CRD and Arguments on Channels that do not @@ -41,11 +39,8 @@ func (c *Channel) SetDefaults(ctx context.Context) { if c != nil && c.Spec.ChannelTemplate == nil { // The singleton may not have been set, if so ignore it and validation will reject the // Channel. - logging.FromContext(ctx).Info("Hereeee template == nil") if cd := ChannelDefaulterSingleton; cd != nil { - logging.FromContext(ctx).Info("Hereeee got channelDefaulter") channelTemplate := cd.GetDefault(c.DeepCopy()) - logging.FromContext(ctx).Sugar().Info("ChannelTemplate retrieved", zap.Any("channelTemplate", channelTemplate)) c.Spec.ChannelTemplate = channelTemplate } } From 4727ae3e8f222d7be23dd96e1a803ddc48c5c901 Mon Sep 17 00:00:00 2001 From: Nacho Cano Date: Mon, 22 Jul 2019 23:08:06 -0700 Subject: [PATCH 11/39] patching subscriptions... infinite loop --- .../messaging/v1alpha1/channel_lifecycle.go | 6 +- pkg/reconciler/channel/channel.go | 62 +++++++++++++++---- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go index a43f321d6f9..06fc1f93ffd 100644 --- a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go +++ b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go @@ -72,10 +72,12 @@ func (cs *ChannelStatus) MarkBackingChannelFailed(reason, messageFormat string, func (cs *ChannelStatus) PropagateChannelReadiness(chs *eventingduck.ChannelableStatus) { // TODO: Once you can get a Ready status from Channelable in a generic way, use it here... - address := cs.AddressStatus.Address + address := chs.AddressStatus.Address if address != nil { + cs.SetAddress(address.URL) chCondSet.Manage(cs).MarkTrue(ChannelConditionBackingChannelReady) } else { - cs.MarkBackingChannelFailed("ChannelNotReady", "Channel is not ready: not addressable") + cs.SetAddress(nil) + cs.MarkBackingChannelFailed("ChannelNotReady", "Backing channel is not ready: not addressable") } } diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index ccbdceb35c9..6a673818db8 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -20,9 +20,13 @@ import ( "context" "fmt" duckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" - "github.com/knative/eventing/pkg/duck" + eventingduck "github.com/knative/eventing/pkg/duck" "github.com/knative/eventing/pkg/reconciler/channel/resources" "github.com/knative/eventing/pkg/utils" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" + "knative.dev/pkg/apis/duck" duckapis "knative.dev/pkg/apis/duck" "reflect" "time" @@ -52,7 +56,7 @@ type Reconciler struct { // listers index properties about resources channelLister listers.ChannelLister - resourceTracker duck.ResourceTracker + resourceTracker eventingduck.ResourceTracker } // Check that our Reconciler implements controller.Reconciler @@ -107,21 +111,31 @@ func (r *Reconciler) Reconcile(ctx context.Context, key string) error { func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { c.Status.InitializeConditions() - // 1. Reconcile the backing Channel CRD and propagate its status. + // 1. Create the backing Channel CRD, if it doesn't exist. + // 2. Patch the subscriptions from this Channel into the backing Channel CRD. + // 3. Propagate the backing Channel CRD status into this Channel. if c.DeletionTimestamp != nil { // Everything is cleaned up by the garbage collector. return nil } - logging.FromContext(ctx).Info("Reconciling the backing channel CRD") - backingChannel, err := r.reconcileBackingChannel(ctx, c) + channelResourceInterface := r.DynamicClientSet.Resource(duckroot.KindToResource(c.Spec.ChannelTemplate.GetObjectKind().GroupVersionKind())).Namespace(c.Namespace) + + backingChannel, err := r.reconcileBackingChannel(ctx, channelResourceInterface, c) if err != nil { logging.FromContext(ctx).Error("Problem reconciling the backing channel", zap.Error(err)) c.Status.MarkBackingChannelFailed("ChannelFailure", "%v", err) return err } + err = r.patchBackingChannelSubscriptions(ctx, channelResourceInterface, c, backingChannel) + if err != nil { + logging.FromContext(ctx).Error("Problem patching subscriptions in the backing channel", zap.Error(err)) + c.Status.MarkBackingChannelFailed("ChannelFailure", "%v", err) + return err + } + // Tell tracker to reconcile this Channel whenever the backing Channel CRD changes. track := r.resourceTracker.TrackInNamespace(c) @@ -137,11 +151,10 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { Name: backingChannel.Name, Namespace: backingChannel.Namespace, } + c.Status.SubscribableStatus = backingChannel.Status.SubscribableStatus c.Status.PropagateChannelReadiness(&backingChannel.Status) - return nil - } func (r *Reconciler) updateStatus(ctx context.Context, desired *v1alpha1.Channel) (*v1alpha1.Channel, error) { @@ -176,10 +189,9 @@ func (r *Reconciler) updateStatus(ctx context.Context, desired *v1alpha1.Channel } // reconcileBackingChannel reconciles Channel's 'c' underlying CRD channel. -func (r *Reconciler) reconcileBackingChannel(ctx context.Context, c *v1alpha1.Channel) (*duckv1alpha1.Channelable, error) { +func (r *Reconciler) reconcileBackingChannel(ctx context.Context, resourceClient dynamic.ResourceInterface, c *v1alpha1.Channel) (*duckv1alpha1.Channelable, error) { - channelResourceInterface := r.DynamicClientSet.Resource(duckroot.KindToResource(c.Spec.ChannelTemplate.GetObjectKind().GroupVersionKind())).Namespace(c.Namespace) - channel, err := channelResourceInterface.Get(c.Name, metav1.GetOptions{}) + channel, err := resourceClient.Get(c.Name, metav1.GetOptions{}) channelable := &duckv1alpha1.Channelable{} // If the resource doesn't exist, we'll create it @@ -191,7 +203,7 @@ func (r *Reconciler) reconcileBackingChannel(ctx context.Context, c *v1alpha1.Ch return nil, err } logging.FromContext(ctx).Debug(fmt.Sprintf("Creating Channel CRD Object: %+v", newChannel)) - channel, err = channelResourceInterface.Create(newChannel, metav1.CreateOptions{}) + channel, err = resourceClient.Create(newChannel, metav1.CreateOptions{}) if err != nil { logging.FromContext(ctx).Error(fmt.Sprintf("Failed to create Channel: %s/%s", c.Namespace, c.Name), zap.Error(err)) return nil, err @@ -212,3 +224,31 @@ func (r *Reconciler) reconcileBackingChannel(ctx context.Context, c *v1alpha1.Ch } return channelable, nil } + +func (r *Reconciler) patchBackingChannelSubscriptions(ctx context.Context, resourceClient dynamic.ResourceInterface, channel *v1alpha1.Channel, backingChannel *duckv1alpha1.Channelable) error { + if equality.Semantic.DeepEqual(channel.Spec.Subscribable, backingChannel.Spec.Subscribable) { + logging.FromContext(ctx).Info("Subscribables in sync") + return nil + } + + after := backingChannel.DeepCopy() + after.Spec.Subscribable = channel.Spec.Subscribable + + patch, err := duck.CreateMergePatch(backingChannel, after) + + if err != nil { + return err + } + + if err != nil { + logging.FromContext(ctx).Warn("Failed to create dynamic resource client", zap.Error(err)) + return err + } + patched, err := resourceClient.Patch(backingChannel.GetName(), types.MergePatchType, patch, metav1.UpdateOptions{}) + if err != nil { + logging.FromContext(ctx).Warn("Failed to patch the Channel", zap.Error(err), zap.Any("patch", patch)) + return err + } + logging.FromContext(ctx).Debug("Patched resource", zap.Any("patched", patched)) + return nil +} From d1723c1be027b953d61b697c0a04b7019ab14ad3 Mon Sep 17 00:00:00 2001 From: Nacho Cano Date: Mon, 22 Jul 2019 23:44:14 -0700 Subject: [PATCH 12/39] updates --- pkg/reconciler/channel/channel.go | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index 6a673818db8..556c8e976e7 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -129,13 +129,6 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { return err } - err = r.patchBackingChannelSubscriptions(ctx, channelResourceInterface, c, backingChannel) - if err != nil { - logging.FromContext(ctx).Error("Problem patching subscriptions in the backing channel", zap.Error(err)) - c.Status.MarkBackingChannelFailed("ChannelFailure", "%v", err) - return err - } - // Tell tracker to reconcile this Channel whenever the backing Channel CRD changes. track := r.resourceTracker.TrackInNamespace(c) @@ -151,9 +144,23 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { Name: backingChannel.Name, Namespace: backingChannel.Namespace, } - c.Status.SubscribableStatus = backingChannel.Status.SubscribableStatus c.Status.PropagateChannelReadiness(&backingChannel.Status) + + if !c.Status.IsReady() { + logging.FromContext(ctx).Error("Channel is not ready. Cannot update subscriber status") + return nil + } + + err = r.patchBackingChannelSubscriptions(ctx, channelResourceInterface, c, backingChannel) + if err != nil { + logging.FromContext(ctx).Error("Problem patching subscriptions in the backing channel", zap.Error(err)) + c.Status.MarkBackingChannelFailed("ChannelFailure", "%v", err) + return err + } + + c.Status.SubscribableStatus = backingChannel.Status.SubscribableStatus + return nil } @@ -227,7 +234,7 @@ func (r *Reconciler) reconcileBackingChannel(ctx context.Context, resourceClient func (r *Reconciler) patchBackingChannelSubscriptions(ctx context.Context, resourceClient dynamic.ResourceInterface, channel *v1alpha1.Channel, backingChannel *duckv1alpha1.Channelable) error { if equality.Semantic.DeepEqual(channel.Spec.Subscribable, backingChannel.Spec.Subscribable) { - logging.FromContext(ctx).Info("Subscribables in sync") + logging.FromContext(ctx).Info("Subscribable in sync, no need to patch") return nil } @@ -237,13 +244,10 @@ func (r *Reconciler) patchBackingChannelSubscriptions(ctx context.Context, resou patch, err := duck.CreateMergePatch(backingChannel, after) if err != nil { + logging.FromContext(ctx).Warn("Failed to create mergePatch", zap.Error(err)) return err } - if err != nil { - logging.FromContext(ctx).Warn("Failed to create dynamic resource client", zap.Error(err)) - return err - } patched, err := resourceClient.Patch(backingChannel.GetName(), types.MergePatchType, patch, metav1.UpdateOptions{}) if err != nil { logging.FromContext(ctx).Warn("Failed to patch the Channel", zap.Error(err), zap.Any("patch", patch)) From 970ada4cfd91a9d412ed1004bde269dc56ecdb8e Mon Sep 17 00:00:00 2001 From: Nacho Cano Date: Tue, 23 Jul 2019 00:20:53 -0700 Subject: [PATCH 13/39] immutable --- Gopkg.lock | 1 + .../messaging/v1alpha1/channel_validation.go | 29 +++++++++++++++++++ pkg/reconciler/channel/channel.go | 13 +++++---- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 089b0c30ceb..7d18bdc6aa4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1492,6 +1492,7 @@ "github.com/cloudevents/sdk-go/pkg/cloudevents", "github.com/cloudevents/sdk-go/pkg/cloudevents/transport/http", "github.com/cloudevents/sdk-go/pkg/cloudevents/types", + "github.com/ghodss/yaml", "github.com/google/go-cmp/cmp", "github.com/google/go-cmp/cmp/cmpopts", "github.com/google/uuid", diff --git a/pkg/apis/messaging/v1alpha1/channel_validation.go b/pkg/apis/messaging/v1alpha1/channel_validation.go index 408b4936c57..d13b7ffa3f0 100644 --- a/pkg/apis/messaging/v1alpha1/channel_validation.go +++ b/pkg/apis/messaging/v1alpha1/channel_validation.go @@ -19,6 +19,7 @@ package v1alpha1 import ( "context" "fmt" + "knative.dev/pkg/kmp" eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "knative.dev/pkg/apis" @@ -62,3 +63,31 @@ func isValidChannelTemplate(ct *eventingduck.ChannelTemplateSpec) *apis.FieldErr } return errs } + +func (c *Channel) CheckImmutableFields(ctx context.Context, og apis.Immutable) *apis.FieldError { + if og == nil { + return nil + } + + original, ok := og.(*Channel) + if !ok { + return &apis.FieldError{Message: "The provided original was not a Channel"} + } + + // All spec fields are immutable. We do this especially for the channelTemplate, as changing it once is set + // will require to delete the backing channel and recreating it. + if diff, err := kmp.ShortDiff(original.Spec, c.Spec); err != nil { + return &apis.FieldError{ + Message: "Failed to diff Channel", + Paths: []string{"spec"}, + Details: err.Error(), + } + } else if diff != "" { + return &apis.FieldError{ + Message: "Immutable fields changed (-old +new)", + Paths: []string{"spec"}, + Details: diff, + } + } + return nil +} diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index 556c8e976e7..75a92cb822d 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -112,8 +112,9 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { c.Status.InitializeConditions() // 1. Create the backing Channel CRD, if it doesn't exist. - // 2. Patch the subscriptions from this Channel into the backing Channel CRD. - // 3. Propagate the backing Channel CRD status into this Channel. + // 2. Propagate the backing Channel CRD status into this Channel. + // 3. Patch the subscriptions from this Channel into the backing Channel CRD. + // 4. Propagate the backing Channel CRD Subscribable status into this Channel. if c.DeletionTimestamp != nil { // Everything is cleaned up by the garbage collector. @@ -122,6 +123,9 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { channelResourceInterface := r.DynamicClientSet.Resource(duckroot.KindToResource(c.Spec.ChannelTemplate.GetObjectKind().GroupVersionKind())).Namespace(c.Namespace) + // Tell tracker to reconcile this Channel whenever the backing Channel CRD changes. + track := r.resourceTracker.TrackInNamespace(c) + backingChannel, err := r.reconcileBackingChannel(ctx, channelResourceInterface, c) if err != nil { logging.FromContext(ctx).Error("Problem reconciling the backing channel", zap.Error(err)) @@ -129,10 +133,7 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { return err } - // Tell tracker to reconcile this Channel whenever the backing Channel CRD changes. - track := r.resourceTracker.TrackInNamespace(c) - - // Start tracking the Channel CRD... + // Start tracking the backing Channel CRD... if err = track(utils.ObjectRef(backingChannel, backingChannel.GroupVersionKind())); err != nil { logging.FromContext(ctx).Error("Unable to track changes to Channel", zap.Error(err)) return err From 870a146f05a5abfc5a16514048ab45f8aedb6be8 Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 11:19:58 -0700 Subject: [PATCH 14/39] problems with cyclic dependencies --- .../{channel_types.go => channel_template_types.go} | 0 pkg/apis/eventing/v1alpha1/broker_defaults.go | 8 ++++++++ pkg/apis/eventing/v1alpha1/broker_types.go | 2 +- pkg/apis/eventing/v1alpha1/broker_validation.go | 7 +++++-- pkg/apis/eventing/v1alpha1/broker_validation_test.go | 8 ++++---- pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go | 6 +++++- pkg/apis/messaging/v1alpha1/channel_defaults.go | 7 +++---- pkg/defaultchannel/channel_defaulter.go | 10 ++++------ 8 files changed, 30 insertions(+), 18 deletions(-) rename pkg/apis/duck/v1alpha1/{channel_types.go => channel_template_types.go} (100%) diff --git a/pkg/apis/duck/v1alpha1/channel_types.go b/pkg/apis/duck/v1alpha1/channel_template_types.go similarity index 100% rename from pkg/apis/duck/v1alpha1/channel_types.go rename to pkg/apis/duck/v1alpha1/channel_template_types.go diff --git a/pkg/apis/eventing/v1alpha1/broker_defaults.go b/pkg/apis/eventing/v1alpha1/broker_defaults.go index 3c6c0726e03..5104654bd36 100644 --- a/pkg/apis/eventing/v1alpha1/broker_defaults.go +++ b/pkg/apis/eventing/v1alpha1/broker_defaults.go @@ -18,9 +18,17 @@ package v1alpha1 import ( "context" + messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" ) func (b *Broker) SetDefaults(ctx context.Context) { + if b != nil && b.Spec.ChannelTemplate == nil { + // The singleton may not have been set, if so ignore it and validation will reject the Broker. + if cd := messagingv1alpha1.ChannelDefaulterSingleton; cd != nil { + channelTemplate := cd.GetDefault(b.Namespace) + b.Spec.ChannelTemplate = channelTemplate + } + } b.Spec.SetDefaults(ctx) setUserInfoAnnotations(b, ctx) } diff --git a/pkg/apis/eventing/v1alpha1/broker_types.go b/pkg/apis/eventing/v1alpha1/broker_types.go index 5ac3828ce4d..bb907630cbd 100644 --- a/pkg/apis/eventing/v1alpha1/broker_types.go +++ b/pkg/apis/eventing/v1alpha1/broker_types.go @@ -73,7 +73,7 @@ type BrokerSpec struct { // ChannelTemplate specifies which Channel CRD to use to create all the Channels used internally by the // Broker. - ChannelTemplate eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplateSpec"` + ChannelTemplate *eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplateSpec,omitempty"` } // BrokerStatus represents the current state of a Broker. diff --git a/pkg/apis/eventing/v1alpha1/broker_validation.go b/pkg/apis/eventing/v1alpha1/broker_validation.go index b68b66b6bb5..ecd866de704 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation.go @@ -41,11 +41,14 @@ func (bs *BrokerSpec) Validate(ctx context.Context) *apis.FieldError { } if !equality.Semantic.DeepEqual(bs.ChannelTemplate, eventingduckv1alpha1.ChannelTemplateSpec{}) { + if bs.ChannelTemplate == nil { + errs = errs.Also(apis.ErrMissingField("channelTemplate")) + return errs + } if cte := isValidChannelTemplate(bs.ChannelTemplate); cte != nil { errs = errs.Also(cte.ViaField("channelTemplateSpec")) } } - // TODO validate that the channelTemplate only specifies the provisioner and arguments. return errs } @@ -65,7 +68,7 @@ func isValidDeprecatedChannelTemplate(dct *ChannelSpec) *apis.FieldError { return errs } -func isValidChannelTemplate(dct eventingduckv1alpha1.ChannelTemplateSpec) *apis.FieldError { +func isValidChannelTemplate(dct *eventingduckv1alpha1.ChannelTemplateSpec) *apis.FieldError { var errs *apis.FieldError if dct.Kind == "" { errs = errs.Also(apis.ErrMissingField("kind")) diff --git a/pkg/apis/eventing/v1alpha1/broker_validation_test.go b/pkg/apis/eventing/v1alpha1/broker_validation_test.go index be2eb4fa3a9..c7b5401f6c4 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation_test.go @@ -102,7 +102,7 @@ func TestValidSpec(t *testing.T) { DeprecatedChannelTemplate: &ChannelSpec{ Provisioner: &corev1.ObjectReference{}, }, - ChannelTemplate: eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}}, + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}}, }, want: func() *apis.FieldError { var errs *apis.FieldError @@ -113,7 +113,7 @@ func TestValidSpec(t *testing.T) { }, { name: "invalid templatespec, missing kind", spec: BrokerSpec{ - ChannelTemplate: eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{APIVersion: "myapiversion"}}, + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{APIVersion: "myapiversion"}}, }, want: func() *apis.FieldError { var errs *apis.FieldError @@ -124,7 +124,7 @@ func TestValidSpec(t *testing.T) { }, { name: "invalid templatespec, missing apiVersion", spec: BrokerSpec{ - ChannelTemplate: eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}}, + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}}, }, want: func() *apis.FieldError { var errs *apis.FieldError @@ -135,7 +135,7 @@ func TestValidSpec(t *testing.T) { }, { name: "valid templatespec", spec: BrokerSpec{ - ChannelTemplate: eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind", APIVersion: "myapiversion"}}, + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind", APIVersion: "myapiversion"}}, }, want: nil, }} diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index 2a24b86d7b4..205bda4d156 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -95,7 +95,11 @@ func (in *BrokerSpec) DeepCopyInto(out *BrokerSpec) { *out = new(ChannelSpec) (*in).DeepCopyInto(*out) } - in.ChannelTemplate.DeepCopyInto(&out.ChannelTemplate) + if in.ChannelTemplate != nil { + in, out := &in.ChannelTemplate, &out.ChannelTemplate + *out = new(duckv1alpha1.ChannelTemplateSpec) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/messaging/v1alpha1/channel_defaults.go b/pkg/apis/messaging/v1alpha1/channel_defaults.go index dbb39ee2657..6a196db5f27 100644 --- a/pkg/apis/messaging/v1alpha1/channel_defaults.go +++ b/pkg/apis/messaging/v1alpha1/channel_defaults.go @@ -24,9 +24,8 @@ import ( // ChannelDefaulter sets the default Channel CRD and Arguments on Channels that do not // specify any implementation. type ChannelDefaulter interface { - // GetDefault determines the default Channel CRD for the given Channel. It does - // not modify the given Channel. - GetDefault(c *Channel) *eventingduckv1alpha1.ChannelTemplateSpec + // GetDefault determines the default Channel CRD for the given namespace. + GetDefault(namespace string) *eventingduckv1alpha1.ChannelTemplateSpec } var ( @@ -40,7 +39,7 @@ func (c *Channel) SetDefaults(ctx context.Context) { // The singleton may not have been set, if so ignore it and validation will reject the // Channel. if cd := ChannelDefaulterSingleton; cd != nil { - channelTemplate := cd.GetDefault(c.DeepCopy()) + channelTemplate := cd.GetDefault(c.Namespace) c.Spec.ChannelTemplate = channelTemplate } } diff --git a/pkg/defaultchannel/channel_defaulter.go b/pkg/defaultchannel/channel_defaulter.go index e44c6381deb..1a01f9cc3a7 100644 --- a/pkg/defaultchannel/channel_defaulter.go +++ b/pkg/defaultchannel/channel_defaulter.go @@ -117,11 +117,9 @@ func (cd *ChannelDefaulter) getConfig() *Config { return nil } -// GetDefault determines the default Channel CRD and arguments for the provided Channel. -func (cd *ChannelDefaulter) GetDefault(c *messagingv1alpha1.Channel) *eventingduckv1alpha1.ChannelTemplateSpec { - if c == nil { - return nil - } +// GetDefault determines the default Channel CRD and arguments for the provided namespace. If there is no default +// for the provided namespace, then use the cluster default. +func (cd *ChannelDefaulter) GetDefault(namespace string) *eventingduckv1alpha1.ChannelTemplateSpec { // Because we are treating this as a singleton, be tolerant to it having not been setup at all. if cd == nil { return nil @@ -130,7 +128,7 @@ func (cd *ChannelDefaulter) GetDefault(c *messagingv1alpha1.Channel) *eventingdu if config == nil { return nil } - channelTemplate := getDefaultChannelTemplate(config, c.Namespace) + channelTemplate := getDefaultChannelTemplate(config, namespace) cd.logger.Info("Defaulting the Channel", zap.Any("defaultChannelTemplate", channelTemplate)) return channelTemplate } From 5e5b8bd31bfd671cf1a4ca22ee87a155d3d8438a Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 12:23:13 -0700 Subject: [PATCH 15/39] moving channel defaulter to duck to avoid cyclic dependency --- cmd/webhook/main.go | 3 +- pkg/apis/duck/v1alpha1/channel_defaulter.go | 36 +++++++++++++++++++ pkg/apis/eventing/v1alpha1/broker_defaults.go | 4 +-- .../messaging/v1alpha1/channel_defaults.go | 15 +------- pkg/defaultchannel/channel_defaulter.go | 3 +- 5 files changed, 42 insertions(+), 19 deletions(-) create mode 100644 pkg/apis/duck/v1alpha1/channel_defaulter.go diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 4bacdd5de46..9d58c069c00 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -24,6 +24,7 @@ import ( "go.uber.org/zap" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" "github.com/knative/eventing/pkg/logconfig" @@ -82,7 +83,7 @@ func main() { // Watch the default-ch-webhook ConfigMap and dynamically update the default // Channel CRD. chDefaulter := defaultchannel.New(logger.Desugar()) - messagingv1alpha1.ChannelDefaulterSingleton = chDefaulter + eventingduckv1alpha1.ChannelDefaulterSingleton = chDefaulter configMapWatcher.Watch(defaultchannel.ConfigMapName, chDefaulter.UpdateConfigMap) if err = configMapWatcher.Start(stopCh); err != nil { diff --git a/pkg/apis/duck/v1alpha1/channel_defaulter.go b/pkg/apis/duck/v1alpha1/channel_defaulter.go new file mode 100644 index 00000000000..3901b496e54 --- /dev/null +++ b/pkg/apis/duck/v1alpha1/channel_defaulter.go @@ -0,0 +1,36 @@ +/* +Copyright 2019 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 v1alpha1 + +// TODO This should be placed in channel_defaults.go within messaging once Subscription is moved to messaging. +// Context: there is a cyclic dependency between eventing and messaging if we place this in messaging. Broker needs to +// depend on this, which is fine. But the problem arises due to messaging depending on eventing, mainly on +// Subscription-related objects for the Sequence type. We should first move Subscription down to messaging and then we +// can move this down. + +// ChannelDefaulter sets the default Channel CRD and Arguments on Channels that do not +// specify any implementation. +type ChannelDefaulter interface { + // GetDefault determines the default Channel CRD for the given namespace. + GetDefault(namespace string) *ChannelTemplateSpec +} + +var ( + // ChannelDefaulterSingleton is the global singleton used to default Channels that do not + // specify a Channel CRD. + ChannelDefaulterSingleton ChannelDefaulter +) diff --git a/pkg/apis/eventing/v1alpha1/broker_defaults.go b/pkg/apis/eventing/v1alpha1/broker_defaults.go index 5104654bd36..ec51242db6b 100644 --- a/pkg/apis/eventing/v1alpha1/broker_defaults.go +++ b/pkg/apis/eventing/v1alpha1/broker_defaults.go @@ -18,13 +18,13 @@ package v1alpha1 import ( "context" - messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" ) func (b *Broker) SetDefaults(ctx context.Context) { if b != nil && b.Spec.ChannelTemplate == nil { // The singleton may not have been set, if so ignore it and validation will reject the Broker. - if cd := messagingv1alpha1.ChannelDefaulterSingleton; cd != nil { + if cd := eventingduckv1alpha1.ChannelDefaulterSingleton; cd != nil { channelTemplate := cd.GetDefault(b.Namespace) b.Spec.ChannelTemplate = channelTemplate } diff --git a/pkg/apis/messaging/v1alpha1/channel_defaults.go b/pkg/apis/messaging/v1alpha1/channel_defaults.go index 6a196db5f27..883d2919c53 100644 --- a/pkg/apis/messaging/v1alpha1/channel_defaults.go +++ b/pkg/apis/messaging/v1alpha1/channel_defaults.go @@ -21,24 +21,11 @@ import ( eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" ) -// ChannelDefaulter sets the default Channel CRD and Arguments on Channels that do not -// specify any implementation. -type ChannelDefaulter interface { - // GetDefault determines the default Channel CRD for the given namespace. - GetDefault(namespace string) *eventingduckv1alpha1.ChannelTemplateSpec -} - -var ( - // ChannelDefaulterSingleton is the global singleton used to default Channels that do not - // specify a Channel CRD. - ChannelDefaulterSingleton ChannelDefaulter -) - func (c *Channel) SetDefaults(ctx context.Context) { if c != nil && c.Spec.ChannelTemplate == nil { // The singleton may not have been set, if so ignore it and validation will reject the // Channel. - if cd := ChannelDefaulterSingleton; cd != nil { + if cd := eventingduckv1alpha1.ChannelDefaulterSingleton; cd != nil { channelTemplate := cd.GetDefault(c.Namespace) c.Spec.ChannelTemplate = channelTemplate } diff --git a/pkg/defaultchannel/channel_defaulter.go b/pkg/defaultchannel/channel_defaulter.go index 1a01f9cc3a7..5f97d4c8cfd 100644 --- a/pkg/defaultchannel/channel_defaulter.go +++ b/pkg/defaultchannel/channel_defaulter.go @@ -22,7 +22,6 @@ import ( "sync/atomic" eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" - messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" ) @@ -58,7 +57,7 @@ type ChannelDefaulter struct { logger *zap.Logger } -var _ messagingv1alpha1.ChannelDefaulter = &ChannelDefaulter{} +var _ eventingduckv1alpha1.ChannelDefaulter = &ChannelDefaulter{} // New creates a new ChannelDefaulter. The caller is expected to set this as the global singleton. // From d0ebf804e68ed6c193f9d83ff7c1261f480cc207 Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 12:49:06 -0700 Subject: [PATCH 16/39] channel defaulter test --- pkg/defaultchannel/channel_defaulter_test.go | 201 +++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 pkg/defaultchannel/channel_defaulter_test.go diff --git a/pkg/defaultchannel/channel_defaulter_test.go b/pkg/defaultchannel/channel_defaulter_test.go new file mode 100644 index 00000000000..e72d1af6259 --- /dev/null +++ b/pkg/defaultchannel/channel_defaulter_test.go @@ -0,0 +1,201 @@ +/* +Copyright 2019 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 defaultchannel + +import ( + "testing" + + "encoding/json" + "github.com/ghodss/yaml" + "github.com/google/go-cmp/cmp" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var ( + config = &Config{ + ClusterDefault: &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: messagingv1alpha1.SchemeGroupVersion.String(), + Kind: "InMemoryChannel", + }, + }, + } + // configYaml is the YAML form of config. It is generated in init(). + configYaml string + + configWithNamespace = &Config{ + ClusterDefault: &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: messagingv1alpha1.SchemeGroupVersion.String(), + Kind: "InMemoryChannel", + }, + }, + NamespaceDefaults: map[string]*eventingduckv1alpha1.ChannelTemplateSpec{ + "testNamespace": { + TypeMeta: v1.TypeMeta{ + APIVersion: messagingv1alpha1.SchemeGroupVersion.String(), + Kind: "OtherChannel", + }, + }, + }, + } +) + +func init() { + configJsonBytes, _ := json.Marshal(config) + configYamlBytes, _ := yaml.JSONToYAML(configJsonBytes) + configYaml = string(configYamlBytes) +} + +func TestChannelDefaulter_GetDefault(t *testing.T) { + testCases := map[string]struct { + config *Config + channel *messagingv1alpha1.Channel + expectedChannelTemplate *eventingduckv1alpha1.ChannelTemplateSpec + }{ + "no default set": { + channel: &messagingv1alpha1.Channel{}, + expectedChannelTemplate: nil, + }, + "cluster defaulted": { + config: config, + channel: &messagingv1alpha1.Channel{}, + expectedChannelTemplate: config.ClusterDefault, + }, + "namespace defaulted": { + config: configWithNamespace, + channel: &messagingv1alpha1.Channel{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "testNamespace", + }, + }, + expectedChannelTemplate: configWithNamespace.NamespaceDefaults["testNamespace"], + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + cd := New(zap.NewNop()) + if tc.config != nil { + cd.setConfig(tc.config) + } + channelTemplate := cd.GetDefault(tc.channel.Namespace) + if diff := cmp.Diff(tc.expectedChannelTemplate, channelTemplate); diff != "" { + t.Fatalf("Unexpected provisioner (-want, +got): %s", diff) + } + }) + } +} + +func TestChannelDefaulter_UpdateConfigMap(t *testing.T) { + testCases := map[string]struct { + initialConfig *corev1.ConfigMap + expectedAfterInitial *eventingduckv1alpha1.ChannelTemplateSpec + updatedConfig *corev1.ConfigMap + expectedAfterUpdate *eventingduckv1alpha1.ChannelTemplateSpec + }{ + "nil config map": { + expectedAfterInitial: nil, + expectedAfterUpdate: nil, + }, + "key missing in update": { + initialConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: configYaml, + }, + }, + expectedAfterInitial: config.ClusterDefault, + updatedConfig: &corev1.ConfigMap{}, + expectedAfterUpdate: config.ClusterDefault, + }, + "bad yaml is ignored": { + initialConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: configYaml, + }, + }, + expectedAfterInitial: config.ClusterDefault, + updatedConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: "foo -> bar", + }, + }, + expectedAfterUpdate: config.ClusterDefault, + }, + "empty config is accepted": { + initialConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: configYaml, + }, + }, + expectedAfterInitial: config.ClusterDefault, + updatedConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: "{}", + }, + }, + expectedAfterUpdate: nil, + }, + "empty string is ignored": { + initialConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: configYaml, + }, + }, + expectedAfterInitial: config.ClusterDefault, + updatedConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: "", + }, + }, + expectedAfterUpdate: config.ClusterDefault, + }, + "update to same channel": { + initialConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: configYaml, + }, + }, + expectedAfterInitial: config.ClusterDefault, + updatedConfig: &corev1.ConfigMap{ + Data: map[string]string{ + channelDefaulterKey: configYaml, + }, + }, + expectedAfterUpdate: config.ClusterDefault, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + cd := New(zap.NewNop()) + cd.UpdateConfigMap(tc.initialConfig) + + channelTemplate := cd.GetDefault("testNamespace") + if diff := cmp.Diff(tc.expectedAfterInitial, channelTemplate); diff != "" { + t.Fatalf("Unexpected difference after initial configMap update (-want, +got): %s", diff) + } + cd.UpdateConfigMap(tc.updatedConfig) + channelTemplate = cd.GetDefault("testNamespace") + if diff := cmp.Diff(tc.expectedAfterUpdate, channelTemplate); diff != "" { + t.Fatalf("Unexpected difference after update configMap update (-want, +got): %s", diff) + } + }) + } +} From 5cdb7e1c2e20ab6b6309dceaf147285776cee2bb Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 14:07:34 -0700 Subject: [PATCH 17/39] updating UTs --- .../v1alpha1/channel_defaults_test.go | 105 +++++++ .../messaging/v1alpha1/channel_lifecycle.go | 13 +- .../v1alpha1/channel_lifecycle_test.go | 294 ++++++++++++++++++ .../in_memory_channel_lifecycle_test.go | 6 +- pkg/apis/messaging/v1alpha1/register.go | 2 + 5 files changed, 406 insertions(+), 14 deletions(-) create mode 100644 pkg/apis/messaging/v1alpha1/channel_defaults_test.go create mode 100644 pkg/apis/messaging/v1alpha1/channel_lifecycle_test.go diff --git a/pkg/apis/messaging/v1alpha1/channel_defaults_test.go b/pkg/apis/messaging/v1alpha1/channel_defaults_test.go new file mode 100644 index 00000000000..581a07bd27a --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/channel_defaults_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2018 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 v1alpha1 + +import ( + "context" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" + + "github.com/google/go-cmp/cmp" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" +) + +var ( + defaultChannelTemplate = &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "InMemoryChannel", + }, + } +) + +func TestChannelSetDefaults(t *testing.T) { + testCases := map[string]struct { + nilChannelDefaulter bool + channelTemplate *eventingduckv1alpha1.ChannelTemplateSpec + initial Channel + expected Channel + }{ + "nil ChannelDefaulter": { + nilChannelDefaulter: true, + expected: Channel{}, + }, + "unset ChannelDefaulter": { + expected: Channel{}, + }, + "set ChannelDefaulter": { + channelTemplate: defaultChannelTemplate, + expected: Channel{ + Spec: ChannelSpec{ + ChannelTemplate: defaultChannelTemplate, + }, + }, + }, + "template already specified": { + channelTemplate: defaultChannelTemplate, + initial: Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "OtherChannel", + }, + }, + }, + }, + expected: Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "OtherChannel", + }, + }, + }, + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + if !tc.nilChannelDefaulter { + eventingduckv1alpha1.ChannelDefaulterSingleton = &channelDefaulter{ + channelTemplate: tc.channelTemplate, + } + defer func() { eventingduckv1alpha1.ChannelDefaulterSingleton = nil }() + } + tc.initial.SetDefaults(context.TODO()) + if diff := cmp.Diff(tc.expected, tc.initial); diff != "" { + t.Fatalf("Unexpected defaults (-want, +got): %s", diff) + } + }) + } +} + +type channelDefaulter struct { + channelTemplate *eventingduckv1alpha1.ChannelTemplateSpec +} + +func (cd *channelDefaulter) GetDefault(_ string) *eventingduckv1alpha1.ChannelTemplateSpec { + return cd.channelTemplate +} diff --git a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go index 06fc1f93ffd..e902994edec 100644 --- a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go +++ b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go @@ -22,7 +22,7 @@ import ( "knative.dev/pkg/apis/duck/v1alpha1" ) -var chCondSet = apis.NewLivingConditionSet(ChannelConditionBackingChannelReady, ChannelConditionAddressable) +var chCondSet = apis.NewLivingConditionSet(ChannelConditionAddressable) const ( // ChannelConditionReady has status True when all subconditions below have been set to True. @@ -31,9 +31,6 @@ const ( // ChannelConditionAddressable has status true when this Channel meets // the Addressable contract and has a non-empty hostname. ChannelConditionAddressable apis.ConditionType = "Addressable" - - // ChannelConditionBackingChannelReady has status True when the backing Channel CRD is ready. - ChannelConditionBackingChannelReady apis.ConditionType = "BackingChannelReady" ) // GetCondition returns the condition currently associated with the given type, or nil. @@ -66,18 +63,12 @@ func (cs *ChannelStatus) SetAddress(url *apis.URL) { } } -func (cs *ChannelStatus) MarkBackingChannelFailed(reason, messageFormat string, messageA ...interface{}) { - chCondSet.Manage(cs).MarkFalse(ChannelConditionBackingChannelReady, reason, messageFormat, messageA...) -} - func (cs *ChannelStatus) PropagateChannelReadiness(chs *eventingduck.ChannelableStatus) { - // TODO: Once you can get a Ready status from Channelable in a generic way, use it here... + // TODO: Once you can get a Ready status from Channelable in a generic way, use it here. address := chs.AddressStatus.Address if address != nil { cs.SetAddress(address.URL) - chCondSet.Manage(cs).MarkTrue(ChannelConditionBackingChannelReady) } else { cs.SetAddress(nil) - cs.MarkBackingChannelFailed("ChannelNotReady", "Backing channel is not ready: not addressable") } } diff --git a/pkg/apis/messaging/v1alpha1/channel_lifecycle_test.go b/pkg/apis/messaging/v1alpha1/channel_lifecycle_test.go new file mode 100644 index 00000000000..3f9b0f35b7c --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/channel_lifecycle_test.go @@ -0,0 +1,294 @@ +/* +Copyright 2019 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 v1alpha1 + +import ( + "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + corev1 "k8s.io/api/core/v1" + "knative.dev/pkg/apis" + duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1" + duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1" +) + +func TestChannelGetCondition(t *testing.T) { + tests := []struct { + name string + cs *ChannelStatus + condQuery apis.ConditionType + want *apis.Condition + }{{ + name: "single condition", + cs: &ChannelStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{ + condReady, + }, + }, + }, + condQuery: apis.ConditionReady, + want: &condReady, + }, { + name: "unknown condition", + cs: &ChannelStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{ + condReady, + }, + }, + }, + condQuery: apis.ConditionType("foo"), + want: nil, + }} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := test.cs.GetCondition(test.condQuery) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("unexpected condition (-want, +got) = %v", diff) + } + }) + } +} + +func TestChannelInitializeConditions(t *testing.T) { + tests := []struct { + name string + cs *ChannelStatus + want *ChannelStatus + }{{ + name: "empty", + cs: &ChannelStatus{}, + want: &ChannelStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{ + Type: ChannelConditionAddressable, + Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionReady, + Status: corev1.ConditionUnknown, + }}, + }, + }, + }, { + name: "one false", + cs: &ChannelStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{ + Type: ChannelConditionAddressable, + Status: corev1.ConditionFalse, + }}, + }, + }, + want: &ChannelStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{ + Type: ChannelConditionAddressable, + Status: corev1.ConditionFalse, + }, { + Type: ChannelConditionReady, + Status: corev1.ConditionUnknown, + }}, + }, + }, + }, { + name: "one true", + cs: &ChannelStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{ + Type: ChannelConditionAddressable, + Status: corev1.ConditionTrue, + }}, + }, + }, + want: &ChannelStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{ + Type: ChannelConditionAddressable, + Status: corev1.ConditionTrue, + }, { + Type: ChannelConditionReady, + Status: corev1.ConditionUnknown, + }}, + }, + }, + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.cs.InitializeConditions() + ignore := cmpopts.IgnoreFields( + apis.Condition{}, + "LastTransitionTime", "Message", "Reason", "Severity") + if diff := cmp.Diff(test.want, test.cs, ignore); diff != "" { + t.Errorf("unexpected conditions (-want, +got) = %v", diff) + } + }) + } +} + +func TestChannelIsReady(t *testing.T) { + tests := []struct { + name string + setAddress bool + wantReady bool + }{{ + name: "all happy", + setAddress: true, + wantReady: true, + }, { + name: "address not set", + setAddress: false, + wantReady: false, + }} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cs := &ChannelStatus{} + cs.InitializeConditions() + if test.setAddress { + cs.SetAddress(&apis.URL{Scheme: "http", Host: "foo.bar"}) + } + got := cs.IsReady() + if test.wantReady != got { + t.Errorf("unexpected readiness: want %v, got %v", test.wantReady, got) + } + }) + } +} + +func TestChannelSetAddressable(t *testing.T) { + testCases := map[string]struct { + url *apis.URL + want *ChannelStatus + }{ + "empty string": { + want: &ChannelStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{ + { + Type: ChannelConditionAddressable, + Status: corev1.ConditionFalse, + }, + // Note that Ready is here because when the condition is marked False, duck + // automatically sets Ready to false. + { + Type: ChannelConditionReady, + Status: corev1.ConditionFalse, + }, + }, + }, + AddressStatus: duckv1alpha1.AddressStatus{Address: &duckv1alpha1.Addressable{}}, + }, + }, + "has domain": { + url: &apis.URL{Scheme: "http", Host: "test-domain"}, + want: &ChannelStatus{ + AddressStatus: duckv1alpha1.AddressStatus{ + Address: &duckv1alpha1.Addressable{ + Addressable: duckv1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: "test-domain", + }, + }, + Hostname: "test-domain", + }, + }, + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{ + { + Type: ChannelConditionAddressable, + Status: corev1.ConditionTrue, + }, + { + // Note that Ready is here because when the condition is marked True, then + // we automatically set Ready to true. + Type: ChannelConditionReady, + Status: corev1.ConditionTrue, + }}, + }, + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + cs := &ChannelStatus{} + cs.SetAddress(tc.url) + ignore := cmpopts.IgnoreFields( + apis.Condition{}, + "LastTransitionTime", "Message", "Reason", "Severity") + if diff := cmp.Diff(tc.want, cs, ignore); diff != "" { + t.Errorf("unexpected conditions (-want, +got) = %v", diff) + } + }) + } +} + +func TestChannelPropagateChannelReadiness(t *testing.T) { + testCases := map[string]struct { + channelableStatus *v1alpha1.ChannelableStatus + wantReady bool + }{ + "address set": { + channelableStatus: &v1alpha1.ChannelableStatus{ + AddressStatus: duckv1alpha1.AddressStatus{ + Address: &duckv1alpha1.Addressable{ + Addressable: duckv1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: "test-domain", + }, + }, + Hostname: "test-domain", + }, + }, + }, + wantReady: true, + }, + "address not set": { + channelableStatus: &v1alpha1.ChannelableStatus{ + AddressStatus: duckv1alpha1.AddressStatus{ + Address: &duckv1alpha1.Addressable{}, + }, + }, + wantReady: false, + }, + "url not set": { + channelableStatus: &v1alpha1.ChannelableStatus{ + AddressStatus: duckv1alpha1.AddressStatus{ + Address: &duckv1alpha1.Addressable{ + Addressable: duckv1beta1.Addressable{}, + Hostname: "test-domain", + }, + }, + }, + wantReady: false, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + cs := &ChannelStatus{} + cs.PropagateChannelReadiness(tc.channelableStatus) + got := cs.IsReady() + if tc.wantReady != got { + t.Errorf("unexpected readiness: want %v, got %v", tc.wantReady, got) + } + }) + } +} diff --git a/pkg/apis/messaging/v1alpha1/in_memory_channel_lifecycle_test.go b/pkg/apis/messaging/v1alpha1/in_memory_channel_lifecycle_test.go index 0dea9475bce..f79b322e237 100644 --- a/pkg/apis/messaging/v1alpha1/in_memory_channel_lifecycle_test.go +++ b/pkg/apis/messaging/v1alpha1/in_memory_channel_lifecycle_test.go @@ -77,7 +77,7 @@ var ignoreAllButTypeAndStatus = cmpopts.IgnoreFields( var ignoreLastTransitionTime = cmpopts.IgnoreFields(apis.Condition{}, "LastTransitionTime") -func TestChannelGetCondition(t *testing.T) { +func TestInMemoryChannelGetCondition(t *testing.T) { tests := []struct { name string cs *InMemoryChannelStatus @@ -117,7 +117,7 @@ func TestChannelGetCondition(t *testing.T) { } } -func TestChannelInitializeConditions(t *testing.T) { +func TestInMemoryChannelInitializeConditions(t *testing.T) { tests := []struct { name string cs *InMemoryChannelStatus @@ -226,7 +226,7 @@ func TestChannelInitializeConditions(t *testing.T) { } } -func TestChannelIsReady(t *testing.T) { +func TestInMemoryChannelIsReady(t *testing.T) { tests := []struct { name string markServiceReady bool diff --git a/pkg/apis/messaging/v1alpha1/register.go b/pkg/apis/messaging/v1alpha1/register.go index ec9e1c7ce61..4f44a546034 100644 --- a/pkg/apis/messaging/v1alpha1/register.go +++ b/pkg/apis/messaging/v1alpha1/register.go @@ -49,6 +49,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &InMemoryChannelList{}, &Sequence{}, &SequenceList{}, + &Channel{}, + &ChannelList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil From 9043e372bdb7641d4739a2149bb311a49d1a2b96 Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 14:19:41 -0700 Subject: [PATCH 18/39] channel validation test --- .../v1alpha1/channel_validation_test.go | 153 ++++++++++++++++++ .../in_memory_channel_validation_test.go | 2 +- 2 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 pkg/apis/messaging/v1alpha1/channel_validation_test.go diff --git a/pkg/apis/messaging/v1alpha1/channel_validation_test.go b/pkg/apis/messaging/v1alpha1/channel_validation_test.go new file mode 100644 index 00000000000..2b6bdd98141 --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/channel_validation_test.go @@ -0,0 +1,153 @@ +/* +Copyright 2019 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 v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" + + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "knative.dev/pkg/apis" +) + +func TestChannelValidation(t *testing.T) { + tests := []CRDTest{{ + name: "empty", + cr: &Channel{ + Spec: ChannelSpec{}, + }, + want: func() *apis.FieldError { + fe := apis.ErrMissingField("spec.channelTemplate") + return fe + }(), + }, { + name: "channel template with no kind", + cr: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + }, + }}, + }, + want: func() *apis.FieldError { + fe := apis.ErrMissingField("spec.channelTemplate.kind") + return fe + }(), + }, { + name: "channel template with no apiVersion", + cr: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + }, + }}, + }, + want: func() *apis.FieldError { + fe := apis.ErrMissingField("spec.channelTemplate.apiVersion") + return fe + }(), + }, { + name: "valid subscribers array", + cr: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + }, + Subscribable: &eventingduck.Subscribable{ + Subscribers: []eventingduck.SubscriberSpec{{ + SubscriberURI: "subscriberendpoint", + ReplyURI: "resultendpoint", + }}, + }}, + }, + want: nil, + }, { + name: "empty subscriber at index 1", + cr: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + }, + Subscribable: &eventingduck.Subscribable{ + Subscribers: []eventingduck.SubscriberSpec{{ + SubscriberURI: "subscriberendpoint", + ReplyURI: "replyendpoint", + }, {}}, + }}, + }, + want: func() *apis.FieldError { + fe := apis.ErrMissingField("spec.subscribable.subscriber[1].replyURI", "spec.subscribable.subscriber[1].subscriberURI") + fe.Details = "expected at least one of, got none" + return fe + }(), + }, { + name: "nil channelTemplate and empty subscriber at index 1", + cr: &Channel{ + Spec: ChannelSpec{ + Subscribable: &eventingduck.Subscribable{ + Subscribers: []eventingduck.SubscriberSpec{{ + SubscriberURI: "subscriberendpoint", + ReplyURI: "replyendpoint", + }, {}}, + }}, + }, + want: func() *apis.FieldError { + var errs *apis.FieldError + fe := apis.ErrMissingField("spec.channelTemplate") + errs = errs.Also(fe) + fe = apis.ErrMissingField("spec.subscribable.subscriber[1].replyURI", "spec.subscribable.subscriber[1].subscriberURI") + fe.Details = "expected at least one of, got none" + errs = errs.Also(fe) + return errs + }(), + }, { + name: "2 empty subscribers", + cr: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + }, + Subscribable: &eventingduck.Subscribable{ + Subscribers: []eventingduck.SubscriberSpec{{}, {}}, + }, + }, + }, + want: func() *apis.FieldError { + var errs *apis.FieldError + fe := apis.ErrMissingField("spec.subscribable.subscriber[0].replyURI", "spec.subscribable.subscriber[0].subscriberURI") + fe.Details = "expected at least one of, got none" + errs = errs.Also(fe) + fe = apis.ErrMissingField("spec.subscribable.subscriber[1].replyURI", "spec.subscribable.subscriber[1].subscriberURI") + fe.Details = "expected at least one of, got none" + errs = errs.Also(fe) + return errs + }(), + }} + + doValidateTest(t, tests) +} diff --git a/pkg/apis/messaging/v1alpha1/in_memory_channel_validation_test.go b/pkg/apis/messaging/v1alpha1/in_memory_channel_validation_test.go index d109565d13c..ee4ea958e2e 100644 --- a/pkg/apis/messaging/v1alpha1/in_memory_channel_validation_test.go +++ b/pkg/apis/messaging/v1alpha1/in_memory_channel_validation_test.go @@ -23,7 +23,7 @@ import ( "knative.dev/pkg/apis" ) -func TestImMemoryChannelValidation(t *testing.T) { +func TestInMemoryChannelValidation(t *testing.T) { tests := []CRDTest{{ name: "empty", cr: &InMemoryChannel{ From db7127b111e5007b57a451a2cfccd8db13e0a9bd Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 14:35:30 -0700 Subject: [PATCH 19/39] updating validation --- .../messaging/v1alpha1/channel_validation.go | 6 +- .../v1alpha1/channel_validation_test.go | 132 ++++++++++++++++++ 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/pkg/apis/messaging/v1alpha1/channel_validation.go b/pkg/apis/messaging/v1alpha1/channel_validation.go index d13b7ffa3f0..adaa2a2f097 100644 --- a/pkg/apis/messaging/v1alpha1/channel_validation.go +++ b/pkg/apis/messaging/v1alpha1/channel_validation.go @@ -19,6 +19,7 @@ package v1alpha1 import ( "context" "fmt" + "github.com/google/go-cmp/cmp/cmpopts" "knative.dev/pkg/kmp" eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" @@ -74,9 +75,8 @@ func (c *Channel) CheckImmutableFields(ctx context.Context, og apis.Immutable) * return &apis.FieldError{Message: "The provided original was not a Channel"} } - // All spec fields are immutable. We do this especially for the channelTemplate, as changing it once is set - // will require to delete the backing channel and recreating it. - if diff, err := kmp.ShortDiff(original.Spec, c.Spec); err != nil { + ignoreArguments := cmpopts.IgnoreFields(ChannelSpec{}, "Subscribable") + if diff, err := kmp.ShortDiff(original.Spec, c.Spec, ignoreArguments); err != nil { return &apis.FieldError{ Message: "Failed to diff Channel", Paths: []string{"spec"}, diff --git a/pkg/apis/messaging/v1alpha1/channel_validation_test.go b/pkg/apis/messaging/v1alpha1/channel_validation_test.go index 2b6bdd98141..814e47d2645 100644 --- a/pkg/apis/messaging/v1alpha1/channel_validation_test.go +++ b/pkg/apis/messaging/v1alpha1/channel_validation_test.go @@ -17,7 +17,10 @@ limitations under the License. package v1alpha1 import ( + "context" + "github.com/google/go-cmp/cmp" "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "testing" eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" @@ -151,3 +154,132 @@ func TestChannelValidation(t *testing.T) { doValidateTest(t, tests) } + +func TestChannelImmutableFields(t *testing.T) { + tests := []struct { + name string + current apis.Immutable + original apis.Immutable + want *apis.FieldError + }{{ + name: "good (no change)", + current: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + Spec: &runtime.RawExtension{ + Raw: []byte(`"foo":"baz"`), + }, + }, + }, + }, + original: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + Spec: &runtime.RawExtension{ + Raw: []byte(`"foo":"baz"`), + }, + }, + }, + }, + want: nil, + }, { + name: "new nil is ok", + current: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + Spec: &runtime.RawExtension{ + Raw: []byte(`"foo":"baz"`), + }, + }, + }, + }, + original: nil, + want: nil, + }, { + name: "bad (channelTemplate change)", + current: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "OtherChannel", + APIVersion: SchemeGroupVersion.String(), + }, + Spec: &runtime.RawExtension{ + Raw: []byte(`"foo":"baz"`), + }, + }, + }, + }, + original: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + Spec: &runtime.RawExtension{ + Raw: []byte(`"foo":"baz"`), + }, + }, + }, + }, + want: &apis.FieldError{ + Message: "Immutable fields changed (-old +new)", + Paths: []string{"spec"}, + Details: `{v1alpha1.ChannelSpec}.ChannelTemplate.TypeMeta.Kind: + -: "InMemoryChannel" + +: "OtherChannel" +`, + }, + }, { + name: "good (subscribable change)", + current: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + }, + Subscribable: &eventingduck.Subscribable{ + Subscribers: []eventingduck.SubscriberSpec{{ + SubscriberURI: "subscriberendpoint", + ReplyURI: "replyendpoint", + }}, + }, + }, + }, + original: &Channel{ + Spec: ChannelSpec{ + ChannelTemplate: &eventingduck.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + Kind: "InMemoryChannel", + APIVersion: SchemeGroupVersion.String(), + }, + }, + }, + }, + want: nil, + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := test.current.CheckImmutableFields(context.TODO(), test.original) + if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { + t.Errorf("CheckImmutableFields (-want, +got) = %v", diff) + } + }) + } +} From eee7e1d37bdd51e6135260feddbf71b679cd304a Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 15:47:37 -0700 Subject: [PATCH 20/39] updating lifecycle --- .../messaging/v1alpha1/channel_lifecycle.go | 40 +++-- .../v1alpha1/channel_lifecycle_test.go | 147 +++++++++++++++--- pkg/reconciler/channel/channel.go | 10 +- 3 files changed, 152 insertions(+), 45 deletions(-) diff --git a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go index e902994edec..5a026183ca1 100644 --- a/pkg/apis/messaging/v1alpha1/channel_lifecycle.go +++ b/pkg/apis/messaging/v1alpha1/channel_lifecycle.go @@ -18,16 +18,20 @@ package v1alpha1 import ( eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + corev1 "k8s.io/api/core/v1" "knative.dev/pkg/apis" "knative.dev/pkg/apis/duck/v1alpha1" ) -var chCondSet = apis.NewLivingConditionSet(ChannelConditionAddressable) +var chCondSet = apis.NewLivingConditionSet(ChannelConditionBackingChannelReady, ChannelConditionAddressable) const ( // ChannelConditionReady has status True when all subconditions below have been set to True. ChannelConditionReady = apis.ConditionReady + // ChannelConditionBackingChannelReady has status True when the backing Channel CRD is ready. + ChannelConditionBackingChannelReady apis.ConditionType = "BackingChannelReady" + // ChannelConditionAddressable has status true when this Channel meets // the Addressable contract and has a non-empty hostname. ChannelConditionAddressable apis.ConditionType = "Addressable" @@ -48,13 +52,13 @@ func (cs *ChannelStatus) InitializeConditions() { chCondSet.Manage(cs).InitializeConditions() } -func (cs *ChannelStatus) SetAddress(url *apis.URL) { +func (cs *ChannelStatus) SetAddress(address *v1alpha1.Addressable) { if cs.Address == nil { cs.Address = &v1alpha1.Addressable{} } - if url != nil { - cs.Address.Hostname = url.Host - cs.Address.URL = url + if address != nil && address.URL != nil { + cs.Address.Hostname = address.URL.Host + cs.Address.URL = address.URL chCondSet.Manage(cs).MarkTrue(ChannelConditionAddressable) } else { cs.Address.Hostname = "" @@ -63,12 +67,26 @@ func (cs *ChannelStatus) SetAddress(url *apis.URL) { } } -func (cs *ChannelStatus) PropagateChannelReadiness(chs *eventingduck.ChannelableStatus) { +func (cs *ChannelStatus) MarkBackingChannelFailed(reason, messageFormat string, messageA ...interface{}) { + chCondSet.Manage(cs).MarkFalse(ChannelConditionBackingChannelReady, reason, messageFormat, messageA...) +} + +func (cs *ChannelStatus) MarkBackingChannelReady() { + chCondSet.Manage(cs).MarkTrue(ChannelConditionBackingChannelReady) +} + +func (cs *ChannelStatus) PropagateStatuses(chs *eventingduck.ChannelableStatus) { // TODO: Once you can get a Ready status from Channelable in a generic way, use it here. - address := chs.AddressStatus.Address - if address != nil { - cs.SetAddress(address.URL) - } else { - cs.SetAddress(nil) + readyCondition := chs.Status.GetCondition(apis.ConditionReady) + if readyCondition != nil { + if readyCondition.Status != corev1.ConditionTrue { + cs.MarkBackingChannelFailed(readyCondition.Reason, readyCondition.Message) + } else { + cs.MarkBackingChannelReady() + } } + // Set the address and update the Addressable conditions. + cs.SetAddress(chs.AddressStatus.Address) + // Set the subscribable status. + cs.SubscribableStatus = chs.SubscribableStatus } diff --git a/pkg/apis/messaging/v1alpha1/channel_lifecycle_test.go b/pkg/apis/messaging/v1alpha1/channel_lifecycle_test.go index 3f9b0f35b7c..b4776aef0ac 100644 --- a/pkg/apis/messaging/v1alpha1/channel_lifecycle_test.go +++ b/pkg/apis/messaging/v1alpha1/channel_lifecycle_test.go @@ -80,6 +80,9 @@ func TestChannelInitializeConditions(t *testing.T) { Conditions: []apis.Condition{{ Type: ChannelConditionAddressable, Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionBackingChannelReady, + Status: corev1.ConditionUnknown, }, { Type: ChannelConditionReady, Status: corev1.ConditionUnknown, @@ -101,6 +104,9 @@ func TestChannelInitializeConditions(t *testing.T) { Conditions: []apis.Condition{{ Type: ChannelConditionAddressable, Status: corev1.ConditionFalse, + }, { + Type: ChannelConditionBackingChannelReady, + Status: corev1.ConditionUnknown, }, { Type: ChannelConditionReady, Status: corev1.ConditionUnknown, @@ -112,7 +118,7 @@ func TestChannelInitializeConditions(t *testing.T) { cs: &ChannelStatus{ Status: duckv1beta1.Status{ Conditions: []apis.Condition{{ - Type: ChannelConditionAddressable, + Type: ChannelConditionBackingChannelReady, Status: corev1.ConditionTrue, }}, }, @@ -121,6 +127,9 @@ func TestChannelInitializeConditions(t *testing.T) { Status: duckv1beta1.Status{ Conditions: []apis.Condition{{ Type: ChannelConditionAddressable, + Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionBackingChannelReady, Status: corev1.ConditionTrue, }, { Type: ChannelConditionReady, @@ -145,24 +154,44 @@ func TestChannelInitializeConditions(t *testing.T) { func TestChannelIsReady(t *testing.T) { tests := []struct { - name string - setAddress bool - wantReady bool + name string + setAddress bool + markBackingChannelReady bool + wantReady bool }{{ - name: "all happy", - setAddress: true, - wantReady: true, + name: "all happy", + setAddress: true, + markBackingChannelReady: true, + wantReady: true, }, { - name: "address not set", - setAddress: false, - wantReady: false, + name: "address not set", + setAddress: false, + markBackingChannelReady: true, + wantReady: false, + }, { + name: "backing channel not ready", + setAddress: true, + markBackingChannelReady: false, + wantReady: false, }} for _, test := range tests { t.Run(test.name, func(t *testing.T) { cs := &ChannelStatus{} cs.InitializeConditions() if test.setAddress { - cs.SetAddress(&apis.URL{Scheme: "http", Host: "foo.bar"}) + cs.SetAddress(&duckv1alpha1.Addressable{ + Addressable: duckv1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: "test-domain", + }, + }, + }) + } + if test.markBackingChannelReady { + cs.MarkBackingChannelReady() + } else { + cs.MarkBackingChannelFailed("ChannelFailure", "testing") } got := cs.IsReady() if test.wantReady != got { @@ -174,10 +203,10 @@ func TestChannelIsReady(t *testing.T) { func TestChannelSetAddressable(t *testing.T) { testCases := map[string]struct { - url *apis.URL - want *ChannelStatus + address *duckv1alpha1.Addressable + want *ChannelStatus }{ - "empty string": { + "nil url": { want: &ChannelStatus{ Status: duckv1beta1.Status{ Conditions: []apis.Condition{ @@ -197,7 +226,15 @@ func TestChannelSetAddressable(t *testing.T) { }, }, "has domain": { - url: &apis.URL{Scheme: "http", Host: "test-domain"}, + address: &duckv1alpha1.Addressable{ + Addressable: duckv1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: "test-domain", + }, + }, + Hostname: "test-domain", + }, want: &ChannelStatus{ AddressStatus: duckv1alpha1.AddressStatus{ Address: &duckv1alpha1.Addressable{ @@ -215,12 +252,6 @@ func TestChannelSetAddressable(t *testing.T) { { Type: ChannelConditionAddressable, Status: corev1.ConditionTrue, - }, - { - // Note that Ready is here because when the condition is marked True, then - // we automatically set Ready to true. - Type: ChannelConditionReady, - Status: corev1.ConditionTrue, }}, }, }, @@ -229,7 +260,7 @@ func TestChannelSetAddressable(t *testing.T) { for n, tc := range testCases { t.Run(n, func(t *testing.T) { cs := &ChannelStatus{} - cs.SetAddress(tc.url) + cs.SetAddress(tc.address) ignore := cmpopts.IgnoreFields( apis.Condition{}, "LastTransitionTime", "Message", "Reason", "Severity") @@ -240,7 +271,7 @@ func TestChannelSetAddressable(t *testing.T) { } } -func TestChannelPropagateChannelReadiness(t *testing.T) { +func TestChannelPropagateStatuses(t *testing.T) { testCases := map[string]struct { channelableStatus *v1alpha1.ChannelableStatus wantReady bool @@ -259,7 +290,7 @@ func TestChannelPropagateChannelReadiness(t *testing.T) { }, }, }, - wantReady: true, + wantReady: false, }, "address not set": { channelableStatus: &v1alpha1.ChannelableStatus{ @@ -280,11 +311,77 @@ func TestChannelPropagateChannelReadiness(t *testing.T) { }, wantReady: false, }, + "all set": { + channelableStatus: &v1alpha1.ChannelableStatus{ + AddressStatus: duckv1alpha1.AddressStatus{ + Address: &duckv1alpha1.Addressable{ + Addressable: duckv1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: "test-domain", + }, + }, + Hostname: "test-domain", + }, + }, + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionReady, + Status: corev1.ConditionTrue, + }}, + }, + }, + wantReady: true, + }, + "backing channel not ready": { + channelableStatus: &v1alpha1.ChannelableStatus{ + AddressStatus: duckv1alpha1.AddressStatus{ + Address: &duckv1alpha1.Addressable{ + Addressable: duckv1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: "test-domain", + }, + }, + Hostname: "test-domain", + }, + }, + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionReady, + Status: corev1.ConditionUnknown, + }}, + }, + }, + wantReady: false, + }, + "no condition ready in backing channel": { + channelableStatus: &v1alpha1.ChannelableStatus{ + AddressStatus: duckv1alpha1.AddressStatus{ + Address: &duckv1alpha1.Addressable{ + Addressable: duckv1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: "test-domain", + }, + }, + Hostname: "test-domain", + }, + }, + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionTrue, + }}, + }, + }, + wantReady: false, + }, } for n, tc := range testCases { t.Run(n, func(t *testing.T) { cs := &ChannelStatus{} - cs.PropagateChannelReadiness(tc.channelableStatus) + cs.PropagateStatuses(tc.channelableStatus) got := cs.IsReady() if tc.wantReady != got { t.Errorf("unexpected readiness: want %v, got %v", tc.wantReady, got) diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index 75a92cb822d..561f35af3b2 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -146,13 +146,6 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { Namespace: backingChannel.Namespace, } - c.Status.PropagateChannelReadiness(&backingChannel.Status) - - if !c.Status.IsReady() { - logging.FromContext(ctx).Error("Channel is not ready. Cannot update subscriber status") - return nil - } - err = r.patchBackingChannelSubscriptions(ctx, channelResourceInterface, c, backingChannel) if err != nil { logging.FromContext(ctx).Error("Problem patching subscriptions in the backing channel", zap.Error(err)) @@ -160,8 +153,7 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { return err } - c.Status.SubscribableStatus = backingChannel.Status.SubscribableStatus - + c.Status.PropagateStatuses(&backingChannel.Status) return nil } From e1925aca4b14f33547b350b542c0c70ee5f9be48 Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 16:46:08 -0700 Subject: [PATCH 21/39] updates to broker --- pkg/apis/eventing/v1alpha1/broker_defaults.go | 6 +- .../eventing/v1alpha1/broker_defaults_test.go | 102 +++++++++++++++++- .../eventing/v1alpha1/broker_validation.go | 22 ++-- .../v1alpha1/broker_validation_test.go | 9 +- pkg/reconciler/broker/broker.go | 2 +- pkg/reconciler/testing/broker.go | 3 +- pkg/reconciler/testing/sequence.go | 3 +- 7 files changed, 126 insertions(+), 21 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/broker_defaults.go b/pkg/apis/eventing/v1alpha1/broker_defaults.go index ec51242db6b..3f190baeaf2 100644 --- a/pkg/apis/eventing/v1alpha1/broker_defaults.go +++ b/pkg/apis/eventing/v1alpha1/broker_defaults.go @@ -22,7 +22,11 @@ import ( ) func (b *Broker) SetDefaults(ctx context.Context) { - if b != nil && b.Spec.ChannelTemplate == nil { + // If we are not using the deprecated channelTemplate and haven't configured the new one, + // then set the default channel to the new channelTemplate. + // This check is done to avoid having both the deprecated and the new channelTemplate set, which will cause + // validation problems. + if b != nil && b.Spec.DeprecatedChannelTemplate == nil && b.Spec.ChannelTemplate == nil { // The singleton may not have been set, if so ignore it and validation will reject the Broker. if cd := eventingduckv1alpha1.ChannelDefaulterSingleton; cd != nil { channelTemplate := cd.GetDefault(b.Namespace) diff --git a/pkg/apis/eventing/v1alpha1/broker_defaults_test.go b/pkg/apis/eventing/v1alpha1/broker_defaults_test.go index f8dc1302150..8a4a5cb61e8 100644 --- a/pkg/apis/eventing/v1alpha1/broker_defaults_test.go +++ b/pkg/apis/eventing/v1alpha1/broker_defaults_test.go @@ -18,11 +18,105 @@ package v1alpha1 import ( "context" + "github.com/google/go-cmp/cmp" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" "testing" ) -// No-op test because method does nothing. -func TestBrokerDefaults(t *testing.T) { - b := Broker{} - b.SetDefaults(context.TODO()) +var ( + defaultChannelTemplate = &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "InMemoryChannel", + }, + } +) + +func TestBrokerSetDefaults(t *testing.T) { + testCases := map[string]struct { + nilChannelDefaulter bool + channelTemplate *eventingduckv1alpha1.ChannelTemplateSpec + initial Broker + expected Broker + }{ + "nil ChannelDefaulter": { + nilChannelDefaulter: true, + expected: Broker{}, + }, + "unset ChannelDefaulter": { + expected: Broker{}, + }, + "set ChannelDefaulter": { + channelTemplate: defaultChannelTemplate, + expected: Broker{ + Spec: BrokerSpec{ + ChannelTemplate: defaultChannelTemplate, + }, + }, + }, + "deprecated template already set": { + channelTemplate: defaultChannelTemplate, + initial: Broker{ + Spec: BrokerSpec{ + DeprecatedChannelTemplate: &ChannelSpec{ + Provisioner: &corev1.ObjectReference{Kind: "mykind", APIVersion: "mapiversion"}, + }, + }, + }, + expected: Broker{ + Spec: BrokerSpec{ + DeprecatedChannelTemplate: &ChannelSpec{ + Provisioner: &corev1.ObjectReference{Kind: "mykind", APIVersion: "mapiversion"}, + }, + }, + }, + }, + "template already specified": { + channelTemplate: defaultChannelTemplate, + initial: Broker{ + Spec: BrokerSpec{ + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "OtherChannel", + }, + }, + }, + }, + expected: Broker{ + Spec: BrokerSpec{ + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "OtherChannel", + }, + }, + }, + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + if !tc.nilChannelDefaulter { + eventingduckv1alpha1.ChannelDefaulterSingleton = &brokerChannelDefaulter{ + channelTemplate: tc.channelTemplate, + } + defer func() { eventingduckv1alpha1.ChannelDefaulterSingleton = nil }() + } + tc.initial.SetDefaults(context.TODO()) + if diff := cmp.Diff(tc.expected, tc.initial); diff != "" { + t.Fatalf("Unexpected defaults (-want, +got): %s", diff) + } + }) + } +} + +type brokerChannelDefaulter struct { + channelTemplate *eventingduckv1alpha1.ChannelTemplateSpec +} + +func (cd *brokerChannelDefaulter) GetDefault(_ string) *eventingduckv1alpha1.ChannelTemplateSpec { + return cd.channelTemplate } diff --git a/pkg/apis/eventing/v1alpha1/broker_validation.go b/pkg/apis/eventing/v1alpha1/broker_validation.go index ecd866de704..3a8a1adc816 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation.go @@ -20,7 +20,6 @@ import ( "context" eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" - "k8s.io/apimachinery/pkg/api/equality" "knative.dev/pkg/apis" ) @@ -31,32 +30,33 @@ func (b *Broker) Validate(ctx context.Context) *apis.FieldError { func (bs *BrokerSpec) Validate(ctx context.Context) *apis.FieldError { var errs *apis.FieldError - if bs.DeprecatedChannelTemplate != nil && !equality.Semantic.DeepEqual(bs.ChannelTemplate, eventingduckv1alpha1.ChannelTemplateSpec{}) { + if bs.DeprecatedChannelTemplate != nil && bs.ChannelTemplate != nil { errs = errs.Also(apis.ErrMultipleOneOf("channelTemplate", "channelTemplateSpec")) return errs } - if dcte := isValidDeprecatedChannelTemplate(bs.DeprecatedChannelTemplate); dcte != nil { - errs = errs.Also(dcte.ViaField("channelTemplate")) + if bs.DeprecatedChannelTemplate == nil && bs.ChannelTemplate == nil { + errs = errs.Also(apis.ErrMissingOneOf("channelTemplate", "channelTemplateSpec")) + return errs } - if !equality.Semantic.DeepEqual(bs.ChannelTemplate, eventingduckv1alpha1.ChannelTemplateSpec{}) { - if bs.ChannelTemplate == nil { - errs = errs.Also(apis.ErrMissingField("channelTemplate")) - return errs + if bs.ChannelTemplate == nil { + // If the new channelTemplate is nil it means that the DeprecatedChannelTemplate is not, validate it then. + if dcte := isValidDeprecatedChannelTemplate(bs.DeprecatedChannelTemplate); dcte != nil { + errs = errs.Also(dcte.ViaField("channelTemplate")) } + } else { + // Validate the new channelTemplate. if cte := isValidChannelTemplate(bs.ChannelTemplate); cte != nil { errs = errs.Also(cte.ViaField("channelTemplateSpec")) } } + // TODO validate that the channelTemplate only specifies the provisioner and arguments. return errs } func isValidDeprecatedChannelTemplate(dct *ChannelSpec) *apis.FieldError { - if dct == nil { - return nil - } var errs *apis.FieldError if dct.DeprecatedGeneration != 0 { errs = errs.Also(apis.ErrDisallowedFields("deprecatedGeneration")) diff --git a/pkg/apis/eventing/v1alpha1/broker_validation_test.go b/pkg/apis/eventing/v1alpha1/broker_validation_test.go index c7b5401f6c4..fe629b5c28f 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation_test.go @@ -53,9 +53,14 @@ func TestValidSpec(t *testing.T) { spec BrokerSpec want *apis.FieldError }{{ - name: "valid empty", + name: "invalid empty", spec: BrokerSpec{}, - want: nil, + want: func() *apis.FieldError { + var errs *apis.FieldError + fe := apis.ErrMissingOneOf("channelTemplate", "channelTemplateSpec") + errs = errs.Also(fe) + return errs + }(), }, { name: "valid provider", spec: BrokerSpec{ diff --git a/pkg/reconciler/broker/broker.go b/pkg/reconciler/broker/broker.go index 6d4cfd1bd74..5cd9a835be3 100644 --- a/pkg/reconciler/broker/broker.go +++ b/pkg/reconciler/broker/broker.go @@ -137,7 +137,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, key string) error { func (r *Reconciler) reconcile(ctx context.Context, b *v1alpha1.Broker) error { logging.FromContext(ctx).Debug("Reconciling", zap.Any("Broker", b)) - if b.Spec.ChannelTemplate.Kind != "" && b.Spec.ChannelTemplate.APIVersion != "" { + if b.Spec.ChannelTemplate != nil { return r.reconcileCRD(ctx, b) } else { return r.reconcileLegacy(ctx, b) diff --git a/pkg/reconciler/testing/broker.go b/pkg/reconciler/testing/broker.go index 02204a83d68..e9436169748 100644 --- a/pkg/reconciler/testing/broker.go +++ b/pkg/reconciler/testing/broker.go @@ -20,6 +20,7 @@ import ( "context" "time" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -66,7 +67,7 @@ func WithBrokerChannelProvisioner(provisioner *corev1.ObjectReference) BrokerOpt // WithBrokerChannelCRD sets the Broker's ChannelTemplateSpec to the specified CRD. func WithBrokerChannelCRD(crdType metav1.TypeMeta) BrokerOption { return func(b *v1alpha1.Broker) { - b.Spec.ChannelTemplate = v1alpha1.ChannelTemplateSpec{ + b.Spec.ChannelTemplate = &eventingduckv1alpha1.ChannelTemplateSpec{ TypeMeta: crdType, } } diff --git a/pkg/reconciler/testing/sequence.go b/pkg/reconciler/testing/sequence.go index ad1b8164015..b8efa0f3aef 100644 --- a/pkg/reconciler/testing/sequence.go +++ b/pkg/reconciler/testing/sequence.go @@ -20,6 +20,7 @@ import ( "context" "time" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" corev1 "k8s.io/api/core/v1" @@ -54,7 +55,7 @@ func WithSequenceDeleted(p *v1alpha1.Sequence) { p.ObjectMeta.SetDeletionTimestamp(&deleteTime) } -func WithSequenceChannelTemplateSpec(cts v1alpha1.ChannelTemplateSpec) SequenceOption { +func WithSequenceChannelTemplateSpec(cts eventingduckv1alpha1.ChannelTemplateSpec) SequenceOption { return func(p *v1alpha1.Sequence) { p.Spec.ChannelTemplate = cts } From c0e7af49b88449fdfc87eb5cbb688fa09905b036 Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 16:49:54 -0700 Subject: [PATCH 22/39] fixing sequence --- pkg/reconciler/sequence/sequence_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/reconciler/sequence/sequence_test.go b/pkg/reconciler/sequence/sequence_test.go index 79decf867b7..a6790872f89 100644 --- a/pkg/reconciler/sequence/sequence_test.go +++ b/pkg/reconciler/sequence/sequence_test.go @@ -34,6 +34,7 @@ import ( logtesting "knative.dev/pkg/logging/testing" . "knative.dev/pkg/reconciler/testing" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" "github.com/knative/eventing/pkg/reconciler" @@ -105,12 +106,12 @@ func createSubscriber(stepNumber int) eventingv1alpha1.SubscriberSpec { func TestAllCases(t *testing.T) { pKey := testNS + "/" + sequenceName - imc := v1alpha1.ChannelTemplateSpec{ - metav1.TypeMeta{ + imc := eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: metav1.TypeMeta{ APIVersion: "messaging.knative.dev/v1alpha1", Kind: "inmemorychannel", }, - &runtime.RawExtension{Raw: []byte("{}")}, + Spec: &runtime.RawExtension{Raw: []byte("{}")}, } table := TableTest{ From 4916884307d5dea3cc1c16d52188e1ea7d40a9b4 Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 17:03:13 -0700 Subject: [PATCH 23/39] broker types --- pkg/apis/eventing/v1alpha1/broker_types.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/broker_types.go b/pkg/apis/eventing/v1alpha1/broker_types.go index bb907630cbd..ddfe46e3959 100644 --- a/pkg/apis/eventing/v1alpha1/broker_types.go +++ b/pkg/apis/eventing/v1alpha1/broker_types.go @@ -66,13 +66,14 @@ var ( type BrokerSpec struct { // DeprecatedChannelTemplate, if specified will be used to create all the Channels used internally by the // Broker. Only Provisioner and Arguments may be specified. If left unspecified, the default - // Channel for the namespace will be used. + // Channel CRD for the namespace will be used using the new ChannelTemplate attribute. // // +optional DeprecatedChannelTemplate *ChannelSpec `json:"channelTemplate,omitempty"` // ChannelTemplate specifies which Channel CRD to use to create all the Channels used internally by the - // Broker. + // Broker. If left unspecified, the default Channel CRD for the namespace will be used. + // +optional ChannelTemplate *eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplateSpec,omitempty"` } From ef016c51e45e20e869bc0f08756c8d946c6613b9 Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 18:20:11 -0700 Subject: [PATCH 24/39] updating deprecated message --- contrib/gcppubsub/pkg/controller/channel/reconcile.go | 2 +- contrib/kafka/pkg/controller/channel/reconcile.go | 2 +- contrib/natss/pkg/controller/channel/reconcile.go | 2 +- pkg/provisioners/inmemory/channel/reconcile.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/gcppubsub/pkg/controller/channel/reconcile.go b/contrib/gcppubsub/pkg/controller/channel/reconcile.go index b40770a47ee..d3f14ba1704 100644 --- a/contrib/gcppubsub/pkg/controller/channel/reconcile.go +++ b/contrib/gcppubsub/pkg/controller/channel/reconcile.go @@ -62,7 +62,7 @@ const ( subscriptionSyncFailed = "SubscriptionSyncFailed" subscriptionDeleteFailed = "SubscriptionDeleteFailed" - deprecatedMessage = "The `gcp-pubsub` ClusterChannelProvisioner is deprecated and will be removed in 0.8. Recommended replacement is using `Channel` CRD from https://github.com/GoogleCloudPlatform/cloud-run-events." + deprecatedMessage = "The `gcp-pubsub` ClusterChannelProvisioner is deprecated and will be removed in 0.9. Recommended replacement is using `Channel` CRD from https://github.com/GoogleCloudPlatform/cloud-run-events." ) // reconciler reconciles GCP-PubSub Channels by creating the K8s Service (ExternalName) diff --git a/contrib/kafka/pkg/controller/channel/reconcile.go b/contrib/kafka/pkg/controller/channel/reconcile.go index 31f585ff515..5d461557c94 100644 --- a/contrib/kafka/pkg/controller/channel/reconcile.go +++ b/contrib/kafka/pkg/controller/channel/reconcile.go @@ -45,7 +45,7 @@ const ( dispatcherReconcileFailed = "DispatcherReconcileFailed" dispatcherUpdateStatusFailed = "DispatcherUpdateStatusFailed" - deprecatedMessage = "The `kafka` ClusterChannelProvisioner is deprecated and will be removed in 0.8. Recommended replacement is using `KafkaChannel` CRD." + deprecatedMessage = "The `kafka` ClusterChannelProvisioner is deprecated and will be removed in 0.9. Recommended replacement is using `KafkaChannel` CRD." ) type channelArgs struct { diff --git a/contrib/natss/pkg/controller/channel/reconcile.go b/contrib/natss/pkg/controller/channel/reconcile.go index ee7e02d1b63..4c48b09e2fe 100644 --- a/contrib/natss/pkg/controller/channel/reconcile.go +++ b/contrib/natss/pkg/controller/channel/reconcile.go @@ -34,7 +34,7 @@ import ( ) const ( - deprecatedMessage = "The `natss` ClusterChannelProvisioner is deprecated and will be removed in 0.8. Recommended replacement is using `NatssChannel` CRD." + deprecatedMessage = "The `natss` ClusterChannelProvisioner is deprecated and will be removed in 0.9. Recommended replacement is using `NatssChannel` CRD." ) type reconciler struct { diff --git a/pkg/provisioners/inmemory/channel/reconcile.go b/pkg/provisioners/inmemory/channel/reconcile.go index 31f74d59b63..c71194f0a6e 100644 --- a/pkg/provisioners/inmemory/channel/reconcile.go +++ b/pkg/provisioners/inmemory/channel/reconcile.go @@ -40,7 +40,7 @@ const ( channelUpdateStatusFailed = "ChannelUpdateStatusFailed" k8sServiceCreateFailed = "K8sServiceCreateFailed" - deprecatedMessage = "The `in-memory` ClusterChannelProvisioner is deprecated and will be removed in 0.8. Recommended replacement is using `InMemoryChannel` CRD." + deprecatedMessage = "The `in-memory` ClusterChannelProvisioner is deprecated and will be removed in 0.9. Recommended replacement is using `InMemoryChannel` CRD." ) type reconciler struct { From dadac4733c7b1d3ca0825bc03da5f8f7724885df Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 18:35:37 -0700 Subject: [PATCH 25/39] updating broker validation --- pkg/apis/eventing/v1alpha1/broker_validation.go | 10 ++++------ pkg/apis/eventing/v1alpha1/broker_validation_test.go | 9 ++------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/broker_validation.go b/pkg/apis/eventing/v1alpha1/broker_validation.go index 3a8a1adc816..721e4ae4c84 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation.go @@ -35,13 +35,8 @@ func (bs *BrokerSpec) Validate(ctx context.Context) *apis.FieldError { return errs } - if bs.DeprecatedChannelTemplate == nil && bs.ChannelTemplate == nil { - errs = errs.Also(apis.ErrMissingOneOf("channelTemplate", "channelTemplateSpec")) - return errs - } - if bs.ChannelTemplate == nil { - // If the new channelTemplate is nil it means that the DeprecatedChannelTemplate is not, validate it then. + // If the new channelTemplate is nil, validate the DeprecatedChannelTemplate. if dcte := isValidDeprecatedChannelTemplate(bs.DeprecatedChannelTemplate); dcte != nil { errs = errs.Also(dcte.ViaField("channelTemplate")) } @@ -58,6 +53,9 @@ func (bs *BrokerSpec) Validate(ctx context.Context) *apis.FieldError { func isValidDeprecatedChannelTemplate(dct *ChannelSpec) *apis.FieldError { var errs *apis.FieldError + if dct == nil { + return nil + } if dct.DeprecatedGeneration != 0 { errs = errs.Also(apis.ErrDisallowedFields("deprecatedGeneration")) } diff --git a/pkg/apis/eventing/v1alpha1/broker_validation_test.go b/pkg/apis/eventing/v1alpha1/broker_validation_test.go index fe629b5c28f..c7b5401f6c4 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation_test.go @@ -53,14 +53,9 @@ func TestValidSpec(t *testing.T) { spec BrokerSpec want *apis.FieldError }{{ - name: "invalid empty", + name: "valid empty", spec: BrokerSpec{}, - want: func() *apis.FieldError { - var errs *apis.FieldError - fe := apis.ErrMissingOneOf("channelTemplate", "channelTemplateSpec") - errs = errs.Also(fe) - return errs - }(), + want: nil, }, { name: "valid provider", spec: BrokerSpec{ From 961b0f1d71df9dfecfb6f7a8dda82c6358ae2a89 Mon Sep 17 00:00:00 2001 From: nachocano Date: Tue, 23 Jul 2019 18:48:21 -0700 Subject: [PATCH 26/39] cosmetic --- pkg/apis/eventing/v1alpha1/test_helper.go | 2 +- pkg/reconciler/broker/broker.go | 2 +- pkg/reconciler/testing/broker.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/test_helper.go b/pkg/apis/eventing/v1alpha1/test_helper.go index 4f7b0006858..98b830c2050 100644 --- a/pkg/apis/eventing/v1alpha1/test_helper.go +++ b/pkg/apis/eventing/v1alpha1/test_helper.go @@ -89,7 +89,7 @@ func (t testHelper) ReadyBrokerStatus() *BrokerStatus { func (t testHelper) ReadyBrokerStatusDeprecated() *BrokerStatus { bs := &BrokerStatus{} - bs.MarkDeprecated("ClusterChannelProvisionerDeprecated", "Provisioners are deprecated and will be removed in 0.8. Recommended replacement is CRD based channels using spec.channelTemplateSpec.") + bs.MarkDeprecated("ClusterChannelProvisionerDeprecated", "Provisioners are deprecated and will be removed in 0.9. Recommended replacement is CRD based channels using spec.channelTemplateSpec.") bs.PropagateIngressDeploymentAvailability(t.AvailableDeployment()) bs.PropagateIngressChannelReadiness(t.ReadyChannelStatus()) bs.PropagateTriggerChannelReadiness(t.ReadyChannelStatus()) diff --git a/pkg/reconciler/broker/broker.go b/pkg/reconciler/broker/broker.go index 5cd9a835be3..6f726385e41 100644 --- a/pkg/reconciler/broker/broker.go +++ b/pkg/reconciler/broker/broker.go @@ -59,7 +59,7 @@ const ( brokerUpdateStatusFailed = "BrokerUpdateStatusFailed" ingressSubscriptionDeleteFailed = "IngressSubscriptionDeleteFailed" ingressSubscriptionCreateFailed = "IngressSubscriptionCreateFailed" - deprecatedMessage = "Provisioners are deprecated and will be removed in 0.8. Recommended replacement is CRD based channels using spec.channelTemplateSpec." + deprecatedMessage = "Provisioners are deprecated and will be removed in 0.9. Recommended replacement is CRD based channels using spec.channelTemplateSpec." ) type Reconciler struct { diff --git a/pkg/reconciler/testing/broker.go b/pkg/reconciler/testing/broker.go index e9436169748..66e44a0841a 100644 --- a/pkg/reconciler/testing/broker.go +++ b/pkg/reconciler/testing/broker.go @@ -148,7 +148,7 @@ func WithBrokerIngressChannelReady() BrokerOption { func WithBrokerDeprecated() BrokerOption { return func(b *v1alpha1.Broker) { - b.Status.MarkDeprecated("ClusterChannelProvisionerDeprecated", "Provisioners are deprecated and will be removed in 0.8. Recommended replacement is CRD based channels using spec.channelTemplateSpec.") + b.Status.MarkDeprecated("ClusterChannelProvisionerDeprecated", "Provisioners are deprecated and will be removed in 0.9. Recommended replacement is CRD based channels using spec.channelTemplateSpec.") } } From d30d7ba2aca667f4289bd09448942af277e27395 Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 09:56:51 -0700 Subject: [PATCH 27/39] injection magic --- pkg/reconciler/channel/controller_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/reconciler/channel/controller_test.go b/pkg/reconciler/channel/controller_test.go index 263f690ae6e..459bae7c42f 100644 --- a/pkg/reconciler/channel/controller_test.go +++ b/pkg/reconciler/channel/controller_test.go @@ -26,6 +26,8 @@ import ( // Fake injection informers _ "github.com/knative/eventing/pkg/client/injection/informers/messaging/v1alpha1/channel/fake" + _ "knative.dev/pkg/injection/clients/dynamicclient/fake" + _ "knative.dev/pkg/injection/clients/kubeclient/fake" ) func TestNew(t *testing.T) { From 476183646f52665e41c76d12b387506f5088b237 Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 10:03:53 -0700 Subject: [PATCH 28/39] update to broker test --- pkg/reconciler/broker/broker_test.go | 1 - pkg/reconciler/channel/channel.go | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/reconciler/broker/broker_test.go b/pkg/reconciler/broker/broker_test.go index d0b6386c7b7..53ef216c880 100644 --- a/pkg/reconciler/broker/broker_test.go +++ b/pkg/reconciler/broker/broker_test.go @@ -1855,7 +1855,6 @@ func createChannelCRD(namespace string, t channelType, ready bool) *unstructured }, "labels": labels, }, - "spec": nil, }, } } diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index 561f35af3b2..14d6282beee 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -112,9 +112,8 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { c.Status.InitializeConditions() // 1. Create the backing Channel CRD, if it doesn't exist. - // 2. Propagate the backing Channel CRD status into this Channel. - // 3. Patch the subscriptions from this Channel into the backing Channel CRD. - // 4. Propagate the backing Channel CRD Subscribable status into this Channel. + // 2. Patch the subscriptions from this Channel into the backing Channel CRD. + // 3. Propagate the backing Channel CRD Status, Address, and SubscribableStatus into this Channel. if c.DeletionTimestamp != nil { // Everything is cleaned up by the garbage collector. From 42de64b29d99fd37ece95bb58069ef9498cc6b3a Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 11:16:00 -0700 Subject: [PATCH 29/39] updating UTs --- pkg/reconciler/channel/channel.go | 2 + pkg/reconciler/channel/channel_test.go | 910 +++++++++++++++++++++ pkg/reconciler/testing/listers.go | 4 + pkg/reconciler/testing/messagingchannel.go | 115 +++ 4 files changed, 1031 insertions(+) create mode 100644 pkg/reconciler/channel/channel_test.go create mode 100644 pkg/reconciler/testing/messagingchannel.go diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index 14d6282beee..ecc35371f07 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -48,6 +48,7 @@ import ( const ( channelReadinessChanged = "ChannelReadinessChanged" channelReconciled = "ChannelReconciled" + channelReconcileError = "ChannelReconcileError" channelUpdateStatusFailed = "ChannelUpdateStatusFailed" ) @@ -93,6 +94,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, key string) error { reconcileErr := r.reconcile(ctx, channel) if reconcileErr != nil { logging.FromContext(ctx).Error("Error reconciling Channel", zap.Error(reconcileErr)) + r.Recorder.Eventf(channel, corev1.EventTypeWarning, channelReconcileError, fmt.Sprintf("Channel reconcile error: %v", reconcileErr)) } else { logging.FromContext(ctx).Debug("Successfully reconciled Channel") r.Recorder.Eventf(channel, corev1.EventTypeNormal, channelReconciled, "Channel reconciled: %s", key) diff --git a/pkg/reconciler/channel/channel_test.go b/pkg/reconciler/channel/channel_test.go new file mode 100644 index 00000000000..974013531e3 --- /dev/null +++ b/pkg/reconciler/channel/channel_test.go @@ -0,0 +1,910 @@ +/* +Copyright 2019 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 channel + +import ( + "context" + "fmt" + "testing" + + "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + "github.com/knative/eventing/pkg/reconciler" + . "github.com/knative/eventing/pkg/reconciler/testing" + "github.com/knative/eventing/pkg/utils" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes/scheme" + clientgotesting "k8s.io/client-go/testing" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + logtesting "knative.dev/pkg/logging/testing" + . "knative.dev/pkg/reconciler/testing" +) + +const ( + testNS = "test-namespace" + channelName = "test-channel" +) + +var ( + trueVal = true + + testKey = fmt.Sprintf("%s/%s", testNS, channelName) + + backingChannelHostname = fmt.Sprintf("foo.bar.svc.%s", utils.GetClusterDomainName()) + + channelGVK = metav1.GroupVersionKind{ + Group: "messaging.knative.dev", + Version: "v1alpha1", + Kind: "Channel", + } + + imcGVK = metav1.GroupVersionKind{ + Group: "messaging.knative.dev", + Version: "v1alpha1", + Kind: "InMemoryChannel", + } +) + +func init() { + // Add types to scheme + _ = v1alpha1.AddToScheme(scheme.Scheme) +} + +type fakeResourceTracker struct{} + +func (fakeResourceTracker) TrackInNamespace(metav1.Object) func(corev1.ObjectReference) error { + return func(corev1.ObjectReference) error { return nil } +} + +func (fakeResourceTracker) Track(ref corev1.ObjectReference, obj interface{}) error { + return nil +} + +func (fakeResourceTracker) OnChanged(obj interface{}) { +} + +func TestReconcile(t *testing.T) { + table := TableTest{ + { + Name: "bad workqueue key", + // Make sure Reconcile handles bad keys. + Key: "too/many/parts", + }, { + Name: "key not found", + // Make sure Reconcile handles good keys that don't exist. + Key: "foo/not-found", + }, + { + Name: "Channel not found", + Key: testKey, + }, + { + Name: "Channel is being deleted", + Key: testKey, + Objects: []runtime.Object{ + NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions, + WithMessagingChannelDeleted), + }, + }, + { + Name: "Backing Channel.Create error", + Key: testKey, + Objects: []runtime.Object{ + NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions), + }, + WantCreates: []runtime.Object{ + createChannelCRD(testNS, channelName, false), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewMessagingChannel(channelName, testNS, + WithInitMessagingChannelConditions, + WithMessagingChannelTemplate(channelCRD()), + WithBackingChannelFailed("ChannelFailure", "inducing failure for create inmemorychannels")), + }}, + WithReactors: []clientgotesting.ReactionFunc{ + InduceFailure("create", "inmemorychannels"), + }, + WantEvents: []string{ + Eventf(corev1.EventTypeWarning, channelReconcileError, "Channel reconcile error: %v", "inducing failure for create inmemorychannels"), + }, + WantErr: true, + }, + //{ + // Name: "Trigger Channel.Create no address", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // }, + // WantCreates: []runtime.Object{ + // createChannelCRD(testNS, triggerChannel, false), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithInitBrokerConditions, + // WithBrokerChannelCRD(channelCRD()), + // WithTriggerChannelFailed("NoAddress", "Channel does not have an address.")), + // }}, + //}, + //{ + // Name: "Trigger Channel is not yet Addressable", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, false), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelFailed("NoAddress", "Channel does not have an address.")), + // }}, + //}, + //{ + // Name: "Filter Deployment.Create error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("create", "deployments"), + // }, + // WantCreates: []runtime.Object{ + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithFilterFailed("DeploymentFailure", "inducing failure for create deployments")), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create deployments"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Filter Deployment.Update error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, "some-other-image", envVars(filterContainerName), containerPorts(8080))), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("update", "deployments"), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithFilterFailed("DeploymentFailure", "inducing failure for update deployments")), + // }}, + // WantUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for update deployments"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Filter Service.Create error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("create", "services"), + // }, + // WantCreates: []runtime.Object{ + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceAnnotations(resources.FilterAnnotations()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithFilterFailed("ServiceFailure", "inducing failure for create services")), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create services"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Filter Service.Update error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 9090))), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("update", "services"), + // }, + // WantUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // }}, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithFilterFailed("ServiceFailure", "inducing failure for update services")), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for update services"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Ingress Deployment.Create error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("create", "deployments"), + // }, + // WantCreates: []runtime.Object{ + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080)), + // ), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithFilterDeploymentAvailable(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithIngressFailed("DeploymentFailure", "inducing failure for create deployments")), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create deployments"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Ingress Deployment.Update error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(9090))), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("update", "deployments"), + // }, + // WantUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), + // }}, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithFilterDeploymentAvailable(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithIngressFailed("DeploymentFailure", "inducing failure for update deployments")), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for update deployments"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Ingress Service.Create error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("create", "services"), + // }, + // WantCreates: []runtime.Object{ + // NewService(ingressServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceAnnotations(resources.IngressAnnotations()), + // WithServiceLabels(resources.IngressLabels(brokerName)), + // WithServicePorts(servicePorts(ingressContainerName, 8080))), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithFilterDeploymentAvailable(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithIngressFailed("ServiceFailure", "inducing failure for create services")), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create services"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Ingress Service.Update error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), + // NewService(ingressServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.IngressLabels(brokerName)), + // WithServicePorts(servicePorts(ingressContainerName, 9090))), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("update", "services"), + // }, + // WantUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewService(ingressServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.IngressLabels(brokerName)), + // WithServicePorts(servicePorts(ingressContainerName, 8080))), + // }}, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithFilterDeploymentAvailable(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithIngressFailed("ServiceFailure", "inducing failure for update services")), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for update services"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Ingress Channel.Create error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), + // NewService(ingressServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.IngressLabels(brokerName)), + // WithServicePorts(servicePorts(ingressContainerName, 8080))), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("create", "inmemorychannels"), + // }, + // WantCreates: []runtime.Object{ + // createChannelCRD(testNS, ingressChannel, false), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithFilterDeploymentAvailable(), + // WithIngressDeploymentAvailable(), + // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), + // WithIngressChannelFailed("ChannelFailure", "inducing failure for create inmemorychannels")), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create inmemorychannels"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Subscription.Create error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // createChannelCRD(testNS, ingressChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), + // NewService(ingressServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.IngressLabels(brokerName)), + // WithServicePorts(servicePorts(ingressContainerName, 8080))), + // }, + // WantCreates: []runtime.Object{ + // NewSubscription("", testNS, + // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), + // WithSubscriptionOwnerReferences(ownerReferences()), + // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), + // WithSubscriptionChannel(imcGVK, ingressChannelName), + // WithSubscriptionSubscriberRef(serviceGVK, ingressServiceName)), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithFilterDeploymentAvailable(), + // WithIngressDeploymentAvailable(), + // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), + // WithBrokerIngressChannelReady(), + // WithBrokerIngressChannel(createIngressChannelCRDRef()), + // WithBrokerIngressSubscriptionFailed("SubscriptionFailure", "inducing failure for create subscriptions"), + // ), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create subscriptions"), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("create", "subscriptions"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Subscription.Delete error", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // createChannelCRD(testNS, ingressChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), + // NewService(ingressServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.IngressLabels(brokerName)), + // WithServicePorts(servicePorts(ingressContainerName, 8080))), + // NewSubscription("subs", testNS, + // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), + // WithSubscriptionOwnerReferences(ownerReferences()), + // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), + // WithSubscriptionChannel(channelGVK, ingressChannelName), + // WithSubscriptionSubscriberRef(serviceGVK, "")), + // }, + // WantDeletes: []clientgotesting.DeleteActionImpl{{ + // Name: "subs", + // }}, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithFilterDeploymentAvailable(), + // WithIngressDeploymentAvailable(), + // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), + // WithBrokerIngressChannelReady(), + // WithBrokerIngressChannel(createIngressChannelCRDRef()), + // WithBrokerIngressSubscriptionFailed("SubscriptionFailure", "inducing failure for delete subscriptions"), + // ), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, ingressSubscriptionDeleteFailed, "%v", "Delete Broker Ingress' subscription failed: inducing failure for delete subscriptions"), + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for delete subscriptions"), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("delete", "subscriptions"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Subscription.Create error when recreating", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // createChannelCRD(testNS, ingressChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), + // NewService(ingressServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.IngressLabels(brokerName)), + // WithServicePorts(servicePorts(ingressContainerName, 8080))), + // NewSubscription("subs", testNS, + // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), + // WithSubscriptionOwnerReferences(ownerReferences()), + // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), + // WithSubscriptionChannel(channelGVK, ingressChannelName), + // WithSubscriptionSubscriberRef(serviceGVK, "")), + // }, + // WantDeletes: []clientgotesting.DeleteActionImpl{{ + // Name: "subs", + // }}, + // WantCreates: []runtime.Object{ + // NewSubscription("", testNS, + // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), + // WithSubscriptionOwnerReferences(ownerReferences()), + // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), + // WithSubscriptionChannel(imcGVK, ingressChannelName), + // WithSubscriptionSubscriberRef(serviceGVK, ingressServiceName)), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions, + // WithTriggerChannelReady(), + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithFilterDeploymentAvailable(), + // WithIngressDeploymentAvailable(), + // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), + // WithBrokerIngressChannelReady(), + // WithBrokerIngressChannel(createIngressChannelCRDRef()), + // WithBrokerIngressSubscriptionFailed("SubscriptionFailure", "inducing failure for create subscriptions"), + // ), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeWarning, ingressSubscriptionCreateFailed, "%v", "Create Broker Ingress' subscription failed: inducing failure for create subscriptions"), + // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create subscriptions"), + // }, + // WithReactors: []clientgotesting.ReactionFunc{ + // InduceFailure("create", "subscriptions"), + // }, + // WantErr: true, + //}, + //{ + // Name: "Successful Reconciliation", + // Key: testKey, + // Objects: []runtime.Object{ + // NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithInitBrokerConditions), + // createChannelCRD(testNS, triggerChannel, true), + // createChannelCRD(testNS, ingressChannel, true), + // NewDeployment(filterDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.FilterLabels(brokerName)), + // WithDeploymentServiceAccount(filterSA), + // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), + // NewService(filterServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.FilterLabels(brokerName)), + // WithServicePorts(servicePorts(filterContainerName, 8080))), + // NewDeployment(ingressDeploymentName, testNS, + // WithDeploymentOwnerReferences(ownerReferences()), + // WithDeploymentLabels(resources.IngressLabels(brokerName)), + // WithDeploymentServiceAccount(ingressSA), + // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), + // NewService(ingressServiceName, testNS, + // WithServiceOwnerReferences(ownerReferences()), + // WithServiceLabels(resources.IngressLabels(brokerName)), + // WithServicePorts(servicePorts(ingressContainerName, 8080))), + // NewSubscription("", testNS, + // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), + // WithSubscriptionOwnerReferences(ownerReferences()), + // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), + // WithSubscriptionChannel(imcGVK, ingressChannelName), + // WithSubscriptionSubscriberRef(serviceGVK, ingressServiceName), + // WithSubscriptionReady), + // }, + // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + // Object: NewBroker(brokerName, testNS, + // WithBrokerChannelCRD(channelCRD()), + // WithBrokerReady, + // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), + // WithBrokerIngressChannel(createIngressChannelCRDRef()), + // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), + // ), + // }}, + // WantEvents: []string{ + // Eventf(corev1.EventTypeNormal, brokerReadinessChanged, "Broker %q became ready", brokerName), + // }, + // }, + } + + defer logtesting.ClearAll() + table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler { + return &Reconciler{ + Base: reconciler.NewBase(ctx, controllerAgentName, cmw), + channelLister: listers.GetMessagingChannelLister(), + resourceTracker: fakeResourceTracker{}, + } + }, + false, + )) +} + +func ownerReferences() []metav1.OwnerReference { + return []metav1.OwnerReference{{ + APIVersion: v1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + Name: channelName, + Controller: &trueVal, + BlockOwnerDeletion: &trueVal, + }} +} + +func channelCRD() metav1.TypeMeta { + return metav1.TypeMeta{ + APIVersion: "messaging.knative.dev/v1alpha1", + Kind: "InMemoryChannel", + } +} + +func containerPorts(httpInternal int32) []corev1.ContainerPort { + return []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: httpInternal, + }, + { + Name: "metrics", + ContainerPort: 9090, + }, + } +} + +func servicePorts(containerName string, httpInternal int) []corev1.ServicePort { + svcPorts := []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(httpInternal), + }, { + Name: "metrics", + Port: 9090, + }, + } + return svcPorts +} + +func createChannelCRD(namespace, name string, ready bool) *unstructured.Unstructured { + unstructured := &unstructured.Unstructured{} + if ready { + unstructured.Object = map[string]interface{}{ + "apiVersion": "messaging.knative.dev/v1alpha1", + "kind": "InMemoryChannel", + "metadata": map[string]interface{}{ + "creationTimestamp": nil, + "namespace": namespace, + "name": name, + "ownerReferences": []interface{}{ + map[string]interface{}{ + "apiVersion": "messaging.knative.dev/v1alpha1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "Channel", + "name": name, + "uid": "", + }, + }, + }, + "status": map[string]interface{}{ + "address": map[string]interface{}{ + "hostname": backingChannelHostname, + "url": fmt.Sprintf("http://%s", backingChannelHostname), + }, + }, + } + } else { + unstructured.Object = map[string]interface{}{ + "apiVersion": "messaging.knative.dev/v1alpha1", + "kind": "InMemoryChannel", + "metadata": map[string]interface{}{ + "creationTimestamp": nil, + "namespace": namespace, + "name": name, + "ownerReferences": []interface{}{ + map[string]interface{}{ + "apiVersion": "messaging.knative.dev/v1alpha1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "Channel", + "name": name, + "uid": "", + }, + }, + }, + } + } + return unstructured +} + +//func createTriggerChannelCRDRef() *corev1.ObjectReference { +// return &corev1.ObjectReference{ +// APIVersion: "messaging.knative.dev/v1alpha1", +// Kind: "InMemoryChannel", +// Namespace: testNS, +// Name: fmt.Sprintf("%s-kn-trigger", brokerName), +// } +//} +// +//func createIngressChannelCRDRef() *corev1.ObjectReference { +// return &corev1.ObjectReference{ +// APIVersion: "messaging.knative.dev/v1alpha1", +// Kind: "InMemoryChannel", +// Namespace: testNS, +// Name: fmt.Sprintf("%s-kn-ingress", brokerName), +// } +//} diff --git a/pkg/reconciler/testing/listers.go b/pkg/reconciler/testing/listers.go index 4b3bde158b4..0acde9142dd 100644 --- a/pkg/reconciler/testing/listers.go +++ b/pkg/reconciler/testing/listers.go @@ -135,6 +135,10 @@ func (l *Listers) GetChannelLister() eventinglisters.ChannelLister { return eventinglisters.NewChannelLister(l.indexerFor(&eventingv1alpha1.Channel{})) } +func (l *Listers) GetMessagingChannelLister() messaginglisters.ChannelLister { + return messaginglisters.NewChannelLister(l.indexerFor(&messagingv1alpha1.Channel{})) +} + func (l *Listers) GetSequenceLister() messaginglisters.SequenceLister { return messaginglisters.NewSequenceLister(l.indexerFor(&messagingv1alpha1.Sequence{})) } diff --git a/pkg/reconciler/testing/messagingchannel.go b/pkg/reconciler/testing/messagingchannel.go new file mode 100644 index 00000000000..fc9f98e7321 --- /dev/null +++ b/pkg/reconciler/testing/messagingchannel.go @@ -0,0 +1,115 @@ +/* +Copyright 2019 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 testing + +import ( + "context" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" + "knative.dev/pkg/apis" + duck "knative.dev/pkg/apis/duck/v1alpha1" + "knative.dev/pkg/apis/duck/v1beta1" +) + +// TODO once we remove Channel from eventing, we should rename this to be just Channel. + +// ChannelOption enables further configuration of a Channel. +type MessagingChannelOption func(*v1alpha1.Channel) + +// NewMessagingChannel creates a Channel with ChannelOptions +func NewMessagingChannel(name, namespace string, o ...MessagingChannelOption) *v1alpha1.Channel { + c := &v1alpha1.Channel{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "messaging.knative.dev/v1alpha1", + Kind: "Channel", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + for _, opt := range o { + opt(c) + } + c.SetDefaults(context.Background()) + return c +} + +// WithInitMessagingChannelConditions initializes the Channel's conditions. +func WithInitMessagingChannelConditions(s *v1alpha1.Channel) { + s.Status.InitializeConditions() +} + +func WithMessagingChannelDeleted(c *v1alpha1.Channel) { + t := metav1.NewTime(time.Unix(1e9, 0)) + c.ObjectMeta.SetDeletionTimestamp(&t) +} + +func WithMessagingChannelTemplate(typeMeta metav1.TypeMeta) MessagingChannelOption { + return func(c *v1alpha1.Channel) { + c.Spec.ChannelTemplate = &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: typeMeta, + } + } +} + +// WithBackingChannelFailed calls .Status.MarkBackingChannelFailed on the Broker. +func WithBackingChannelFailed(reason, msg string) MessagingChannelOption { + return func(c *v1alpha1.Channel) { + c.Status.MarkBackingChannelFailed(reason, msg) + } +} + +func WithMessagingChannelAddress(hostname string) MessagingChannelOption { + return func(c *v1alpha1.Channel) { + address := &duck.Addressable{ + Addressable: v1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: hostname, + }, + }, + } + c.Status.SetAddress(address) + } +} + +func WithMessagingChannelReady(c *v1alpha1.Channel) { + cs := v1alpha1.ChannelStatus{} + cs.MarkBackingChannelReady() + cs.SetAddress(&duck.Addressable{ + Addressable: v1beta1.Addressable{ + URL: &apis.URL{ + Scheme: "http", + Host: "foo", + }, + }, + }) + c.Status = cs +} + +func WithMessagingChannelSubscribers(subscribers []eventingduckv1alpha1.SubscriberSpec) MessagingChannelOption { + return func(c *v1alpha1.Channel) { + c.Spec.Subscribable = &eventingduckv1alpha1.Subscribable{ + Subscribers: subscribers, + } + } +} From 2734cb2222229832dc86e1b7b9421d20e9c8e068 Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 13:16:36 -0700 Subject: [PATCH 30/39] more UTs --- pkg/reconciler/channel/channel_test.go | 915 +++++---------------- pkg/reconciler/testing/messagingchannel.go | 42 +- 2 files changed, 210 insertions(+), 747 deletions(-) diff --git a/pkg/reconciler/channel/channel_test.go b/pkg/reconciler/channel/channel_test.go index 974013531e3..1d8bbd945cd 100644 --- a/pkg/reconciler/channel/channel_test.go +++ b/pkg/reconciler/channel/channel_test.go @@ -18,9 +18,11 @@ package channel import ( "context" + "encoding/json" "fmt" "testing" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" "github.com/knative/eventing/pkg/reconciler" . "github.com/knative/eventing/pkg/reconciler/testing" @@ -29,7 +31,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes/scheme" clientgotesting "k8s.io/client-go/testing" "knative.dev/pkg/configmap" @@ -82,6 +83,7 @@ func (fakeResourceTracker) OnChanged(obj interface{}) { } func TestReconcile(t *testing.T) { + table := TableTest{ { Name: "bad workqueue key", @@ -131,656 +133,128 @@ func TestReconcile(t *testing.T) { }, WantErr: true, }, - //{ - // Name: "Trigger Channel.Create no address", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // }, - // WantCreates: []runtime.Object{ - // createChannelCRD(testNS, triggerChannel, false), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithInitBrokerConditions, - // WithBrokerChannelCRD(channelCRD()), - // WithTriggerChannelFailed("NoAddress", "Channel does not have an address.")), - // }}, - //}, - //{ - // Name: "Trigger Channel is not yet Addressable", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, false), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelFailed("NoAddress", "Channel does not have an address.")), - // }}, - //}, - //{ - // Name: "Filter Deployment.Create error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("create", "deployments"), - // }, - // WantCreates: []runtime.Object{ - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithFilterFailed("DeploymentFailure", "inducing failure for create deployments")), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create deployments"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Filter Deployment.Update error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, "some-other-image", envVars(filterContainerName), containerPorts(8080))), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("update", "deployments"), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithFilterFailed("DeploymentFailure", "inducing failure for update deployments")), - // }}, - // WantUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for update deployments"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Filter Service.Create error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("create", "services"), - // }, - // WantCreates: []runtime.Object{ - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceAnnotations(resources.FilterAnnotations()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithFilterFailed("ServiceFailure", "inducing failure for create services")), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create services"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Filter Service.Update error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 9090))), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("update", "services"), - // }, - // WantUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // }}, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithFilterFailed("ServiceFailure", "inducing failure for update services")), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for update services"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Ingress Deployment.Create error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("create", "deployments"), - // }, - // WantCreates: []runtime.Object{ - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080)), - // ), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithFilterDeploymentAvailable(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithIngressFailed("DeploymentFailure", "inducing failure for create deployments")), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create deployments"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Ingress Deployment.Update error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(9090))), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("update", "deployments"), - // }, - // WantUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), - // }}, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithFilterDeploymentAvailable(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithIngressFailed("DeploymentFailure", "inducing failure for update deployments")), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for update deployments"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Ingress Service.Create error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("create", "services"), - // }, - // WantCreates: []runtime.Object{ - // NewService(ingressServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceAnnotations(resources.IngressAnnotations()), - // WithServiceLabels(resources.IngressLabels(brokerName)), - // WithServicePorts(servicePorts(ingressContainerName, 8080))), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithFilterDeploymentAvailable(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithIngressFailed("ServiceFailure", "inducing failure for create services")), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create services"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Ingress Service.Update error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), - // NewService(ingressServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.IngressLabels(brokerName)), - // WithServicePorts(servicePorts(ingressContainerName, 9090))), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("update", "services"), - // }, - // WantUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewService(ingressServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.IngressLabels(brokerName)), - // WithServicePorts(servicePorts(ingressContainerName, 8080))), - // }}, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithFilterDeploymentAvailable(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithIngressFailed("ServiceFailure", "inducing failure for update services")), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for update services"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Ingress Channel.Create error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), - // NewService(ingressServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.IngressLabels(brokerName)), - // WithServicePorts(servicePorts(ingressContainerName, 8080))), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("create", "inmemorychannels"), - // }, - // WantCreates: []runtime.Object{ - // createChannelCRD(testNS, ingressChannel, false), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithFilterDeploymentAvailable(), - // WithIngressDeploymentAvailable(), - // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), - // WithIngressChannelFailed("ChannelFailure", "inducing failure for create inmemorychannels")), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create inmemorychannels"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Subscription.Create error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // createChannelCRD(testNS, ingressChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), - // NewService(ingressServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.IngressLabels(brokerName)), - // WithServicePorts(servicePorts(ingressContainerName, 8080))), - // }, - // WantCreates: []runtime.Object{ - // NewSubscription("", testNS, - // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), - // WithSubscriptionOwnerReferences(ownerReferences()), - // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), - // WithSubscriptionChannel(imcGVK, ingressChannelName), - // WithSubscriptionSubscriberRef(serviceGVK, ingressServiceName)), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithFilterDeploymentAvailable(), - // WithIngressDeploymentAvailable(), - // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), - // WithBrokerIngressChannelReady(), - // WithBrokerIngressChannel(createIngressChannelCRDRef()), - // WithBrokerIngressSubscriptionFailed("SubscriptionFailure", "inducing failure for create subscriptions"), - // ), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create subscriptions"), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("create", "subscriptions"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Subscription.Delete error", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // createChannelCRD(testNS, ingressChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), - // NewService(ingressServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.IngressLabels(brokerName)), - // WithServicePorts(servicePorts(ingressContainerName, 8080))), - // NewSubscription("subs", testNS, - // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), - // WithSubscriptionOwnerReferences(ownerReferences()), - // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), - // WithSubscriptionChannel(channelGVK, ingressChannelName), - // WithSubscriptionSubscriberRef(serviceGVK, "")), - // }, - // WantDeletes: []clientgotesting.DeleteActionImpl{{ - // Name: "subs", - // }}, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithFilterDeploymentAvailable(), - // WithIngressDeploymentAvailable(), - // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), - // WithBrokerIngressChannelReady(), - // WithBrokerIngressChannel(createIngressChannelCRDRef()), - // WithBrokerIngressSubscriptionFailed("SubscriptionFailure", "inducing failure for delete subscriptions"), - // ), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, ingressSubscriptionDeleteFailed, "%v", "Delete Broker Ingress' subscription failed: inducing failure for delete subscriptions"), - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for delete subscriptions"), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("delete", "subscriptions"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Subscription.Create error when recreating", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // createChannelCRD(testNS, ingressChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), - // NewService(ingressServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.IngressLabels(brokerName)), - // WithServicePorts(servicePorts(ingressContainerName, 8080))), - // NewSubscription("subs", testNS, - // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), - // WithSubscriptionOwnerReferences(ownerReferences()), - // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), - // WithSubscriptionChannel(channelGVK, ingressChannelName), - // WithSubscriptionSubscriberRef(serviceGVK, "")), - // }, - // WantDeletes: []clientgotesting.DeleteActionImpl{{ - // Name: "subs", - // }}, - // WantCreates: []runtime.Object{ - // NewSubscription("", testNS, - // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), - // WithSubscriptionOwnerReferences(ownerReferences()), - // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), - // WithSubscriptionChannel(imcGVK, ingressChannelName), - // WithSubscriptionSubscriberRef(serviceGVK, ingressServiceName)), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions, - // WithTriggerChannelReady(), - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithFilterDeploymentAvailable(), - // WithIngressDeploymentAvailable(), - // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), - // WithBrokerIngressChannelReady(), - // WithBrokerIngressChannel(createIngressChannelCRDRef()), - // WithBrokerIngressSubscriptionFailed("SubscriptionFailure", "inducing failure for create subscriptions"), - // ), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeWarning, ingressSubscriptionCreateFailed, "%v", "Create Broker Ingress' subscription failed: inducing failure for create subscriptions"), - // Eventf(corev1.EventTypeWarning, brokerReconcileError, "Broker reconcile error: %v", "inducing failure for create subscriptions"), - // }, - // WithReactors: []clientgotesting.ReactionFunc{ - // InduceFailure("create", "subscriptions"), - // }, - // WantErr: true, - //}, - //{ - // Name: "Successful Reconciliation", - // Key: testKey, - // Objects: []runtime.Object{ - // NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithInitBrokerConditions), - // createChannelCRD(testNS, triggerChannel, true), - // createChannelCRD(testNS, ingressChannel, true), - // NewDeployment(filterDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.FilterLabels(brokerName)), - // WithDeploymentServiceAccount(filterSA), - // WithDeploymentContainer(filterContainerName, filterImage, envVars(filterContainerName), containerPorts(8080))), - // NewService(filterServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.FilterLabels(brokerName)), - // WithServicePorts(servicePorts(filterContainerName, 8080))), - // NewDeployment(ingressDeploymentName, testNS, - // WithDeploymentOwnerReferences(ownerReferences()), - // WithDeploymentLabels(resources.IngressLabels(brokerName)), - // WithDeploymentServiceAccount(ingressSA), - // WithDeploymentContainer(ingressContainerName, ingressImage, envVars(ingressContainerName), containerPorts(8080))), - // NewService(ingressServiceName, testNS, - // WithServiceOwnerReferences(ownerReferences()), - // WithServiceLabels(resources.IngressLabels(brokerName)), - // WithServicePorts(servicePorts(ingressContainerName, 8080))), - // NewSubscription("", testNS, - // WithSubscriptionGenerateName(ingressSubscriptionGenerateName), - // WithSubscriptionOwnerReferences(ownerReferences()), - // WithSubscriptionLabels(ingressSubscriptionLabels(brokerName)), - // WithSubscriptionChannel(imcGVK, ingressChannelName), - // WithSubscriptionSubscriberRef(serviceGVK, ingressServiceName), - // WithSubscriptionReady), - // }, - // WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ - // Object: NewBroker(brokerName, testNS, - // WithBrokerChannelCRD(channelCRD()), - // WithBrokerReady, - // WithBrokerTriggerChannel(createTriggerChannelCRDRef()), - // WithBrokerIngressChannel(createIngressChannelCRDRef()), - // WithBrokerAddress(fmt.Sprintf("%s.%s.svc.%s", ingressServiceName, testNS, utils.GetClusterDomainName())), - // ), - // }}, - // WantEvents: []string{ - // Eventf(corev1.EventTypeNormal, brokerReadinessChanged, "Broker %q became ready", brokerName), - // }, - // }, + { + Name: "Backing Channel.Patch Subscriptions failed", + Key: testKey, + Objects: []runtime.Object{ + NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions, + WithMessagingChannelSubscribers(subscribers())), + NewInMemoryChannel(channelName, testNS, + WithInitInMemoryChannelConditions), + }, + WantPatches: []clientgotesting.PatchActionImpl{ + patchSubscribers(testNS, channelName, subscribers()), + }, + WithReactors: []clientgotesting.ReactionFunc{ + InduceFailure("patch", "inmemorychannels"), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions, + WithMessagingChannelSubscribers(subscribers()), + WithBackingChannelObjRef(backingChannelObjRef()), + WithBackingChannelFailed("ChannelFailure", "inducing failure for patch inmemorychannels")), + }}, + WantEvents: []string{ + Eventf(corev1.EventTypeWarning, channelReconcileError, "Channel reconcile error: %v", "inducing failure for patch inmemorychannels"), + }, + WantErr: true, + }, + { + Name: "Successful reconciliation", + Key: testKey, + Objects: []runtime.Object{ + NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions, + WithMessagingChannelSubscribers(subscribers())), + NewInMemoryChannel(channelName, testNS, + WithInitInMemoryChannelConditions, + WithInMemoryChannelDeploymentReady(), + WithInMemoryChannelServiceReady(), + WithInMemoryChannelEndpointsReady(), + WithInMemoryChannelChannelServiceReady(), + WithInMemoryChannelAddress(backingChannelHostname)), + }, + WantPatches: []clientgotesting.PatchActionImpl{ + patchSubscribers(testNS, channelName, subscribers()), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions, + WithMessagingChannelSubscribers(subscribers()), + WithBackingChannelObjRef(backingChannelObjRef()), + WithBackingChannelReady, + WithMessagingChannelAddress(backingChannelHostname)), + }}, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, channelReconciled, "Channel reconciled: %s", testKey), + Eventf(corev1.EventTypeNormal, channelReadinessChanged, "Channel %q became ready", channelName), + }, + }, + { + Name: "Already reconciled", + Key: testKey, + Objects: []runtime.Object{ + NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions, + WithBackingChannelObjRef(backingChannelObjRef()), + WithMessagingChannelSubscribers(subscribers()), + WithBackingChannelReady, + WithMessagingChannelAddress(backingChannelHostname)), + NewInMemoryChannel(channelName, testNS, + WithInitInMemoryChannelConditions, + WithInMemoryChannelDeploymentReady(), + WithInMemoryChannelServiceReady(), + WithInMemoryChannelEndpointsReady(), + WithInMemoryChannelChannelServiceReady(), + WithInMemoryChannelSubscribers(subscribers()), + WithInMemoryChannelAddress(backingChannelHostname)), + }, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, channelReconciled, "Channel reconciled: %s", testKey), + }, + }, + { + Name: "Updating subscribers statuses", + Key: testKey, + Objects: []runtime.Object{ + NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions, + WithBackingChannelObjRef(backingChannelObjRef()), + WithBackingChannelReady, + WithMessagingChannelAddress(backingChannelHostname), + WithMessagingChannelSubscribers(subscribers())), + NewInMemoryChannel(channelName, testNS, + WithInitInMemoryChannelConditions, + WithInMemoryChannelDeploymentReady(), + WithInMemoryChannelServiceReady(), + WithInMemoryChannelEndpointsReady(), + WithInMemoryChannelChannelServiceReady(), + WithInMemoryChannelAddress(backingChannelHostname), + WithInMemoryChannelSubscribers(subscribers()), + WithInMemoryChannelStatusSubscribers(subscriberStatuses())), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewMessagingChannel(channelName, testNS, + WithMessagingChannelTemplate(channelCRD()), + WithInitMessagingChannelConditions, + WithMessagingChannelSubscribers(subscribers()), + WithBackingChannelObjRef(backingChannelObjRef()), + WithBackingChannelReady, + WithMessagingChannelAddress(backingChannelHostname), + WithMesssagingChannelSubscriberStatuses(subscriberStatuses())), + }}, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, channelReconciled, "Channel reconciled: %s", testKey), + }, + }, } defer logtesting.ClearAll() @@ -795,16 +269,6 @@ func TestReconcile(t *testing.T) { )) } -func ownerReferences() []metav1.OwnerReference { - return []metav1.OwnerReference{{ - APIVersion: v1alpha1.SchemeGroupVersion.String(), - Kind: "Channel", - Name: channelName, - Controller: &trueVal, - BlockOwnerDeletion: &trueVal, - }} -} - func channelCRD() metav1.TypeMeta { return metav1.TypeMeta{ APIVersion: "messaging.knative.dev/v1alpha1", @@ -812,37 +276,59 @@ func channelCRD() metav1.TypeMeta { } } -func containerPorts(httpInternal int32) []corev1.ContainerPort { - return []corev1.ContainerPort{ - { - Name: "http", - ContainerPort: httpInternal, - }, - { - Name: "metrics", - ContainerPort: 9090, - }, - } +func subscribers() []eventingduckv1alpha1.SubscriberSpec { + return []eventingduckv1alpha1.SubscriberSpec{{ + UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1", + Generation: 1, + SubscriberURI: "call1", + ReplyURI: "sink2", + }, { + UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1", + Generation: 2, + SubscriberURI: "call2", + ReplyURI: "sink2", + }} } -func servicePorts(containerName string, httpInternal int) []corev1.ServicePort { - svcPorts := []corev1.ServicePort{ - { - Name: "http", - Port: 80, - TargetPort: intstr.FromInt(httpInternal), - }, { - Name: "metrics", - Port: 9090, - }, +func subscriberStatuses() []eventingduckv1alpha1.SubscriberStatus { + return []eventingduckv1alpha1.SubscriberStatus{{ + UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1", + ObservedGeneration: 1, + Ready: "True", + }, { + UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1", + ObservedGeneration: 2, + Ready: "True", + }} +} + +func patchSubscribers(namespace, name string, subscribers []eventingduckv1alpha1.SubscriberSpec) clientgotesting.PatchActionImpl { + action := clientgotesting.PatchActionImpl{} + action.Name = name + action.Namespace = namespace + + var spec string + if subscribers != nil { + b, err := json.Marshal(subscribers) + ss := make([]map[string]interface{}, 0) + err = json.Unmarshal(b, &ss) + subs, err := json.Marshal(ss) + if err != nil { + return action + } + spec = fmt.Sprintf(`{"subscribable":{"subscribers":%s}}`, subs) + } else { + spec = `{"subscribable":{}}` } - return svcPorts + + patch := `{"spec":` + spec + `}` + action.Patch = []byte(patch) + return action } func createChannelCRD(namespace, name string, ready bool) *unstructured.Unstructured { - unstructured := &unstructured.Unstructured{} - if ready { - unstructured.Object = map[string]interface{}{ + unstructured := &unstructured.Unstructured{ + Object: map[string]interface{}{ "apiVersion": "messaging.knative.dev/v1alpha1", "kind": "InMemoryChannel", "metadata": map[string]interface{}{ @@ -860,51 +346,24 @@ func createChannelCRD(namespace, name string, ready bool) *unstructured.Unstruct }, }, }, - "status": map[string]interface{}{ - "address": map[string]interface{}{ - "hostname": backingChannelHostname, - "url": fmt.Sprintf("http://%s", backingChannelHostname), - }, - }, - } - } else { - unstructured.Object = map[string]interface{}{ - "apiVersion": "messaging.knative.dev/v1alpha1", - "kind": "InMemoryChannel", - "metadata": map[string]interface{}{ - "creationTimestamp": nil, - "namespace": namespace, - "name": name, - "ownerReferences": []interface{}{ - map[string]interface{}{ - "apiVersion": "messaging.knative.dev/v1alpha1", - "blockOwnerDeletion": true, - "controller": true, - "kind": "Channel", - "name": name, - "uid": "", - }, - }, + }, + } + if ready { + unstructured.Object["status"] = map[string]interface{}{ + "address": map[string]interface{}{ + "hostname": backingChannelHostname, + "url": fmt.Sprintf("http://%s", backingChannelHostname), }, } } return unstructured } -//func createTriggerChannelCRDRef() *corev1.ObjectReference { -// return &corev1.ObjectReference{ -// APIVersion: "messaging.knative.dev/v1alpha1", -// Kind: "InMemoryChannel", -// Namespace: testNS, -// Name: fmt.Sprintf("%s-kn-trigger", brokerName), -// } -//} -// -//func createIngressChannelCRDRef() *corev1.ObjectReference { -// return &corev1.ObjectReference{ -// APIVersion: "messaging.knative.dev/v1alpha1", -// Kind: "InMemoryChannel", -// Namespace: testNS, -// Name: fmt.Sprintf("%s-kn-ingress", brokerName), -// } -//} +func backingChannelObjRef() *corev1.ObjectReference { + return &corev1.ObjectReference{ + APIVersion: "messaging.knative.dev/v1alpha1", + Kind: "InMemoryChannel", + Namespace: testNS, + Name: channelName, + } +} diff --git a/pkg/reconciler/testing/messagingchannel.go b/pkg/reconciler/testing/messagingchannel.go index fc9f98e7321..d93f04d44df 100644 --- a/pkg/reconciler/testing/messagingchannel.go +++ b/pkg/reconciler/testing/messagingchannel.go @@ -18,6 +18,7 @@ package testing import ( "context" + "k8s.io/api/core/v1" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,10 +32,10 @@ import ( // TODO once we remove Channel from eventing, we should rename this to be just Channel. -// ChannelOption enables further configuration of a Channel. +// MessagingChannelOption enables further configuration of a Channel. type MessagingChannelOption func(*v1alpha1.Channel) -// NewMessagingChannel creates a Channel with ChannelOptions +// NewMessagingChannel creates a Channel with MessagingChannelOptions func NewMessagingChannel(name, namespace string, o ...MessagingChannelOption) *v1alpha1.Channel { c := &v1alpha1.Channel{ TypeMeta: metav1.TypeMeta{ @@ -54,8 +55,8 @@ func NewMessagingChannel(name, namespace string, o ...MessagingChannelOption) *v } // WithInitMessagingChannelConditions initializes the Channel's conditions. -func WithInitMessagingChannelConditions(s *v1alpha1.Channel) { - s.Status.InitializeConditions() +func WithInitMessagingChannelConditions(c *v1alpha1.Channel) { + c.Status.InitializeConditions() } func WithMessagingChannelDeleted(c *v1alpha1.Channel) { @@ -71,13 +72,22 @@ func WithMessagingChannelTemplate(typeMeta metav1.TypeMeta) MessagingChannelOpti } } -// WithBackingChannelFailed calls .Status.MarkBackingChannelFailed on the Broker. func WithBackingChannelFailed(reason, msg string) MessagingChannelOption { return func(c *v1alpha1.Channel) { c.Status.MarkBackingChannelFailed(reason, msg) } } +func WithBackingChannelReady(c *v1alpha1.Channel) { + c.Status.MarkBackingChannelReady() +} + +func WithBackingChannelObjRef(objRef *v1.ObjectReference) MessagingChannelOption { + return func(c *v1alpha1.Channel) { + c.Status.Channel = objRef + } +} + func WithMessagingChannelAddress(hostname string) MessagingChannelOption { return func(c *v1alpha1.Channel) { address := &duck.Addressable{ @@ -92,20 +102,6 @@ func WithMessagingChannelAddress(hostname string) MessagingChannelOption { } } -func WithMessagingChannelReady(c *v1alpha1.Channel) { - cs := v1alpha1.ChannelStatus{} - cs.MarkBackingChannelReady() - cs.SetAddress(&duck.Addressable{ - Addressable: v1beta1.Addressable{ - URL: &apis.URL{ - Scheme: "http", - Host: "foo", - }, - }, - }) - c.Status = cs -} - func WithMessagingChannelSubscribers(subscribers []eventingduckv1alpha1.SubscriberSpec) MessagingChannelOption { return func(c *v1alpha1.Channel) { c.Spec.Subscribable = &eventingduckv1alpha1.Subscribable{ @@ -113,3 +109,11 @@ func WithMessagingChannelSubscribers(subscribers []eventingduckv1alpha1.Subscrib } } } + +func WithMesssagingChannelSubscriberStatuses(subscriberStatuses []eventingduckv1alpha1.SubscriberStatus) MessagingChannelOption { + return func(c *v1alpha1.Channel) { + c.Status.SubscribableStatus = &eventingduckv1alpha1.SubscribableStatus{ + Subscribers: subscriberStatuses, + } + } +} From b44c8ddef226475055b49d48747999b761c60a40 Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 13:18:50 -0700 Subject: [PATCH 31/39] eventingchannel package --- cmd/controller/main.go | 4 ++-- .../{channeleventing => eventingchannel}/channel.go | 2 +- .../{channeleventing => eventingchannel}/channel_test.go | 2 +- .../{channeleventing => eventingchannel}/controller.go | 2 +- .../{channeleventing => eventingchannel}/controller_test.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename pkg/reconciler/{channeleventing => eventingchannel}/channel.go (99%) rename pkg/reconciler/{channeleventing => eventingchannel}/channel_test.go (99%) rename pkg/reconciler/{channeleventing => eventingchannel}/controller.go (98%) rename pkg/reconciler/{channeleventing => eventingchannel}/controller_test.go (97%) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 6aed771b2f2..40c6898a912 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -24,7 +24,7 @@ import ( "github.com/knative/eventing/pkg/reconciler/broker" "github.com/knative/eventing/pkg/reconciler/channel" - "github.com/knative/eventing/pkg/reconciler/channeleventing" + "github.com/knative/eventing/pkg/reconciler/eventingchannel" "github.com/knative/eventing/pkg/reconciler/eventtype" "github.com/knative/eventing/pkg/reconciler/namespace" "github.com/knative/eventing/pkg/reconciler/sequence" @@ -36,7 +36,7 @@ func main() { sharedmain.Main("controller", subscription.NewController, namespace.NewController, - channeleventing.NewController, + eventingchannel.NewController, channel.NewController, trigger.NewController, broker.NewController, diff --git a/pkg/reconciler/channeleventing/channel.go b/pkg/reconciler/eventingchannel/channel.go similarity index 99% rename from pkg/reconciler/channeleventing/channel.go rename to pkg/reconciler/eventingchannel/channel.go index 13442d60e6a..150eadd6c38 100644 --- a/pkg/reconciler/channeleventing/channel.go +++ b/pkg/reconciler/eventingchannel/channel.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package channeleventing +package eventingchannel import ( "context" diff --git a/pkg/reconciler/channeleventing/channel_test.go b/pkg/reconciler/eventingchannel/channel_test.go similarity index 99% rename from pkg/reconciler/channeleventing/channel_test.go rename to pkg/reconciler/eventingchannel/channel_test.go index b7527d0af5e..bb798f048ce 100644 --- a/pkg/reconciler/channeleventing/channel_test.go +++ b/pkg/reconciler/eventingchannel/channel_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package channeleventing +package eventingchannel import ( "context" diff --git a/pkg/reconciler/channeleventing/controller.go b/pkg/reconciler/eventingchannel/controller.go similarity index 98% rename from pkg/reconciler/channeleventing/controller.go rename to pkg/reconciler/eventingchannel/controller.go index baee4bb5ddc..38ca553c596 100644 --- a/pkg/reconciler/channeleventing/controller.go +++ b/pkg/reconciler/eventingchannel/controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package channeleventing +package eventingchannel import ( "context" diff --git a/pkg/reconciler/channeleventing/controller_test.go b/pkg/reconciler/eventingchannel/controller_test.go similarity index 97% rename from pkg/reconciler/channeleventing/controller_test.go rename to pkg/reconciler/eventingchannel/controller_test.go index 35b7017abb7..15338b3e847 100644 --- a/pkg/reconciler/channeleventing/controller_test.go +++ b/pkg/reconciler/eventingchannel/controller_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package channeleventing +package eventingchannel import ( "testing" From 7c57d389448a5a4869a8d48ecb9a2af628020c33 Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 13:24:06 -0700 Subject: [PATCH 32/39] cosmetic --- test/e2e/sequence_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/sequence_test.go b/test/e2e/sequence_test.go index a3a1590dd88..c3bc4e5fc7c 100644 --- a/test/e2e/sequence_test.go +++ b/test/e2e/sequence_test.go @@ -22,8 +22,8 @@ import ( "fmt" "testing" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" "github.com/knative/eventing/test/base/resources" "github.com/knative/eventing/test/common" "k8s.io/apimachinery/pkg/util/uuid" @@ -74,7 +74,7 @@ func TestSequence(t *testing.T) { } // create channelTemplate for the Sequence - channelTemplate := messagingv1alpha1.ChannelTemplateSpec{ + channelTemplate := eventingduckv1alpha1.ChannelTemplateSpec{ TypeMeta: *(channelTypeMeta), } From 376a5b5d3b36fac3124989f20bc06d2b9d45a319 Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 15:00:37 -0700 Subject: [PATCH 33/39] cosmetics --- pkg/apis/eventing/v1alpha1/broker_types.go | 3 ++- pkg/apis/eventing/v1alpha1/broker_validation.go | 2 +- pkg/apis/messaging/v1alpha1/channel_types.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/broker_types.go b/pkg/apis/eventing/v1alpha1/broker_types.go index ddfe46e3959..eed2778722d 100644 --- a/pkg/apis/eventing/v1alpha1/broker_types.go +++ b/pkg/apis/eventing/v1alpha1/broker_types.go @@ -72,7 +72,8 @@ type BrokerSpec struct { DeprecatedChannelTemplate *ChannelSpec `json:"channelTemplate,omitempty"` // ChannelTemplate specifies which Channel CRD to use to create all the Channels used internally by the - // Broker. If left unspecified, the default Channel CRD for the namespace will be used. + // Broker. If left unspecified, it is set to the default Channel CRD for the namespace (or cluster, in case there + // are no defaults for the namespace). // +optional ChannelTemplate *eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplateSpec,omitempty"` } diff --git a/pkg/apis/eventing/v1alpha1/broker_validation.go b/pkg/apis/eventing/v1alpha1/broker_validation.go index 721e4ae4c84..2dfcf7a5b8e 100644 --- a/pkg/apis/eventing/v1alpha1/broker_validation.go +++ b/pkg/apis/eventing/v1alpha1/broker_validation.go @@ -52,10 +52,10 @@ func (bs *BrokerSpec) Validate(ctx context.Context) *apis.FieldError { } func isValidDeprecatedChannelTemplate(dct *ChannelSpec) *apis.FieldError { - var errs *apis.FieldError if dct == nil { return nil } + var errs *apis.FieldError if dct.DeprecatedGeneration != 0 { errs = errs.Also(apis.ErrDisallowedFields("deprecatedGeneration")) } diff --git a/pkg/apis/messaging/v1alpha1/channel_types.go b/pkg/apis/messaging/v1alpha1/channel_types.go index dfe3d53ed58..8e83ed3788a 100644 --- a/pkg/apis/messaging/v1alpha1/channel_types.go +++ b/pkg/apis/messaging/v1alpha1/channel_types.go @@ -80,7 +80,7 @@ type ChannelStatus struct { // Subscribers is populated with the statuses of each of the Channelable's subscribers. eventingduck.SubscribableTypeStatus `json:",inline"` - // Channel is an ObjectReference to the object for the Channel CRD. + // Channel is an ObjectReference to the Channel CRD backing this Channel. Channel *corev1.ObjectReference `json:"channel,omitempty"` } From c018c2dbf06097a17d5e40d14fb43cf5cdd7abb8 Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 15:22:12 -0700 Subject: [PATCH 34/39] compilation, my bad... --- test/base/resources/eventing.go | 3 ++- test/base/resources/messaging.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/base/resources/eventing.go b/test/base/resources/eventing.go index c70f7b8224f..d67f9c61713 100644 --- a/test/base/resources/eventing.go +++ b/test/base/resources/eventing.go @@ -19,6 +19,7 @@ package resources // This file contains functions that construct Eventing resources. import ( + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -114,7 +115,7 @@ func WithDeprecatedChannelTemplateForBroker(provisionerName string) BrokerOption // WithChannelTemplateForBroker returns a function that adds a ChannelTemplate for the given Broker. func WithChannelTemplateForBroker(channelTypeMeta metav1.TypeMeta) BrokerOption { return func(b *eventingv1alpha1.Broker) { - channelTemplate := eventingv1alpha1.ChannelTemplateSpec{ + channelTemplate := &eventingduckv1alpha1.ChannelTemplateSpec{ TypeMeta: channelTypeMeta, } b.Spec.ChannelTemplate = channelTemplate diff --git a/test/base/resources/messaging.go b/test/base/resources/messaging.go index 57c764597b3..2f8dfaa87d5 100644 --- a/test/base/resources/messaging.go +++ b/test/base/resources/messaging.go @@ -21,6 +21,7 @@ package resources import ( kafkamessagingv1alpha1 "github.com/knative/eventing/contrib/kafka/pkg/apis/messaging/v1alpha1" natssmessagingv1alpha1 "github.com/knative/eventing/contrib/natss/pkg/apis/messaging/v1alpha1" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -70,7 +71,7 @@ func WithReplyForSequence(name string, typemeta *metav1.TypeMeta) SequenceOption func Sequence( name string, steps []eventingv1alpha1.SubscriberSpec, - channelTemplate messagingv1alpha1.ChannelTemplateSpec, + channelTemplate eventingduckv1alpha1.ChannelTemplateSpec, options ...SequenceOption, ) *messagingv1alpha1.Sequence { sequence := &messagingv1alpha1.Sequence{ From 025462e53d9b3678149cd3aa6362393b0c1cb3fb Mon Sep 17 00:00:00 2001 From: nachocano Date: Wed, 24 Jul 2019 15:29:47 -0700 Subject: [PATCH 35/39] hopefully the last one --- test/common/creation.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/common/creation.go b/test/common/creation.go index 4147a5f788d..b7080b97dd6 100644 --- a/test/common/creation.go +++ b/test/common/creation.go @@ -17,8 +17,8 @@ limitations under the License. package common import ( + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - messagingv1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1" sourcesv1alpha1 "github.com/knative/eventing/pkg/apis/sources/v1alpha1" "github.com/knative/eventing/test/base/resources" corev1 "k8s.io/api/core/v1" @@ -153,7 +153,7 @@ func (client *Client) CreateTriggerOrFail(name string, options ...resources.Trig func (client *Client) CreateSequenceOrFail( name string, steps []eventingv1alpha1.SubscriberSpec, - channelTemplate messagingv1alpha1.ChannelTemplateSpec, + channelTemplate eventingduckv1alpha1.ChannelTemplateSpec, options ...resources.SequenceOption, ) { namespace := client.Namespace From 678e589288d88e8335db4d1fcccde085d1dbdca5 Mon Sep 17 00:00:00 2001 From: nachocano Date: Thu, 25 Jul 2019 10:27:09 -0700 Subject: [PATCH 36/39] Ville's comments --- config/300-channel-eventing.yaml | 6 +++++- pkg/apis/duck/v1alpha1/channel_defaulter.go | 3 ++- pkg/apis/messaging/v1alpha1/channel_defaults_test.go | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/config/300-channel-eventing.yaml b/config/300-channel-eventing.yaml index 9bbcc3ae485..00a97f304ba 100644 --- a/config/300-channel-eventing.yaml +++ b/config/300-channel-eventing.yaml @@ -1,4 +1,4 @@ -# Copyright 2018 The Knative Authors +# Copyright 2019 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. @@ -11,6 +11,10 @@ # 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. + +# TODO This is the provisioner-based Channel object, which is being deprecated in 0.8 and removed in 0.9. +# See https://github.com/knative/eventing/issues/1561 + apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: diff --git a/pkg/apis/duck/v1alpha1/channel_defaulter.go b/pkg/apis/duck/v1alpha1/channel_defaulter.go index 3901b496e54..d11c262a405 100644 --- a/pkg/apis/duck/v1alpha1/channel_defaulter.go +++ b/pkg/apis/duck/v1alpha1/channel_defaulter.go @@ -20,7 +20,8 @@ package v1alpha1 // Context: there is a cyclic dependency between eventing and messaging if we place this in messaging. Broker needs to // depend on this, which is fine. But the problem arises due to messaging depending on eventing, mainly on // Subscription-related objects for the Sequence type. We should first move Subscription down to messaging and then we -// can move this down. +// can move this down. See https://github.com/knative/eventing/issues/1562. +// // ChannelDefaulter sets the default Channel CRD and Arguments on Channels that do not // specify any implementation. diff --git a/pkg/apis/messaging/v1alpha1/channel_defaults_test.go b/pkg/apis/messaging/v1alpha1/channel_defaults_test.go index 581a07bd27a..4494fe1a4d7 100644 --- a/pkg/apis/messaging/v1alpha1/channel_defaults_test.go +++ b/pkg/apis/messaging/v1alpha1/channel_defaults_test.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Knative Authors +Copyright 2019 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. From 356ca7c1161efd3c5e445258d1ae5d6449609e22 Mon Sep 17 00:00:00 2001 From: nachocano Date: Thu, 25 Jul 2019 11:42:40 -0700 Subject: [PATCH 37/39] Adam's comments, plus cosmetic tests to try to make coverage pass. --- config/300-channel.yaml | 4 +-- config/400-default-ch-config.yaml | 4 +-- .../duck/v1alpha1/channel_template_types.go | 2 +- pkg/apis/eventing/v1alpha1/broker_types.go | 2 +- pkg/apis/messaging/v1alpha1/channel_types.go | 3 ++- .../messaging/v1alpha1/channel_types_test.go | 27 +++++++++++++++++++ .../messaging/v1alpha1/channel_validation.go | 1 + .../v1alpha1/in_memory_channel_types_test.go | 27 +++++++++++++++++++ .../messaging/v1alpha1/sequence_types_test.go | 27 +++++++++++++++++++ pkg/defaultchannel/channel_defaulter.go | 2 +- pkg/reconciler/channel/channel.go | 23 ++++++---------- pkg/reconciler/channel/channel_test.go | 4 +-- 12 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 pkg/apis/messaging/v1alpha1/channel_types_test.go create mode 100644 pkg/apis/messaging/v1alpha1/in_memory_channel_types_test.go create mode 100644 pkg/apis/messaging/v1alpha1/sequence_types_test.go diff --git a/config/300-channel.yaml b/config/300-channel.yaml index e01862da38a..1af4dbf8520 100644 --- a/config/300-channel.yaml +++ b/config/300-channel.yaml @@ -47,9 +47,9 @@ spec: - name: Reason type: string JSONPath: ".status.conditions[?(@.type==\"Ready\")].reason" - - name: Hostname + - name: URL type: string - JSONPath: .status.address.hostname + JSONPath: .status.address.url - name: Age type: date JSONPath: .metadata.creationTimestamp diff --git a/config/400-default-ch-config.yaml b/config/400-default-ch-config.yaml index a8c9374d03b..34bb2d2035e 100644 --- a/config/400-default-ch-config.yaml +++ b/config/400-default-ch-config.yaml @@ -20,10 +20,10 @@ metadata: data: # Configuration for defaulting channels that do not specify CRD implementations. default-ch-config: | - clusterdefault: + clusterDefault: apiVersion: messaging.knative.dev/v1alpha1 kind: InMemoryChannel - namespacedefaults: + namespaceDefaults: some-namespace: apiVersion: messaging.knative.dev/v1alpha1 kind: InMemoryChannel diff --git a/pkg/apis/duck/v1alpha1/channel_template_types.go b/pkg/apis/duck/v1alpha1/channel_template_types.go index 80f0b09961e..819127d7048 100644 --- a/pkg/apis/duck/v1alpha1/channel_template_types.go +++ b/pkg/apis/duck/v1alpha1/channel_template_types.go @@ -29,7 +29,7 @@ type ChannelTemplateSpec struct { Spec *runtime.RawExtension `json:"spec,omitempty"` } -// Internal version of ChannelTemplateSpec that includes ObjectMeta so that +// ChannelTemplateSpecInternal is an internal only version that includes ObjectMeta so that // we can easily create new Channels off of it. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type ChannelTemplateSpecInternal struct { diff --git a/pkg/apis/eventing/v1alpha1/broker_types.go b/pkg/apis/eventing/v1alpha1/broker_types.go index eed2778722d..224b2971a82 100644 --- a/pkg/apis/eventing/v1alpha1/broker_types.go +++ b/pkg/apis/eventing/v1alpha1/broker_types.go @@ -66,7 +66,7 @@ var ( type BrokerSpec struct { // DeprecatedChannelTemplate, if specified will be used to create all the Channels used internally by the // Broker. Only Provisioner and Arguments may be specified. If left unspecified, the default - // Channel CRD for the namespace will be used using the new ChannelTemplate attribute. + // Channel CRD for the namespace will be used using the channelTemplateSpec attribute. // // +optional DeprecatedChannelTemplate *ChannelSpec `json:"channelTemplate,omitempty"` diff --git a/pkg/apis/messaging/v1alpha1/channel_types.go b/pkg/apis/messaging/v1alpha1/channel_types.go index 8e83ed3788a..2ece25b0ea7 100644 --- a/pkg/apis/messaging/v1alpha1/channel_types.go +++ b/pkg/apis/messaging/v1alpha1/channel_types.go @@ -31,7 +31,7 @@ import ( // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// Channel is a resource representing a Channel. +// Channel represents a generic Channel. It is normally used when we want a Channel, but don't need a specific Channel implementation. type Channel struct { metav1.TypeMeta `json:",inline"` // +optional @@ -57,6 +57,7 @@ var _ webhook.GenericCRD = (*Channel)(nil) type ChannelSpec struct { // ChannelTemplate specifies which Channel CRD to use to create the CRD Channel backing this Channel. + // This is immutable after creation. Normally this is set by the Channel defaulter, not directly by the user. ChannelTemplate *eventingduck.ChannelTemplateSpec `json:"channelTemplate"` // Channel conforms to Duck type Subscribable. diff --git a/pkg/apis/messaging/v1alpha1/channel_types_test.go b/pkg/apis/messaging/v1alpha1/channel_types_test.go new file mode 100644 index 00000000000..075b34691fd --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/channel_types_test.go @@ -0,0 +1,27 @@ +/* + * Copyright 2019 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 v1alpha1 + +import "testing" + +func TestChannel_GetGroupVersionKind(t *testing.T) { + c := Channel{} + gvk := c.GetGroupVersionKind() + if gvk.Kind != "Channel" { + t.Errorf("Should be Channel.") + } +} diff --git a/pkg/apis/messaging/v1alpha1/channel_validation.go b/pkg/apis/messaging/v1alpha1/channel_validation.go index adaa2a2f097..22bcd063dc6 100644 --- a/pkg/apis/messaging/v1alpha1/channel_validation.go +++ b/pkg/apis/messaging/v1alpha1/channel_validation.go @@ -34,6 +34,7 @@ func (cs *ChannelSpec) Validate(ctx context.Context) *apis.FieldError { var errs *apis.FieldError if cs.ChannelTemplate == nil { + // The Channel defaulter is expected to set this, not the users. errs = errs.Also(apis.ErrMissingField("channelTemplate")) } else { if cte := isValidChannelTemplate(cs.ChannelTemplate); cte != nil { diff --git a/pkg/apis/messaging/v1alpha1/in_memory_channel_types_test.go b/pkg/apis/messaging/v1alpha1/in_memory_channel_types_test.go new file mode 100644 index 00000000000..84e5f81e951 --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/in_memory_channel_types_test.go @@ -0,0 +1,27 @@ +/* + * Copyright 2019 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 v1alpha1 + +import "testing" + +func TestInMemoryChannel_GetGroupVersionKind(t *testing.T) { + imc := InMemoryChannel{} + gvk := imc.GetGroupVersionKind() + if gvk.Kind != "InMemoryChannel" { + t.Errorf("Should be InMemoryChannel.") + } +} diff --git a/pkg/apis/messaging/v1alpha1/sequence_types_test.go b/pkg/apis/messaging/v1alpha1/sequence_types_test.go new file mode 100644 index 00000000000..ed14923857c --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/sequence_types_test.go @@ -0,0 +1,27 @@ +/* + * Copyright 2019 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 v1alpha1 + +import "testing" + +func TestSequence_GetGroupVersionKind(t *testing.T) { + s := Sequence{} + gvk := s.GetGroupVersionKind() + if gvk.Kind != "Sequence" { + t.Errorf("Should be Sequence.") + } +} diff --git a/pkg/defaultchannel/channel_defaulter.go b/pkg/defaultchannel/channel_defaulter.go index 5f97d4c8cfd..c84f3e0d611 100644 --- a/pkg/defaultchannel/channel_defaulter.go +++ b/pkg/defaultchannel/channel_defaulter.go @@ -128,7 +128,7 @@ func (cd *ChannelDefaulter) GetDefault(namespace string) *eventingduckv1alpha1.C return nil } channelTemplate := getDefaultChannelTemplate(config, namespace) - cd.logger.Info("Defaulting the Channel", zap.Any("defaultChannelTemplate", channelTemplate)) + cd.logger.Debug("Defaulting the Channel", zap.Any("defaultChannelTemplate", channelTemplate)) return channelTemplate } diff --git a/pkg/reconciler/channel/channel.go b/pkg/reconciler/channel/channel.go index ecc35371f07..da0d5dab143 100644 --- a/pkg/reconciler/channel/channel.go +++ b/pkg/reconciler/channel/channel.go @@ -129,15 +129,13 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { backingChannel, err := r.reconcileBackingChannel(ctx, channelResourceInterface, c) if err != nil { - logging.FromContext(ctx).Error("Problem reconciling the backing channel", zap.Error(err)) c.Status.MarkBackingChannelFailed("ChannelFailure", "%v", err) - return err + return fmt.Errorf("problem reconciling the backing channel: %v", err) } // Start tracking the backing Channel CRD... if err = track(utils.ObjectRef(backingChannel, backingChannel.GroupVersionKind())); err != nil { - logging.FromContext(ctx).Error("Unable to track changes to Channel", zap.Error(err)) - return err + return fmt.Errorf("unable to track changes to the backing channel: %v", err) } c.Status.Channel = &corev1.ObjectReference{ @@ -149,9 +147,8 @@ func (r *Reconciler) reconcile(ctx context.Context, c *v1alpha1.Channel) error { err = r.patchBackingChannelSubscriptions(ctx, channelResourceInterface, c, backingChannel) if err != nil { - logging.FromContext(ctx).Error("Problem patching subscriptions in the backing channel", zap.Error(err)) c.Status.MarkBackingChannelFailed("ChannelFailure", "%v", err) - return err + return fmt.Errorf("problem patching subscriptions in the backing channel: %v", err) } c.Status.PropagateStatuses(&backingChannel.Status) @@ -191,7 +188,6 @@ func (r *Reconciler) updateStatus(ctx context.Context, desired *v1alpha1.Channel // reconcileBackingChannel reconciles Channel's 'c' underlying CRD channel. func (r *Reconciler) reconcileBackingChannel(ctx context.Context, resourceClient dynamic.ResourceInterface, c *v1alpha1.Channel) (*duckv1alpha1.Channelable, error) { - channel, err := resourceClient.Get(c.Name, metav1.GetOptions{}) channelable := &duckv1alpha1.Channelable{} @@ -203,24 +199,21 @@ func (r *Reconciler) reconcileBackingChannel(ctx context.Context, resourceClient logging.FromContext(ctx).Error("Failed to create Channel from ChannelTemplate", zap.Any("channelTemplate", c.Spec.ChannelTemplate), zap.Error(err)) return nil, err } - logging.FromContext(ctx).Debug(fmt.Sprintf("Creating Channel CRD Object: %+v", newChannel)) channel, err = resourceClient.Create(newChannel, metav1.CreateOptions{}) if err != nil { - logging.FromContext(ctx).Error(fmt.Sprintf("Failed to create Channel: %s/%s", c.Namespace, c.Name), zap.Error(err)) + logging.FromContext(ctx).Error("Failed to create Channel", zap.Any("channel", newChannel), zap.Error(err)) return nil, err } - logging.FromContext(ctx).Info(fmt.Sprintf("Created Channel: %s/%s", c.Namespace, c.Name), zap.Any("channel", newChannel)) + logging.FromContext(ctx).Info("Created Channel", zap.Any("channel", newChannel)) } else { logging.FromContext(ctx).Error(fmt.Sprintf("Failed to get Channel: %s/%s", c.Namespace, c.Name), zap.Error(err)) return nil, err } - } else { - logging.FromContext(ctx).Debug(fmt.Sprintf("Found Channel: %s/%s", c.Namespace, c.Name), zap.Any("channel", c)) } - + logging.FromContext(ctx).Debug("Found Channel", zap.Any("channel", c)) err = duckapis.FromUnstructured(channel, channelable) if err != nil { - logging.FromContext(ctx).Error(fmt.Sprintf("Failed to convert to Channelable Object: %s/%s", c.Namespace, c.Name), zap.Error(err)) + logging.FromContext(ctx).Error("Failed to convert to Channelable Object", zap.Error(err), zap.Any("channel", channel)) return nil, err } return channelable, nil @@ -228,7 +221,7 @@ func (r *Reconciler) reconcileBackingChannel(ctx context.Context, resourceClient func (r *Reconciler) patchBackingChannelSubscriptions(ctx context.Context, resourceClient dynamic.ResourceInterface, channel *v1alpha1.Channel, backingChannel *duckv1alpha1.Channelable) error { if equality.Semantic.DeepEqual(channel.Spec.Subscribable, backingChannel.Spec.Subscribable) { - logging.FromContext(ctx).Info("Subscribable in sync, no need to patch") + logging.FromContext(ctx).Debug("Subscribable in sync, no need to patch") return nil } diff --git a/pkg/reconciler/channel/channel_test.go b/pkg/reconciler/channel/channel_test.go index 1d8bbd945cd..fac91e637f4 100644 --- a/pkg/reconciler/channel/channel_test.go +++ b/pkg/reconciler/channel/channel_test.go @@ -129,7 +129,7 @@ func TestReconcile(t *testing.T) { InduceFailure("create", "inmemorychannels"), }, WantEvents: []string{ - Eventf(corev1.EventTypeWarning, channelReconcileError, "Channel reconcile error: %v", "inducing failure for create inmemorychannels"), + Eventf(corev1.EventTypeWarning, channelReconcileError, "Channel reconcile error: problem reconciling the backing channel: %v", "inducing failure for create inmemorychannels"), }, WantErr: true, }, @@ -159,7 +159,7 @@ func TestReconcile(t *testing.T) { WithBackingChannelFailed("ChannelFailure", "inducing failure for patch inmemorychannels")), }}, WantEvents: []string{ - Eventf(corev1.EventTypeWarning, channelReconcileError, "Channel reconcile error: %v", "inducing failure for patch inmemorychannels"), + Eventf(corev1.EventTypeWarning, channelReconcileError, "Channel reconcile error: problem patching subscriptions in the backing channel: %v", "inducing failure for patch inmemorychannels"), }, WantErr: true, }, From 0ee86796850c2d11dd3a4dbcae2ff38bd3c7fd05 Mon Sep 17 00:00:00 2001 From: nachocano Date: Thu, 25 Jul 2019 14:45:42 -0700 Subject: [PATCH 38/39] sequence defaults --- .../messaging/v1alpha1/sequence_defaults.go | 17 ++- .../v1alpha1/sequence_defaults_test.go | 105 ++++++++++++++++++ pkg/apis/messaging/v1alpha1/sequence_types.go | 6 +- .../messaging/v1alpha1/sequence_validation.go | 4 +- .../v1alpha1/sequence_validation_test.go | 6 +- .../v1alpha1/zz_generated.deepcopy.go | 6 +- pkg/reconciler/sequence/sequence_test.go | 2 +- pkg/reconciler/testing/sequence.go | 2 +- 8 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 pkg/apis/messaging/v1alpha1/sequence_defaults_test.go diff --git a/pkg/apis/messaging/v1alpha1/sequence_defaults.go b/pkg/apis/messaging/v1alpha1/sequence_defaults.go index 79c0cbe4f4a..a33cfde938e 100644 --- a/pkg/apis/messaging/v1alpha1/sequence_defaults.go +++ b/pkg/apis/messaging/v1alpha1/sequence_defaults.go @@ -16,12 +16,21 @@ limitations under the License. package v1alpha1 -import "context" +import ( + "context" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" +) func (s *Sequence) SetDefaults(ctx context.Context) { + if s != nil && s.Spec.ChannelTemplate == nil { + // The singleton may not have been set, if so ignore it and validation will reject the + // Channel. + if cd := eventingduckv1alpha1.ChannelDefaulterSingleton; cd != nil { + channelTemplate := cd.GetDefault(s.Namespace) + s.Spec.ChannelTemplate = channelTemplate + } + } s.Spec.SetDefaults(ctx) } -func (ss *SequenceSpec) SetDefaults(ctx context.Context) { - // TODO anything? -} +func (ss *SequenceSpec) SetDefaults(ctx context.Context) {} diff --git a/pkg/apis/messaging/v1alpha1/sequence_defaults_test.go b/pkg/apis/messaging/v1alpha1/sequence_defaults_test.go new file mode 100644 index 00000000000..1eb820252e3 --- /dev/null +++ b/pkg/apis/messaging/v1alpha1/sequence_defaults_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2019 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 v1alpha1 + +import ( + "context" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" + + "github.com/google/go-cmp/cmp" + eventingduckv1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" +) + +var ( + defaultTemplate = &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "InMemoryChannel", + }, + } +) + +func TestSequenceSetDefaults(t *testing.T) { + testCases := map[string]struct { + nilChannelDefaulter bool + channelTemplate *eventingduckv1alpha1.ChannelTemplateSpec + initial Sequence + expected Sequence + }{ + "nil ChannelDefaulter": { + nilChannelDefaulter: true, + expected: Sequence{}, + }, + "unset ChannelDefaulter": { + expected: Sequence{}, + }, + "set ChannelDefaulter": { + channelTemplate: defaultChannelTemplate, + expected: Sequence{ + Spec: SequenceSpec{ + ChannelTemplate: defaultChannelTemplate, + }, + }, + }, + "template already specified": { + channelTemplate: defaultChannelTemplate, + initial: Sequence{ + Spec: SequenceSpec{ + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "OtherChannel", + }, + }, + }, + }, + expected: Sequence{ + Spec: SequenceSpec{ + ChannelTemplate: &eventingduckv1alpha1.ChannelTemplateSpec{ + TypeMeta: v1.TypeMeta{ + APIVersion: SchemeGroupVersion.String(), + Kind: "OtherChannel", + }, + }, + }, + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + if !tc.nilChannelDefaulter { + eventingduckv1alpha1.ChannelDefaulterSingleton = &sequenceChannelDefaulter{ + channelTemplate: tc.channelTemplate, + } + defer func() { eventingduckv1alpha1.ChannelDefaulterSingleton = nil }() + } + tc.initial.SetDefaults(context.TODO()) + if diff := cmp.Diff(tc.expected, tc.initial); diff != "" { + t.Fatalf("Unexpected defaults (-want, +got): %s", diff) + } + }) + } +} + +type sequenceChannelDefaulter struct { + channelTemplate *eventingduckv1alpha1.ChannelTemplateSpec +} + +func (cd *sequenceChannelDefaulter) GetDefault(_ string) *eventingduckv1alpha1.ChannelTemplateSpec { + return cd.channelTemplate +} diff --git a/pkg/apis/messaging/v1alpha1/sequence_types.go b/pkg/apis/messaging/v1alpha1/sequence_types.go index 6899e9d9a59..a0c193d91fa 100644 --- a/pkg/apis/messaging/v1alpha1/sequence_types.go +++ b/pkg/apis/messaging/v1alpha1/sequence_types.go @@ -61,8 +61,10 @@ type SequenceSpec struct { // provided. Steps []eventingv1alpha1.SubscriberSpec `json:"steps"` - // ChannelTemplate specifies which Channel CRD to use - ChannelTemplate eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplate"` + // ChannelTemplate specifies which Channel CRD to use. If left unspecified, it is set to the default Channel CRD + // for the namespace (or cluster, in case there are no defaults for the namespace). + // +optional + ChannelTemplate *eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplate,omitempty"` // Reply is a Reference to where the result of the last Subscriber gets sent to. // diff --git a/pkg/apis/messaging/v1alpha1/sequence_validation.go b/pkg/apis/messaging/v1alpha1/sequence_validation.go index 80095758113..a260300996f 100644 --- a/pkg/apis/messaging/v1alpha1/sequence_validation.go +++ b/pkg/apis/messaging/v1alpha1/sequence_validation.go @@ -18,9 +18,7 @@ package v1alpha1 import ( "context" - eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "k8s.io/apimachinery/pkg/api/equality" "knative.dev/pkg/apis" ) @@ -41,7 +39,7 @@ func (ps *SequenceSpec) Validate(ctx context.Context) *apis.FieldError { } } - if equality.Semantic.DeepEqual(ps.ChannelTemplate, eventingduck.ChannelTemplateSpec{}) { + if ps.ChannelTemplate == nil { errs = errs.Also(apis.ErrMissingField("channelTemplate")) return errs } diff --git a/pkg/apis/messaging/v1alpha1/sequence_validation_test.go b/pkg/apis/messaging/v1alpha1/sequence_validation_test.go index 49fb630710e..f624f4bc0ef 100644 --- a/pkg/apis/messaging/v1alpha1/sequence_validation_test.go +++ b/pkg/apis/messaging/v1alpha1/sequence_validation_test.go @@ -65,7 +65,7 @@ func makeInvalidReply(channelName string) *corev1.ObjectReference { func TestSequenceSpecValidation(t *testing.T) { subscriberURI := "http://example.com" - validChannelTemplate := eventingduck.ChannelTemplateSpec{ + validChannelTemplate := &eventingduck.ChannelTemplateSpec{ TypeMeta: metav1.TypeMeta{ Kind: "mykind", APIVersion: "myapiversion", @@ -104,7 +104,7 @@ func TestSequenceSpecValidation(t *testing.T) { }, { name: "invalid channeltemplatespec missing APIVersion", ts: &SequenceSpec{ - ChannelTemplate: eventingduck.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}, Spec: &runtime.RawExtension{}}, + ChannelTemplate: &eventingduck.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{Kind: "mykind"}, Spec: &runtime.RawExtension{}}, Steps: []eventingv1alpha1.SubscriberSpec{{URI: &subscriberURI}}, }, want: func() *apis.FieldError { @@ -114,7 +114,7 @@ func TestSequenceSpecValidation(t *testing.T) { }, { name: "invalid channeltemplatespec missing Kind", ts: &SequenceSpec{ - ChannelTemplate: eventingduck.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{APIVersion: "myapiversion"}, Spec: &runtime.RawExtension{}}, + ChannelTemplate: &eventingduck.ChannelTemplateSpec{TypeMeta: metav1.TypeMeta{APIVersion: "myapiversion"}, Spec: &runtime.RawExtension{}}, Steps: []eventingv1alpha1.SubscriberSpec{{URI: &subscriberURI}}, }, want: func() *apis.FieldError { diff --git a/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go index 876ea568f7c..5b9d0c92e26 100644 --- a/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/messaging/v1alpha1/zz_generated.deepcopy.go @@ -328,7 +328,11 @@ func (in *SequenceSpec) DeepCopyInto(out *SequenceSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.ChannelTemplate.DeepCopyInto(&out.ChannelTemplate) + if in.ChannelTemplate != nil { + in, out := &in.ChannelTemplate, &out.ChannelTemplate + *out = new(duckv1alpha1.ChannelTemplateSpec) + (*in).DeepCopyInto(*out) + } if in.Reply != nil { in, out := &in.Reply, &out.Reply *out = new(v1.ObjectReference) diff --git a/pkg/reconciler/sequence/sequence_test.go b/pkg/reconciler/sequence/sequence_test.go index a6790872f89..62c7a223bb6 100644 --- a/pkg/reconciler/sequence/sequence_test.go +++ b/pkg/reconciler/sequence/sequence_test.go @@ -106,7 +106,7 @@ func createSubscriber(stepNumber int) eventingv1alpha1.SubscriberSpec { func TestAllCases(t *testing.T) { pKey := testNS + "/" + sequenceName - imc := eventingduckv1alpha1.ChannelTemplateSpec{ + imc := &eventingduckv1alpha1.ChannelTemplateSpec{ TypeMeta: metav1.TypeMeta{ APIVersion: "messaging.knative.dev/v1alpha1", Kind: "inmemorychannel", diff --git a/pkg/reconciler/testing/sequence.go b/pkg/reconciler/testing/sequence.go index b8efa0f3aef..b9c33334a01 100644 --- a/pkg/reconciler/testing/sequence.go +++ b/pkg/reconciler/testing/sequence.go @@ -55,7 +55,7 @@ func WithSequenceDeleted(p *v1alpha1.Sequence) { p.ObjectMeta.SetDeletionTimestamp(&deleteTime) } -func WithSequenceChannelTemplateSpec(cts eventingduckv1alpha1.ChannelTemplateSpec) SequenceOption { +func WithSequenceChannelTemplateSpec(cts *eventingduckv1alpha1.ChannelTemplateSpec) SequenceOption { return func(p *v1alpha1.Sequence) { p.Spec.ChannelTemplate = cts } From 2afd344a035fc58ba456b8edcf37490a92a87270 Mon Sep 17 00:00:00 2001 From: nachocano Date: Thu, 25 Jul 2019 15:11:16 -0700 Subject: [PATCH 39/39] compiling tests... hopefully --- test/base/resources/messaging.go | 2 +- test/common/creation.go | 2 +- test/e2e/sequence_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/base/resources/messaging.go b/test/base/resources/messaging.go index 2f8dfaa87d5..d48ae948637 100644 --- a/test/base/resources/messaging.go +++ b/test/base/resources/messaging.go @@ -71,7 +71,7 @@ func WithReplyForSequence(name string, typemeta *metav1.TypeMeta) SequenceOption func Sequence( name string, steps []eventingv1alpha1.SubscriberSpec, - channelTemplate eventingduckv1alpha1.ChannelTemplateSpec, + channelTemplate *eventingduckv1alpha1.ChannelTemplateSpec, options ...SequenceOption, ) *messagingv1alpha1.Sequence { sequence := &messagingv1alpha1.Sequence{ diff --git a/test/common/creation.go b/test/common/creation.go index b7080b97dd6..85799ad9e34 100644 --- a/test/common/creation.go +++ b/test/common/creation.go @@ -153,7 +153,7 @@ func (client *Client) CreateTriggerOrFail(name string, options ...resources.Trig func (client *Client) CreateSequenceOrFail( name string, steps []eventingv1alpha1.SubscriberSpec, - channelTemplate eventingduckv1alpha1.ChannelTemplateSpec, + channelTemplate *eventingduckv1alpha1.ChannelTemplateSpec, options ...resources.SequenceOption, ) { namespace := client.Namespace diff --git a/test/e2e/sequence_test.go b/test/e2e/sequence_test.go index c3bc4e5fc7c..d6ba5269ce4 100644 --- a/test/e2e/sequence_test.go +++ b/test/e2e/sequence_test.go @@ -74,7 +74,7 @@ func TestSequence(t *testing.T) { } // create channelTemplate for the Sequence - channelTemplate := eventingduckv1alpha1.ChannelTemplateSpec{ + channelTemplate := &eventingduckv1alpha1.ChannelTemplateSpec{ TypeMeta: *(channelTypeMeta), }