From 8df08c4b2a0898cc468dfeeb218cbd49a81b2649 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 27 Sep 2018 17:03:57 -0700 Subject: [PATCH 01/22] Start on the new Stub bus controller and provisioner. --- pkg/buses/bus.go | 3 +- pkg/buses/eventing/bus2.yaml | 52 +++ pkg/buses/eventing/channel.yaml | 14 + .../eventing/cluster-bus-provisioner.yaml | 57 +++ pkg/buses/eventing/stub/channel/controller.go | 87 ++++ pkg/buses/eventing/stub/channel/reconciler.go | 435 ++++++++++++++++++ .../stub/clusterprovisioner/controller.go | 95 ++++ .../stub/clusterprovisioner/reconciler.go | 328 +++++++++++++ .../stub/clusterprovisioner/stub_test.go | 24 + pkg/buses/eventing/stub/dispatcher/main.go | 78 ++++ pkg/buses/eventing/system.yaml | 19 + pkg/buses/message_dispatcher.go | 55 ++- pkg/buses/message_dispatcher_test.go | 380 +++++++++++++++ pkg/buses/message_receiver.go | 6 +- .../configmaphandler/config_map_handler.go | 190 ++++++++ .../config_map_handler_test.go | 390 ++++++++++++++++ pkg/sidecar/fanout/fanout_handler.go | 192 ++++++++ pkg/sidecar/fanout/fanout_handler_test.go | 257 +++++++++++ .../multi_channel_fanout_handler.go | 112 +++++ .../multi_channel_fanout_handler_test.go | 339 ++++++++++++++ pkg/sidecar/swappable/swappable.go | 68 +++ pkg/sidecar/swappable/swappable_test.go | 136 ++++++ 22 files changed, 3288 insertions(+), 29 deletions(-) create mode 100644 pkg/buses/eventing/bus2.yaml create mode 100644 pkg/buses/eventing/channel.yaml create mode 100644 pkg/buses/eventing/cluster-bus-provisioner.yaml create mode 100644 pkg/buses/eventing/stub/channel/controller.go create mode 100644 pkg/buses/eventing/stub/channel/reconciler.go create mode 100644 pkg/buses/eventing/stub/clusterprovisioner/controller.go create mode 100644 pkg/buses/eventing/stub/clusterprovisioner/reconciler.go create mode 100644 pkg/buses/eventing/stub/clusterprovisioner/stub_test.go create mode 100644 pkg/buses/eventing/stub/dispatcher/main.go create mode 100644 pkg/buses/eventing/system.yaml create mode 100644 pkg/buses/message_dispatcher_test.go create mode 100644 pkg/sidecar/configmaphandler/config_map_handler.go create mode 100644 pkg/sidecar/configmaphandler/config_map_handler_test.go create mode 100644 pkg/sidecar/fanout/fanout_handler.go create mode 100644 pkg/sidecar/fanout/fanout_handler_test.go create mode 100644 pkg/sidecar/multichannelfanout/multi_channel_fanout_handler.go create mode 100644 pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go create mode 100644 pkg/sidecar/swappable/swappable.go create mode 100644 pkg/sidecar/swappable/swappable_test.go diff --git a/pkg/buses/bus.go b/pkg/buses/bus.go index ccc2596eec8..0ee44279a26 100644 --- a/pkg/buses/bus.go +++ b/pkg/buses/bus.go @@ -193,7 +193,6 @@ func (b *bus) dispatchMessage(subscription *channelsv1alpha1.Subscription, messa subscriber := subscription.Spec.Subscriber defaults := DispatchDefaults{ Namespace: subscription.Namespace, - ReplyTo: subscription.Spec.ReplyTo, } - return b.dispatcher.DispatchMessage(message, subscriber, defaults) + return b.dispatcher.DispatchMessage(message, subscriber, subscription.Spec.ReplyTo, defaults) } diff --git a/pkg/buses/eventing/bus2.yaml b/pkg/buses/eventing/bus2.yaml new file mode 100644 index 00000000000..d8db6f414dd --- /dev/null +++ b/pkg/buses/eventing/bus2.yaml @@ -0,0 +1,52 @@ +apiVersion: eventing.knative.dev/v1alpha1 +kind: ClusterProvisioner +metadata: + name: stub-bus-provisioner-dispatcher-foo +spec: + reconciles: + group: eventing.knative.dev/v1alpha1 + kind: Channel + +--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Channel +metadata: + name: foo-bus +spec: + provisioner: + ref: + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterProvisioner + name: stub-bus-provisioner-dispatcher + channelable: + subscribers: + - sinkableDomain: message-dumper-foo.default.svc.cluster.local # Already exists + callableDomain: event-changer.default.svc.cluster.local +--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: ClusterProvisioner +metadata: + name: stub-bus-provisioner-dispatcher-bar +spec: + reconciles: + group: eventing.knative.dev/v1alpha1 + kind: Channel + +--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Channel +metadata: + name: bar-bus +spec: + provisioner: + ref: + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterProvisioner + name: stub-bus-provisioner-dispatcher + channelable: + subscribers: + - sinkableDomain: message-dumper-bar.default.svc.cluster.local # Already exists + callableDomain: event-changer.default.svc.cluster.local diff --git a/pkg/buses/eventing/channel.yaml b/pkg/buses/eventing/channel.yaml new file mode 100644 index 00000000000..b66f8e10794 --- /dev/null +++ b/pkg/buses/eventing/channel.yaml @@ -0,0 +1,14 @@ +apiVersion: eventing.knative.dev/v1alpha1 +kind: Channel +metadata: + name: wacky-channel +spec: + provisioner: + ref: + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterProvisioner + name: stub-bus-provisioner-dispatcher + channelable: + subscribers: + - callableDomain: foo + sinkableDomain: bar diff --git a/pkg/buses/eventing/cluster-bus-provisioner.yaml b/pkg/buses/eventing/cluster-bus-provisioner.yaml new file mode 100644 index 00000000000..dba4c5a3323 --- /dev/null +++ b/pkg/buses/eventing/cluster-bus-provisioner.yaml @@ -0,0 +1,57 @@ +# 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. + +apiVersion: eventing.knative.dev/v1alpha1 +kind: ClusterProvisioner +metadata: + name: stub-bus-provisioner-dispatcher +spec: + reconciles: + group: eventing.knative.dev/v1alpha1 + kind: Channel + +--- + +apiVersion: apps/v1beta1 +kind: Deployment +metadata: + name: stub-bus-controller-dispatcher + namespace: &namespace knative-eventing +spec: + replicas: 1 + selector: + matchLabels: &labels + clusterProvisioner: stub-bus-provisioner-dispatcher + role: controller-dispatcher + template: + metadata: + annotations: + sidecar.istio.io/inject: "true" + labels: *labels + spec: + serviceAccountName: eventing-controller + containers: + - name: controller-dispatcher + image: github.com/knative/eventing/pkg/buses/eventing/stub/dispatcher + env: + - name: PORT + value: "8080" + - name: BUS_NAME + value: stub + - name: BUS_NAMESPACE + value: *namespace + args: + - -logtostderr + - -stderrthreshold + - INFO diff --git a/pkg/buses/eventing/stub/channel/controller.go b/pkg/buses/eventing/stub/channel/controller.go new file mode 100644 index 00000000000..bbb41c12d07 --- /dev/null +++ b/pkg/buses/eventing/stub/channel/controller.go @@ -0,0 +1,87 @@ +/* +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 channel + +import ( + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +const ( + // controllerAgentName is the string used by this controller to identify + // itself when creating events. + controllerAgentName = "stub-bus-cluster-provisioner-controller" +) + +// ProvideController returns a flow controller. +func ProvideController(mgr manager.Manager, logger *zap.Logger, cpRef *corev1.ObjectReference, syncHttpChannelConfig func() error) (*ConfigAndStopCh, error) { + logger = logger.With(zap.String("clusterProvisioner", cpRef.Name)) + + // Setup a new controller to Reconcile Channels that belong to this Cluster Provisioner (Stub + // buses). + r := &reconciler{ + recorder: mgr.GetRecorder(controllerAgentName), + logger: logger, + stubProvisioner: cpRef, + + syncHttpChannelConfig: syncHttpChannelConfig, + } + c, err := controller.New(controllerAgentName, mgr, controller.Options{ + Reconciler: r, + }) + if err != nil { + logger.Error("Unable to create controller.", zap.Error(err)) + return nil, err + } + + // Watch Channels. + err = c.Watch(&source.Kind{ + Type: &eventingv1alpha1.Channel{}, + }, &handler.EnqueueRequestForObject{}) + if err != nil { + logger.Error("Unable to watch Channels.", zap.Error(err), zap.Any("type", &eventingv1alpha1.Channel{})) + return nil, err + } + + // TODO: Should we watch the K8s service and Istio Virtual Service as well? If they change, we + // probably should change it back. + + return &ConfigAndStopCh{ + controller: c, + Config: r.getConfig, + StopCh: make(chan struct{}, 1), + }, nil +} + +type ConfigAndStopCh struct { + controller controller.Controller + Config func() multichannelfanout.Config + StopCh chan<- struct{} +} + +func (cas *ConfigAndStopCh) BackgroundStart() { + stopCh := make(chan struct{}, 1) + go func() { + cas.controller.Start(stopCh) + }() +} diff --git a/pkg/buses/eventing/stub/channel/reconciler.go b/pkg/buses/eventing/stub/channel/reconciler.go new file mode 100644 index 00000000000..14a74ce48be --- /dev/null +++ b/pkg/buses/eventing/stub/channel/reconciler.go @@ -0,0 +1,435 @@ +/* +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 channel + +import ( + "context" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/controller" + "github.com/knative/eventing/pkg/sidecar/fanout" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "github.com/knative/eventing/pkg/system" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +const ( + PortName = "http" + PortNumber = 80 +) + +type reconciler struct { + client client.Client + restConfig *rest.Config + recorder record.EventRecorder + + // This reconciler controls Channels provisioned by this stubProvisioner. + stubProvisioner *corev1.ObjectReference + + config multichannelfanout.Config + + syncHttpChannelConfig func() error + + logger *zap.Logger +} + +// Verify the struct implements reconcile.Reconciler +var _ reconcile.Reconciler = &reconciler{} + +func (r *reconciler) InjectClient(c client.Client) error { + r.client = c + return nil +} + +func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { + //TODO use this to store the logger and set a deadline + ctx := context.TODO() + logger := r.logger.With(zap.Any("request", request)) + + c := &eventingv1alpha1.Channel{} + err := r.client.Get(ctx, request.NamespacedName, c) + + // The Channel may have been deleted since it was added to the workqueue. If so, there is + // nothing to be done. + if errors.IsNotFound(err) { + logger.Info("Could not find Channel", zap.Error(err)) + return reconcile.Result{}, nil + } + + // Any other error should be retried in another reconciliation. + if err != nil { + logger.Error("Unable to Get Channel", zap.Error(err)) + return reconcile.Result{}, err + } + + // Does this Controller control this Channel? + if !r.shouldReconcile(c) { + logger.Info("Not reconciling Channel, it is not controlled by this Controller", zap.Any("stubProvisioner", r.stubProvisioner), zap.Any("ref", c.Spec.Provisioner.Ref)) + return reconcile.Result{}, nil + } + logger.Debug("Reconciling Channel") + + err = r.reconcile(ctx, c) + if err != nil { + logger.Info("Error reconciling Channel", zap.Error(err)) + // Note that we do not return the error here, because we want to update the Status + // regardless of the error. + } + + updateStatusErr := r.updateChannelStatus(ctx, c) + if updateStatusErr != nil { + logger.Info("Error updating Channel Status", zap.Error(updateStatusErr)) + return reconcile.Result{}, updateStatusErr + } + + return reconcile.Result{}, err +} + +// shouldReconcile determines if this Controller should control (and therefore reconcile) a given +// ClusterProvisioner. This Controller only handles Stub buses. +func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { + return equality.Semantic.DeepEqual(r.stubProvisioner, c.Spec.Provisioner.Ref) +} + +func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) error { + logger := r.logger.With(zap.Any("channel", c)) + + // We are syncing three things: + // 1. The K8s Service to talk to this Channel. + // 2. The Istio VirtualService to talk to this Channel. + // 3. The configuration of Channel's subscriptions. + + // We always need to sync the Channel config, so do it first. + err := r.syncChannelConfig(ctx) + if err != nil { + logger.Info("Error removing the Channel's config", zap.Error(err)) + return err + } + + if c.DeletionTimestamp != nil { + err := r.deleteVirtualService(ctx, c) + if err != nil { + logger.Info("Error deleting the Provisioner Controller for the ClusterProvisioner.", zap.Error(err)) + return err + } + err = r.deleteK8sService(ctx, c) + if err != nil { + logger.Info("Error deleting the ClusterProvisioner's K8s Service", zap.Error(err)) + return err + } + return nil + } + + err = r.syncChannelConfig(ctx) + if err != nil { + logger.Info("Error syncing the Channel config", zap.Error(err)) + return err + } + err = r.createK8sService(ctx, c) + if err != nil { + logger.Info("Error creating the Channel's K8s Service", zap.Error(err)) + return err + } + + err = r.createVirtualService(ctx, c) + if err != nil { + logger.Info("Error creating the Virtual Service for the Channel", zap.Error(err)) + return err + } + + r.setStatusReady(c) + return nil +} + +func (r *reconciler) getK8sService(ctx context.Context, c *eventingv1alpha1.Channel) (*corev1.Service, error) { + svcKey := types.NamespacedName{ + Namespace: c.Namespace, + Name: controller.ChannelServiceName(c.Name), + } + svc := &corev1.Service{} + err := r.client.Get(ctx, svcKey, svc) + return svc, err +} + +func (r *reconciler) createK8sService(ctx context.Context, c *eventingv1alpha1.Channel) error { + svc, err := r.getK8sService(ctx, c) + + if errors.IsNotFound(err) { + svc = newK8sService(c) + err = r.client.Create(ctx, svc) + } + + // If an error occurred in either Get or Create, we need to reconcile again. + if err != nil { + return err + } + + // Ensure this Channel is the owner of the K8s service. + if !metav1.IsControlledBy(svc, c) { + r.logger.Warn("Channel's K8s Service is not owned by the Channel", zap.Any("channel", c), zap.Any("service", svc)) + } + return nil +} + +func (r *reconciler) deleteK8sService(ctx context.Context, c *eventingv1alpha1.Channel) error { + // TODO: Rely on the garbage collector? + svc, err := r.getK8sService(ctx, c) + + if errors.IsNotFound(err) { + // Nothing to delete. + return nil + } + + if err != nil { + return err + } + + // Verify that we own the Service before deleting it. + if metav1.IsControlledBy(svc, c) { + return r.client.Delete(ctx, svc) + } else { + r.logger.Warn("Channel's K8s Service is not owned by the Channel. Not deleting it.", zap.Any("channel", c), zap.Any("service", svc)) + return nil + } +} + + + + + +func (r *reconciler) createVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) error { + virtualService, err := r.getVirtualService(ctx, c) + + // If the resource doesn't exist, we'll create it + if errors.IsNotFound(err) { + virtualService = newVirtualService(c) + err = r.client.Create(ctx, virtualService) + } + + // If an error occurs during Get/Create, we'll requeue the item so we can + // attempt processing again later. This could have been caused by a + // temporary network failure, or any other transient reason. + if err != nil { + return err + } + + // If the Virtual Service is not controlled by this Channel, we should log a warning, but don't + // consider it an error. + if !metav1.IsControlledBy(virtualService, c) { + r.logger.Warn("VirtualService not owned by Channel", zap.Any("channel", c), zap.Any("virtualService", virtualService)) + } + return nil +} + +func (r *reconciler) deleteVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) error { + virtualService, err := r.getVirtualService(ctx, c) + + // If the resource doesn't exist, there is nothing to delete. + if errors.IsNotFound(err) { + return nil + } + + if err != nil { + return err + } + + // Verify that we own the VirtualService before deleting it. + if metav1.IsControlledBy(virtualService, c) { + return r.client.Delete(ctx, virtualService) + } else { + r.logger.Warn("VirtualService exists, but not owned by Channel. Not deleting it.", zap.Any("channel", c), zap.Any("virtualService", virtualService)) + return nil + } +} + +func (r *reconciler) getVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) (*istiov1alpha3.VirtualService, error) { + vsk := client.ObjectKey{ + Namespace: c.Namespace, + Name: controller.ChannelVirtualServiceName(c.ObjectMeta.Name), + } + vs := &istiov1alpha3.VirtualService{} + err := r.client.Get(ctx, vsk, vs) + return vs, err +} + +// newService creates a new Service for a Channel resource. It also sets +// the appropriate OwnerReferences on the resource so handleObject can discover +// the Channel resource that 'owns' it. +func newK8sService(c *eventingv1alpha1.Channel) *corev1.Service { + labels := map[string]string{ + "channel": c.Name, + "provisioner": c.Spec.Provisioner.Ref.Name, + } + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: controller.ChannelServiceName(c.ObjectMeta.Name), + Namespace: c.Namespace, + Labels: labels, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(c, schema.GroupVersionKind{ + Group: eventingv1alpha1.SchemeGroupVersion.Group, + Version: eventingv1alpha1.SchemeGroupVersion.Version, + Kind: "Channel", + }), + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: PortName, + Port: PortNumber, + }, + }, + }, + } +} + +// newVirtualService creates a new VirtualService for a Channel resource. It also sets +// the appropriate OwnerReferences on the resource so handleObject can discover +// the Channel resource that 'owns' it. +func newVirtualService(channel *eventingv1alpha1.Channel) *istiov1alpha3.VirtualService { + labels := map[string]string{ + "channel": channel.Name, + "provisioner": channel.Spec.Provisioner.Ref.Name, + } + destinationHost := controller.ServiceHostName(controller.ClusterBusDispatcherServiceName(channel.Spec.Provisioner.Ref.Name), system.Namespace) + return &istiov1alpha3.VirtualService{ + ObjectMeta: metav1.ObjectMeta{ + Name: controller.ChannelVirtualServiceName(channel.Name), + Namespace: channel.Namespace, + Labels: labels, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(channel, schema.GroupVersionKind{ + Group: eventingv1alpha1.SchemeGroupVersion.Group, + Version: eventingv1alpha1.SchemeGroupVersion.Version, + Kind: "Channel", + }), + }, + }, + Spec: istiov1alpha3.VirtualServiceSpec{ + Hosts: []string{ + controller.ServiceHostName(controller.ChannelServiceName(channel.Name), channel.Namespace), + controller.ChannelHostName(channel.Name, channel.Namespace), + }, + Http: []istiov1alpha3.HTTPRoute{{ + Rewrite: &istiov1alpha3.HTTPRewrite{ + Authority: controller.ChannelHostName(channel.Name, channel.Namespace), + }, + Route: []istiov1alpha3.DestinationWeight{{ + Destination: istiov1alpha3.Destination{ + Host: destinationHost, + Port: istiov1alpha3.PortSelector{ + Number: PortNumber, + }, + }}, + }}, + }, + }, + } +} + +func (r *reconciler) setStatusReady(c *eventingv1alpha1.Channel) { + c.Status.Conditions = []duckv1alpha1.Condition{ + { + Type: duckv1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + } +} +func (r *reconciler) updateChannelStatus(ctx context.Context, u *eventingv1alpha1.Channel) error { + o := &eventingv1alpha1.Channel{} + err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o) + if err != nil { + r.logger.Info("Error getting Channel for status update", zap.Error(err), zap.Any("updatedChannel", u)) + return err + } + + if !equality.Semantic.DeepEqual(o.Status, u.Status) { + o.Status = u.Status + return r.client.Update(ctx, o) + } + return nil +} + +func (r *reconciler) syncChannelConfig(ctx context.Context) error { + channels, err := r.listAllChannels(ctx) + if err != nil { + r.logger.Info("Unable to list channels", zap.Error(err)) + return err + } + config := multiChannelFanoutConfig(channels) + r.config = config + return r.syncHttpChannelConfig() +} + +func (r *reconciler) getConfig() multichannelfanout.Config { + return r.config +} + +func multiChannelFanoutConfig(channels []eventingv1alpha1.Channel) multichannelfanout.Config { + cc := make([]multichannelfanout.ChannelConfig, 0) + for _, c := range channels { + channelable := c.Spec.Channelable + if channelable != nil { + cc = append(cc, multichannelfanout.ChannelConfig{ + Namespace: c.Namespace, + Name: c.Name, + FanoutConfig: fanout.Config{ + Subscriptions: c.Spec.Channelable.Subscribers, + }, + }) + } + } + return multichannelfanout.Config{ + ChannelConfigs: cc, + } +} + +func (r *reconciler) listAllChannels(ctx context.Context) ([]eventingv1alpha1.Channel, error) { + channels := make([]eventingv1alpha1.Channel, 0) + + opts := &client.ListOptions{} + for { + cl := &eventingv1alpha1.ChannelList{} + err := r.client.List(ctx, opts, cl) + if err != nil { + return nil, err + } + + for _, c := range cl.Items { + if r.shouldReconcile(&c) { + channels = append(channels, c) + } + } + if cl.Continue != "" { + opts.Raw.Continue = cl.Continue + } else { + return channels, err + } + } +} diff --git a/pkg/buses/eventing/stub/clusterprovisioner/controller.go b/pkg/buses/eventing/stub/clusterprovisioner/controller.go new file mode 100644 index 00000000000..b34ed414601 --- /dev/null +++ b/pkg/buses/eventing/stub/clusterprovisioner/controller.go @@ -0,0 +1,95 @@ +/* +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 clusterprovisioner + +import ( + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/buses/eventing/stub/channel" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "github.com/knative/eventing/pkg/sidecar/swappable" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "net/http" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/source" + "sync" +) + +const ( + // controllerAgentName is the string used by this controller to identify + // itself when creating events. + controllerAgentName = "stub-bus-cluster-provisioner-controller" +) + +// ProvideController returns a flow controller. +func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, http.Handler, error) { + logger = logger.With(zap.String("controller", controllerAgentName)) + + h, err := swappable.NewEmptyHandler(logger) + if err != nil { + logger.Error("Unable to create HTTP handler", zap.Error(err)) + return nil, nil, err + } + + // Setup a new controller to Reconcile ClusterProvisioners that are Stub buses. + r := &reconciler{ + mgr: mgr, + recorder: mgr.GetRecorder(controllerAgentName), + logger: logger, + swapHttpHandlerConfig: swapHttpHandlerConfig(h, sync.Mutex{}), + channelControllers: make(map[corev1.ObjectReference]*channel.ConfigAndStopCh), + } + c, err := controller.New(controllerAgentName, mgr, controller.Options{ + Reconciler: r, + }) + if err != nil { + logger.Error("Unable to create controller.", zap.Error(err)) + return nil, nil, err + } + + // Watch ClusterProvisioners. + err = c.Watch(&source.Kind{ + Type: &eventingv1alpha1.ClusterProvisioner{}, + }, &handler.EnqueueRequestForObject{}) + if err != nil { + logger.Error("Unable to watch ClusterProvisioners.", zap.Error(err), zap.Any("type", &eventingv1alpha1.ClusterProvisioner{})) + return nil, nil, err + } + + // TODO: Should we watch the K8s service as well? If it changes, we probably should change it + // back. + + return c, h, nil +} + +func swapHttpHandlerConfig(s *swappable.Handler, sLock sync.Mutex) func(multichannelfanout.Config) error { + return func(config multichannelfanout.Config) error { + sLock.Lock() + defer sLock.Unlock() + mch := s.GetMultiChannelFanoutHandler() + if diff := mch.ConfigDiff(config); diff != "" { + newH, err := mch.CopyWithNewConfig(config) + if err != nil { + return err + } + s.SetMultiChannelFanoutHandler(newH) + } + return nil + } +} diff --git a/pkg/buses/eventing/stub/clusterprovisioner/reconciler.go b/pkg/buses/eventing/stub/clusterprovisioner/reconciler.go new file mode 100644 index 00000000000..99b30a0d8b5 --- /dev/null +++ b/pkg/buses/eventing/stub/clusterprovisioner/reconciler.go @@ -0,0 +1,328 @@ +/* +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 clusterprovisioner + +import ( + "context" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/buses/eventing/stub/channel" + "github.com/knative/eventing/pkg/controller" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "github.com/knative/eventing/pkg/system" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "strings" + "sync" +) + +type reconciler struct { + mgr manager.Manager + client client.Client + restConfig *rest.Config + recorder record.EventRecorder + + channelControllersLock sync.Mutex + channelControllers map[corev1.ObjectReference]*channel.ConfigAndStopCh + + swapHttpHandlerConfig func(config multichannelfanout.Config) error + + logger *zap.Logger +} + +// Verify the struct implements reconcile.Reconciler +var _ reconcile.Reconciler = &reconciler{} + +func (r *reconciler) InjectClient(c client.Client) error { + r.client = c + return nil +} + +func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { + //TODO use this to store the logger and set a deadline + ctx := context.TODO() + logger := r.logger.With(zap.Any("request", request)) + + cp := &eventingv1alpha1.ClusterProvisioner{} + err := r.client.Get(ctx, request.NamespacedName, cp) + + // The ClusterProvisioner may have been deleted since it was added to the workqueue. If so, + // there is nothing to be done. + if errors.IsNotFound(err) { + logger.Info("Could not find ClusterProvisioner", zap.Error(err)) + return reconcile.Result{}, nil + } + + // Any other error should be retried in another reconciliation. + if err != nil { + logger.Error("Unable to Get ClusterProvisioner", zap.Error(err)) + return reconcile.Result{}, err + } + + // Does this Controller control this ClusterProvisioner? + if !shouldReconcile(cp) { + logger.Info("Not reconciling ClusterProvisioner, it is not controlled by this Controller", zap.String("APIVersion", cp.APIVersion), zap.String("Kind", cp.Kind), zap.String("Namespace", cp.Namespace), zap.String("name", cp.Name)) + return reconcile.Result{}, nil + } + + err = r.reconcile(ctx, cp) + if err != nil { + logger.Info("Error reconciling ClusterProvisioner", zap.Error(err)) + // Note that we do not return the error here, because we want to update the Status + // regardless of the error. + } + + updateStatusErr := r.updateClusterProvisionerStatus(ctx, cp) + if updateStatusErr != nil { + logger.Info("Error updating ClusterProvisioner Status", zap.Error(updateStatusErr)) + return reconcile.Result{}, updateStatusErr + } + + return reconcile.Result{}, err +} + +// shouldReconcile determines if this Controller should control (and therefore reconcile) a given +// ClusterProvisioner. This Controller only handles Stub buses. +func shouldReconcile(cp *eventingv1alpha1.ClusterProvisioner) bool { + return cp.Namespace == "" && strings.HasPrefix(cp.Name, "stub-bus-provisioner-dispatcher") +} + +func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { + logger := r.logger.With(zap.Any("clusterProvisioner", cp)) + + // We are syncing two things: + // 1. The K8s Service to talk to this Stub bus. + // - There is a single K8s Service for all requests going to this Stub bus. + // 2. The in-memory Controller watching for Channels using this ClusterProvisioner. + + if cp.DeletionTimestamp != nil { + // Delete the in-memory controller for the channels. + // Delete the K8s service. + err := r.deleteProvisioner(ctx, cp) + if err != nil { + logger.Info("Error deleting the Provisioner Controller for the ClusterProvisioner.", zap.Error(err)) + return err + } + err = r.deleteDispatcherService(ctx, cp) + if err != nil { + logger.Info("Error deleting the ClusterProvisioner's K8s Service", zap.Error(err)) + return err + } + // TODO: Delete the ClusterProvisioner? Remove a finalizer? + return nil + } + + err := r.createDispatcherService(ctx, cp) + if err != nil { + logger.Info("Error creating the ClusterProvisioner's K8s Service", zap.Error(err)) + return err + } + + err = r.syncProvisioner(ctx, cp) + if err != nil { + logger.Info("Error creating the Provisioner Controller for the ClusterProvisioner", zap.Error(err)) + return err + } + + r.setStatusReady(cp) + return nil +} + +func (r *reconciler) createDispatcherService(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { + svcName := controller.ClusterBusDispatcherServiceName(cp.Name) + svcKey := types.NamespacedName{ + Namespace: system.Namespace, + Name: svcName, + } + svc := &corev1.Service{} + err := r.client.Get(ctx, svcKey, svc) + + if errors.IsNotFound(err) { + svc = newDispatcherService(cp) + err = r.client.Create(ctx, svc) + } + + // If an error occurred in either Get or Create, we need to reconcile again. + if err != nil { + return err + } + + // Ensure this ClusterProvisioner is the owner of the K8s service. + if !metav1.IsControlledBy(svc, cp) { + r.logger.Warn("ClusterProvisioner's K8s Service is not owned by the ClusterProvisioner", zap.Any("clusterProvisioner", cp), zap.Any("service", svc)) + } + return nil +} + +func (r *reconciler) deleteDispatcherService(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { + // TODO: Rely on the garbage collector? + return nil +} + +func (r *reconciler) syncProvisioner(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { + cpRef := objectRef(cp) + + cas, err := func() (*channel.ConfigAndStopCh, error) { + r.channelControllersLock.Lock() + defer r.channelControllersLock.Unlock() + + if _, ok := r.channelControllers[cpRef]; !ok { + cas, err := channel.ProvideController(r.mgr, r.logger, &cpRef, r.syncAllChannelsToHttpHandler) + if err != nil { + return nil, err + } + r.channelControllers[cpRef] = cas + return cas, nil + } else { + return nil, nil + } + }() + if err != nil { + return err + } + if cas != nil { + cas.BackgroundStart() + r.logger.Info("Starting Channel Controller", zap.Any("clusterProvisioner", cp)) + } + return nil +} + +func (r *reconciler) deleteProvisioner(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { + cpRef := objectRef(cp) + + // Keep the lock for as little time as possible. + stopCh := func () chan<- struct{} { + r.channelControllersLock.Lock() + defer r.channelControllersLock.Unlock() + + if cc, ok := r.channelControllers[cpRef]; ok { + delete(r.channelControllers, cpRef) + return cc.StopCh + } else { + return nil + } + }() + if stopCh != nil { + stopCh <- struct{}{} + r.logger.Info("Stopping Channel Controller", zap.Any("clusterProvisioner", cp)) + } + return nil +} + +func (r *reconciler) syncAllChannelsToHttpHandler() error { + // Do in a function to hold the lock for as short a time as possible. + configs := func() []multichannelfanout.Config { + configs := make([]multichannelfanout.Config, 0) + r.channelControllersLock.Lock() + defer r.channelControllersLock.Unlock() + for _, cas := range r.channelControllers { + configs = append(configs, cas.Config()) + } + return configs + }() + + channelConfigs := make([]multichannelfanout.ChannelConfig, 0) + for _, config := range configs { + channelConfigs = append(channelConfigs, config.ChannelConfigs...) + } + combined := multichannelfanout.Config{ + ChannelConfigs: channelConfigs, + } + return r.swapHttpHandlerConfig(combined) +} + +func objectRef(cp *eventingv1alpha1.ClusterProvisioner) corev1.ObjectReference { + return corev1.ObjectReference{ + Namespace: cp.Namespace, + Name: cp.Name, + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "ClusterProvisioner", + } +} + +func (r *reconciler) setStatusReady(cp *eventingv1alpha1.ClusterProvisioner) { + cp.Status.Conditions = []duckv1alpha1.Condition{ + { + Type: duckv1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + } +} +func (r *reconciler) updateClusterProvisionerStatus(ctx context.Context, u *eventingv1alpha1.ClusterProvisioner) error { + o := &eventingv1alpha1.ClusterProvisioner{} + err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o) + if err != nil { + r.logger.Info("Error getting ClusterProvisioner for status update", zap.Error(err), zap.Any("updatedClusterProvisioner", u)) + return err + } + + if !equality.Semantic.DeepEqual(o.Status, u.Status) { + o.Status = u.Status + return r.client.Update(ctx, o) + } + return nil +} + +// newDispatcherService creates a new Service for a ClusterBus resource. It also sets +// the appropriate OwnerReferences on the resource so handleObject can discover +// the ClusterBus resource that 'owns' it. +func newDispatcherService(cp *eventingv1alpha1.ClusterProvisioner) *corev1.Service { + labels := dispatcherLabels(cp.Name) + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: controller.ClusterBusDispatcherServiceName(cp.Name), + Namespace: system.Namespace, + Labels: labels, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(cp, schema.GroupVersionKind{ + Group: eventingv1alpha1.SchemeGroupVersion.Group, + Version: eventingv1alpha1.SchemeGroupVersion.Version, + Kind: "ClusterProvisioner", + }), + }, + }, + Spec: corev1.ServiceSpec{ + Selector: labels, + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + }, + } +} + +func dispatcherLabels(cpName string) map[string]string { + return map[string]string{ + "clusterProvisioner": cpName, + "role": "controller-dispatcher", + } +} + diff --git a/pkg/buses/eventing/stub/clusterprovisioner/stub_test.go b/pkg/buses/eventing/stub/clusterprovisioner/stub_test.go new file mode 100644 index 00000000000..5d31b26ceee --- /dev/null +++ b/pkg/buses/eventing/stub/clusterprovisioner/stub_test.go @@ -0,0 +1,24 @@ +/* +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 clusterprovisioner + +import "testing" + +func TestStub(t *testing.T) { + // TODO: Implement tests in this directory, please. + // This file is used to make code coverage on this directly count. +} diff --git a/pkg/buses/eventing/stub/dispatcher/main.go b/pkg/buses/eventing/stub/dispatcher/main.go new file mode 100644 index 00000000000..fd8a7174eca --- /dev/null +++ b/pkg/buses/eventing/stub/dispatcher/main.go @@ -0,0 +1,78 @@ +/* + * 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 main + +import ( + "flag" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/buses" + "github.com/knative/eventing/pkg/buses/eventing/stub/clusterprovisioner" + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" + "github.com/knative/pkg/signals" + "go.uber.org/zap" + "net/http" + "os" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +func main() { + ref := buses.NewBusReferenceFromNames( + os.Getenv("BUS_NAME"), + os.Getenv("BUS_NAMESPACE"), + ) + + logConfig := buses.NewLoggingConfig() + logger := buses.NewBusLoggerFromConfig(logConfig) + defer logger.Sync() + logger = logger.With( + // TODO: probably replace, Bus isn't really a thing anymore. + zap.String("eventing.knative.dev/bus", ref.String()), + zap.String("eventing.knative.dev/busType", "stub"), + zap.String("eventing.knative.dev/busComponent", buses.Dispatcher), + ) + flag.Parse() + + + //config.GetConfigOrDie() + + mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{}) + if err != nil { + logger.Fatal("Error starting up.", zap.Error(err)) + } + + // Add custom types to this array to get them into the manager's scheme. + eventingv1alpha1.AddToScheme(mgr.GetScheme()) + istiov1alpha3.AddToScheme(mgr.GetScheme()) + + _, handler, err := clusterprovisioner.ProvideController(mgr, logger.Desugar()) + + // set up signals so we handle the first shutdown signal gracefully + stopCh := signals.SetupSignalHandler() + // Start blocks forever. + go mgr.Start(stopCh) + + s := &http.Server{ + Addr: ":8080", + Handler: handler, + ErrorLog: zap.NewStdLog(logger.Desugar()), + } + logger.Info("Stub dispatcher started...") + + err = s.ListenAndServe() + logger.Error("Stub dispatcher stopped", zap.Error(err)) +} diff --git a/pkg/buses/eventing/system.yaml b/pkg/buses/eventing/system.yaml new file mode 100644 index 00000000000..fa098902cec --- /dev/null +++ b/pkg/buses/eventing/system.yaml @@ -0,0 +1,19 @@ + +#apiVersion: + +#--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Channel +metadata: + name: system-test +spec: + provisioner: + ref: + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterProvisioner + name: stub-bus-provisioner-dispatcher + channelable: + subscribers: + - sinkableDomain: message-dumper-bar.default.svc.cluster.local # Already exists + callableDomain: event-changer.default.svc.cluster.local diff --git a/pkg/buses/message_dispatcher.go b/pkg/buses/message_dispatcher.go index ffb055dd2bb..917087b63b0 100644 --- a/pkg/buses/message_dispatcher.go +++ b/pkg/buses/message_dispatcher.go @@ -42,7 +42,6 @@ type MessageDispatcher struct { // DispatchDefaults provides default parameter values used when dispatching a message. type DispatchDefaults struct { Namespace string - ReplyTo string } // NewMessageDispatcher creates a new message dispatcher that can dispatch @@ -66,14 +65,21 @@ func NewMessageDispatcher(logger *zap.SugaredLogger) *MessageDispatcher { // The destination and replyTo are DNS names. For names with a single label, // the default namespace is used to expand it into a fully qualified name // within the cluster. -func (d *MessageDispatcher) DispatchMessage(message *Message, destination string, defaults DispatchDefaults) error { - destinationURL := d.resolveURL(destination, defaults.Namespace) - reply, err := d.executeRequest(destinationURL, message) - if err != nil { - return fmt.Errorf("Unable to complete request %v", err) +func (d *MessageDispatcher) DispatchMessage(message *Message, destination, replyTo string, defaults DispatchDefaults) error { + var err error + // Default to replying with the original message. If there is a destination, then replace it + // with the response from the call to the destination instead. + reply := message + if destination != "" { + destinationURL := d.resolveURL(destination, defaults.Namespace) + reply, err = d.executeRequest(destinationURL, message) + if err != nil { + return fmt.Errorf("Unable to complete request %v", err) + } } - if defaults.ReplyTo != "" && reply != nil { - replyToURL := d.resolveURL(defaults.ReplyTo, defaults.Namespace) + + if replyTo != "" && reply != nil { + replyToURL := d.resolveURL(replyTo, defaults.Namespace) _, err = d.executeRequest(replyToURL, reply) if err != nil { return fmt.Errorf("Failed to forward reply %v", err) @@ -93,23 +99,24 @@ func (d *MessageDispatcher) executeRequest(url *url.URL, message *Message) (*Mes if err != nil { return nil, err } - if res != nil { - if res.StatusCode < 200 || res.StatusCode >= 300 { - // reject non-successful (2xx) responses - return nil, fmt.Errorf("unexpected HTTP response, expected 2xx, got %d", res.StatusCode) - } - headers := d.fromHTTPHeaders(res.Header) - // TODO: add configurable whitelisting of propagated headers/prefixes (configmap?) - if correlationID, ok := message.Headers[correlationIDHeaderName]; ok { - headers[correlationIDHeaderName] = correlationID - } - payload, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, fmt.Errorf("Unable to read response %v", err) - } - return &Message{headers, payload}, nil + if res.StatusCode < 200 || res.StatusCode >= 300 { + // reject non-successful (2xx) responses + return nil, fmt.Errorf("unexpected HTTP response, expected 2xx, got %d", res.StatusCode) + } + headers := d.fromHTTPHeaders(res.Header) + // TODO: add configurable whitelisting of propagated headers/prefixes (configmap?) + if correlationID, ok := message.Headers[correlationIDHeaderName]; ok { + headers[correlationIDHeaderName] = correlationID + } + payload, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, fmt.Errorf("Unable to read response %v", err) + } + if len(payload) == 0 { + // The response body is empty, the event has 'finished'. + return nil, nil } - return nil, nil + return &Message{headers, payload}, nil } // toHTTPHeaders converts message headers to HTTP headers. diff --git a/pkg/buses/message_dispatcher_test.go b/pkg/buses/message_dispatcher_test.go new file mode 100644 index 00000000000..62293bc26f3 --- /dev/null +++ b/pkg/buses/message_dispatcher_test.go @@ -0,0 +1,380 @@ +/* +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 buses + +import ( + "bytes" + "github.com/google/go-cmp/cmp" + "go.uber.org/zap" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +var ( + // Headers that are added to the response, but we don't want to check in our assertions. + unimportantHeaders = map[string]struct{}{ + "accept-encoding": {}, + "content-length": {}, + "content-type": {}, + "user-agent": {}, + } +) + +func TestDispatchMessage(t *testing.T) { + testCases := map[string]struct { + sendToDestination bool + sendToReply bool + message *Message + fakeResponse *http.Response + expectedErr bool + expectedDestRequest *requestValidation + expectedReplyRequest *requestValidation + }{ + "destination - only": { + sendToDestination: true, + message: &Message{ + Headers: map[string]string{ + // do-not-forward should not get forwarded. + "do-not-forward": "header", + "x-request-id": "id123", + "knative-1": "knative-1-value", + "knative-2": "knative-2-value", + "ce-abc": "ce-abc-value", + }, + Payload: []byte("destination"), + }, + expectedDestRequest: &requestValidation{ + Headers: map[string][]string{ + "x-request-id": {"id123"}, + "knative-1": {"knative-1-value"}, + "knative-2": {"knative-2-value"}, + "ce-abc": {"ce-abc-value"}, + }, + Body: "destination", + }, + }, + "destination - only -- error": { + sendToDestination: true, + message: &Message{ + Headers: map[string]string{ + // do-not-forward should not get forwarded. + "do-not-forward": "header", + "x-request-id": "id123", + "knative-1": "knative-1-value", + "knative-2": "knative-2-value", + "ce-abc": "ce-abc-value", + }, + Payload: []byte("destination"), + }, + expectedDestRequest: &requestValidation{ + Headers: map[string][]string{ + "x-request-id": {"id123"}, + "knative-1": {"knative-1-value"}, + "knative-2": {"knative-2-value"}, + "ce-abc": {"ce-abc-value"}, + }, + Body: "destination", + }, + fakeResponse: &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewBufferString("destination-response")), + }, + expectedErr: true, + }, + "reply - only": { + sendToReply: true, + message: &Message{ + Headers: map[string]string{ + // do-not-forward should not get forwarded. + "do-not-forward": "header", + "x-request-id": "id123", + "knative-1": "knative-1-value", + "knative-2": "knative-2-value", + "ce-abc": "ce-abc-value", + }, + Payload: []byte("replyTo"), + }, + expectedReplyRequest: &requestValidation{ + Headers: map[string][]string{ + "x-request-id": {"id123"}, + "knative-1": {"knative-1-value"}, + "knative-2": {"knative-2-value"}, + "ce-abc": {"ce-abc-value"}, + }, + Body: "replyTo", + }, + }, + "reply - only -- error": { + sendToReply: true, + message: &Message{ + Headers: map[string]string{ + // do-not-forward should not get forwarded. + "do-not-forward": "header", + "x-request-id": "id123", + "knative-1": "knative-1-value", + "knative-2": "knative-2-value", + "ce-abc": "ce-abc-value", + }, + Payload: []byte("replyTo"), + }, + expectedReplyRequest: &requestValidation{ + Headers: map[string][]string{ + "x-request-id": {"id123"}, + "knative-1": {"knative-1-value"}, + "knative-2": {"knative-2-value"}, + "ce-abc": {"ce-abc-value"}, + }, + Body: "replyTo", + }, + fakeResponse: &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(bytes.NewBufferString("destination-response")), + }, + expectedErr: true, + }, + "destination and reply - dest returns bad status code": { + sendToDestination: true, + sendToReply: true, + message: &Message{ + Headers: map[string]string{ + // do-not-forward should not get forwarded. + "do-not-forward": "header", + "x-request-id": "id123", + "knative-1": "knative-1-value", + "knative-2": "knative-2-value", + "ce-abc": "ce-abc-value", + }, + Payload: []byte("destination"), + }, + expectedDestRequest: &requestValidation{ + Headers: map[string][]string{ + "x-request-id": {"id123"}, + "knative-1": {"knative-1-value"}, + "knative-2": {"knative-2-value"}, + "ce-abc": {"ce-abc-value"}, + }, + Body: "destination", + }, + fakeResponse: &http.Response{ + StatusCode: http.StatusInternalServerError, + Body: ioutil.NopCloser(bytes.NewBufferString("destination-response")), + }, + expectedErr: true, + }, + "destination and reply - dest returns empty body": { + sendToDestination: true, + sendToReply: true, + message: &Message{ + Headers: map[string]string{ + // do-not-forward should not get forwarded. + "do-not-forward": "header", + "x-request-id": "id123", + "knative-1": "knative-1-value", + "knative-2": "knative-2-value", + "ce-abc": "ce-abc-value", + }, + Payload: []byte("destination"), + }, + expectedDestRequest: &requestValidation{ + Headers: map[string][]string{ + "x-request-id": {"id123"}, + "knative-1": {"knative-1-value"}, + "knative-2": {"knative-2-value"}, + "ce-abc": {"ce-abc-value"}, + }, + Body: "destination", + }, + fakeResponse: &http.Response{ + StatusCode: http.StatusAccepted, + Header: map[string][]string{ + "do-not-passthrough": {"no"}, + "x-request-id": {"altered-id"}, + "knative-1": {"new-knative-1-value"}, + "ce-abc": {"new-ce-abc-value"}, + }, + Body: ioutil.NopCloser(bytes.NewBufferString("")), + }, + }, + "destination and reply": { + sendToDestination: true, + sendToReply: true, + message: &Message{ + Headers: map[string]string{ + // do-not-forward should not get forwarded. + "do-not-forward": "header", + "x-request-id": "id123", + "knative-1": "knative-1-value", + "knative-2": "knative-2-value", + "ce-abc": "ce-abc-value", + }, + Payload: []byte("destination"), + }, + expectedDestRequest: &requestValidation{ + Headers: map[string][]string{ + "x-request-id": {"id123"}, + "knative-1": {"knative-1-value"}, + "knative-2": {"knative-2-value"}, + "ce-abc": {"ce-abc-value"}, + }, + Body: "destination", + }, + fakeResponse: &http.Response{ + StatusCode: http.StatusAccepted, + Header: map[string][]string{ + "do-not-passthrough": {"no"}, + "x-request-id": {"altered-id"}, + "knative-1": {"new-knative-1-value"}, + "ce-abc": {"new-ce-abc-value"}, + }, + Body: ioutil.NopCloser(bytes.NewBufferString("destination-response")), + }, + expectedReplyRequest: &requestValidation{ + Headers: map[string][]string{ + "x-request-id": {"altered-id"}, + "knative-1": {"new-knative-1-value"}, + "ce-abc": {"new-ce-abc-value"}, + }, + Body: "destination-response", + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + destHandler := &fakeHandler{ + t: t, + response: tc.fakeResponse, + requests: make([]requestValidation, 0), + } + destServer := httptest.NewServer(destHandler) + defer destServer.Close() + replyHandler := &fakeHandler{ + t: t, + response: tc.fakeResponse, + requests: make([]requestValidation, 0), + } + replyServer := httptest.NewServer(replyHandler) + defer replyServer.Close() + + md := NewMessageDispatcher(zap.NewNop().Sugar()) + err := md.DispatchMessage(tc.message, + getDomain(tc.sendToDestination, destServer.URL[7:]), + getDomain(tc.sendToReply, replyServer.URL[7:]), + DispatchDefaults{}) + if tc.expectedErr != (err != nil) { + t.Errorf("Unexpected error from DispatchRequest. Expected %v. Actual: %v", tc.expectedErr, err) + } + if tc.expectedDestRequest != nil { + rv := destHandler.popRequest(t) + assertEquality(t, destServer.URL, *tc.expectedDestRequest, rv) + } + if tc.expectedReplyRequest != nil { + rv := replyHandler.popRequest(t) + assertEquality(t, replyServer.URL, *tc.expectedReplyRequest, rv) + } + if len(destHandler.requests) != 0 { + t.Errorf("Unexpected destination requests: %+v", destHandler.requests) + } + if len(replyHandler.requests) != 0 { + t.Errorf("Unexpected reply requests: %+v", replyHandler.requests) + } + }) + } +} + +func getDomain(shouldSend bool, domain string) string { + if shouldSend { + return domain + } + return "" +} + +type requestValidation struct { + Host string + Headers http.Header + Body string +} + +type fakeHandler struct { + t *testing.T + response *http.Response + requests []requestValidation +} + +func (f *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + + // Make a copy of the request. + body, err := ioutil.ReadAll(r.Body) + if err != nil { + f.t.Error("Failed to read the request body") + } + f.requests = append(f.requests, requestValidation{ + Host: r.Host, + Headers: r.Header, + Body: string(body), + }) + + // Write the response. + if f.response != nil { + for h, vs := range f.response.Header { + for _, v := range vs { + w.Header().Add(h, v) + } + } + w.WriteHeader(f.response.StatusCode) + var buf bytes.Buffer + buf.ReadFrom(f.response.Body) + w.Write(buf.Bytes()) + } else { + w.WriteHeader(http.StatusOK) + w.Write([]byte("")) + } +} + +func (f *fakeHandler) popRequest(t *testing.T) requestValidation { + if len(f.requests) == 0 { + t.Error("Unable to pop request") + } + rv := f.requests[0] + f.requests = f.requests[1:] + return rv +} + +func assertEquality(t *testing.T, replacementURL string, expected, actual requestValidation) { + expected.Host = replacementURL[7:] + canonicalizeHeaders(expected, actual) + if diff := cmp.Diff(expected, actual); diff != "" { + t.Errorf("Unexpected difference (-want, +got): %v", diff) + } +} + +func canonicalizeHeaders(rvs ...requestValidation) { + // HTTP header names are case-insensitive, so normalize them to lower case for comparison. + for _, rv := range rvs { + headers := rv.Headers + for n, v := range headers { + delete(headers, n) + ln := strings.ToLower(n) + if _, present := unimportantHeaders[ln]; !present { + headers[ln] = v + } + } + } +} diff --git a/pkg/buses/message_receiver.go b/pkg/buses/message_receiver.go index 7639a7aec51..34c34c28953 100644 --- a/pkg/buses/message_receiver.go +++ b/pkg/buses/message_receiver.go @@ -103,7 +103,7 @@ func (r *MessageReceiver) stop(srv *http.Server) { func (r *MessageReceiver) HandleRequest(res http.ResponseWriter, req *http.Request) { host := req.Host r.logger.Infof("Received request for %s", host) - channel := r.parseChannel(host) + channel := ParseChannel(host) message, err := r.fromRequest(req) if err != nil { @@ -163,9 +163,9 @@ func (r *MessageReceiver) fromHTTPHeaders(headers http.Header) map[string]string return safe } -// parseChannel converts the channel's hostname into a channel +// ParseChannel converts the channel's hostname into a channel // reference. -func (r *MessageReceiver) parseChannel(host string) ChannelReference { +func ParseChannel(host string) ChannelReference { chunks := strings.Split(host, ".") return ChannelReference{ Name: chunks[0], diff --git a/pkg/sidecar/configmaphandler/config_map_handler.go b/pkg/sidecar/configmaphandler/config_map_handler.go new file mode 100644 index 00000000000..7a362dd38b2 --- /dev/null +++ b/pkg/sidecar/configmaphandler/config_map_handler.go @@ -0,0 +1,190 @@ +/* +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 configmaphandler + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/fsnotify/fsnotify" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "github.com/knative/pkg/configmap" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/util/yaml" + "net/http" + "sync/atomic" +) + +const ( + // The mount path of the configMap volume. + ConfigDir = "/etc/config/fanout_sidecar" + // The config key that contains all the configuration data. + multiChannelFanoutConfigKey = "multiChannelFanoutConfig" +) + +// http.Handler that monitors an attached ConfigMap volume for updated configuration and updates its +// behavior based on the configuration. +type configMapHandler struct { + logger *zap.Logger + // The directory to read the configMap from. + dir string + // Stop the watcher by closing this channel. Expected to only be used by tests. + watcherStopCh chan<- bool + // The current multichannelfanout.Handler to delegate HTTP requests to. Never use this directly, + // instead use {get,set}MultiChannelFanoutHandler, which enforces the type we expect. + fanout atomic.Value +} + +// NewHandler creates a new configmaphandler.Handler. +func NewHandler(logger *zap.Logger, dir string) (http.Handler, error) { + conf, err := readConfigMap(logger, dir) + if err != nil { + logger.Error("Unable to read configMap", zap.Error(err)) + return nil, err + } + + logger.Info("Read initial configMap", zap.Any("conf", conf)) + + mcfh, err := multichannelfanout.NewHandler(logger, conf) + if err != nil { + logger.Error("Unable to create multichannelfanout.Handler: %v", zap.Error(err)) + return nil, err + } + + cmh := &configMapHandler{ + logger: logger, + dir: dir, + } + cmh.setMultiChannelFanoutHandler(mcfh) + watcherStopCh, err := cmh.startWatcher(dir) + if err != nil { + logger.Error("Unable to start the configMap file watcher", zap.Error(err)) + return nil, err + } + cmh.watcherStopCh = watcherStopCh + return cmh, nil +} + +// getMultiChannelFanoutHandler gets the current multichannelfanout.Handler to delegate all HTTP +// requests to. +func (cmh *configMapHandler) getMultiChannelFanoutHandler() *multichannelfanout.Handler { + return cmh.fanout.Load().(*multichannelfanout.Handler) +} + +// setMultiChannelFanoutHandler sets a new multichannelfanout.Handler to delegate all subsequent +// HTTP requests to. +func (cmh *configMapHandler) setMultiChannelFanoutHandler(new *multichannelfanout.Handler) { + cmh.fanout.Store(new) +} + +// readConfigMap attempts to read the configMap from the attached volume. +func readConfigMap(logger *zap.Logger, dir string) (multichannelfanout.Config, error) { + cm, err := configmap.Load(dir) + if err != nil { + logger.Error("Unable to read configMap", zap.Error(err)) + return multichannelfanout.Config{}, err + } + + if _, present := cm[multiChannelFanoutConfigKey]; !present { + logger.Error("Expected key not found", zap.String("key", multiChannelFanoutConfigKey)) + return multichannelfanout.Config{}, fmt.Errorf("expected key not found: %v", multiChannelFanoutConfigKey) + } + jb, err := yaml.ToJSON([]byte(cm[multiChannelFanoutConfigKey])) + if err != nil { + logger.Error("Unable to convert multiChannelFanoutConfig to JSON", zap.Error(err)) + return multichannelfanout.Config{}, err + } + var conf multichannelfanout.Config + err = unmarshallJsonDisallowUnknownFields(jb, &conf) + return conf, err +} + +// readConfigMapAndUpdateSubs reads the configMap data and updates configuration of cmh.fanout, if +// it has changed. +// +// Note that this is often called multiple times when the configMap is updated, so it should do its +// best to discard redundant calls. +func (cmh *configMapHandler) readConfigMapAndUpdateConfig() { + conf, err := readConfigMap(cmh.logger, cmh.dir) + if err != nil { + cmh.logger.Error("Unable to read the configMap", zap.Error(err)) + return + } + current := cmh.getMultiChannelFanoutHandler() + if diff := current.ConfigDiff(conf); diff != "" { + cmh.logger.Info("Updating multiChannelFanout config", zap.String("diff (-old, +new)", diff)) + updated, err := current.CopyWithNewConfig(conf) + if err != nil { + cmh.logger.Error("Unable to create updated multichannelfanout.Handler", zap.Error(err)) + return + } + cmh.setMultiChannelFanoutHandler(updated) + } else { + cmh.logger.Info("fanout config unchanged") + } +} + +// startWatcher starts a background go routine that gets events when the filesystem in configDir is +// changed. +func (cmh *configMapHandler) startWatcher(dir string) (chan<- bool, error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, err + } + stopCh := make(chan bool) + go func() { + for { + select { + case _, ok := <-watcher.Events: + if !ok { + // Channel closed. + cmh.logger.Error("watcher.Events channel closed") // TODO: Should this be fatal? + return + } + cmh.readConfigMapAndUpdateConfig() + case err, ok := <-watcher.Errors: + if !ok { + // Channel closed. + cmh.logger.Error("watcher.Errors channel closed") // TODO: Should this be fatal? + return + } + cmh.logger.Error("watcher.Errors", zap.Error(err)) + case _, ok := <-stopCh: + if !ok { + // stopCh has been closed + return + } + } + } + }() + + return stopCh, watcher.Add(dir) +} + +// ServeHTTP delegates all HTTP requests to the current multichannelfanout.Handler. +func (cmh *configMapHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Hand work off to the current multi channel fanout handler. + cmh.getMultiChannelFanoutHandler().ServeHTTP(w, r) +} + +// unmarshallJsonDisallowUnknownFields unmarshalls JSON, but unlike json.Unmarshall, will fail if +// given an unknown field (rather than json.Unmarshall's ignoring the unknown field). +func unmarshallJsonDisallowUnknownFields(jb []byte, v interface{}) error { + d := json.NewDecoder(bytes.NewReader(jb)) + d.DisallowUnknownFields() + return d.Decode(v) +} diff --git a/pkg/sidecar/configmaphandler/config_map_handler_test.go b/pkg/sidecar/configmaphandler/config_map_handler_test.go new file mode 100644 index 00000000000..0b847d467e7 --- /dev/null +++ b/pkg/sidecar/configmaphandler/config_map_handler_test.go @@ -0,0 +1,390 @@ +/* +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 configmaphandler + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + "github.com/knative/eventing/pkg/sidecar/fanout" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "go.uber.org/atomic" + "go.uber.org/zap" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + "time" +) + +const ( + replaceDomain = "replaceDomain" +) + +func TestReadConfigMap(t *testing.T) { + testCases := []struct { + name string + createDir bool + config string + expected multichannelfanout.Config + expectedErr bool + }{ + { + name: "dir does not exist", + createDir: false, + }, + { + name: "no data", + createDir: true, + expectedErr: true, + }, + { + name: "invalid YAML", + createDir: true, + config: ` + key: + - value + - different indent level + `, + expectedErr: true, + }, + { + name: "valid YAML -- invalid JSON", + config: "{ nil: Key }", + createDir: true, + expectedErr: true, + }, + { + name: "unknown field", + config: "{ channelConfigs: [ { not: a-defined-field } ] }", + createDir: true, + expectedErr: true, + }, + { + name: "valid", + createDir: true, + config: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - callableDomain: event-changer.default.svc.cluster.local + sinkableDomain: message-dumper-bar.default.svc.cluster.local + - callableDomain: message-dumper-foo.default.svc.cluster.local + - sinkableDomain: message-dumper-bar.default.svc.cluster.local + - namespace: default + name: c2 + fanoutConfig: + subscriptions: + - sinkableDomain: message-dumper-foo.default.svc.cluster.local + - namespace: other + name: c3 + fanoutConfig: + subscriptions: + - sinkableDomain: message-dumper-foo.default.svc.cluster.local + `, + expected: multichannelfanout.Config{ + ChannelConfigs: []multichannelfanout.ChannelConfig{ + { + Namespace: "default", + Name: "c1", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "event-changer.default.svc.cluster.local", + SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + }, + { + CallableDomain: "message-dumper-foo.default.svc.cluster.local", + }, + { + SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + }, + }, + }, + }, + { + Namespace: "default", + Name: "c2", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + }, + }, + }, + }, + { + Namespace: "other", + Name: "c3", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + }, + }, + }, + }, + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var dir string + if tc.createDir { + dir = createTempDir(t) + defer os.RemoveAll(dir) + } else { + dir = "/tmp/doesNotExist" + } + writeConfig(t, dir, tc.config) + c, e := readConfigMap(zap.NewNop(), dir) + if tc.expectedErr { + if e == nil { + t.Errorf("Expected an error, actual nil") + } + return + } + if !cmp.Equal(c, tc.expected) { + t.Errorf("Unexpected config. Expected '%v'. Actual '%v'.", tc.expected, c) + } + }) + } +} + +func TestNewHandler(t *testing.T) { + testCases := []struct { + name string + createDir bool + config string + expectErr bool + }{ + { + name: "dir does not exist", + createDir: false, + expectErr: true, + }, + { + name: "duplicate channel key", + createDir: true, + config: ` + channelConfigs: + - namespace: default + name: duplicate + - namespace: default + name: duplicate + `, + expectErr: true, + }, + { + name: "success", + createDir: true, + config: ` + channelConfigs: [] + `, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var dir string + if tc.createDir { + dir = createTempDir(t) + defer os.RemoveAll(dir) + } else { + dir = "/tmp/doesNotExist" + } + writeConfig(t, dir, tc.config) + cmh, err := NewHandler(zap.NewNop(), dir) + if err == nil { + // This is not yet about the logic of the test, just ensuring we don't accidentally + // leave the channel open, which will leave the watcher running. + defer close(cmh.(*configMapHandler).watcherStopCh) + } + if tc.expectErr { + if err == nil { + t.Errorf("Expected an error, actually nil") + } + return + } + }) + } +} + +func TestServeHTTP(t *testing.T) { + testCases := []struct { + name string + initialConfig string + updatedConfig string + initialRequests int32 + initialRequestsAfterUpdate int32 + updateRequests int32 + }{ + { + name: "send to config", + initialConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + initialRequests: 1, + }, + { + name: "change config", + initialConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + initialRequests: 1, + updatedConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + updateRequests: 1, + }, + { + name: "bad config update -- keeps serving old config", + initialConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + initialRequests: 1, + updatedConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + # Duplicate namespace/name + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + initialRequestsAfterUpdate: 2, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + initialHandler := &fakeHandler{} + initialServer := httptest.NewServer(initialHandler) + defer initialServer.Close() + updateHandler := &fakeHandler{} + updateServer := httptest.NewServer(updateHandler) + defer updateServer.Close() + + dir := createTempDir(t) + defer os.RemoveAll(dir) + writeConfig(t, dir, replaceDomains(tc.initialConfig, initialServer.URL[7:])) + + cmh, _ := NewHandler(zap.NewNop(), dir) + + w := httptest.NewRecorder() + cmh.ServeHTTP(w, makeRequest("default", "c1")) + if w.Result().StatusCode != http.StatusOK { + t.Errorf("Unexpected initial status code: %v", w.Result().StatusCode) + } + if tc.initialRequests != initialHandler.requests.Load() { + t.Errorf("Incorrect initial request count. Expected %v. Actual %v.", + tc.initialRequests, initialHandler.requests.Load()) + } + + if tc.updatedConfig != "" { + writeConfig(t, dir, replaceDomains(tc.updatedConfig, updateServer.URL[7:])) + // The watcher is running in another routine, give it some time to notice the + // change. + time.Sleep(100 * time.Millisecond) + w = httptest.NewRecorder() + cmh.ServeHTTP(w, makeRequest("default", "c1")) + if w.Result().StatusCode != http.StatusOK { + t.Errorf("Unexpected updated status code: %v", w.Result().StatusCode) + } + if tc.updateRequests != updateHandler.requests.Load() { + t.Errorf("Incorrect update request count. Expected %v. Actual %v.", tc.updateRequests, updateHandler.requests.Load()) + } + if tc.initialRequestsAfterUpdate != 0 && tc.initialRequestsAfterUpdate != initialHandler.requests.Load() { + t.Errorf("Incorrect requests to initialHandler after config update. Expected %v, actual %v", + tc.initialRequestsAfterUpdate, initialHandler.requests.Load()) + } + } + }) + } +} + +func createTempDir(t *testing.T) string { + dir, err := ioutil.TempDir("", "configMapHandlerTest") + if err != nil { + t.Errorf("Unable to make temp directory: %v", err) + } + return dir +} + +func writeConfig(t *testing.T, dir, config string) { + if config != "" { + // Golang editors tend to replace leading spaces with tabs. YAML is left whitespace + // sensitive, so let's replace the tabs with spaces. + leftSpaceConfig := strings.Replace(config, "\t", " ", -1) + err := ioutil.WriteFile(fmt.Sprintf("%s/%s", dir, multiChannelFanoutConfigKey), []byte(leftSpaceConfig), 0700) + if err != nil { + t.Errorf("Problem writing the config file: %v", err) + } + } +} + +func body(body string) io.ReadCloser { + return ioutil.NopCloser(strings.NewReader(body)) +} + +func makeRequest(namespace, name string) *http.Request { + r := httptest.NewRequest("POST", fmt.Sprintf("http://%s.%s/", name, namespace), body("")) + return r +} + +type fakeHandler struct { + requests atomic.Int32 +} + +func (h *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + w.WriteHeader(http.StatusOK) + h.requests.Inc() +} + +func replaceDomains(config, replacement string) string { + return strings.Replace(config, replaceDomain, replacement, -1) +} diff --git a/pkg/sidecar/fanout/fanout_handler.go b/pkg/sidecar/fanout/fanout_handler.go new file mode 100644 index 00000000000..13e380f43e4 --- /dev/null +++ b/pkg/sidecar/fanout/fanout_handler.go @@ -0,0 +1,192 @@ +/* +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 fanout + +import ( + "fmt" + "github.com/knative/eventing/pkg/buses" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "go.uber.org/zap" + "math/rand" + "net/http" + "sync" + "time" +) + +const ( + // The header attached to requests sent through the MessageReceiver. It is used to correlate the + // request going into the MessageReceiver and the request coming out of the MessageReceiver. It + // is removed before being sent downstream. + // Note that it MUST be in the headers forwarded by the MessageReceiver. + uniqueFanoutHeader = "Knative-Fanout-Message-Tracker" + + defaultTimeout = 1 * time.Minute +) + +// Configuration for a fanout.Handler. +type Config struct { + Subscriptions []duckv1alpha1.ChannelSubscriberSpec `json:"subscriptions"` +} + +// http.Handler that takes a single request in and fans it out to N other servers. +type fanoutHandler struct { + config Config + + receivedMessages *messageStorage + receiver *buses.MessageReceiver + dispatcher *buses.MessageDispatcher + + timeout time.Duration + + logger *zap.Logger +} + +var _ http.Handler = &fanoutHandler{} + +func NewHandler(logger *zap.Logger, config Config) http.Handler { + handler := &fanoutHandler{ + logger: logger, + config: config, + dispatcher: buses.NewMessageDispatcher(logger.Sugar()), + receivedMessages: &messageStorage{ + messages: make(map[string]*buses.Message), + }, + timeout: defaultTimeout, + } + // The receiver function needs to point back at the handler itself, so setup it after + // initialization. + handler.receiver = buses.NewMessageReceiver(createReceiverFunction(handler), logger.Sugar()) + return handler +} + +func createReceiverFunction(f *fanoutHandler) func(buses.ChannelReference, *buses.Message) error { + return func(_ buses.ChannelReference, m *buses.Message) error { + f.logger.Debug("Putting message", zap.String("key", m.Headers[uniqueFanoutHeader])) + f.receivedMessages.Put(m) + return nil + } +} + +// ServeHTTP takes the request, fans it out to each subscription in f.config. If all the fanned out +// requests return successfully, then return successfully. Else, return failure. +func (f *fanoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + key := addTrackingHeader(r) + receiverResponse := &response{} + f.receiver.HandleRequest(receiverResponse, r) + + if receiverResponse.statusCode != http.StatusAccepted { + f.logger.Info("MessageReceiver rejected the request", zap.Int("statusCode", receiverResponse.statusCode)) + w.WriteHeader(receiverResponse.statusCode) + return + } + + f.logger.Debug("Pulling message", zap.String("key", key)) + m, err := f.receivedMessages.pull(key) + if err != nil { + f.logger.Info("Could not find tracked message", zap.Error(err), zap.Any("key", r.Header[uniqueFanoutHeader])) + w.WriteHeader(http.StatusInternalServerError) + return + } + removeTrackingHeader(m) + + errorCh := make(chan error, len(f.config.Subscriptions)) + for _, sub := range f.config.Subscriptions { + go func(s duckv1alpha1.ChannelSubscriberSpec) { + errorCh <- f.makeFanoutRequest(r, *m, s) + }(sub) + } + + sc := http.StatusOK +Loop: + for range f.config.Subscriptions { + select { + case err := <-errorCh: + if err != nil { + f.logger.Error("Fanout had an error", zap.Error(err)) + sc = http.StatusInternalServerError + } + case <-time.After(f.timeout): + f.logger.Error("Fanout timed out") + sc = http.StatusInternalServerError + break Loop + } + } + + w.WriteHeader(sc) +} + +// makeFanoutRequest sends the request to exactly one subscription. It handles both the `call` and +// the `to` portions of the subscription. +func (f *fanoutHandler) makeFanoutRequest(r *http.Request, m buses.Message, sub duckv1alpha1.ChannelSubscriberSpec) error { + return f.dispatcher.DispatchMessage(&m, sub.CallableDomain, sub.SinkableDomain, buses.DispatchDefaults{}) +} + +// response is used to capture the statusCode returned by the messageReceiver. +type response struct { + statusCode int +} + +var _ http.ResponseWriter = &response{} + +func (*response) Header() http.Header { + return http.Header{} +} + +func (*response) Write(b []byte) (int, error) { + return len(b), nil +} + +func (r *response) WriteHeader(statusCode int) { + r.statusCode = statusCode +} + +type messageStorage struct { + messagesLock sync.Mutex + messages map[string]*buses.Message +} + +func (ms *messageStorage) Put(m *buses.Message) { + key := m.Headers[uniqueFanoutHeader] + + ms.messagesLock.Lock() + defer ms.messagesLock.Unlock() + + ms.messages[key] = m +} + +func (ms *messageStorage) pull(key string) (*buses.Message, error) { + ms.messagesLock.Lock() + defer ms.messagesLock.Unlock() + + if m, ok := ms.messages[key]; ok { + delete(ms.messages, key) + return m, nil + } else { + return nil, fmt.Errorf("could not find message: %v", key) + } +} + +func addTrackingHeader(r *http.Request) string { + // Use two random 63 bit ints, as a poor approximation of a UUID. + key := fmt.Sprintf("%X-%X", rand.Int63(), rand.Int63()) + r.Header.Set(uniqueFanoutHeader, key) + return key +} + +func removeTrackingHeader(m *buses.Message) { + delete(m.Headers, uniqueFanoutHeader) +} diff --git a/pkg/sidecar/fanout/fanout_handler_test.go b/pkg/sidecar/fanout/fanout_handler_test.go new file mode 100644 index 00000000000..831b9184026 --- /dev/null +++ b/pkg/sidecar/fanout/fanout_handler_test.go @@ -0,0 +1,257 @@ +/* +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 fanout + +import ( + "errors" + "fmt" + "github.com/knative/eventing/pkg/buses" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "go.uber.org/atomic" + "go.uber.org/zap" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" +) + +// Domains used in subscriptions, which will be replaced by the real domains of the started HTTP +// servers. +const ( + replaceCallable = "replaceCallable" + replaceSinkable = "replaceSinkable" +) + +var ( + cloudEventReq = httptest.NewRequest("POST", "http://channelname.channelnamespace/", body(cloudEvent)) + cloudEvent = `{ + "cloudEventsVersion" : "0.1", + "eventType" : "com.example.someevent", + "eventTypeVersion" : "1.0", + "source" : "/mycontext", + "eventID" : "A234-1234-1234", + "eventTime" : "2018-04-05T17:31:00Z", + "extensions" : { + "comExampleExtension" : "value" + }, + "contentType" : "text/xml", + "data" : "" +}` +) + +func TestFanoutHandler_ServeHTTP(t *testing.T) { + testCases := map[string]struct { + receiverFunc func(buses.ChannelReference, *buses.Message) error + timeout time.Duration + subs []duckv1alpha1.ChannelSubscriberSpec + callable func(http.ResponseWriter, *http.Request) + sinkable func(http.ResponseWriter, *http.Request) + expectedStatus int + }{ + "rejected by receiver": { + receiverFunc: func(buses.ChannelReference, *buses.Message) error { + return errors.New("Rejected by test-receiver") + }, + expectedStatus: http.StatusInternalServerError, + }, + "could not find tracked message": { + receiverFunc: func(buses.ChannelReference, *buses.Message) error { + // Not being written to messageStorage. + return nil + }, + expectedStatus: http.StatusInternalServerError, + }, + "fanout times out": { + timeout: time.Millisecond, + subs: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: replaceCallable, + }, + }, + callable: func(writer http.ResponseWriter, _ *http.Request) { + time.Sleep(10 * time.Millisecond) + writer.WriteHeader(http.StatusOK) + }, + expectedStatus: http.StatusInternalServerError, + }, + "zero subs succeed": { + subs: []duckv1alpha1.ChannelSubscriberSpec{}, + expectedStatus: http.StatusOK, + }, + "empty sub succeeds": { + subs: []duckv1alpha1.ChannelSubscriberSpec{ + {}, + }, + expectedStatus: http.StatusOK, + }, + "sinkable fails": { + subs: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: replaceSinkable, + }, + }, + sinkable: func(writer http.ResponseWriter, _ *http.Request) { + writer.WriteHeader(http.StatusNotFound) + }, + expectedStatus: http.StatusInternalServerError, + }, + "callable fails": { + subs: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: replaceCallable, + }, + }, + callable: func(writer http.ResponseWriter, _ *http.Request) { + writer.WriteHeader(http.StatusNotFound) + }, + expectedStatus: http.StatusInternalServerError, + }, + "callable succeeds, sinkable fails": { + subs: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: replaceCallable, + SinkableDomain: replaceSinkable, + }, + }, + callable: callableSucceed, + sinkable: func(writer http.ResponseWriter, _ *http.Request) { + writer.WriteHeader(http.StatusForbidden) + }, + expectedStatus: http.StatusInternalServerError, + }, + "one sub succeeds": { + subs: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: replaceCallable, + SinkableDomain: replaceSinkable, + }, + }, + callable: callableSucceed, + sinkable: func(writer http.ResponseWriter, _ *http.Request) { + writer.WriteHeader(http.StatusAccepted) + }, + expectedStatus: http.StatusOK, + }, + "one sub succeeds, one sub fails": { + subs: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: replaceCallable, + SinkableDomain: replaceSinkable, + }, + { + CallableDomain: replaceCallable, + SinkableDomain: replaceSinkable, + }, + }, + callable: callableSucceed, + sinkable: (&succeedOnce{}).handler, + expectedStatus: http.StatusInternalServerError, + }, + "all subs succeed": { + subs: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: replaceCallable, + SinkableDomain: replaceSinkable, + }, + { + CallableDomain: replaceCallable, + SinkableDomain: replaceSinkable, + }, + { + CallableDomain: replaceCallable, + SinkableDomain: replaceSinkable, + }, + }, + callable: callableSucceed, + sinkable: func(writer http.ResponseWriter, _ *http.Request) { + writer.WriteHeader(http.StatusAccepted) + }, + expectedStatus: http.StatusOK, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + callableServer := httptest.NewServer(&fakeHandler{ + handler: tc.callable, + }) + defer callableServer.Close() + sinkableServer := httptest.NewServer(&fakeHandler{ + handler: tc.sinkable, + }) + defer sinkableServer.Close() + + // Rewrite the subs to use the servers we just started. + subs := make([]duckv1alpha1.ChannelSubscriberSpec, 0) + for _, sub := range tc.subs { + if sub.CallableDomain == replaceCallable { + sub.CallableDomain = callableServer.URL[7:] // strip the leading 'http://' + } + if sub.SinkableDomain == replaceSinkable { + sub.SinkableDomain = sinkableServer.URL[7:] // strip the leading 'http://' + } + subs = append(subs, sub) + } + + h := NewHandler(zap.NewNop(), Config{Subscriptions: subs}).(*fanoutHandler) + if tc.receiverFunc != nil { + h.receiver = buses.NewMessageReceiver(tc.receiverFunc, zap.NewNop().Sugar()) + } + if tc.timeout != 0 { + h.timeout = tc.timeout + } + + w := httptest.NewRecorder() + fmt.Sprintf("hello %v", n) + h.ServeHTTP(w, cloudEventReq) + if w.Code != tc.expectedStatus { + t.Errorf("Unexpected status code. Expected %v, Actual %v", tc.expectedStatus, w.Code) + } + }) + } +} + +type fakeHandler struct { + handler func(http.ResponseWriter, *http.Request) +} + +func (h *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + r.Body.Close() + h.handler(w, r) +} + +type succeedOnce struct { + called atomic.Bool +} + +func (s *succeedOnce) handler(w http.ResponseWriter, _ *http.Request) { + if s.called.CAS(false, true) { + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusForbidden) + } +} + +func body(body string) io.ReadCloser { + return ioutil.NopCloser(strings.NewReader(body)) +} +func callableSucceed(writer http.ResponseWriter, _ *http.Request) { + writer.WriteHeader(http.StatusOK) + writer.Write([]byte(cloudEvent)) +} diff --git a/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler.go b/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler.go new file mode 100644 index 00000000000..5228b55e6e3 --- /dev/null +++ b/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler.go @@ -0,0 +1,112 @@ +/* +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 multichannelfanout + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + "github.com/knative/eventing/pkg/buses" + "github.com/knative/eventing/pkg/sidecar/fanout" + "go.uber.org/zap" + "net/http" +) + +// The configuration of this handler. +type Config struct { + // The configuration of each channel in this handler. + ChannelConfigs []ChannelConfig `json:"channelConfigs"` +} + +type ChannelConfig struct { + Namespace string `json:"namespace"` + Name string `json:"name"` + FanoutConfig fanout.Config `json:"fanoutConfig"` +} + +// MakeChannelKey creates the value to be sent in the HTTP header +// multichannelfanout.ChannelKeyHeader. This represents the `Channel` the request is being sent +// upon. +func makeChannelKey(namespace, name string) string { + return fmt.Sprintf("%s/%s", namespace, name) +} + +// makeChannelKeyFromConfig creates the channel key for a given channelConfig. It is a helper around +// MakeChannelKey. +func makeChannelKeyFromConfig(config ChannelConfig) string { + return makeChannelKey(config.Namespace, config.Name) +} + +// getChannelKey extracts the channel key from the given HTTP request. +func getChannelKey(r *http.Request) string { + cr := buses.ParseChannel(r.Host) + return fmt.Sprintf("%s/%s", cr.Namespace, cr.Name) +} + +// Handler is an http.Handler that looks in the HTTP headers of a request for the +// multichannelfanout.ChannelKeyHeader and uses its value to determine which fanout.Handler to +// delegate the request to. +type Handler struct { + logger *zap.Logger + handlers map[string]http.Handler + config Config +} + +func NewHandler(logger *zap.Logger, conf Config) (*Handler, error) { + handlers := make(map[string]http.Handler, len(conf.ChannelConfigs)) + + for _, cc := range conf.ChannelConfigs { + key := makeChannelKeyFromConfig(cc) + handler := fanout.NewHandler(logger, cc.FanoutConfig) + if _, present := handlers[key]; present { + logger.Error("Duplicate channel key", zap.String("channelKey", key)) + return nil, fmt.Errorf("duplicate channel key: %v", key) + } + handlers[key] = handler + } + + return &Handler{ + logger: logger, + config: conf, + handlers: handlers, + }, nil +} + +// ConfigDiffs diffs the new config with the existing config. If there are no differences, then the +// empty string is returned. If there are differences, then a non-empty string is returned +// describing the differences. +func (h *Handler) ConfigDiff(updated Config) string { + return cmp.Diff(h.config, updated) +} + +// CopyWithNewConfig creates a new copy of this Handler with all the fields identical, except the +// new Handler uses conf, rather than copying the existing Handler's config. +func (h *Handler) CopyWithNewConfig(conf Config) (*Handler, error) { + return NewHandler(h.logger, conf) +} + +// ServeHTTP delegates the actual handling of the request to a fanout.Handler, based on the +// request's channel key. +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + channelKey := getChannelKey(r) + fh, ok := h.handlers[channelKey] + if !ok { + h.logger.Error("Unable to find a handler for request", zap.String("channelKey", channelKey)) + w.WriteHeader(http.StatusInternalServerError) + return + } + fh.ServeHTTP(w, r) +} diff --git a/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go b/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go new file mode 100644 index 00000000000..57be69cd0e4 --- /dev/null +++ b/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go @@ -0,0 +1,339 @@ +/* +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 multichannelfanout + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + "github.com/knative/eventing/pkg/sidecar/fanout" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "go.uber.org/zap" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +const ( + // The httptest.Server's host name will replace this value in all ChannelConfigs. + replaceDomain = "replaceDomain" +) + +func TestMakeChannelKey(t *testing.T) { + testCases := []struct { + namespace string + name string + key string + }{ + { + namespace: "default", + name: "channel", + key: "default/channel", + }, + { + namespace: "foo", + name: "bar", + key: "foo/bar", + }, + } + for _, tc := range testCases { + name := fmt.Sprintf("%s, %s -> %s", tc.namespace, tc.name, tc.key) + t.Run(name, func(t *testing.T) { + if key := makeChannelKey(tc.namespace, tc.name); key != tc.key { + t.Errorf("Unexpected ChannelKey. Expected '%v'. Actual '%v'", tc.key, key) + } + }) + } +} + +func TestNewHandler(t *testing.T) { + testCases := []struct { + name string + config Config + createErr string + }{ + { + name: "duplicate channel key", + config: Config{ + ChannelConfigs: []ChannelConfig{ + { + Namespace: "default", + Name: "duplicate", + }, + { + Namespace: "default", + Name: "duplicate", + }, + }, + }, + createErr: "duplicate channel key: default/duplicate", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := NewHandler(zap.NewNop(), tc.config) + if tc.createErr != "" { + if err == nil { + t.Errorf("Expected NewHandler error: '%v'. Actual nil", tc.createErr) + } else if err.Error() != tc.createErr { + t.Errorf("Unexpected NewHandler error. Expected '%v'. Actual '%v'", tc.createErr, err) + } + return + } else if err != nil { + t.Errorf("Unexpected NewHandler error. Expected nil. Actual '%v'", err) + } + }) + } +} + +func TestCopyWithNewConfig(t *testing.T) { + orig := Config{ + ChannelConfigs: []ChannelConfig{ + { + Namespace: "default", + Name: "c1", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "callabledomain", + }, + }, + }, + }, + }, + } + updated := Config{ + ChannelConfigs: []ChannelConfig{ + { + Namespace: "default", + Name: "somethingdifferent", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: "sinkabledomain", + }, + }, + }, + }, + }, + } + if cmp.Equal(orig, updated) { + t.Errorf("Orig and updated must be different") + } + h, err := NewHandler(zap.NewNop(), orig) + if err != nil { + t.Errorf("Unable to create handler, %v", err) + } + if !cmp.Equal(h.config, orig) { + t.Errorf("Incorrect config. Expected '%v'. Actual '%v'", orig, h.config) + } + newH, err := h.CopyWithNewConfig(updated) + if err != nil { + t.Errorf("Unable to copy handler: %v", err) + } + if h.logger != newH.logger { + t.Errorf("Did not copy logger") + } + if !cmp.Equal(newH.config, updated) { + t.Errorf("Incorrect copied config. Expected '%v'. Actual '%v'", updated, newH.config) + } +} + +func TestConfigDiff(t *testing.T) { + config := Config{ + ChannelConfigs: []ChannelConfig{ + { + Namespace: "default", + Name: "c1", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "callabledomain", + }, + }, + }, + }, + }, + } + testCases := []struct { + name string + orig Config + updated Config + expectedDiff bool + }{ + { + name: "same", + orig: config, + updated: config, + expectedDiff: false, + }, + { + name: "different", + orig: config, + updated: Config{ + ChannelConfigs: []ChannelConfig{ + { + Namespace: "default", + Name: "c1", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "different", + }, + }, + }, + }, + }, + }, + expectedDiff: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + h, err := NewHandler(zap.NewNop(), tc.orig) + if err != nil { + t.Errorf("Unable to create handler: %v", err) + } + diff := h.ConfigDiff(tc.updated) + + if hasDiff := diff != ""; hasDiff != tc.expectedDiff { + t.Errorf("Unexpected diff result. Expected %v. Actual %v", tc.expectedDiff, hasDiff) + } + }) + } +} + +func TestServeHTTP(t *testing.T) { + testCases := map[string]struct { + name string + config Config + respStatusCode int + key string + expectedStatusCode int + }{ + "non-existent channel": { + config: Config{}, + key: "default.does-not-exist", + expectedStatusCode: http.StatusInternalServerError, + }, + "pass through failure": { + config: Config{ + ChannelConfigs: []ChannelConfig{ + { + Namespace: "default", + Name: "first-channel", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: replaceDomain, + }, + }, + }, + }, + }, + }, + respStatusCode: http.StatusInternalServerError, + key: "first-channel.default", + expectedStatusCode: http.StatusInternalServerError, + }, + "choose channel": { + config: Config{ + ChannelConfigs: []ChannelConfig{ + { + Namespace: "default", + Name: "first-channel", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: "first-to-domain", + }, + }, + }, + }, + { + Namespace: "default", + Name: "second-channel", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: replaceDomain, + }, + }, + }, + }, + }, + }, + respStatusCode: http.StatusOK, + key: "second-channel.default", + expectedStatusCode: http.StatusOK, + }, + } + requestWithChannelKey := func(key string) *http.Request { + r := httptest.NewRequest("POST", fmt.Sprintf("http://%s/", key), strings.NewReader("{}")) + return r + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + server := httptest.NewServer(&fakeHandler{statusCode: tc.respStatusCode}) + defer server.Close() + + // Rewrite the replaceDomains to call the server we just created. + replaceDomains(tc.config, server.URL[7:]) + + h, err := NewHandler(zap.NewNop(), tc.config) + if err != nil { + t.Errorf("Unexpected NewHandler error: '%v'", err) + } + + r := requestWithChannelKey(tc.key) + w := httptest.NewRecorder() + h.ServeHTTP(w, r) + resp := w.Result() + if resp.StatusCode != tc.expectedStatusCode { + t.Errorf("Unexpected status code. Expected %v, actual %v", tc.expectedStatusCode, resp.StatusCode) + } + if w.Body.String() != "" { + t.Errorf("Expected empty response body. Actual: %v", w.Body) + } + }) + } +} + +func replaceDomains(config Config, replacement string) { + for i, cc := range config.ChannelConfigs { + for j, sub := range cc.FanoutConfig.Subscriptions { + if sub.CallableDomain == replaceDomain { + sub.CallableDomain = replacement + } + if sub.SinkableDomain == replaceDomain { + sub.SinkableDomain = replacement + } + cc.FanoutConfig.Subscriptions[j] = sub + } + config.ChannelConfigs[i] = cc + } +} + +type fakeHandler struct { + statusCode int +} + +func (h *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + w.WriteHeader(h.statusCode) +} diff --git a/pkg/sidecar/swappable/swappable.go b/pkg/sidecar/swappable/swappable.go new file mode 100644 index 00000000000..6c8f75d0d9e --- /dev/null +++ b/pkg/sidecar/swappable/swappable.go @@ -0,0 +1,68 @@ +/* +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 swappable + +import ( + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "go.uber.org/zap" + "net/http" + "sync/atomic" +) + +// http.Handler that atomically swapping between underlying handlers. +type Handler struct { + // The current multichannelfanout.Handler to delegate HTTP requests to. Never use this directly, + // instead use {get,set}MultiChannelFanoutHandler, which enforces the type we expect. + fanout atomic.Value + logger *zap.Logger +} + +// NewHandler creates a new swappable.Handler. +func NewHandler(handler *multichannelfanout.Handler, logger *zap.Logger) *Handler { + h := &Handler{ + logger: logger.With(zap.String("httpHandler", "swappable")), + } + h.SetMultiChannelFanoutHandler(handler) + return h +} + +func NewEmptyHandler(logger *zap.Logger) (*Handler, error) { + h, err := multichannelfanout.NewHandler(logger, multichannelfanout.Config{}) + if err != nil { + return nil, err + } + return NewHandler(h, logger), nil +} + +// getMultiChannelFanoutHandler gets the current multichannelfanout.Handler to delegate all HTTP +// requests to. +func (h *Handler) GetMultiChannelFanoutHandler() *multichannelfanout.Handler { + return h.fanout.Load().(*multichannelfanout.Handler) +} + +// setMultiChannelFanoutHandler sets a new multichannelfanout.Handler to delegate all subsequent +// HTTP requests to. +func (h *Handler) SetMultiChannelFanoutHandler(new *multichannelfanout.Handler) { + h.fanout.Store(new) +} + +// ServeHTTP delegates all HTTP requests to the current multichannelfanout.Handler. +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Hand work off to the current multi channel fanout handler. + h.logger.Debug("ServeHTTP request received") + h.GetMultiChannelFanoutHandler().ServeHTTP(w, r) +} diff --git a/pkg/sidecar/swappable/swappable_test.go b/pkg/sidecar/swappable/swappable_test.go new file mode 100644 index 00000000000..55de79207bd --- /dev/null +++ b/pkg/sidecar/swappable/swappable_test.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 swappable + +import ( + "fmt" + "github.com/knative/eventing/pkg/sidecar/fanout" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "go.uber.org/zap" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +const ( + namespace = "default" + name = "channel1" + replaceDomain = "replaceDomain" +) + +func TestHandler(t *testing.T) { + testCases := map[string]struct { + configs []multichannelfanout.Config + }{ + "config change": { + configs: []multichannelfanout.Config{ + { + ChannelConfigs: []multichannelfanout.ChannelConfig{ + { + Namespace: namespace, + Name: name, + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: replaceDomain, + }, + }, + }, + }, + }, + }, + { + ChannelConfigs: []multichannelfanout.ChannelConfig{ + { + Namespace: namespace, + Name: name, + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: replaceDomain, + }, + }, + }, + }, + }, + }, + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + h, err := NewEmptyHandler(zap.NewNop()) + if err != nil { + t.Errorf("Unexpected error creating handler: %v", err) + } + for _, c := range tc.configs { + updateConfigAndTest(t, h, c) + } + }) + } +} + +func updateConfigAndTest(t *testing.T, h *Handler, config multichannelfanout.Config) { + server := httptest.NewServer(&successHandler{}) + defer server.Close() + + nh, err := multichannelfanout.NewHandler(zap.NewNop(), replaceDomains(config, server.URL[7:])) + if err != nil { + t.Errorf("Unexpected error creating multiChannelFanoutHandler: %v", err) + } + orig := h.GetMultiChannelFanoutHandler() + h.SetMultiChannelFanoutHandler(nh) + if orig == h.GetMultiChannelFanoutHandler() { + t.Errorf("Expected the inner multiChannelFanoutHandler to change, it didn't: %v", orig) + } + + w := httptest.NewRecorder() + h.ServeHTTP(w, makeRequest(namespace, name)) + if w.Code != http.StatusOK { + t.Errorf("Unexpected response code. Expected 200. Actual %v", w.Code) + } +} + +type successHandler struct{} + +func (*successHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + r.Body.Close() +} + +func makeRequest(namespace, name string) *http.Request { + r := httptest.NewRequest("POST", fmt.Sprintf("http://%s.%s/", name, namespace), strings.NewReader("")) + return r +} + +func replaceDomains(c multichannelfanout.Config, replacement string) multichannelfanout.Config { + for i, cc := range c.ChannelConfigs { + for j, sub := range cc.FanoutConfig.Subscriptions { + if sub.SinkableDomain == replaceDomain { + sub.SinkableDomain = replacement + } + if sub.CallableDomain == replaceDomain { + sub.CallableDomain = replacement + } + cc.FanoutConfig.Subscriptions[j] = sub + } + c.ChannelConfigs[i] = cc + } + return c +} From e2c2f1fbc7e2178d08d75354fc995b8c2d3f86b2 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Fri, 28 Sep 2018 09:52:04 -0700 Subject: [PATCH 02/22] Start on unit tests. --- .../stub/clusterprovisioner/controller.go | 1 + .../clusterprovisioner/controller_test.go | 19 ++++ .../clusterprovisioner/reconciler_test.go | 88 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 pkg/buses/eventing/stub/clusterprovisioner/controller_test.go create mode 100644 pkg/buses/eventing/stub/clusterprovisioner/reconciler_test.go diff --git a/pkg/buses/eventing/stub/clusterprovisioner/controller.go b/pkg/buses/eventing/stub/clusterprovisioner/controller.go index b34ed414601..a2a7a821007 100644 --- a/pkg/buses/eventing/stub/clusterprovisioner/controller.go +++ b/pkg/buses/eventing/stub/clusterprovisioner/controller.go @@ -78,6 +78,7 @@ func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Cont return c, h, nil } +// swapHttpHandlerConfig creates a function that will lock func swapHttpHandlerConfig(s *swappable.Handler, sLock sync.Mutex) func(multichannelfanout.Config) error { return func(config multichannelfanout.Config) error { sLock.Lock() diff --git a/pkg/buses/eventing/stub/clusterprovisioner/controller_test.go b/pkg/buses/eventing/stub/clusterprovisioner/controller_test.go new file mode 100644 index 00000000000..503bbde66e2 --- /dev/null +++ b/pkg/buses/eventing/stub/clusterprovisioner/controller_test.go @@ -0,0 +1,19 @@ +/* +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 clusterprovisioner + +// Test the http handler. diff --git a/pkg/buses/eventing/stub/clusterprovisioner/reconciler_test.go b/pkg/buses/eventing/stub/clusterprovisioner/reconciler_test.go new file mode 100644 index 00000000000..0ea094e62ba --- /dev/null +++ b/pkg/buses/eventing/stub/clusterprovisioner/reconciler_test.go @@ -0,0 +1,88 @@ +/* +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 clusterprovisioner + +import ( + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "testing" + + controllertesting "github.com/knative/eventing/pkg/controller/testing" +) + +func init() { + // Add types to scheme + eventingv1alpha1.AddToScheme(scheme.Scheme) +} + +func TestInjectClient(t *testing.T) { + r := &reconciler{} + orig := r.client + n := fake.NewFakeClient() + if orig == n { + t.Errorf("Original and new clients are identical: %v", orig) + } + err := r.InjectClient(n) + if err != nil { + t.Errorf("Unexpected error injecting the client: %v", err) + } + if n != r.client { + t.Errorf("Unexpected client. Expected: '%v'. Actual: '%v'", n, r.client) + } +} + +func TestReconcile(t *testing.T) { + testCases := []controllertesting.TestCase{ + { + Name: "CP not found", + }, + { + Name: "Unable to get CP", + }, + { + Name: "Should not reconcile", + }, + { + Name: "Delete provisioner fails", + }, + { + Name: "Delete dispatcher fails", + }, + { + Name: "Delete succeeds", + }, + { + Name: "Creates dispatcher fails", + }, + { + Name: "Creates dispatcher - already exists", + }, + { + Name: "Creates dispatcher - now owned by CP", + }, + { + Name: "Creates dispatcher succeeds", + }, + { + Name: "Sync provisioners - fails", + }, + { + Name: "Sync provisioners", + }, + } +} From b632f71df566ebe08fec218ea37f2bb1142f8547 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Fri, 28 Sep 2018 12:26:30 -0700 Subject: [PATCH 03/22] Fulfill Sinkable and Subscribable. --- cmd/fanoutsidecar/kodata/LICENSE | 201 + cmd/fanoutsidecar/kodata/VENDOR-LICENSE | 5805 +++++++++++++++++ cmd/fanoutsidecar/main.go | 109 + .../buses/stub2/stub-bus.yaml | 42 +- .../buses/stub2/tmp}/bus2.yaml | 25 +- pkg/buses/eventing/channel.yaml | 14 - .../clusterprovisioner/controller_test.go | 19 - .../stub/clusterprovisioner/stub_test.go | 24 - pkg/buses/eventing/system.yaml | 19 - .../eventing/stub/channel/controller.go | 47 +- .../eventing/stub/channel/reconcile.go} | 226 +- .../stub/clusterprovisioner/controller.go | 39 +- .../stub/clusterprovisioner/reconcile.go} | 141 +- .../clusterprovisioner/reconcile_test.go} | 0 .../eventing/stub/controller}/main.go | 26 +- .../filesystem/config_map_handler.go | 138 + .../filesystem/config_map_handler_test.go | 390 ++ pkg/sidecar/configmap/parse/parse.go | 63 + pkg/sidecar/configmap/watcher/watcher.go | 118 + pkg/sidecar/swappable/swappable.go | 41 +- 20 files changed, 7065 insertions(+), 422 deletions(-) create mode 100644 cmd/fanoutsidecar/kodata/LICENSE create mode 100644 cmd/fanoutsidecar/kodata/VENDOR-LICENSE create mode 100644 cmd/fanoutsidecar/main.go rename pkg/buses/eventing/cluster-bus-provisioner.yaml => config/buses/stub2/stub-bus.yaml (60%) rename {pkg/buses/eventing => config/buses/stub2/tmp}/bus2.yaml (60%) delete mode 100644 pkg/buses/eventing/channel.yaml delete mode 100644 pkg/buses/eventing/stub/clusterprovisioner/controller_test.go delete mode 100644 pkg/buses/eventing/stub/clusterprovisioner/stub_test.go delete mode 100644 pkg/buses/eventing/system.yaml rename pkg/{buses => controller}/eventing/stub/channel/controller.go (65%) rename pkg/{buses/eventing/stub/channel/reconciler.go => controller/eventing/stub/channel/reconcile.go} (70%) rename pkg/{buses => controller}/eventing/stub/clusterprovisioner/controller.go (63%) rename pkg/{buses/eventing/stub/clusterprovisioner/reconciler.go => controller/eventing/stub/clusterprovisioner/reconcile.go} (62%) rename pkg/{buses/eventing/stub/clusterprovisioner/reconciler_test.go => controller/eventing/stub/clusterprovisioner/reconcile_test.go} (100%) rename pkg/{buses/eventing/stub/dispatcher => controller/eventing/stub/controller}/main.go (77%) create mode 100644 pkg/sidecar/configmap/filesystem/config_map_handler.go create mode 100644 pkg/sidecar/configmap/filesystem/config_map_handler_test.go create mode 100644 pkg/sidecar/configmap/parse/parse.go create mode 100644 pkg/sidecar/configmap/watcher/watcher.go diff --git a/cmd/fanoutsidecar/kodata/LICENSE b/cmd/fanoutsidecar/kodata/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/cmd/fanoutsidecar/kodata/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/cmd/fanoutsidecar/kodata/VENDOR-LICENSE b/cmd/fanoutsidecar/kodata/VENDOR-LICENSE new file mode 100644 index 00000000000..332850b0579 --- /dev/null +++ b/cmd/fanoutsidecar/kodata/VENDOR-LICENSE @@ -0,0 +1,5805 @@ + +=========================================================== +Import: github.com/knative/eventing/vendor/cloud.google.com/go + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/beorn7/perks + +Copyright (C) 2013 Blake Mizerany + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/davecgh/go-spew + +ISC License + +Copyright (c) 2012-2016 Dave Collins + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/ghodss/yaml + +The MIT License (MIT) + +Copyright (c) 2014 Sam Ghods + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/go-logr/logr + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/go-logr/zapr + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/gogo/protobuf + +Protocol Buffers for Go with Gadgets + +Copyright (c) 2013, The GoGo Authors. All rights reserved. +http://github.com/gogo/protobuf + +Go support for Protocol Buffers - Google's data interchange format + +Copyright 2010 The Go Authors. All rights reserved. +https://github.com/golang/protobuf + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/golang/glog + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/golang/groupcache + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/golang/protobuf + +Go support for Protocol Buffers - Google's data interchange format + +Copyright 2010 The Go Authors. All rights reserved. +https://github.com/golang/protobuf + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/google/btree + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/google/go-cmp + +Copyright (c) 2017 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/google/gofuzz + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/google/uuid + +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/googleapis/gnostic + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/gregjones/httpcache + +Copyright © 2012 Greg Jones (greg.jones@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/hashicorp/golang-lru + +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/imdario/mergo + +Copyright (c) 2013 Dario Castañé. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/json-iterator/go + +MIT License + +Copyright (c) 2016 json-iterator + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/knative/pkg + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf_extensions + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/peterbourgon/diskv + +Copyright (c) 2011-2012 Peter Bourgon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/prometheus/client_golang + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/prometheus/client_model + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/prometheus/common + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/prometheus/procfs + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/spf13/pflag + +Copyright (c) 2012 Alex Ogier. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/atomic + +Copyright (c) 2016 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/multierr + +Copyright (c) 2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/zap + +Copyright (c) 2016-2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/crypto + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/net + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/oauth2 + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/sys + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/text + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/time + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/gopkg.in/inf.v0 + +Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go +Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/gopkg.in/yaml.v2 + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/k8s.io/api + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/k8s.io/apimachinery + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/k8s.io/client-go + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/k8s.io/kube-openapi + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/sigs.k8s.io/controller-runtime + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/cmd/fanoutsidecar/main.go b/cmd/fanoutsidecar/main.go new file mode 100644 index 00000000000..389fb679ae1 --- /dev/null +++ b/cmd/fanoutsidecar/main.go @@ -0,0 +1,109 @@ +/* +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. +*/ + +// A sidecar that implements filtering of Cloud Events sent out via HTTP. Implemented as an HTTP +// proxy that the main containers need to write through. + +package main + +import ( + "flag" + "fmt" + "github.com/knative/eventing/pkg/sidecar/configmap/filesystem" + "github.com/knative/eventing/pkg/sidecar/configmap/watcher" + "github.com/knative/eventing/pkg/sidecar/swappable" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "net/http" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/runtime/signals" + "time" +) + +var ( + readTimeout = time.Minute + writeTimeout = time.Minute +) + +func main() { + portFlag := flag.Int("sidecar_port", -1, "The port to run the sidecar on.") + configMapFlag := flag.String("config_map_noticer", "", "The system to notice changes to the ConfigMap. Valid values are: 'volume', 'watcher'.") + + flag.Parse() + + logger, err := zap.NewProduction() + if err != nil { + panic(err) + } + + if *portFlag < 0 { + logger.Fatal("--sidecar_port flag must be set") + } + + sh, err := swappable.NewEmptyHandler(logger) + if err != nil { + logger.Fatal("Unable to create swappable.Handler", zap.Error(err)) + } + + // Setup something to notice that the ConfigMap has updated. + switch *configMapFlag { + case "volume": + _, err = filesystem.NewConfigMapWatcher(logger, filesystem.ConfigDir, sh.UpdateConfig) + if err != nil { + logger.Fatal("Unable to create filesystem.configMapWatcher", zap.Error(err)) + } + case "watcher": + err = setupWatcher(logger, sh.UpdateConfig) + if err != nil { + logger.Fatal("Unable to create K8s ConfigMap watcher.", zap.Error(err)) + } + default: + logger.Fatal("Need to provide the --config_map_noticer flag") + } + + s := &http.Server{ + Addr: fmt.Sprintf(":%d", *portFlag), + Handler: sh, + ErrorLog: zap.NewStdLog(logger), + ReadTimeout: readTimeout, + WriteTimeout: writeTimeout, + } + logger.Info("Fanout sidecar Listening...") + s.ListenAndServe() +} + +func setupWatcher(logger *zap.Logger, configUpdated swappable.UpdateConfig) error { + mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{}) + if err != nil { + logger.Fatal("Error starting manager.", zap.Error(err)) + } + + // Add custom types to this array to get them into the manager's scheme. + corev1.AddToScheme(mgr.GetScheme()) + + + _, err = watcher.NewWatcher(logger, mgr, configUpdated) + if err != nil { + logger.Fatal("Unable to create watcher.configMapWatcher", zap.Error(err)) + } + + // set up signals so we handle the first shutdown signal gracefully + stopCh := signals.SetupSignalHandler() + // Start blocks forever. + go mgr.Start(stopCh) + return nil +} diff --git a/pkg/buses/eventing/cluster-bus-provisioner.yaml b/config/buses/stub2/stub-bus.yaml similarity index 60% rename from pkg/buses/eventing/cluster-bus-provisioner.yaml rename to config/buses/stub2/stub-bus.yaml index dba4c5a3323..b0d801780b3 100644 --- a/pkg/buses/eventing/cluster-bus-provisioner.yaml +++ b/config/buses/stub2/stub-bus.yaml @@ -15,7 +15,7 @@ apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner metadata: - name: stub-bus-provisioner-dispatcher + name: stub-bus-provisioner spec: reconciles: group: eventing.knative.dev/v1alpha1 @@ -26,27 +26,23 @@ spec: apiVersion: apps/v1beta1 kind: Deployment metadata: - name: stub-bus-controller-dispatcher + name: stub-bus-controller namespace: &namespace knative-eventing spec: replicas: 1 selector: matchLabels: &labels - clusterProvisioner: stub-bus-provisioner-dispatcher - role: controller-dispatcher + clusterProvisioner: stub-bus-provisioner + role: controller template: metadata: - annotations: - sidecar.istio.io/inject: "true" labels: *labels spec: serviceAccountName: eventing-controller containers: - name: controller-dispatcher - image: github.com/knative/eventing/pkg/buses/eventing/stub/dispatcher + image: github.com/knative/eventing/pkg/controller/eventing/stub/controller env: - - name: PORT - value: "8080" - name: BUS_NAME value: stub - name: BUS_NAMESPACE @@ -55,3 +51,31 @@ spec: - -logtostderr - -stderrthreshold - INFO + +--- + +apiVersion: apps/v1beta1 +kind: Deployment +metadata: + name: stub-bus-disptacher + namespace: knative-eventing +spec: + replicas: 1 + selector: + matchLabels: &labels + clusterProvisioner: stub-bus-provisioner + role: dispatcher + template: + metadata: + annotations: + sidecar.isio.io/inject: "true" + labels: *labels + spec: + serviceAccountName: eventing-controller # TODO: Change to something much less powerful. + containers: + - name: dispatcher + image: github.com/knative/eventing/cmd/fanoutsidecar + args: + - --sidecar_port=8080 + - --config_map_noticer=watcher + diff --git a/pkg/buses/eventing/bus2.yaml b/config/buses/stub2/tmp/bus2.yaml similarity index 60% rename from pkg/buses/eventing/bus2.yaml rename to config/buses/stub2/tmp/bus2.yaml index d8db6f414dd..dba9f30665f 100644 --- a/pkg/buses/eventing/bus2.yaml +++ b/config/buses/stub2/tmp/bus2.yaml @@ -1,13 +1,3 @@ -apiVersion: eventing.knative.dev/v1alpha1 -kind: ClusterProvisioner -metadata: - name: stub-bus-provisioner-dispatcher-foo -spec: - reconciles: - group: eventing.knative.dev/v1alpha1 - kind: Channel - ---- apiVersion: eventing.knative.dev/v1alpha1 kind: Channel @@ -18,21 +8,12 @@ spec: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner - name: stub-bus-provisioner-dispatcher + name: stub-bus-provisioner channelable: subscribers: - sinkableDomain: message-dumper-foo.default.svc.cluster.local # Already exists callableDomain: event-changer.default.svc.cluster.local ---- - -apiVersion: eventing.knative.dev/v1alpha1 -kind: ClusterProvisioner -metadata: - name: stub-bus-provisioner-dispatcher-bar -spec: - reconciles: - group: eventing.knative.dev/v1alpha1 - kind: Channel + - sinkableDomain: message-dumper-bar.default.svc.cluster.local # Already exists --- @@ -45,7 +26,7 @@ spec: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner - name: stub-bus-provisioner-dispatcher + name: stub-bus-provisioner channelable: subscribers: - sinkableDomain: message-dumper-bar.default.svc.cluster.local # Already exists diff --git a/pkg/buses/eventing/channel.yaml b/pkg/buses/eventing/channel.yaml deleted file mode 100644 index b66f8e10794..00000000000 --- a/pkg/buses/eventing/channel.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: eventing.knative.dev/v1alpha1 -kind: Channel -metadata: - name: wacky-channel -spec: - provisioner: - ref: - apiVersion: eventing.knative.dev/v1alpha1 - kind: ClusterProvisioner - name: stub-bus-provisioner-dispatcher - channelable: - subscribers: - - callableDomain: foo - sinkableDomain: bar diff --git a/pkg/buses/eventing/stub/clusterprovisioner/controller_test.go b/pkg/buses/eventing/stub/clusterprovisioner/controller_test.go deleted file mode 100644 index 503bbde66e2..00000000000 --- a/pkg/buses/eventing/stub/clusterprovisioner/controller_test.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -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 clusterprovisioner - -// Test the http handler. diff --git a/pkg/buses/eventing/stub/clusterprovisioner/stub_test.go b/pkg/buses/eventing/stub/clusterprovisioner/stub_test.go deleted file mode 100644 index 5d31b26ceee..00000000000 --- a/pkg/buses/eventing/stub/clusterprovisioner/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -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 clusterprovisioner - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/buses/eventing/system.yaml b/pkg/buses/eventing/system.yaml deleted file mode 100644 index fa098902cec..00000000000 --- a/pkg/buses/eventing/system.yaml +++ /dev/null @@ -1,19 +0,0 @@ - -#apiVersion: - -#--- - -apiVersion: eventing.knative.dev/v1alpha1 -kind: Channel -metadata: - name: system-test -spec: - provisioner: - ref: - apiVersion: eventing.knative.dev/v1alpha1 - kind: ClusterProvisioner - name: stub-bus-provisioner-dispatcher - channelable: - subscribers: - - sinkableDomain: message-dumper-bar.default.svc.cluster.local # Already exists - callableDomain: event-changer.default.svc.cluster.local diff --git a/pkg/buses/eventing/stub/channel/controller.go b/pkg/controller/eventing/stub/channel/controller.go similarity index 65% rename from pkg/buses/eventing/stub/channel/controller.go rename to pkg/controller/eventing/stub/channel/controller.go index bbb41c12d07..1e032c1837f 100644 --- a/pkg/buses/eventing/stub/channel/controller.go +++ b/pkg/controller/eventing/stub/channel/controller.go @@ -18,9 +18,9 @@ package channel import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "github.com/knative/eventing/pkg/system" "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -30,21 +30,25 @@ import ( const ( // controllerAgentName is the string used by this controller to identify // itself when creating events. - controllerAgentName = "stub-bus-cluster-provisioner-controller" + controllerAgentName = "stub-bus-channel-controller" + configMapName = "stub-bus-dispatcher-config-map" ) -// ProvideController returns a flow controller. -func ProvideController(mgr manager.Manager, logger *zap.Logger, cpRef *corev1.ObjectReference, syncHttpChannelConfig func() error) (*ConfigAndStopCh, error) { - logger = logger.With(zap.String("clusterProvisioner", cpRef.Name)) +var ( + DefaultConfigMapKey = types.NamespacedName{ + Namespace: system.Namespace, + Name: configMapName, + } +) +// ProvideController returns a flow controller. +func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, error) { // Setup a new controller to Reconcile Channels that belong to this Cluster Provisioner (Stub // buses). - r := &reconciler{ - recorder: mgr.GetRecorder(controllerAgentName), - logger: logger, - stubProvisioner: cpRef, - - syncHttpChannelConfig: syncHttpChannelConfig, + r := &reconciler{ + configMapKey: DefaultConfigMapKey, + recorder: mgr.GetRecorder(controllerAgentName), + logger: logger, } c, err := controller.New(controllerAgentName, mgr, controller.Options{ Reconciler: r, @@ -66,22 +70,5 @@ func ProvideController(mgr manager.Manager, logger *zap.Logger, cpRef *corev1.Ob // TODO: Should we watch the K8s service and Istio Virtual Service as well? If they change, we // probably should change it back. - return &ConfigAndStopCh{ - controller: c, - Config: r.getConfig, - StopCh: make(chan struct{}, 1), - }, nil -} - -type ConfigAndStopCh struct { - controller controller.Controller - Config func() multichannelfanout.Config - StopCh chan<- struct{} -} - -func (cas *ConfigAndStopCh) BackgroundStart() { - stopCh := make(chan struct{}, 1) - go func() { - cas.controller.Start(stopCh) - }() + return c, nil } diff --git a/pkg/buses/eventing/stub/channel/reconciler.go b/pkg/controller/eventing/stub/channel/reconcile.go similarity index 70% rename from pkg/buses/eventing/stub/channel/reconciler.go rename to pkg/controller/eventing/stub/channel/reconcile.go index 14a74ce48be..e6d9ed90b5e 100644 --- a/pkg/buses/eventing/stub/channel/reconciler.go +++ b/pkg/controller/eventing/stub/channel/reconcile.go @@ -20,6 +20,8 @@ import ( "context" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/controller" + cpcontroller "github.com/knative/eventing/pkg/controller/eventing/stub/clusterprovisioner" + multichannelfanoutparse "github.com/knative/eventing/pkg/sidecar/configmap/parse" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" "github.com/knative/eventing/pkg/system" @@ -32,6 +34,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" @@ -39,8 +42,9 @@ import ( ) const ( - PortName = "http" - PortNumber = 80 + PortName = "http" + PortNumber = 80 + finalizerName = controllerAgentName ) type reconciler struct { @@ -48,12 +52,7 @@ type reconciler struct { restConfig *rest.Config recorder record.EventRecorder - // This reconciler controls Channels provisioned by this stubProvisioner. - stubProvisioner *corev1.ObjectReference - - config multichannelfanout.Config - - syncHttpChannelConfig func() error + configMapKey client.ObjectKey logger *zap.Logger } @@ -67,7 +66,7 @@ func (r *reconciler) InjectClient(c client.Client) error { } func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { - //TODO use this to store the logger and set a deadline + // TODO: use this to store the logger and set a deadline ctx := context.TODO() logger := r.logger.With(zap.Any("request", request)) @@ -89,10 +88,10 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // Does this Controller control this Channel? if !r.shouldReconcile(c) { - logger.Info("Not reconciling Channel, it is not controlled by this Controller", zap.Any("stubProvisioner", r.stubProvisioner), zap.Any("ref", c.Spec.Provisioner.Ref)) + logger.Info("Not reconciling Channel, it is not controlled by this Controller", zap.Any("ref", c.Spec.Provisioner.Ref)) return reconcile.Result{}, nil } - logger.Debug("Reconciling Channel") + logger.Info("Reconciling Channel") err = r.reconcile(ctx, c) if err != nil { @@ -101,7 +100,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // regardless of the error. } - updateStatusErr := r.updateChannelStatus(ctx, c) + updateStatusErr := r.updateChannel(ctx, c) if updateStatusErr != nil { logger.Info("Error updating Channel Status", zap.Error(updateStatusErr)) return reconcile.Result{}, updateStatusErr @@ -113,7 +112,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // shouldReconcile determines if this Controller should control (and therefore reconcile) a given // ClusterProvisioner. This Controller only handles Stub buses. func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { - return equality.Semantic.DeepEqual(r.stubProvisioner, c.Spec.Provisioner.Ref) + return cpcontroller.IsControlled(c.Spec.Provisioner) } func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) error { @@ -122,39 +121,31 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) // We are syncing three things: // 1. The K8s Service to talk to this Channel. // 2. The Istio VirtualService to talk to this Channel. - // 3. The configuration of Channel's subscriptions. + // 3. The configuration of all Channel subscriptions. // We always need to sync the Channel config, so do it first. err := r.syncChannelConfig(ctx) if err != nil { - logger.Info("Error removing the Channel's config", zap.Error(err)) + logger.Info("Error updating syncing the Channel config", zap.Error(err)) return err } if c.DeletionTimestamp != nil { - err := r.deleteVirtualService(ctx, c) - if err != nil { - logger.Info("Error deleting the Provisioner Controller for the ClusterProvisioner.", zap.Error(err)) - return err - } - err = r.deleteK8sService(ctx, c) - if err != nil { - logger.Info("Error deleting the ClusterProvisioner's K8s Service", zap.Error(err)) - return err - } + // K8s garbage collection will delete the K8s service and VirtualService for this channel. + // We use a finalizer to ensure the channel config has been synced. + r.removeFinalizer(c) return nil } - err = r.syncChannelConfig(ctx) - if err != nil { - logger.Info("Error syncing the Channel config", zap.Error(err)) - return err - } - err = r.createK8sService(ctx, c) + r.addFinalizer(c) + r.makeSubscribable(c) + + svc, err := r.createK8sService(ctx, c) if err != nil { logger.Info("Error creating the Channel's K8s Service", zap.Error(err)) return err } + r.makeSinkable(c, svc) err = r.createVirtualService(ctx, c) if err != nil { @@ -166,8 +157,36 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) return nil } +// makeSubscribable alters the Channel to conform to the Knative Eventing Subscribable interface. +func (r *reconciler) makeSubscribable(c *eventingv1alpha1.Channel) { + // Point at itself. + c.Status.Subscribable.Channelable = corev1.ObjectReference{ + Kind: "Channel", + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Namespace: c.Namespace, + Name: c.Name, + } +} + +// makeSinkable alters the Channel to conform to the Knative Eventing Sinkable interface. +func (r *reconciler) makeSinkable(c *eventingv1alpha1.Channel, svc *corev1.Service) { + c.Status.Sinkable.DomainInternal = controller.ServiceHostName(svc.Name, svc.Namespace) +} + +func (r *reconciler) addFinalizer(c *eventingv1alpha1.Channel) { + finalizers := sets.NewString(c.Finalizers...) + finalizers.Insert(finalizerName) + c.Finalizers = finalizers.List() +} + +func (r *reconciler) removeFinalizer(c *eventingv1alpha1.Channel) { + finalizers := sets.NewString(c.Finalizers...) + finalizers.Delete(finalizerName) + c.Finalizers = finalizers.List() +} + func (r *reconciler) getK8sService(ctx context.Context, c *eventingv1alpha1.Channel) (*corev1.Service, error) { - svcKey := types.NamespacedName{ + svcKey := types.NamespacedName{ Namespace: c.Namespace, Name: controller.ChannelServiceName(c.Name), } @@ -176,7 +195,7 @@ func (r *reconciler) getK8sService(ctx context.Context, c *eventingv1alpha1.Chan return svc, err } -func (r *reconciler) createK8sService(ctx context.Context, c *eventingv1alpha1.Channel) error { +func (r *reconciler) createK8sService(ctx context.Context, c *eventingv1alpha1.Channel) (*corev1.Service, error) { svc, err := r.getK8sService(ctx, c) if errors.IsNotFound(err) { @@ -186,42 +205,26 @@ func (r *reconciler) createK8sService(ctx context.Context, c *eventingv1alpha1.C // If an error occurred in either Get or Create, we need to reconcile again. if err != nil { - return err + return nil, err } - // Ensure this Channel is the owner of the K8s service. + // Check if this Channel is the owner of the K8s service. if !metav1.IsControlledBy(svc, c) { r.logger.Warn("Channel's K8s Service is not owned by the Channel", zap.Any("channel", c), zap.Any("service", svc)) } - return nil + return svc, nil } -func (r *reconciler) deleteK8sService(ctx context.Context, c *eventingv1alpha1.Channel) error { - // TODO: Rely on the garbage collector? - svc, err := r.getK8sService(ctx, c) - - if errors.IsNotFound(err) { - // Nothing to delete. - return nil - } - - if err != nil { - return err - } - - // Verify that we own the Service before deleting it. - if metav1.IsControlledBy(svc, c) { - return r.client.Delete(ctx, svc) - } else { - r.logger.Warn("Channel's K8s Service is not owned by the Channel. Not deleting it.", zap.Any("channel", c), zap.Any("service", svc)) - return nil +func (r *reconciler) getVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) (*istiov1alpha3.VirtualService, error) { + vsk := client.ObjectKey{ + Namespace: c.Namespace, + Name: controller.ChannelVirtualServiceName(c.ObjectMeta.Name), } + vs := &istiov1alpha3.VirtualService{} + err := r.client.Get(ctx, vsk, vs) + return vs, err } - - - - func (r *reconciler) createVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) error { virtualService, err := r.getVirtualService(ctx, c) @@ -246,43 +249,12 @@ func (r *reconciler) createVirtualService(ctx context.Context, c *eventingv1alph return nil } -func (r *reconciler) deleteVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) error { - virtualService, err := r.getVirtualService(ctx, c) - - // If the resource doesn't exist, there is nothing to delete. - if errors.IsNotFound(err) { - return nil - } - - if err != nil { - return err - } - - // Verify that we own the VirtualService before deleting it. - if metav1.IsControlledBy(virtualService, c) { - return r.client.Delete(ctx, virtualService) - } else { - r.logger.Warn("VirtualService exists, but not owned by Channel. Not deleting it.", zap.Any("channel", c), zap.Any("virtualService", virtualService)) - return nil - } -} - -func (r *reconciler) getVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) (*istiov1alpha3.VirtualService, error) { - vsk := client.ObjectKey{ - Namespace: c.Namespace, - Name: controller.ChannelVirtualServiceName(c.ObjectMeta.Name), - } - vs := &istiov1alpha3.VirtualService{} - err := r.client.Get(ctx, vsk, vs) - return vs, err -} - -// newService creates a new Service for a Channel resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the Channel resource that 'owns' it. +// newK8sService creates a new Service for a Channel resource. It also sets the appropriate +// OwnerReferences on the resource so handleObject can discover the Channel resource that 'owns' it. +// As well as being garbage collected when the Channel is deleted. func newK8sService(c *eventingv1alpha1.Channel) *corev1.Service { labels := map[string]string{ - "channel": c.Name, + "channel": c.Name, "provisioner": c.Spec.Provisioner.Ref.Name, } return &corev1.Service{ @@ -309,9 +281,9 @@ func newK8sService(c *eventingv1alpha1.Channel) *corev1.Service { } } -// newVirtualService creates a new VirtualService for a Channel resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the Channel resource that 'owns' it. +// newVirtualService creates a new VirtualService for a Channel resource. It also sets the +// appropriate OwnerReferences on the resource so handleObject can discover the Channel resource +// that 'owns' it. As well as being garbage collected when the Channel is deleted. func newVirtualService(channel *eventingv1alpha1.Channel) *istiov1alpha3.VirtualService { labels := map[string]string{ "channel": channel.Name, @@ -361,7 +333,7 @@ func (r *reconciler) setStatusReady(c *eventingv1alpha1.Channel) { }, } } -func (r *reconciler) updateChannelStatus(ctx context.Context, u *eventingv1alpha1.Channel) error { +func (r *reconciler) updateChannel(ctx context.Context, u *eventingv1alpha1.Channel) error { o := &eventingv1alpha1.Channel{} err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o) if err != nil { @@ -369,8 +341,17 @@ func (r *reconciler) updateChannelStatus(ctx context.Context, u *eventingv1alpha return err } + updated := false + if !equality.Semantic.DeepEqual(o.Finalizers, u.Finalizers) { + updated = true + o.SetFinalizers(u.Finalizers) + } if !equality.Semantic.DeepEqual(o.Status, u.Status) { + updated = true o.Status = u.Status + } + + if updated { return r.client.Update(ctx, o) } return nil @@ -383,15 +364,52 @@ func (r *reconciler) syncChannelConfig(ctx context.Context) error { return err } config := multiChannelFanoutConfig(channels) - r.config = config - return r.syncHttpChannelConfig() + return r.writeConfigMap(ctx, config) +} + +func (r *reconciler) writeConfigMap(ctx context.Context, config *multichannelfanout.Config) error { + logger := r.logger.With(zap.Any("configMap", r.configMapKey)) + + updated, err := multichannelfanoutparse.SerializeConfig(*config) + if err != nil { + r.logger.Error("Unable to serialize config", zap.Error(err), zap.Any("config", config)) + return err + } + + cm := &corev1.ConfigMap{} + err = r.client.Get(ctx, r.configMapKey, cm) + if errors.IsNotFound(err) { + cm = r.createNewConfigMap(updated) + err = r.client.Create(ctx, cm) + } + if err != nil { + logger.Info("Unable to get/create ConfigMap", zap.Error(err)) + return err + } + + cur := cm.Data[multichannelfanoutparse.MultiChannelFanoutConfigKey] + if equality.Semantic.DeepEqual(cur, updated) { + // Nothing to update. + return nil + } + + cm.Data[multichannelfanoutparse.MultiChannelFanoutConfigKey] = updated + return r.client.Update(ctx, cm) } -func (r *reconciler) getConfig() multichannelfanout.Config { - return r.config +func (r *reconciler) createNewConfigMap(config string) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.configMapKey.Namespace, + Name: r.configMapKey.Name, + }, + Data: map[string]string{ + multichannelfanoutparse.MultiChannelFanoutConfigKey: config, + }, + } } -func multiChannelFanoutConfig(channels []eventingv1alpha1.Channel) multichannelfanout.Config { +func multiChannelFanoutConfig(channels []eventingv1alpha1.Channel) *multichannelfanout.Config { cc := make([]multichannelfanout.ChannelConfig, 0) for _, c := range channels { channelable := c.Spec.Channelable @@ -405,7 +423,7 @@ func multiChannelFanoutConfig(channels []eventingv1alpha1.Channel) multichannelf }) } } - return multichannelfanout.Config{ + return &multichannelfanout.Config{ ChannelConfigs: cc, } } diff --git a/pkg/buses/eventing/stub/clusterprovisioner/controller.go b/pkg/controller/eventing/stub/clusterprovisioner/controller.go similarity index 63% rename from pkg/buses/eventing/stub/clusterprovisioner/controller.go rename to pkg/controller/eventing/stub/clusterprovisioner/controller.go index a2a7a821007..f92c89d41af 100644 --- a/pkg/buses/eventing/stub/clusterprovisioner/controller.go +++ b/pkg/controller/eventing/stub/clusterprovisioner/controller.go @@ -18,17 +18,11 @@ package clusterprovisioner import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "github.com/knative/eventing/pkg/buses/eventing/stub/channel" - "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - "github.com/knative/eventing/pkg/sidecar/swappable" "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "net/http" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/source" - "sync" ) const ( @@ -38,29 +32,21 @@ const ( ) // ProvideController returns a flow controller. -func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, http.Handler, error) { +func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, error) { logger = logger.With(zap.String("controller", controllerAgentName)) - h, err := swappable.NewEmptyHandler(logger) - if err != nil { - logger.Error("Unable to create HTTP handler", zap.Error(err)) - return nil, nil, err - } - // Setup a new controller to Reconcile ClusterProvisioners that are Stub buses. r := &reconciler{ mgr: mgr, recorder: mgr.GetRecorder(controllerAgentName), logger: logger, - swapHttpHandlerConfig: swapHttpHandlerConfig(h, sync.Mutex{}), - channelControllers: make(map[corev1.ObjectReference]*channel.ConfigAndStopCh), } c, err := controller.New(controllerAgentName, mgr, controller.Options{ Reconciler: r, }) if err != nil { logger.Error("Unable to create controller.", zap.Error(err)) - return nil, nil, err + return nil, err } // Watch ClusterProvisioners. @@ -69,28 +55,11 @@ func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Cont }, &handler.EnqueueRequestForObject{}) if err != nil { logger.Error("Unable to watch ClusterProvisioners.", zap.Error(err), zap.Any("type", &eventingv1alpha1.ClusterProvisioner{})) - return nil, nil, err + return nil, err } // TODO: Should we watch the K8s service as well? If it changes, we probably should change it // back. - return c, h, nil -} - -// swapHttpHandlerConfig creates a function that will lock -func swapHttpHandlerConfig(s *swappable.Handler, sLock sync.Mutex) func(multichannelfanout.Config) error { - return func(config multichannelfanout.Config) error { - sLock.Lock() - defer sLock.Unlock() - mch := s.GetMultiChannelFanoutHandler() - if diff := mch.ConfigDiff(config); diff != "" { - newH, err := mch.CopyWithNewConfig(config) - if err != nil { - return err - } - s.SetMultiChannelFanoutHandler(newH) - } - return nil - } + return c, nil } diff --git a/pkg/buses/eventing/stub/clusterprovisioner/reconciler.go b/pkg/controller/eventing/stub/clusterprovisioner/reconcile.go similarity index 62% rename from pkg/buses/eventing/stub/clusterprovisioner/reconciler.go rename to pkg/controller/eventing/stub/clusterprovisioner/reconcile.go index 99b30a0d8b5..5ea77eebf0e 100644 --- a/pkg/buses/eventing/stub/clusterprovisioner/reconciler.go +++ b/pkg/controller/eventing/stub/clusterprovisioner/reconcile.go @@ -19,9 +19,7 @@ package clusterprovisioner import ( "context" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "github.com/knative/eventing/pkg/buses/eventing/stub/channel" "github.com/knative/eventing/pkg/controller" - "github.com/knative/eventing/pkg/sidecar/multichannelfanout" "github.com/knative/eventing/pkg/system" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "go.uber.org/zap" @@ -37,21 +35,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "strings" - "sync" ) type reconciler struct { - mgr manager.Manager + mgr manager.Manager client client.Client restConfig *rest.Config recorder record.EventRecorder - channelControllersLock sync.Mutex - channelControllers map[corev1.ObjectReference]*channel.ConfigAndStopCh - - swapHttpHandlerConfig func(config multichannelfanout.Config) error - logger *zap.Logger } @@ -85,10 +76,11 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } // Does this Controller control this ClusterProvisioner? - if !shouldReconcile(cp) { + if !shouldReconcile(cp.Namespace, cp.Name) { logger.Info("Not reconciling ClusterProvisioner, it is not controlled by this Controller", zap.String("APIVersion", cp.APIVersion), zap.String("Kind", cp.Kind), zap.String("Namespace", cp.Namespace), zap.String("name", cp.Name)) return reconcile.Result{}, nil } + logger.Info("Reconciling ClusterProvisioner.") err = r.reconcile(ctx, cp) if err != nil { @@ -106,34 +98,29 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err return reconcile.Result{}, err } +func IsControlled(ref *eventingv1alpha1.ProvisionerReference) bool { + if ref != nil { + return shouldReconcile(ref.Ref.Namespace, ref.Ref.Name) + } + return false +} + // shouldReconcile determines if this Controller should control (and therefore reconcile) a given // ClusterProvisioner. This Controller only handles Stub buses. -func shouldReconcile(cp *eventingv1alpha1.ClusterProvisioner) bool { - return cp.Namespace == "" && strings.HasPrefix(cp.Name, "stub-bus-provisioner-dispatcher") +func shouldReconcile(namespace, name string) bool { + return namespace == "" && name == "stub-bus-provisioner" } func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { logger := r.logger.With(zap.Any("clusterProvisioner", cp)) - // We are syncing two things: + // We are syncing one thing. // 1. The K8s Service to talk to this Stub bus. // - There is a single K8s Service for all requests going to this Stub bus. - // 2. The in-memory Controller watching for Channels using this ClusterProvisioner. if cp.DeletionTimestamp != nil { - // Delete the in-memory controller for the channels. - // Delete the K8s service. - err := r.deleteProvisioner(ctx, cp) - if err != nil { - logger.Info("Error deleting the Provisioner Controller for the ClusterProvisioner.", zap.Error(err)) - return err - } - err = r.deleteDispatcherService(ctx, cp) - if err != nil { - logger.Info("Error deleting the ClusterProvisioner's K8s Service", zap.Error(err)) - return err - } - // TODO: Delete the ClusterProvisioner? Remove a finalizer? + // K8s garbage collection will delete the dispatcher service, once this ClusterProvisioner + // is deleted, so we don't need to do anything. return nil } @@ -143,12 +130,6 @@ func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.Cluster return err } - err = r.syncProvisioner(ctx, cp) - if err != nil { - logger.Info("Error creating the Provisioner Controller for the ClusterProvisioner", zap.Error(err)) - return err - } - r.setStatusReady(cp) return nil } @@ -172,99 +153,13 @@ func (r *reconciler) createDispatcherService(ctx context.Context, cp *eventingv1 return err } - // Ensure this ClusterProvisioner is the owner of the K8s service. + // Check if this ClusterProvisioner is the owner of the K8s service. if !metav1.IsControlledBy(svc, cp) { r.logger.Warn("ClusterProvisioner's K8s Service is not owned by the ClusterProvisioner", zap.Any("clusterProvisioner", cp), zap.Any("service", svc)) } return nil } -func (r *reconciler) deleteDispatcherService(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { - // TODO: Rely on the garbage collector? - return nil -} - -func (r *reconciler) syncProvisioner(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { - cpRef := objectRef(cp) - - cas, err := func() (*channel.ConfigAndStopCh, error) { - r.channelControllersLock.Lock() - defer r.channelControllersLock.Unlock() - - if _, ok := r.channelControllers[cpRef]; !ok { - cas, err := channel.ProvideController(r.mgr, r.logger, &cpRef, r.syncAllChannelsToHttpHandler) - if err != nil { - return nil, err - } - r.channelControllers[cpRef] = cas - return cas, nil - } else { - return nil, nil - } - }() - if err != nil { - return err - } - if cas != nil { - cas.BackgroundStart() - r.logger.Info("Starting Channel Controller", zap.Any("clusterProvisioner", cp)) - } - return nil -} - -func (r *reconciler) deleteProvisioner(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { - cpRef := objectRef(cp) - - // Keep the lock for as little time as possible. - stopCh := func () chan<- struct{} { - r.channelControllersLock.Lock() - defer r.channelControllersLock.Unlock() - - if cc, ok := r.channelControllers[cpRef]; ok { - delete(r.channelControllers, cpRef) - return cc.StopCh - } else { - return nil - } - }() - if stopCh != nil { - stopCh <- struct{}{} - r.logger.Info("Stopping Channel Controller", zap.Any("clusterProvisioner", cp)) - } - return nil -} - -func (r *reconciler) syncAllChannelsToHttpHandler() error { - // Do in a function to hold the lock for as short a time as possible. - configs := func() []multichannelfanout.Config { - configs := make([]multichannelfanout.Config, 0) - r.channelControllersLock.Lock() - defer r.channelControllersLock.Unlock() - for _, cas := range r.channelControllers { - configs = append(configs, cas.Config()) - } - return configs - }() - - channelConfigs := make([]multichannelfanout.ChannelConfig, 0) - for _, config := range configs { - channelConfigs = append(channelConfigs, config.ChannelConfigs...) - } - combined := multichannelfanout.Config{ - ChannelConfigs: channelConfigs, - } - return r.swapHttpHandlerConfig(combined) -} - -func objectRef(cp *eventingv1alpha1.ClusterProvisioner) corev1.ObjectReference { - return corev1.ObjectReference{ - Namespace: cp.Namespace, - Name: cp.Name, - APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), - Kind: "ClusterProvisioner", - } -} - func (r *reconciler) setStatusReady(cp *eventingv1alpha1.ClusterProvisioner) { cp.Status.Conditions = []duckv1alpha1.Condition{ { @@ -273,6 +168,7 @@ func (r *reconciler) setStatusReady(cp *eventingv1alpha1.ClusterProvisioner) { }, } } + func (r *reconciler) updateClusterProvisionerStatus(ctx context.Context, u *eventingv1alpha1.ClusterProvisioner) error { o := &eventingv1alpha1.ClusterProvisioner{} err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o) @@ -322,7 +218,6 @@ func newDispatcherService(cp *eventingv1alpha1.ClusterProvisioner) *corev1.Servi func dispatcherLabels(cpName string) map[string]string { return map[string]string{ "clusterProvisioner": cpName, - "role": "controller-dispatcher", + "role": "dispatcher", } } - diff --git a/pkg/buses/eventing/stub/clusterprovisioner/reconciler_test.go b/pkg/controller/eventing/stub/clusterprovisioner/reconcile_test.go similarity index 100% rename from pkg/buses/eventing/stub/clusterprovisioner/reconciler_test.go rename to pkg/controller/eventing/stub/clusterprovisioner/reconcile_test.go diff --git a/pkg/buses/eventing/stub/dispatcher/main.go b/pkg/controller/eventing/stub/controller/main.go similarity index 77% rename from pkg/buses/eventing/stub/dispatcher/main.go rename to pkg/controller/eventing/stub/controller/main.go index fd8a7174eca..aa65eaf4a14 100644 --- a/pkg/buses/eventing/stub/dispatcher/main.go +++ b/pkg/controller/eventing/stub/controller/main.go @@ -20,11 +20,11 @@ import ( "flag" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/buses" - "github.com/knative/eventing/pkg/buses/eventing/stub/clusterprovisioner" + "github.com/knative/eventing/pkg/controller/eventing/stub/channel" + "github.com/knative/eventing/pkg/controller/eventing/stub/clusterprovisioner" istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "github.com/knative/pkg/signals" "go.uber.org/zap" - "net/http" "os" "sigs.k8s.io/controller-runtime/pkg/client/config" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -43,13 +43,10 @@ func main() { // TODO: probably replace, Bus isn't really a thing anymore. zap.String("eventing.knative.dev/bus", ref.String()), zap.String("eventing.knative.dev/busType", "stub"), - zap.String("eventing.knative.dev/busComponent", buses.Dispatcher), + zap.String("eventing.knative.dev/busComponent", buses.Provisioner), ) flag.Parse() - - //config.GetConfigOrDie() - mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{}) if err != nil { logger.Fatal("Error starting up.", zap.Error(err)) @@ -59,20 +56,13 @@ func main() { eventingv1alpha1.AddToScheme(mgr.GetScheme()) istiov1alpha3.AddToScheme(mgr.GetScheme()) - _, handler, err := clusterprovisioner.ProvideController(mgr, logger.Desugar()) + // The controllers for both the ClusterProvisioner and the Channels created by that + // ClusterProvisioner run in this process. + _, err = clusterprovisioner.ProvideController(mgr, logger.Desugar()) + _, err = channel.ProvideController(mgr, logger.Desugar()) // set up signals so we handle the first shutdown signal gracefully stopCh := signals.SetupSignalHandler() // Start blocks forever. - go mgr.Start(stopCh) - - s := &http.Server{ - Addr: ":8080", - Handler: handler, - ErrorLog: zap.NewStdLog(logger.Desugar()), - } - logger.Info("Stub dispatcher started...") - - err = s.ListenAndServe() - logger.Error("Stub dispatcher stopped", zap.Error(err)) + mgr.Start(stopCh) } diff --git a/pkg/sidecar/configmap/filesystem/config_map_handler.go b/pkg/sidecar/configmap/filesystem/config_map_handler.go new file mode 100644 index 00000000000..d739eb0f785 --- /dev/null +++ b/pkg/sidecar/configmap/filesystem/config_map_handler.go @@ -0,0 +1,138 @@ +/* +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 filesystem + +import ( + "github.com/fsnotify/fsnotify" + "github.com/knative/eventing/pkg/sidecar/configmap/parse" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "github.com/knative/eventing/pkg/sidecar/swappable" + "github.com/knative/pkg/configmap" + "go.uber.org/zap" +) + +const ( + // The mount path of the configMap volume. + ConfigDir = "/etc/config/fanout_sidecar" +) + +// http.Handler that monitors an attached ConfigMap volume for updated configuration and updates its +// behavior based on the configuration. +type configMapWatcher struct { + logger *zap.Logger + // The directory to read the configMap from. + dir string + // Stop the watcher by closing this channel. + watcherStopCh chan<- bool + + // The function to call when the configuration is updated. + configUpdated swappable.UpdateConfig +} + +// NewHandler creates a new configmaphandler.Handler. +func NewConfigMapWatcher(logger *zap.Logger, dir string, updateConfig swappable.UpdateConfig) (chan<- bool, error) { + conf, err := readConfigMap(logger, dir) + if err != nil { + logger.Error("Unable to read configMap", zap.Error(err)) + return nil, err + } + + logger.Info("Read initial configMap", zap.Any("conf", conf)) + + err = updateConfig(conf) + if err != nil { + logger.Error("Unable to use the initial configMap: %v", zap.Error(err)) + return nil, err + } + + cmh := &configMapWatcher{ + logger: logger, + dir: dir, + configUpdated: updateConfig, + } + watcherStopCh, err := cmh.startWatcher(dir) + if err != nil { + logger.Error("Unable to start the configMap file watcher", zap.Error(err)) + return nil, err + } + return watcherStopCh, nil +} + +// readConfigMap attempts to read the configMap from the attached volume. +func readConfigMap(logger *zap.Logger, dir string) (*multichannelfanout.Config, error) { + cm, err := configmap.Load(dir) + if err != nil { + logger.Error("Unable to read configMap", zap.Error(err)) + return nil, err + } + return parse.ConfigMapData(logger, cm) +} + +// readConfigMapAndUpdateSubs reads the configMap data and updates configuration of cmh.fanout, if +// it has changed. +// +// Note that this is often called multiple times when the configMap is updated, so it should do its +// best to discard redundant calls. +func (cmh *configMapWatcher) readConfigMapAndUpdateConfig() { + conf, err := readConfigMap(cmh.logger, cmh.dir) + if err != nil { + cmh.logger.Error("Unable to read the configMap", zap.Error(err)) + return + } + err = cmh.configUpdated(conf) + if err != nil { + cmh.logger.Error("Unable to update config", zap.Error(err)) + return + } +} + +// startWatcher starts a background go routine that gets events when the filesystem in configDir is +// changed. +func (cmh *configMapWatcher) startWatcher(dir string) (chan<- bool, error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, err + } + stopCh := make(chan bool) + go func() { + for { + select { + case _, ok := <-watcher.Events: + if !ok { + // Channel closed. + cmh.logger.Error("watcher.Events channel closed") // TODO: Should this be fatal? + return + } + cmh.readConfigMapAndUpdateConfig() + case err, ok := <-watcher.Errors: + if !ok { + // Channel closed. + cmh.logger.Error("watcher.Errors channel closed") // TODO: Should this be fatal? + return + } + cmh.logger.Error("watcher.Errors", zap.Error(err)) + case _, ok := <-stopCh: + if !ok { + // stopCh has been closed + return + } + } + } + }() + + return stopCh, watcher.Add(dir) +} diff --git a/pkg/sidecar/configmap/filesystem/config_map_handler_test.go b/pkg/sidecar/configmap/filesystem/config_map_handler_test.go new file mode 100644 index 00000000000..235d8772649 --- /dev/null +++ b/pkg/sidecar/configmap/filesystem/config_map_handler_test.go @@ -0,0 +1,390 @@ +/* +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 filesystem + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + "github.com/knative/eventing/pkg/sidecar/fanout" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "go.uber.org/atomic" + "go.uber.org/zap" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + "time" +) + +const ( + replaceDomain = "replaceDomain" +) + +func TestReadConfigMap(t *testing.T) { + testCases := []struct { + name string + createDir bool + config string + expected multichannelfanout.Config + expectedErr bool + }{ + { + name: "dir does not exist", + createDir: false, + }, + { + name: "no data", + createDir: true, + expectedErr: true, + }, + { + name: "invalid YAML", + createDir: true, + config: ` + key: + - value + - different indent level + `, + expectedErr: true, + }, + { + name: "valid YAML -- invalid JSON", + config: "{ nil: Key }", + createDir: true, + expectedErr: true, + }, + { + name: "unknown field", + config: "{ channelConfigs: [ { not: a-defined-field } ] }", + createDir: true, + expectedErr: true, + }, + { + name: "valid", + createDir: true, + config: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - callableDomain: event-changer.default.svc.cluster.local + sinkableDomain: message-dumper-bar.default.svc.cluster.local + - callableDomain: message-dumper-foo.default.svc.cluster.local + - sinkableDomain: message-dumper-bar.default.svc.cluster.local + - namespace: default + name: c2 + fanoutConfig: + subscriptions: + - sinkableDomain: message-dumper-foo.default.svc.cluster.local + - namespace: other + name: c3 + fanoutConfig: + subscriptions: + - sinkableDomain: message-dumper-foo.default.svc.cluster.local + `, + expected: multichannelfanout.Config{ + ChannelConfigs: []multichannelfanout.ChannelConfig{ + { + Namespace: "default", + Name: "c1", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "event-changer.default.svc.cluster.local", + SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + }, + { + CallableDomain: "message-dumper-foo.default.svc.cluster.local", + }, + { + SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + }, + }, + }, + }, + { + Namespace: "default", + Name: "c2", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + }, + }, + }, + }, + { + Namespace: "other", + Name: "c3", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + }, + }, + }, + }, + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var dir string + if tc.createDir { + dir = createTempDir(t) + defer os.RemoveAll(dir) + } else { + dir = "/tmp/doesNotExist" + } + writeConfig(t, dir, tc.config) + c, e := readConfigMap(zap.NewNop(), dir) + if tc.expectedErr { + if e == nil { + t.Errorf("Expected an error, actual nil") + } + return + } + if !cmp.Equal(c, tc.expected) { + t.Errorf("Unexpected config. Expected '%v'. Actual '%v'.", tc.expected, c) + } + }) + } +} + +func TestNewHandler(t *testing.T) { + testCases := []struct { + name string + createDir bool + config string + expectErr bool + }{ + { + name: "dir does not exist", + createDir: false, + expectErr: true, + }, + { + name: "duplicate channel key", + createDir: true, + config: ` + channelConfigs: + - namespace: default + name: duplicate + - namespace: default + name: duplicate + `, + expectErr: true, + }, + { + name: "success", + createDir: true, + config: ` + channelConfigs: [] + `, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var dir string + if tc.createDir { + dir = createTempDir(t) + defer os.RemoveAll(dir) + } else { + dir = "/tmp/doesNotExist" + } + writeConfig(t, dir, tc.config) + cmh, err := NewHandler(zap.NewNop(), dir) + if err == nil { + // This is not yet about the logic of the test, just ensuring we don't accidentally + // leave the channel open, which will leave the watcher running. + defer close(cmh.(*configMapWatcher).watcherStopCh) + } + if tc.expectErr { + if err == nil { + t.Errorf("Expected an error, actually nil") + } + return + } + }) + } +} + +func TestServeHTTP(t *testing.T) { + testCases := []struct { + name string + initialConfig string + updatedConfig string + initialRequests int32 + initialRequestsAfterUpdate int32 + updateRequests int32 + }{ + { + name: "send to config", + initialConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + initialRequests: 1, + }, + { + name: "change config", + initialConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + initialRequests: 1, + updatedConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + updateRequests: 1, + }, + { + name: "bad config update -- keeps serving old config", + initialConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + initialRequests: 1, + updatedConfig: ` + channelConfigs: + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + # Duplicate namespace/name + - namespace: default + name: c1 + fanoutConfig: + subscriptions: + - sinkableDomain: replaceDomain + `, + initialRequestsAfterUpdate: 2, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + initialHandler := &fakeHandler{} + initialServer := httptest.NewServer(initialHandler) + defer initialServer.Close() + updateHandler := &fakeHandler{} + updateServer := httptest.NewServer(updateHandler) + defer updateServer.Close() + + dir := createTempDir(t) + defer os.RemoveAll(dir) + writeConfig(t, dir, replaceDomains(tc.initialConfig, initialServer.URL[7:])) + + cmh, _ := NewHandler(zap.NewNop(), dir) + + w := httptest.NewRecorder() + cmh.ServeHTTP(w, makeRequest("default", "c1")) + if w.Result().StatusCode != http.StatusOK { + t.Errorf("Unexpected initial status code: %v", w.Result().StatusCode) + } + if tc.initialRequests != initialHandler.requests.Load() { + t.Errorf("Incorrect initial request count. Expected %v. Actual %v.", + tc.initialRequests, initialHandler.requests.Load()) + } + + if tc.updatedConfig != "" { + writeConfig(t, dir, replaceDomains(tc.updatedConfig, updateServer.URL[7:])) + // The watcher is running in another routine, give it some time to notice the + // change. + time.Sleep(100 * time.Millisecond) + w = httptest.NewRecorder() + cmh.ServeHTTP(w, makeRequest("default", "c1")) + if w.Result().StatusCode != http.StatusOK { + t.Errorf("Unexpected updated status code: %v", w.Result().StatusCode) + } + if tc.updateRequests != updateHandler.requests.Load() { + t.Errorf("Incorrect update request count. Expected %v. Actual %v.", tc.updateRequests, updateHandler.requests.Load()) + } + if tc.initialRequestsAfterUpdate != 0 && tc.initialRequestsAfterUpdate != initialHandler.requests.Load() { + t.Errorf("Incorrect requests to initialHandler after config update. Expected %v, actual %v", + tc.initialRequestsAfterUpdate, initialHandler.requests.Load()) + } + } + }) + } +} + +func createTempDir(t *testing.T) string { + dir, err := ioutil.TempDir("", "configMapHandlerTest") + if err != nil { + t.Errorf("Unable to make temp directory: %v", err) + } + return dir +} + +func writeConfig(t *testing.T, dir, config string) { + if config != "" { + // Golang editors tend to replace leading spaces with tabs. YAML is left whitespace + // sensitive, so let's replace the tabs with spaces. + leftSpaceConfig := strings.Replace(config, "\t", " ", -1) + err := ioutil.WriteFile(fmt.Sprintf("%s/%s", dir, multiChannelFanoutConfigKey), []byte(leftSpaceConfig), 0700) + if err != nil { + t.Errorf("Problem writing the config file: %v", err) + } + } +} + +func body(body string) io.ReadCloser { + return ioutil.NopCloser(strings.NewReader(body)) +} + +func makeRequest(namespace, name string) *http.Request { + r := httptest.NewRequest("POST", fmt.Sprintf("http://%s.%s/", name, namespace), body("")) + return r +} + +type fakeHandler struct { + requests atomic.Int32 +} + +func (h *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + w.WriteHeader(http.StatusOK) + h.requests.Inc() +} + +func replaceDomains(config, replacement string) string { + return strings.Replace(config, replaceDomain, replacement, -1) +} diff --git a/pkg/sidecar/configmap/parse/parse.go b/pkg/sidecar/configmap/parse/parse.go new file mode 100644 index 00000000000..1b79c25603e --- /dev/null +++ b/pkg/sidecar/configmap/parse/parse.go @@ -0,0 +1,63 @@ +/* +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 parse + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/util/yaml" +) + +const ( + // The config key that contains all the configuration data. + MultiChannelFanoutConfigKey = "multiChannelFanoutConfig" +) + +// ConfigMapData attempts to parse the config map's data into a multichannelfanout.Config. +func ConfigMapData(logger *zap.Logger, data map[string]string) (*multichannelfanout.Config, error) { + if _, present := data[MultiChannelFanoutConfigKey]; !present { + logger.Error("Expected key not found", zap.String("key", MultiChannelFanoutConfigKey)) + return nil, fmt.Errorf("expected key not found: %v", MultiChannelFanoutConfigKey) + } + jb, err := yaml.ToJSON([]byte(data[MultiChannelFanoutConfigKey])) + if err != nil { + logger.Error("Unable to convert multiChannelFanoutConfig to JSON", zap.Error(err)) + return nil, err + } + var conf multichannelfanout.Config + err = unmarshallJsonDisallowUnknownFields(jb, &conf) + return &conf, err +} + +// unmarshallJsonDisallowUnknownFields unmarshalls JSON, but unlike json.Unmarshall, will fail if +// given an unknown field (rather than json.Unmarshall's ignoring the unknown field). +func unmarshallJsonDisallowUnknownFields(jb []byte, v interface{}) error { + d := json.NewDecoder(bytes.NewReader(jb)) + d.DisallowUnknownFields() + return d.Decode(v) +} + +func SerializeConfig(config multichannelfanout.Config) (string, error) { + jb, err := json.Marshal(config) + if err != nil { + return "", err + } + return string(jb), nil +} diff --git a/pkg/sidecar/configmap/watcher/watcher.go b/pkg/sidecar/configmap/watcher/watcher.go new file mode 100644 index 00000000000..dc5adf0b02e --- /dev/null +++ b/pkg/sidecar/configmap/watcher/watcher.go @@ -0,0 +1,118 @@ +/* +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 watcher + +import ( + "context" + "github.com/knative/eventing/pkg/sidecar/configmap/parse" + "github.com/knative/eventing/pkg/sidecar/swappable" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +const ( + watcherName = "config-map-k8s-watcher" +) + +// ProvideController returns a flow controller. +func NewWatcher(logger *zap.Logger, mgr manager.Manager, configUpdated swappable.UpdateConfig) (controller.Controller, error) { + // Setup a new controller to Reconcile ClusterProvisioners that are Stub buses. + r := &reconciler{ + logger: logger, + configUpdated: configUpdated, + } + c, err := controller.New(watcherName, mgr, controller.Options{ + Reconciler: r, + }) + if err != nil { + logger.Error("Unable to create controller.", zap.Error(err)) + return nil, err + } + + // Watch ConfigMaps. + err = c.Watch(&source.Kind{ + Type: &corev1.ConfigMap{}, + }, &handler.EnqueueRequestForObject{}) + if err != nil { + logger.Error("Unable to watch ConfiMaps.", zap.Error(err)) + return nil, err + } + return c, nil +} + +var _ reconcile.Reconciler = &reconciler{} +type reconciler struct { + logger *zap.Logger + client client.Client + configUpdated swappable.UpdateConfig +} + +func (r *reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) { + //TODO use this to store the logger and set a deadline + ctx := context.TODO() + + + // DO NOT SUBMIT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + if req.Name != "stub-bus-dispatcher-config-map" { + return reconcile.Result{}, nil + } + + cm := &corev1.ConfigMap{} + err := r.client.Get(ctx, req.NamespacedName, cm) + + // The ConfigMap may have been deleted since it was added to the workqueue. If so + // there's nothing to be done. + if errors.IsNotFound(err) { + r.logger.Error("ConfigMap not found", zap.Any("request", req)) + return reconcile.Result{}, nil + } + + // If the ConfigMap exists but could not be retrieved, then we should retry. + if err != nil { + r.logger.Error("Could not get ConfigMap", + zap.Any("request", req), + zap.Error(err)) + return reconcile.Result{}, err + } + + config, err := parse.ConfigMapData(r.logger, cm.Data) + if err != nil { + r.logger.Error("Could not parse ConfigMap", zap.Error(err), + zap.Any("configMap.Data", cm.Data)) + return reconcile.Result{}, err + } + + err = r.configUpdated(config) + if err != nil { + r.logger.Error("Unable to update config", zap.Error(err)) + return reconcile.Result{}, err + } + + return reconcile.Result{}, nil +} + +func (r *reconciler) InjectClient(c client.Client) error { + r.client = c + return nil +} diff --git a/pkg/sidecar/swappable/swappable.go b/pkg/sidecar/swappable/swappable.go index 6c8f75d0d9e..7ae09073755 100644 --- a/pkg/sidecar/swappable/swappable.go +++ b/pkg/sidecar/swappable/swappable.go @@ -17,9 +17,11 @@ limitations under the License. package swappable import ( + "errors" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" "go.uber.org/zap" "net/http" + "sync" "sync/atomic" ) @@ -28,15 +30,20 @@ type Handler struct { // The current multichannelfanout.Handler to delegate HTTP requests to. Never use this directly, // instead use {get,set}MultiChannelFanoutHandler, which enforces the type we expect. fanout atomic.Value + updateLock sync.Mutex logger *zap.Logger } +type UpdateConfig func(config *multichannelfanout.Config) error + +var _ UpdateConfig = (&Handler{}).UpdateConfig + // NewHandler creates a new swappable.Handler. -func NewHandler(handler *multichannelfanout.Handler, logger *zap.Logger) *Handler { +func NewHandler(handler *multichannelfanout.Handler, logger *zap.Logger) *Handler { h := &Handler{ logger: logger.With(zap.String("httpHandler", "swappable")), } - h.SetMultiChannelFanoutHandler(handler) + h.setMultiChannelFanoutHandler(handler) return h } @@ -50,19 +57,43 @@ func NewEmptyHandler(logger *zap.Logger) (*Handler, error) { // getMultiChannelFanoutHandler gets the current multichannelfanout.Handler to delegate all HTTP // requests to. -func (h *Handler) GetMultiChannelFanoutHandler() *multichannelfanout.Handler { +func (h *Handler) getMultiChannelFanoutHandler() *multichannelfanout.Handler { return h.fanout.Load().(*multichannelfanout.Handler) } // setMultiChannelFanoutHandler sets a new multichannelfanout.Handler to delegate all subsequent // HTTP requests to. -func (h *Handler) SetMultiChannelFanoutHandler(new *multichannelfanout.Handler) { +func (h *Handler) setMultiChannelFanoutHandler(new *multichannelfanout.Handler) { h.fanout.Store(new) } +// UpdateConfig copies the current inner multichannelfanout.Handler with the new configuration. If +// the new configuration is valid, then the new inner handler is swapped in and will start serving +// HTTP traffic. +func (h *Handler) UpdateConfig(config *multichannelfanout.Config) error { + if config == nil { + return errors.New("nil config") + } + + h.updateLock.Lock() + defer h.updateLock.Unlock() + + ih := h.getMultiChannelFanoutHandler() + if diff := ih.ConfigDiff(*config); diff != "" { + h.logger.Info("Updating config (-old +new)", zap.String("diff", diff)) + newIh, err := ih.CopyWithNewConfig(*config) + if err != nil { + h.logger.Info("Unable to update config", zap.Error(err), zap.Any("config", config)) + return err + } + h.setMultiChannelFanoutHandler(newIh) + } + return nil +} + // ServeHTTP delegates all HTTP requests to the current multichannelfanout.Handler. func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Hand work off to the current multi channel fanout handler. h.logger.Debug("ServeHTTP request received") - h.GetMultiChannelFanoutHandler().ServeHTTP(w, r) + h.getMultiChannelFanoutHandler().ServeHTTP(w, r) } From eb2c458d2af35f7e0cf2ec27a0c10682137e5e37 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Mon, 1 Oct 2018 14:49:33 -0700 Subject: [PATCH 04/22] Merge updates from fanout. --- .../stub/clusterprovisioner/reconcile_test.go | 2 + .../filesystem/config_map_handler_test.go | 82 +--- .../configmaphandler/config_map_handler.go | 190 --------- .../config_map_handler_test.go | 390 ------------------ pkg/sidecar/swappable/swappable_test.go | 6 +- 5 files changed, 20 insertions(+), 650 deletions(-) delete mode 100644 pkg/sidecar/configmaphandler/config_map_handler.go delete mode 100644 pkg/sidecar/configmaphandler/config_map_handler_test.go diff --git a/pkg/controller/eventing/stub/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/stub/clusterprovisioner/reconcile_test.go index 0ea094e62ba..2df795949dd 100644 --- a/pkg/controller/eventing/stub/clusterprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/stub/clusterprovisioner/reconcile_test.go @@ -17,6 +17,7 @@ limitations under the License. package clusterprovisioner import ( + "fmt" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -85,4 +86,5 @@ func TestReconcile(t *testing.T) { Name: "Sync provisioners", }, } + fmt.Sprintf("%v", testCases) } diff --git a/pkg/sidecar/configmap/filesystem/config_map_handler_test.go b/pkg/sidecar/configmap/filesystem/config_map_handler_test.go index 235d8772649..ad80c53ef46 100644 --- a/pkg/sidecar/configmap/filesystem/config_map_handler_test.go +++ b/pkg/sidecar/configmap/filesystem/config_map_handler_test.go @@ -1,12 +1,9 @@ /* 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. @@ -19,8 +16,10 @@ package filesystem import ( "fmt" "github.com/google/go-cmp/cmp" + "github.com/knative/eventing/pkg/sidecar/configmap/parse" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "github.com/knative/eventing/pkg/sidecar/swappable" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "go.uber.org/atomic" "go.uber.org/zap" @@ -43,7 +42,7 @@ func TestReadConfigMap(t *testing.T) { name string createDir bool config string - expected multichannelfanout.Config + expected *multichannelfanout.Config expectedErr bool }{ { @@ -101,7 +100,7 @@ func TestReadConfigMap(t *testing.T) { subscriptions: - sinkableDomain: message-dumper-foo.default.svc.cluster.local `, - expected: multichannelfanout.Config{ + expected: &multichannelfanout.Config{ ChannelConfigs: []multichannelfanout.ChannelConfig{ { Namespace: "default", @@ -171,64 +170,6 @@ func TestReadConfigMap(t *testing.T) { } } -func TestNewHandler(t *testing.T) { - testCases := []struct { - name string - createDir bool - config string - expectErr bool - }{ - { - name: "dir does not exist", - createDir: false, - expectErr: true, - }, - { - name: "duplicate channel key", - createDir: true, - config: ` - channelConfigs: - - namespace: default - name: duplicate - - namespace: default - name: duplicate - `, - expectErr: true, - }, - { - name: "success", - createDir: true, - config: ` - channelConfigs: [] - `, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var dir string - if tc.createDir { - dir = createTempDir(t) - defer os.RemoveAll(dir) - } else { - dir = "/tmp/doesNotExist" - } - writeConfig(t, dir, tc.config) - cmh, err := NewHandler(zap.NewNop(), dir) - if err == nil { - // This is not yet about the logic of the test, just ensuring we don't accidentally - // leave the channel open, which will leave the watcher running. - defer close(cmh.(*configMapWatcher).watcherStopCh) - } - if tc.expectErr { - if err == nil { - t.Errorf("Expected an error, actually nil") - } - return - } - }) - } -} - func TestServeHTTP(t *testing.T) { testCases := []struct { name string @@ -312,10 +253,17 @@ func TestServeHTTP(t *testing.T) { defer os.RemoveAll(dir) writeConfig(t, dir, replaceDomains(tc.initialConfig, initialServer.URL[7:])) - cmh, _ := NewHandler(zap.NewNop(), dir) + sh, err := swappable.NewEmptyHandler(zap.NewNop()) + if err != nil { + t.Errorf("Unexpected error making swappable.Handler: %+v", err) + } + _, err = NewConfigMapWatcher(zap.NewNop(), dir, sh.UpdateConfig) + if err != nil { + t.Errorf("Unexpected error making filesystem.configMapWatcher") + } w := httptest.NewRecorder() - cmh.ServeHTTP(w, makeRequest("default", "c1")) + sh.ServeHTTP(w, makeRequest("default", "c1")) if w.Result().StatusCode != http.StatusOK { t.Errorf("Unexpected initial status code: %v", w.Result().StatusCode) } @@ -330,7 +278,7 @@ func TestServeHTTP(t *testing.T) { // change. time.Sleep(100 * time.Millisecond) w = httptest.NewRecorder() - cmh.ServeHTTP(w, makeRequest("default", "c1")) + sh.ServeHTTP(w, makeRequest("default", "c1")) if w.Result().StatusCode != http.StatusOK { t.Errorf("Unexpected updated status code: %v", w.Result().StatusCode) } @@ -359,7 +307,7 @@ func writeConfig(t *testing.T, dir, config string) { // Golang editors tend to replace leading spaces with tabs. YAML is left whitespace // sensitive, so let's replace the tabs with spaces. leftSpaceConfig := strings.Replace(config, "\t", " ", -1) - err := ioutil.WriteFile(fmt.Sprintf("%s/%s", dir, multiChannelFanoutConfigKey), []byte(leftSpaceConfig), 0700) + err := ioutil.WriteFile(fmt.Sprintf("%s/%s", dir, parse.MultiChannelFanoutConfigKey), []byte(leftSpaceConfig), 0700) if err != nil { t.Errorf("Problem writing the config file: %v", err) } diff --git a/pkg/sidecar/configmaphandler/config_map_handler.go b/pkg/sidecar/configmaphandler/config_map_handler.go deleted file mode 100644 index 7a362dd38b2..00000000000 --- a/pkg/sidecar/configmaphandler/config_map_handler.go +++ /dev/null @@ -1,190 +0,0 @@ -/* -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 configmaphandler - -import ( - "bytes" - "encoding/json" - "fmt" - "github.com/fsnotify/fsnotify" - "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - "github.com/knative/pkg/configmap" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/util/yaml" - "net/http" - "sync/atomic" -) - -const ( - // The mount path of the configMap volume. - ConfigDir = "/etc/config/fanout_sidecar" - // The config key that contains all the configuration data. - multiChannelFanoutConfigKey = "multiChannelFanoutConfig" -) - -// http.Handler that monitors an attached ConfigMap volume for updated configuration and updates its -// behavior based on the configuration. -type configMapHandler struct { - logger *zap.Logger - // The directory to read the configMap from. - dir string - // Stop the watcher by closing this channel. Expected to only be used by tests. - watcherStopCh chan<- bool - // The current multichannelfanout.Handler to delegate HTTP requests to. Never use this directly, - // instead use {get,set}MultiChannelFanoutHandler, which enforces the type we expect. - fanout atomic.Value -} - -// NewHandler creates a new configmaphandler.Handler. -func NewHandler(logger *zap.Logger, dir string) (http.Handler, error) { - conf, err := readConfigMap(logger, dir) - if err != nil { - logger.Error("Unable to read configMap", zap.Error(err)) - return nil, err - } - - logger.Info("Read initial configMap", zap.Any("conf", conf)) - - mcfh, err := multichannelfanout.NewHandler(logger, conf) - if err != nil { - logger.Error("Unable to create multichannelfanout.Handler: %v", zap.Error(err)) - return nil, err - } - - cmh := &configMapHandler{ - logger: logger, - dir: dir, - } - cmh.setMultiChannelFanoutHandler(mcfh) - watcherStopCh, err := cmh.startWatcher(dir) - if err != nil { - logger.Error("Unable to start the configMap file watcher", zap.Error(err)) - return nil, err - } - cmh.watcherStopCh = watcherStopCh - return cmh, nil -} - -// getMultiChannelFanoutHandler gets the current multichannelfanout.Handler to delegate all HTTP -// requests to. -func (cmh *configMapHandler) getMultiChannelFanoutHandler() *multichannelfanout.Handler { - return cmh.fanout.Load().(*multichannelfanout.Handler) -} - -// setMultiChannelFanoutHandler sets a new multichannelfanout.Handler to delegate all subsequent -// HTTP requests to. -func (cmh *configMapHandler) setMultiChannelFanoutHandler(new *multichannelfanout.Handler) { - cmh.fanout.Store(new) -} - -// readConfigMap attempts to read the configMap from the attached volume. -func readConfigMap(logger *zap.Logger, dir string) (multichannelfanout.Config, error) { - cm, err := configmap.Load(dir) - if err != nil { - logger.Error("Unable to read configMap", zap.Error(err)) - return multichannelfanout.Config{}, err - } - - if _, present := cm[multiChannelFanoutConfigKey]; !present { - logger.Error("Expected key not found", zap.String("key", multiChannelFanoutConfigKey)) - return multichannelfanout.Config{}, fmt.Errorf("expected key not found: %v", multiChannelFanoutConfigKey) - } - jb, err := yaml.ToJSON([]byte(cm[multiChannelFanoutConfigKey])) - if err != nil { - logger.Error("Unable to convert multiChannelFanoutConfig to JSON", zap.Error(err)) - return multichannelfanout.Config{}, err - } - var conf multichannelfanout.Config - err = unmarshallJsonDisallowUnknownFields(jb, &conf) - return conf, err -} - -// readConfigMapAndUpdateSubs reads the configMap data and updates configuration of cmh.fanout, if -// it has changed. -// -// Note that this is often called multiple times when the configMap is updated, so it should do its -// best to discard redundant calls. -func (cmh *configMapHandler) readConfigMapAndUpdateConfig() { - conf, err := readConfigMap(cmh.logger, cmh.dir) - if err != nil { - cmh.logger.Error("Unable to read the configMap", zap.Error(err)) - return - } - current := cmh.getMultiChannelFanoutHandler() - if diff := current.ConfigDiff(conf); diff != "" { - cmh.logger.Info("Updating multiChannelFanout config", zap.String("diff (-old, +new)", diff)) - updated, err := current.CopyWithNewConfig(conf) - if err != nil { - cmh.logger.Error("Unable to create updated multichannelfanout.Handler", zap.Error(err)) - return - } - cmh.setMultiChannelFanoutHandler(updated) - } else { - cmh.logger.Info("fanout config unchanged") - } -} - -// startWatcher starts a background go routine that gets events when the filesystem in configDir is -// changed. -func (cmh *configMapHandler) startWatcher(dir string) (chan<- bool, error) { - watcher, err := fsnotify.NewWatcher() - if err != nil { - return nil, err - } - stopCh := make(chan bool) - go func() { - for { - select { - case _, ok := <-watcher.Events: - if !ok { - // Channel closed. - cmh.logger.Error("watcher.Events channel closed") // TODO: Should this be fatal? - return - } - cmh.readConfigMapAndUpdateConfig() - case err, ok := <-watcher.Errors: - if !ok { - // Channel closed. - cmh.logger.Error("watcher.Errors channel closed") // TODO: Should this be fatal? - return - } - cmh.logger.Error("watcher.Errors", zap.Error(err)) - case _, ok := <-stopCh: - if !ok { - // stopCh has been closed - return - } - } - } - }() - - return stopCh, watcher.Add(dir) -} - -// ServeHTTP delegates all HTTP requests to the current multichannelfanout.Handler. -func (cmh *configMapHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Hand work off to the current multi channel fanout handler. - cmh.getMultiChannelFanoutHandler().ServeHTTP(w, r) -} - -// unmarshallJsonDisallowUnknownFields unmarshalls JSON, but unlike json.Unmarshall, will fail if -// given an unknown field (rather than json.Unmarshall's ignoring the unknown field). -func unmarshallJsonDisallowUnknownFields(jb []byte, v interface{}) error { - d := json.NewDecoder(bytes.NewReader(jb)) - d.DisallowUnknownFields() - return d.Decode(v) -} diff --git a/pkg/sidecar/configmaphandler/config_map_handler_test.go b/pkg/sidecar/configmaphandler/config_map_handler_test.go deleted file mode 100644 index 0b847d467e7..00000000000 --- a/pkg/sidecar/configmaphandler/config_map_handler_test.go +++ /dev/null @@ -1,390 +0,0 @@ -/* -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 configmaphandler - -import ( - "fmt" - "github.com/google/go-cmp/cmp" - "github.com/knative/eventing/pkg/sidecar/fanout" - "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" - "go.uber.org/atomic" - "go.uber.org/zap" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "strings" - "testing" - "time" -) - -const ( - replaceDomain = "replaceDomain" -) - -func TestReadConfigMap(t *testing.T) { - testCases := []struct { - name string - createDir bool - config string - expected multichannelfanout.Config - expectedErr bool - }{ - { - name: "dir does not exist", - createDir: false, - }, - { - name: "no data", - createDir: true, - expectedErr: true, - }, - { - name: "invalid YAML", - createDir: true, - config: ` - key: - - value - - different indent level - `, - expectedErr: true, - }, - { - name: "valid YAML -- invalid JSON", - config: "{ nil: Key }", - createDir: true, - expectedErr: true, - }, - { - name: "unknown field", - config: "{ channelConfigs: [ { not: a-defined-field } ] }", - createDir: true, - expectedErr: true, - }, - { - name: "valid", - createDir: true, - config: ` - channelConfigs: - - namespace: default - name: c1 - fanoutConfig: - subscriptions: - - callableDomain: event-changer.default.svc.cluster.local - sinkableDomain: message-dumper-bar.default.svc.cluster.local - - callableDomain: message-dumper-foo.default.svc.cluster.local - - sinkableDomain: message-dumper-bar.default.svc.cluster.local - - namespace: default - name: c2 - fanoutConfig: - subscriptions: - - sinkableDomain: message-dumper-foo.default.svc.cluster.local - - namespace: other - name: c3 - fanoutConfig: - subscriptions: - - sinkableDomain: message-dumper-foo.default.svc.cluster.local - `, - expected: multichannelfanout.Config{ - ChannelConfigs: []multichannelfanout.ChannelConfig{ - { - Namespace: "default", - Name: "c1", - FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ - { - CallableDomain: "event-changer.default.svc.cluster.local", - SinkableDomain: "message-dumper-bar.default.svc.cluster.local", - }, - { - CallableDomain: "message-dumper-foo.default.svc.cluster.local", - }, - { - SinkableDomain: "message-dumper-bar.default.svc.cluster.local", - }, - }, - }, - }, - { - Namespace: "default", - Name: "c2", - FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ - { - SinkableDomain: "message-dumper-foo.default.svc.cluster.local", - }, - }, - }, - }, - { - Namespace: "other", - Name: "c3", - FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ - { - SinkableDomain: "message-dumper-foo.default.svc.cluster.local", - }, - }, - }, - }, - }, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var dir string - if tc.createDir { - dir = createTempDir(t) - defer os.RemoveAll(dir) - } else { - dir = "/tmp/doesNotExist" - } - writeConfig(t, dir, tc.config) - c, e := readConfigMap(zap.NewNop(), dir) - if tc.expectedErr { - if e == nil { - t.Errorf("Expected an error, actual nil") - } - return - } - if !cmp.Equal(c, tc.expected) { - t.Errorf("Unexpected config. Expected '%v'. Actual '%v'.", tc.expected, c) - } - }) - } -} - -func TestNewHandler(t *testing.T) { - testCases := []struct { - name string - createDir bool - config string - expectErr bool - }{ - { - name: "dir does not exist", - createDir: false, - expectErr: true, - }, - { - name: "duplicate channel key", - createDir: true, - config: ` - channelConfigs: - - namespace: default - name: duplicate - - namespace: default - name: duplicate - `, - expectErr: true, - }, - { - name: "success", - createDir: true, - config: ` - channelConfigs: [] - `, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var dir string - if tc.createDir { - dir = createTempDir(t) - defer os.RemoveAll(dir) - } else { - dir = "/tmp/doesNotExist" - } - writeConfig(t, dir, tc.config) - cmh, err := NewHandler(zap.NewNop(), dir) - if err == nil { - // This is not yet about the logic of the test, just ensuring we don't accidentally - // leave the channel open, which will leave the watcher running. - defer close(cmh.(*configMapHandler).watcherStopCh) - } - if tc.expectErr { - if err == nil { - t.Errorf("Expected an error, actually nil") - } - return - } - }) - } -} - -func TestServeHTTP(t *testing.T) { - testCases := []struct { - name string - initialConfig string - updatedConfig string - initialRequests int32 - initialRequestsAfterUpdate int32 - updateRequests int32 - }{ - { - name: "send to config", - initialConfig: ` - channelConfigs: - - namespace: default - name: c1 - fanoutConfig: - subscriptions: - - sinkableDomain: replaceDomain - `, - initialRequests: 1, - }, - { - name: "change config", - initialConfig: ` - channelConfigs: - - namespace: default - name: c1 - fanoutConfig: - subscriptions: - - sinkableDomain: replaceDomain - `, - initialRequests: 1, - updatedConfig: ` - channelConfigs: - - namespace: default - name: c1 - fanoutConfig: - subscriptions: - - sinkableDomain: replaceDomain - `, - updateRequests: 1, - }, - { - name: "bad config update -- keeps serving old config", - initialConfig: ` - channelConfigs: - - namespace: default - name: c1 - fanoutConfig: - subscriptions: - - sinkableDomain: replaceDomain - `, - initialRequests: 1, - updatedConfig: ` - channelConfigs: - - namespace: default - name: c1 - fanoutConfig: - subscriptions: - - sinkableDomain: replaceDomain - # Duplicate namespace/name - - namespace: default - name: c1 - fanoutConfig: - subscriptions: - - sinkableDomain: replaceDomain - `, - initialRequestsAfterUpdate: 2, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - initialHandler := &fakeHandler{} - initialServer := httptest.NewServer(initialHandler) - defer initialServer.Close() - updateHandler := &fakeHandler{} - updateServer := httptest.NewServer(updateHandler) - defer updateServer.Close() - - dir := createTempDir(t) - defer os.RemoveAll(dir) - writeConfig(t, dir, replaceDomains(tc.initialConfig, initialServer.URL[7:])) - - cmh, _ := NewHandler(zap.NewNop(), dir) - - w := httptest.NewRecorder() - cmh.ServeHTTP(w, makeRequest("default", "c1")) - if w.Result().StatusCode != http.StatusOK { - t.Errorf("Unexpected initial status code: %v", w.Result().StatusCode) - } - if tc.initialRequests != initialHandler.requests.Load() { - t.Errorf("Incorrect initial request count. Expected %v. Actual %v.", - tc.initialRequests, initialHandler.requests.Load()) - } - - if tc.updatedConfig != "" { - writeConfig(t, dir, replaceDomains(tc.updatedConfig, updateServer.URL[7:])) - // The watcher is running in another routine, give it some time to notice the - // change. - time.Sleep(100 * time.Millisecond) - w = httptest.NewRecorder() - cmh.ServeHTTP(w, makeRequest("default", "c1")) - if w.Result().StatusCode != http.StatusOK { - t.Errorf("Unexpected updated status code: %v", w.Result().StatusCode) - } - if tc.updateRequests != updateHandler.requests.Load() { - t.Errorf("Incorrect update request count. Expected %v. Actual %v.", tc.updateRequests, updateHandler.requests.Load()) - } - if tc.initialRequestsAfterUpdate != 0 && tc.initialRequestsAfterUpdate != initialHandler.requests.Load() { - t.Errorf("Incorrect requests to initialHandler after config update. Expected %v, actual %v", - tc.initialRequestsAfterUpdate, initialHandler.requests.Load()) - } - } - }) - } -} - -func createTempDir(t *testing.T) string { - dir, err := ioutil.TempDir("", "configMapHandlerTest") - if err != nil { - t.Errorf("Unable to make temp directory: %v", err) - } - return dir -} - -func writeConfig(t *testing.T, dir, config string) { - if config != "" { - // Golang editors tend to replace leading spaces with tabs. YAML is left whitespace - // sensitive, so let's replace the tabs with spaces. - leftSpaceConfig := strings.Replace(config, "\t", " ", -1) - err := ioutil.WriteFile(fmt.Sprintf("%s/%s", dir, multiChannelFanoutConfigKey), []byte(leftSpaceConfig), 0700) - if err != nil { - t.Errorf("Problem writing the config file: %v", err) - } - } -} - -func body(body string) io.ReadCloser { - return ioutil.NopCloser(strings.NewReader(body)) -} - -func makeRequest(namespace, name string) *http.Request { - r := httptest.NewRequest("POST", fmt.Sprintf("http://%s.%s/", name, namespace), body("")) - return r -} - -type fakeHandler struct { - requests atomic.Int32 -} - -func (h *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - w.WriteHeader(http.StatusOK) - h.requests.Inc() -} - -func replaceDomains(config, replacement string) string { - return strings.Replace(config, replaceDomain, replacement, -1) -} diff --git a/pkg/sidecar/swappable/swappable_test.go b/pkg/sidecar/swappable/swappable_test.go index 55de79207bd..3d1b23f18ef 100644 --- a/pkg/sidecar/swappable/swappable_test.go +++ b/pkg/sidecar/swappable/swappable_test.go @@ -94,9 +94,9 @@ func updateConfigAndTest(t *testing.T, h *Handler, config multichannelfanout.Con if err != nil { t.Errorf("Unexpected error creating multiChannelFanoutHandler: %v", err) } - orig := h.GetMultiChannelFanoutHandler() - h.SetMultiChannelFanoutHandler(nh) - if orig == h.GetMultiChannelFanoutHandler() { + orig := h.getMultiChannelFanoutHandler() + h.setMultiChannelFanoutHandler(nh) + if orig == h.getMultiChannelFanoutHandler() { t.Errorf("Expected the inner multiChannelFanoutHandler to change, it didn't: %v", orig) } From e87adf0061f1938c0a08b770fd3af15fd4cb51c1 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Tue, 2 Oct 2018 10:58:29 -0700 Subject: [PATCH 05/22] Rename to in-memory bus. --- .../in-memory.yaml} | 17 ++- config/buses/in-memory/tmp/bus-with-subs.yaml | 104 ++++++++++++++++++ .../buses/{stub2 => in-memory}/tmp/bus2.yaml | 9 +- .../{stub => inmemory}/channel/controller.go | 10 +- .../{stub => inmemory}/channel/reconcile.go | 4 +- .../clusterprovisioner/controller.go | 4 +- .../clusterprovisioner/reconcile.go | 8 +- .../clusterprovisioner/reconcile_test.go | 0 .../{stub => inmemory}/controller/main.go | 6 +- pkg/sidecar/configmap/watcher/watcher.go | 3 +- 10 files changed, 135 insertions(+), 30 deletions(-) rename config/buses/{stub2/stub-bus.yaml => in-memory/in-memory.yaml} (86%) create mode 100644 config/buses/in-memory/tmp/bus-with-subs.yaml rename config/buses/{stub2 => in-memory}/tmp/bus2.yaml (88%) rename pkg/controller/eventing/{stub => inmemory}/channel/controller.go (91%) rename pkg/controller/eventing/{stub => inmemory}/channel/reconcile.go (99%) rename pkg/controller/eventing/{stub => inmemory}/clusterprovisioner/controller.go (95%) rename pkg/controller/eventing/{stub => inmemory}/clusterprovisioner/reconcile.go (97%) rename pkg/controller/eventing/{stub => inmemory}/clusterprovisioner/reconcile_test.go (100%) rename pkg/controller/eventing/{stub => inmemory}/controller/main.go (90%) diff --git a/config/buses/stub2/stub-bus.yaml b/config/buses/in-memory/in-memory.yaml similarity index 86% rename from config/buses/stub2/stub-bus.yaml rename to config/buses/in-memory/in-memory.yaml index b0d801780b3..3f21e37e785 100644 --- a/config/buses/stub2/stub-bus.yaml +++ b/config/buses/in-memory/in-memory.yaml @@ -15,7 +15,7 @@ apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner metadata: - name: stub-bus-provisioner + name: in-memory-bus-provisioner spec: reconciles: group: eventing.knative.dev/v1alpha1 @@ -26,13 +26,13 @@ spec: apiVersion: apps/v1beta1 kind: Deployment metadata: - name: stub-bus-controller + name: in-memory-bus-controller namespace: &namespace knative-eventing spec: replicas: 1 selector: matchLabels: &labels - clusterProvisioner: stub-bus-provisioner + clusterProvisioner: in-memory-bus-provisioner role: controller template: metadata: @@ -40,11 +40,11 @@ spec: spec: serviceAccountName: eventing-controller containers: - - name: controller-dispatcher - image: github.com/knative/eventing/pkg/controller/eventing/stub/controller + - name: controller + image: github.com/knative/eventing/pkg/controller/eventing/inmemory/controller env: - name: BUS_NAME - value: stub + value: in-memory - name: BUS_NAMESPACE value: *namespace args: @@ -57,13 +57,13 @@ spec: apiVersion: apps/v1beta1 kind: Deployment metadata: - name: stub-bus-disptacher + name: in-memory-bus-dispatcher namespace: knative-eventing spec: replicas: 1 selector: matchLabels: &labels - clusterProvisioner: stub-bus-provisioner + clusterProvisioner: in-memory-bus-provisioner role: dispatcher template: metadata: @@ -78,4 +78,3 @@ spec: args: - --sidecar_port=8080 - --config_map_noticer=watcher - diff --git a/config/buses/in-memory/tmp/bus-with-subs.yaml b/config/buses/in-memory/tmp/bus-with-subs.yaml new file mode 100644 index 00000000000..e10fa43287c --- /dev/null +++ b/config/buses/in-memory/tmp/bus-with-subs.yaml @@ -0,0 +1,104 @@ +# This sets up two channels and some subscriptions: +# qux-1 -> event-changer -> qux-2 -> message-dumper-foo +# V L> message-dumper-bar +# message-dumper-foo +# +# So any message going to qux-1 is expected to appear in message-dumper-foo twice (one of which is +# altered) and message-dumper-bar once. + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Channel +metadata: + name: qux-1 +spec: + provisioner: + ref: + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterProvisioner + name: in-memory-bus-provisioner + +--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Channel +metadata: + name: qux-2 +spec: + provisioner: + ref: + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterProvisioner + name: in-memory-bus-provisioner + +--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Subscription +metadata: + name: qux-1-dumper-foo +spec: + from: + apiVersion: eventing.knative.dev/v1alpha1 + kind: Channel + name: qux-1 + call: + target: + apiVersion: v1 + kind: Service + name: message-dumper-foo + +--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Subscription +metadata: + name: qux-changer +spec: + from: + apiVersion: eventing.knative.dev/v1alpha1 + kind: Channel + name: qux-1 + call: + target: + apiVersion: v1 + kind: Service + name: event-changer + result: + target: + apiVersion: eventing.knative.dev/v1alpha1 + kind: Channel + name: qux-2 + +--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Subscription +metadata: + name: qux-2-dumper-foo +spec: + from: + apiVersion: eventing.knative.dev/v1alpha1 + kind: Channel + name: qux-2 + call: + target: + apiVersion: v1 + kind: Service + name: message-dumper-foo + +--- + +apiVersion: eventing.knative.dev/v1alpha1 +kind: Subscription +metadata: + name: qux-2-dumper-bar +spec: + from: + apiVersion: eventing.knative.dev/v1alpha1 + kind: Channel + name: qux-2 + call: + target: + apiVersion: v1 + kind: Service + name: message-dumper-bar diff --git a/config/buses/stub2/tmp/bus2.yaml b/config/buses/in-memory/tmp/bus2.yaml similarity index 88% rename from config/buses/stub2/tmp/bus2.yaml rename to config/buses/in-memory/tmp/bus2.yaml index dba9f30665f..7ce54d816eb 100644 --- a/config/buses/stub2/tmp/bus2.yaml +++ b/config/buses/in-memory/tmp/bus2.yaml @@ -2,13 +2,13 @@ apiVersion: eventing.knative.dev/v1alpha1 kind: Channel metadata: - name: foo-bus + name: foo spec: provisioner: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner - name: stub-bus-provisioner + name: in-memory-bus-provisioner channelable: subscribers: - sinkableDomain: message-dumper-foo.default.svc.cluster.local # Already exists @@ -20,14 +20,15 @@ spec: apiVersion: eventing.knative.dev/v1alpha1 kind: Channel metadata: - name: bar-bus + name: bar spec: provisioner: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner - name: stub-bus-provisioner + name: in-memory-bus-provisioner channelable: subscribers: - sinkableDomain: message-dumper-bar.default.svc.cluster.local # Already exists callableDomain: event-changer.default.svc.cluster.local + diff --git a/pkg/controller/eventing/stub/channel/controller.go b/pkg/controller/eventing/inmemory/channel/controller.go similarity index 91% rename from pkg/controller/eventing/stub/channel/controller.go rename to pkg/controller/eventing/inmemory/channel/controller.go index 1e032c1837f..87266ae257a 100644 --- a/pkg/controller/eventing/stub/channel/controller.go +++ b/pkg/controller/eventing/inmemory/channel/controller.go @@ -30,21 +30,21 @@ import ( const ( // controllerAgentName is the string used by this controller to identify // itself when creating events. - controllerAgentName = "stub-bus-channel-controller" - configMapName = "stub-bus-dispatcher-config-map" + controllerAgentName = "in-memory-bus-channel-controller" + ConfigMapName = "in-memory-bus-dispatcher-config-map" ) var ( DefaultConfigMapKey = types.NamespacedName{ Namespace: system.Namespace, - Name: configMapName, + Name: ConfigMapName, } ) // ProvideController returns a flow controller. func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, error) { - // Setup a new controller to Reconcile Channels that belong to this Cluster Provisioner (Stub - // buses). + // Setup a new controller to Reconcile Channels that belong to this Cluster Provisioner + // (in-memory buses). r := &reconciler{ configMapKey: DefaultConfigMapKey, recorder: mgr.GetRecorder(controllerAgentName), diff --git a/pkg/controller/eventing/stub/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go similarity index 99% rename from pkg/controller/eventing/stub/channel/reconcile.go rename to pkg/controller/eventing/inmemory/channel/reconcile.go index e6d9ed90b5e..18730d412b0 100644 --- a/pkg/controller/eventing/stub/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -20,7 +20,7 @@ import ( "context" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/controller" - cpcontroller "github.com/knative/eventing/pkg/controller/eventing/stub/clusterprovisioner" + cpcontroller "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterprovisioner" multichannelfanoutparse "github.com/knative/eventing/pkg/sidecar/configmap/parse" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" @@ -110,7 +110,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } // shouldReconcile determines if this Controller should control (and therefore reconcile) a given -// ClusterProvisioner. This Controller only handles Stub buses. +// ClusterProvisioner. This Controller only handles in-memory buses. func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { return cpcontroller.IsControlled(c.Spec.Provisioner) } diff --git a/pkg/controller/eventing/stub/clusterprovisioner/controller.go b/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go similarity index 95% rename from pkg/controller/eventing/stub/clusterprovisioner/controller.go rename to pkg/controller/eventing/inmemory/clusterprovisioner/controller.go index f92c89d41af..153a92a882b 100644 --- a/pkg/controller/eventing/stub/clusterprovisioner/controller.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go @@ -28,14 +28,14 @@ import ( const ( // controllerAgentName is the string used by this controller to identify // itself when creating events. - controllerAgentName = "stub-bus-cluster-provisioner-controller" + controllerAgentName = "in-memory-bus-cluster-provisioner-controller" ) // ProvideController returns a flow controller. func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, error) { logger = logger.With(zap.String("controller", controllerAgentName)) - // Setup a new controller to Reconcile ClusterProvisioners that are Stub buses. + // Setup a new controller to Reconcile ClusterProvisioners that are in-memory buses. r := &reconciler{ mgr: mgr, recorder: mgr.GetRecorder(controllerAgentName), diff --git a/pkg/controller/eventing/stub/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go similarity index 97% rename from pkg/controller/eventing/stub/clusterprovisioner/reconcile.go rename to pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go index 5ea77eebf0e..374e34abd2f 100644 --- a/pkg/controller/eventing/stub/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go @@ -106,17 +106,17 @@ func IsControlled(ref *eventingv1alpha1.ProvisionerReference) bool { } // shouldReconcile determines if this Controller should control (and therefore reconcile) a given -// ClusterProvisioner. This Controller only handles Stub buses. +// ClusterProvisioner. This Controller only handles in-memory buses. func shouldReconcile(namespace, name string) bool { - return namespace == "" && name == "stub-bus-provisioner" + return namespace == "" && name == "in-memory-bus-provisioner" } func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { logger := r.logger.With(zap.Any("clusterProvisioner", cp)) // We are syncing one thing. - // 1. The K8s Service to talk to this Stub bus. - // - There is a single K8s Service for all requests going to this Stub bus. + // 1. The K8s Service to talk to this in-memory bus. + // - There is a single K8s Service for all requests going to this in-memory bus. if cp.DeletionTimestamp != nil { // K8s garbage collection will delete the dispatcher service, once this ClusterProvisioner diff --git a/pkg/controller/eventing/stub/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go similarity index 100% rename from pkg/controller/eventing/stub/clusterprovisioner/reconcile_test.go rename to pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go diff --git a/pkg/controller/eventing/stub/controller/main.go b/pkg/controller/eventing/inmemory/controller/main.go similarity index 90% rename from pkg/controller/eventing/stub/controller/main.go rename to pkg/controller/eventing/inmemory/controller/main.go index aa65eaf4a14..2ec2cfe8ae2 100644 --- a/pkg/controller/eventing/stub/controller/main.go +++ b/pkg/controller/eventing/inmemory/controller/main.go @@ -20,8 +20,8 @@ import ( "flag" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/buses" - "github.com/knative/eventing/pkg/controller/eventing/stub/channel" - "github.com/knative/eventing/pkg/controller/eventing/stub/clusterprovisioner" + "github.com/knative/eventing/pkg/controller/eventing/inmemory/channel" + "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterprovisioner" istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "github.com/knative/pkg/signals" "go.uber.org/zap" @@ -42,7 +42,7 @@ func main() { logger = logger.With( // TODO: probably replace, Bus isn't really a thing anymore. zap.String("eventing.knative.dev/bus", ref.String()), - zap.String("eventing.knative.dev/busType", "stub"), + zap.String("eventing.knative.dev/busType", "in-memory"), zap.String("eventing.knative.dev/busComponent", buses.Provisioner), ) flag.Parse() diff --git a/pkg/sidecar/configmap/watcher/watcher.go b/pkg/sidecar/configmap/watcher/watcher.go index dc5adf0b02e..0e1ccfec59e 100644 --- a/pkg/sidecar/configmap/watcher/watcher.go +++ b/pkg/sidecar/configmap/watcher/watcher.go @@ -18,6 +18,7 @@ package watcher import ( "context" + "github.com/knative/eventing/pkg/controller/eventing/inmemory/channel" "github.com/knative/eventing/pkg/sidecar/configmap/parse" "github.com/knative/eventing/pkg/sidecar/swappable" "go.uber.org/zap" @@ -74,7 +75,7 @@ func (r *reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) // DO NOT SUBMIT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - if req.Name != "stub-bus-dispatcher-config-map" { + if req.Name != channel.ConfigMapName { return reconcile.Result{}, nil } From a9bb764b72c4254502cc1d48da0398ec7ec0d3c8 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Tue, 2 Oct 2018 15:40:01 -0700 Subject: [PATCH 06/22] Add unit tests for the ClusterProvisioner Controller. --- .../eventing/inmemory/channel/reconcile.go | 17 +- .../inmemory/channel/reconcile_test.go | 23 ++ .../inmemory/clusterprovisioner/controller.go | 5 +- .../inmemory/clusterprovisioner/reconcile.go | 19 +- .../clusterprovisioner/reconcile_test.go | 295 +++++++++++++++++- 5 files changed, 324 insertions(+), 35 deletions(-) create mode 100644 pkg/controller/eventing/inmemory/channel/reconcile_test.go diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 18730d412b0..cf32e0f625e 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -35,7 +35,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -48,13 +47,11 @@ const ( ) type reconciler struct { - client client.Client - restConfig *rest.Config - recorder record.EventRecorder + client client.Client + recorder record.EventRecorder + logger *zap.Logger configMapKey client.ObjectKey - - logger *zap.Logger } // Verify the struct implements reconcile.Reconciler @@ -161,10 +158,10 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) func (r *reconciler) makeSubscribable(c *eventingv1alpha1.Channel) { // Point at itself. c.Status.Subscribable.Channelable = corev1.ObjectReference{ - Kind: "Channel", + Kind: "Channel", APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), - Namespace: c.Namespace, - Name: c.Name, + Namespace: c.Namespace, + Name: c.Name, } } @@ -401,7 +398,7 @@ func (r *reconciler) createNewConfigMap(config string) *corev1.ConfigMap { return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: r.configMapKey.Namespace, - Name: r.configMapKey.Name, + Name: r.configMapKey.Name, }, Data: map[string]string{ multichannelfanoutparse.MultiChannelFanoutConfigKey: config, diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go new file mode 100644 index 00000000000..dcaf40871ff --- /dev/null +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -0,0 +1,23 @@ +/* +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 channel + +import "testing" + +func TestReconciler_Reconcile(t *testing.T) { + +} diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go b/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go index 153a92a882b..234ce3e14d6 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go @@ -36,10 +36,9 @@ func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Cont logger = logger.With(zap.String("controller", controllerAgentName)) // Setup a new controller to Reconcile ClusterProvisioners that are in-memory buses. - r := &reconciler{ - mgr: mgr, + r := &reconciler{ recorder: mgr.GetRecorder(controllerAgentName), - logger: logger, + logger: logger, } c, err := controller.New(controllerAgentName, mgr, controller.Options{ Reconciler: r, diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go index 374e34abd2f..c933585301d 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go @@ -30,20 +30,19 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -type reconciler struct { - mgr manager.Manager - client client.Client - restConfig *rest.Config - recorder record.EventRecorder +const ( + cpName = "in-memory-bus-provisioner" +) - logger *zap.Logger +type reconciler struct { + client client.Client + recorder record.EventRecorder + logger *zap.Logger } // Verify the struct implements reconcile.Reconciler @@ -99,7 +98,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } func IsControlled(ref *eventingv1alpha1.ProvisionerReference) bool { - if ref != nil { + if ref != nil && ref.Ref != nil { return shouldReconcile(ref.Ref.Namespace, ref.Ref.Name) } return false @@ -108,7 +107,7 @@ func IsControlled(ref *eventingv1alpha1.ProvisionerReference) bool { // shouldReconcile determines if this Controller should control (and therefore reconcile) a given // ClusterProvisioner. This Controller only handles in-memory buses. func shouldReconcile(namespace, name string) bool { - return namespace == "" && name == "in-memory-bus-provisioner" + return namespace == "" && name == cpName } func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go index 2df795949dd..128b47bfdb7 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go @@ -17,22 +17,47 @@ limitations under the License. package clusterprovisioner import ( + "context" + "errors" "fmt" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/system" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "testing" controllertesting "github.com/knative/eventing/pkg/controller/testing" ) +const ( + cpUid = "test-uid" + testErrorMessage = "test-induced-error" +) + +var ( + // deletionTime is used when objects are marked as deleted. Rfc3339Copy() + // truncates to seconds to match the loss of precision during serialization. + deletionTime = metav1.Now().Rfc3339Copy() + + truePointer = true +) + func init() { // Add types to scheme eventingv1alpha1.AddToScheme(scheme.Scheme) + corev1.AddToScheme(scheme.Scheme) } func TestInjectClient(t *testing.T) { - r := &reconciler{} + r := &reconciler{} orig := r.client n := fake.NewFakeClient() if orig == n { @@ -47,6 +72,57 @@ func TestInjectClient(t *testing.T) { } } +func TestIsControlled(t *testing.T) { + testCases := map[string]struct { + ref *eventingv1alpha1.ProvisionerReference + isControlled bool + }{ + "nil": { + ref: nil, + isControlled: false, + }, + "ref nil": { + ref: &eventingv1alpha1.ProvisionerReference{ + Ref: nil, + }, + isControlled: false, + }, + "wrong namespace": { + ref: &eventingv1alpha1.ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Namespace: "other", + Name: cpName, + }, + }, + isControlled: false, + }, + "wrong name": { + ref: &eventingv1alpha1.ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: "other-name", + }, + }, + isControlled: false, + }, + "is controlled": { + ref: &eventingv1alpha1.ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: cpName, + }, + }, + isControlled: true, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + isControlled := IsControlled(tc.ref) + if isControlled != tc.isControlled { + t.Errorf("Expected: %v. Actual: %v", tc.isControlled, isControlled) + } + }) + } +} + func TestReconcile(t *testing.T) { testCases := []controllertesting.TestCase{ { @@ -54,37 +130,232 @@ func TestReconcile(t *testing.T) { }, { Name: "Unable to get CP", + Mocks: controllertesting.Mocks{ + MockGets: []controllertesting.MockGet{ + errorGettingClusterProvisioner(), + }, + }, + WantErrMsg: testErrorMessage, }, { - Name: "Should not reconcile", + Name: "Should not reconcile - namespace", + InitialState: []runtime.Object{ + &eventingv1alpha1.ClusterProvisioner{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "not empty string", + Name: cpName, + }, + }, + }, }, { - Name: "Delete provisioner fails", + Name: "Should not reconcile - name", + InitialState: []runtime.Object{ + &eventingv1alpha1.ClusterProvisioner{ + ObjectMeta: metav1.ObjectMeta{ + Name: "wrong-name", + }, + }, + }, + ReconcileKey: "/wrong-name", }, { - Name: "Delete dispatcher fails", + Name: "Delete succeeds", + // Deleting does nothing. + InitialState: []runtime.Object{ + makeDeletingClusterProvisioner(), + }, }, { - Name: "Delete succeeds", + Name: "Create dispatcher fails", + InitialState: []runtime.Object{ + makeClusterProvisioner(), + }, + Mocks: controllertesting.Mocks{ + MockGets: []controllertesting.MockGet{ + errorGettingK8sService(), + }, + }, + WantErrMsg: testErrorMessage, }, { - Name: "Creates dispatcher fails", + Name: "Create dispatcher - already exists", + InitialState: []runtime.Object{ + makeClusterProvisioner(), + makeK8sService(), + }, + WantPresent: []runtime.Object{ + makeReadyClusterProvisioner(), + }, }, { - Name: "Creates dispatcher - already exists", + Name: "Create dispatcher - not owned by CP", + InitialState: []runtime.Object{ + makeClusterProvisioner(), + makeK8sServiceNotOwnedByClusterProvisioner(), + }, + WantPresent: []runtime.Object{ + makeReadyClusterProvisioner(), + }, }, { - Name: "Creates dispatcher - now owned by CP", + Name: "Create dispatcher succeeds", + InitialState: []runtime.Object{ + makeClusterProvisioner(), + }, + WantPresent: []runtime.Object{ + makeReadyClusterProvisioner(), + makeK8sService(), + }, }, { - Name: "Creates dispatcher succeeds", + Name: "Error getting CP for updating Status", + // Nothing to create or update other than the status of CP itself. + InitialState: []runtime.Object{ + makeClusterProvisioner(), + makeK8sService(), + }, + Mocks: controllertesting.Mocks{ + MockGets: oneSuccessfulClusterProvisionerGet(), + }, + WantErrMsg: testErrorMessage, }, { - Name: "Sync provisioners - fails", + Name: "Error updating Status", + // Nothing to create or update other than the status of CP itself. + InitialState: []runtime.Object{ + makeClusterProvisioner(), + makeK8sService(), + }, + Mocks: controllertesting.Mocks{ + MockUpdates: []controllertesting.MockUpdate{ + errorUpdating(), + }, + }, + WantErrMsg: testErrorMessage, + }, + } + recorder := record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) + for _, tc := range testCases { + if tc.Name != "Create dispatcher - already exists" { + } + c := tc.GetClient() + r := &reconciler{ + client: c, + recorder: recorder, + logger: zap.NewNop(), + } + if tc.ReconcileKey == "" { + tc.ReconcileKey = fmt.Sprintf("/%s", cpName) + } + t.Run(tc.Name, tc.Runner(t, r, c)) + } +} + +func makeClusterProvisioner() *eventingv1alpha1.ClusterProvisioner { + return &eventingv1alpha1.ClusterProvisioner{ + TypeMeta: metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "ClusterProvisioner", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: cpName, + UID: cpUid, }, + } +} + +func makeReadyClusterProvisioner() *eventingv1alpha1.ClusterProvisioner { + cp := makeClusterProvisioner() + cp.Status.Conditions = []duckv1alpha1.Condition{ { - Name: "Sync provisioners", + Type: duckv1alpha1.ConditionReady, + Status: corev1.ConditionTrue, }, } - fmt.Sprintf("%v", testCases) + return cp +} + +func makeDeletingClusterProvisioner() *eventingv1alpha1.ClusterProvisioner { + cp := makeClusterProvisioner() + cp.DeletionTimestamp = &deletionTime + return cp +} + +func makeK8sService() *corev1.Service { + return &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: system.Namespace, + Name: fmt.Sprintf("%s-clusterbus", cpName), + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "ClusterProvisioner", + Name: cpName, + UID: cpUid, + Controller: &truePointer, + BlockOwnerDeletion: &truePointer, + }, + }, + Labels: dispatcherLabels(cpName), + }, + Spec: corev1.ServiceSpec{ + Selector: dispatcherLabels(cpName), + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + }, + } +} + +func makeK8sServiceNotOwnedByClusterProvisioner() *corev1.Service { + svc := makeK8sService() + svc.OwnerReferences = nil + return svc +} + +func errorGettingClusterProvisioner() controllertesting.MockGet { + return func(client.Client, context.Context, client.ObjectKey, runtime.Object) (controllertesting.MockHandled, error) { + return controllertesting.Handled, errors.New(testErrorMessage) + } +} + +func errorGettingK8sService() controllertesting.MockGet { + return func(_ client.Client, _ context.Context, _ client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*corev1.Service); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + } +} + +func oneSuccessfulClusterProvisionerGet() []controllertesting.MockGet { + return []controllertesting.MockGet{ + // The first one is a pass through. + func(innerClient client.Client, ctx context.Context, key client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + err := innerClient.Get(ctx, key, obj) + return controllertesting.Handled, err + }, + // All subsequent ClusterProvisioner Gets fail. + func(_ client.Client, _ context.Context, _ client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*eventingv1alpha1.ClusterProvisioner); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorUpdating() controllertesting.MockUpdate { + return func(client.Client, context.Context, runtime.Object) (controllertesting.MockHandled, error) { + return controllertesting.Handled, errors.New(testErrorMessage) + } } From 10034f6a9e1465b618dc95d595772ef5053dec0c Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Wed, 3 Oct 2018 07:29:18 -0700 Subject: [PATCH 07/22] Add unit tests for the Channel reconciler. --- .../eventing/inmemory/channel/reconcile.go | 24 +- .../inmemory/channel/reconcile_test.go | 801 +++++++++++++++++- .../clusterprovisioner/reconcile_test.go | 2 - 3 files changed, 819 insertions(+), 8 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index cf32e0f625e..442d931b748 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -85,7 +85,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // Does this Controller control this Channel? if !r.shouldReconcile(c) { - logger.Info("Not reconciling Channel, it is not controlled by this Controller", zap.Any("ref", c.Spec.Provisioner.Ref)) + logger.Info("Not reconciling Channel, it is not controlled by this Controller", zap.Any("ref", c.Spec)) return reconcile.Result{}, nil } logger.Info("Reconciling Channel") @@ -109,7 +109,10 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // shouldReconcile determines if this Controller should control (and therefore reconcile) a given // ClusterProvisioner. This Controller only handles in-memory buses. func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { - return cpcontroller.IsControlled(c.Spec.Provisioner) + if c.Spec.Provisioner != nil { + return cpcontroller.IsControlled(c.Spec.Provisioner) + } + return false } func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) error { @@ -390,6 +393,10 @@ func (r *reconciler) writeConfigMap(ctx context.Context, config *multichannelfan return nil } + if cm.Data == nil { + cm.Data = map[string]string{} + } + cm.Data[multichannelfanoutparse.MultiChannelFanoutConfigKey] = updated return r.client.Update(ctx, cm) } @@ -428,7 +435,16 @@ func multiChannelFanoutConfig(channels []eventingv1alpha1.Channel) *multichannel func (r *reconciler) listAllChannels(ctx context.Context) ([]eventingv1alpha1.Channel, error) { channels := make([]eventingv1alpha1.Channel, 0) - opts := &client.ListOptions{} + opts := &client.ListOptions{ + // TODO this is here because the fake client needs it. Remove this when it's no longer + // needed. + Raw: &metav1.ListOptions{ + TypeMeta: metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + }, + }, + } for { cl := &eventingv1alpha1.ChannelList{} err := r.client.List(ctx, opts, cl) @@ -444,7 +460,7 @@ func (r *reconciler) listAllChannels(ctx context.Context) ([]eventingv1alpha1.Ch if cl.Continue != "" { opts.Raw.Continue = cl.Continue } else { - return channels, err + return channels, nil } } } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index dcaf40871ff..44c2d6095e2 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -16,8 +16,805 @@ limitations under the License. package channel -import "testing" +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/google/go-cmp/cmp" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + controllertesting "github.com/knative/eventing/pkg/controller/testing" + "github.com/knative/eventing/pkg/sidecar/configmap/parse" + "github.com/knative/eventing/pkg/sidecar/fanout" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "testing" +) -func TestReconciler_Reconcile(t *testing.T) { +const ( + cpName = "in-memory-bus-provisioner" + cNamespace = "test-namespace" + cName = "test-channel" + cUID = "test-uid" + + cmNamespace = cNamespace + cmName = "test-config-map" + + testErrorMessage = "test induced error" + + insertedByVerifyConfigMapData = "data inserted by verifyConfigMapData so that it can be WantPresent" +) + +var ( + // deletionTime is used when objects are marked as deleted. Rfc3339Copy() + // truncates to seconds to match the loss of precision during serialization. + deletionTime = metav1.Now().Rfc3339Copy() + + truePointer = true + + // channelsConfig and channels are linked together. A change to one, will likely require a + // change to the other. channelsConfig is the serialized config of channels for everything + // provisioned by the in-memory-provisioner. + channelsConfig = multichannelfanout.Config{ + ChannelConfigs: []multichannelfanout.ChannelConfig{ + { + Namespace: cNamespace, + Name: "c1", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "foo", + }, + { + SinkableDomain: "bar", + }, + { + CallableDomain: "baz", + SinkableDomain: "qux", + }, + }, + }, + }, + { + Namespace: cNamespace, + Name: "c3", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "steve", + }, + }, + }, + }, + }, + } + + channels = []eventingv1alpha1.Channel{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: cNamespace, + Name: "c1", + }, + Spec: eventingv1alpha1.ChannelSpec{ + Provisioner: &eventingv1alpha1.ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: cpName, + }, + }, + Channelable: &duckv1alpha1.Channelable{ + Subscribers: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "foo", + }, + { + SinkableDomain: "bar", + }, + { + CallableDomain: "baz", + SinkableDomain: "qux", + }, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: cNamespace, + Name: "c2", + }, + Spec: eventingv1alpha1.ChannelSpec{ + Provisioner: &eventingv1alpha1.ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: "some-other-provisioner", + }, + }, + Channelable: &duckv1alpha1.Channelable{ + Subscribers: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "anything", + }, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: cNamespace, + Name: "c3", + }, + Spec: eventingv1alpha1.ChannelSpec{ + Provisioner: &eventingv1alpha1.ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: cpName, + }, + }, + Channelable: &duckv1alpha1.Channelable{ + Subscribers: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "steve", + }, + }, + }, + }, + }, + } +) + +func init() { + // Add types to scheme. + eventingv1alpha1.AddToScheme(scheme.Scheme) + corev1.AddToScheme(scheme.Scheme) + istiov1alpha3.AddToScheme(scheme.Scheme) +} + +func TestInjectClient(t *testing.T) { + r := &reconciler{} + orig := r.client + n := fake.NewFakeClient() + if orig == n { + t.Errorf("Original and new clients are identical: %v", orig) + } + err := r.InjectClient(n) + if err != nil { + t.Errorf("Unexpected error injecting the client: %v", err) + } + if n != r.client { + t.Errorf("Unexpected client. Expected: '%v'. Actual: '%v'", n, r.client) + } +} + +func TestReconcile(t *testing.T) { + testCases := []controllertesting.TestCase{ + { + Name: "Channel not found", + }, + { + Name: "Error getting Channel", + Mocks: controllertesting.Mocks{ + MockGets: errorGettingChannel(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "Channel not reconciled - nil provisioner", + InitialState: []runtime.Object{ + makeChannelNilProvisioner(), + }, + }, + { + Name: "Channel not reconciled - nil ref", + InitialState: []runtime.Object{ + makeChannelNilRef(), + }, + }, + { + Name: "Channel not reconciled - namespace", + InitialState: []runtime.Object{ + makeChannelWithWrongProvisionerNamespace(), + }, + }, + { + Name: "Channel not reconciled - name", + InitialState: []runtime.Object{ + makeChannelWithWrongProvisionerName(), + }, + }, + { + Name: "Channel deleted - Channel config sync fails", + InitialState: []runtime.Object{ + makeDeletingChannel(), + }, + Mocks: controllertesting.Mocks{ + MockLists: errorListingChannels(), + }, + WantPresent: []runtime.Object{ + // Finalizer has not been removed. + makeDeletingChannel(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "Channel deleted - finalizer removed", + InitialState: []runtime.Object{ + makeDeletingChannel(), + }, + WantPresent: []runtime.Object{ + makeDeletingChannelWithoutFinalizer(), + }, + }, + { + Name: "Channel config sync fails - can't list Channels", + InitialState: []runtime.Object{ + makeChannel(), + }, + Mocks: controllertesting.Mocks{ + MockLists: errorListingChannels(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "Channel config sync fails - can't get ConfigMap", + InitialState: []runtime.Object{ + makeChannel(), + }, + Mocks: controllertesting.Mocks{ + MockGets: errorGettingConfigMap(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "Channel config sync fails - can't create ConfigMap", + InitialState: []runtime.Object{ + makeChannel(), + }, + Mocks: controllertesting.Mocks{ + MockCreates: errorCreatingConfigMap(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "Channel config sync fails - can't update ConfigMap", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + }, + Mocks: controllertesting.Mocks{ + MockUpdates: errorUpdatingConfigMap(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "K8s service get fails", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + }, + Mocks: controllertesting.Mocks{ + MockGets: errorGettingK8sService(), + }, + WantPresent: []runtime.Object{ + makeChannelWithFinalizerAndSubscribable(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "K8s service creation fails", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + }, + Mocks: controllertesting.Mocks{ + MockCreates: errorCreatingK8sService(), + }, + WantPresent: []runtime.Object{ + // TODO: This should have a useful error message saying that the K8s Service failed. + makeChannelWithFinalizerAndSubscribable(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "K8s service already exists - not owned by Channel", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + makeK8sServiceNotOwnedByChannel(), + }, + WantPresent: []runtime.Object{ + makeReadyChannel(), + }, + }, + { + Name: "Virtual service get fails", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + makeK8sService(), + makeVirtualService(), + }, + Mocks: controllertesting.Mocks{ + MockGets: errorGettingVirtualService(), + }, + WantPresent: []runtime.Object{ + // TODO: This should have a useful error message saying that the VirtualService + // failed. + makeChannelWithFinalizerAndSubscribableAndSinkable(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "Virtual service creation fails", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + makeK8sService(), + }, + Mocks: controllertesting.Mocks{ + MockCreates: errorCreatingVirtualService(), + }, + WantPresent: []runtime.Object{ + // TODO: This should have a useful error message saying that the VirtualService + // failed. + makeChannelWithFinalizerAndSubscribableAndSinkable(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "VirtualService already exists - not owned by Channel", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + makeK8sService(), + makeVirtualServiceNowOwnedByChannel(), + }, + WantPresent: []runtime.Object{ + makeReadyChannel(), + }, + }, + { + Name: "Channel get for update fails", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + makeK8sService(), + makeVirtualService(), + }, + Mocks: controllertesting.Mocks{ + MockGets: errorOnSecondChannelGet(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "Channel update fails", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + makeK8sService(), + makeVirtualService(), + }, + Mocks: controllertesting.Mocks{ + MockUpdates: errorUpdatingChannel(), + }, + WantErrMsg: testErrorMessage, + }, + { + Name: "Channel reconcile successful - Channel list follows pagination", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + }, + Mocks: controllertesting.Mocks{ + MockLists: (&paginatedChannelsListStruct{channels: channels}).MockLists(), + // This is more accurate to be in WantPresent, but we need to check JSON equality, + // not string equality, so it can't be done in WantPresent. Instead, we verify + // during the update call, swapping out the data and WantPresent with that inserted + // data. + MockUpdates: verifyConfigMapData(), + }, + WantPresent: []runtime.Object{ + makeReadyChannel(), + makeK8sService(), + makeVirtualService(), + makeConfigMapWithVerifyConfigMapData(), + }, + }, + } + recorder := record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) + for _, tc := range testCases { + configMapKey := types.NamespacedName{ + Namespace: cmNamespace, + Name: cmName, + } + c := tc.GetClient() + r := &reconciler{ + client: c, + recorder: recorder, + logger: zap.NewNop(), + configMapKey: configMapKey, + } + if tc.ReconcileKey == "" { + tc.ReconcileKey = fmt.Sprintf("/%s", cName) + } + t.Run(tc.Name, tc.Runner(t, r, c)) + } +} + +func makeChannel() *eventingv1alpha1.Channel { + return &eventingv1alpha1.Channel{ + TypeMeta: metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: cNamespace, + Name: cName, + UID: cUID, + }, + Spec: eventingv1alpha1.ChannelSpec{ + Provisioner: &eventingv1alpha1.ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: cpName, + }, + }, + }, + } +} + +func makeChannelWithFinalizerAndSubscribable() *eventingv1alpha1.Channel { + c := makeChannelWithFinalizer() + c.Status.Subscribable.Channelable = corev1.ObjectReference{ + Kind: "Channel", + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Namespace: c.Namespace, + Name: c.Name, + } + return c +} + +func makeChannelWithFinalizerAndSubscribableAndSinkable() *eventingv1alpha1.Channel { + c := makeChannelWithFinalizerAndSubscribable() + c.Status.Sinkable.DomainInternal = fmt.Sprintf("%s-channel.%s.svc.cluster.local", cName, cNamespace) + return c +} + +func makeReadyChannel() *eventingv1alpha1.Channel { + // Ready channels have the finalizer and are Subscribable and Sinkable. + c := makeChannelWithFinalizerAndSubscribableAndSinkable() + c.Status.Conditions = []duckv1alpha1.Condition{ + { + Type: duckv1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + } + return c +} + +func makeChannelNilProvisioner() *eventingv1alpha1.Channel { + c := makeChannel() + c.Spec.Provisioner = nil + return c +} + +func makeChannelNilRef() *eventingv1alpha1.Channel { + c := makeChannel() + c.Spec.Provisioner.Ref = nil + return c +} + +func makeChannelWithWrongProvisionerNamespace() *eventingv1alpha1.Channel { + c := makeChannel() + c.Spec.Provisioner.Ref.Namespace = "wrong-namespace" + return c +} + +func makeChannelWithWrongProvisionerName() *eventingv1alpha1.Channel { + c := makeChannel() + c.Spec.Provisioner.Ref.Name = "wrong-name" + return c +} + +func makeChannelWithFinalizer() *eventingv1alpha1.Channel { + c := makeChannel() + c.Finalizers = []string{finalizerName} + return c +} + +func makeDeletingChannel() *eventingv1alpha1.Channel { + c := makeChannelWithFinalizer() + c.DeletionTimestamp = &deletionTime + return c +} + +func makeDeletingChannelWithoutFinalizer() *eventingv1alpha1.Channel { + c := makeDeletingChannel() + c.Finalizers = nil + return c +} + +func makeConfigMap() *corev1.ConfigMap { + return &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: cmNamespace, + Name: cmName, + }, + } +} + +func makeConfigMapWithVerifyConfigMapData() *corev1.ConfigMap { + cm := makeConfigMap() + cm.Data = map[string]string{} + cm.Data[parse.MultiChannelFanoutConfigKey] = insertedByVerifyConfigMapData + return cm +} + +func makeK8sService() *corev1.Service { + return &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-channel", cName), + Namespace: cNamespace, + Labels: map[string]string{ + "channel": cName, + "provisioner": cpName, + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + Name: cName, + UID: cUID, + Controller: &truePointer, + BlockOwnerDeletion: &truePointer, + }, + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: PortName, + Port: PortNumber, + }, + }, + }, + } +} + +func makeK8sServiceNotOwnedByChannel() *corev1.Service { + svc := makeK8sService() + svc.OwnerReferences = nil + return svc +} + +func makeVirtualService() *istiov1alpha3.VirtualService { + return &istiov1alpha3.VirtualService{ + TypeMeta: metav1.TypeMeta{ + APIVersion: istiov1alpha3.SchemeGroupVersion.String(), + Kind: "VirtualService", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-channel", cName), + Namespace: cNamespace, + Labels: map[string]string{ + "channel": cName, + "provisioner": cpName, + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + Name: cName, + UID: cUID, + Controller: &truePointer, + BlockOwnerDeletion: &truePointer, + }, + }, + }, + Spec: istiov1alpha3.VirtualServiceSpec{ + Hosts: []string{ + fmt.Sprintf("%s-channel.%s.svc.cluster.local", cName, cNamespace), + fmt.Sprintf("%s.%s.channels.cluster.local", cName, cNamespace), + }, + Http: []istiov1alpha3.HTTPRoute{{ + Rewrite: &istiov1alpha3.HTTPRewrite{ + Authority: fmt.Sprintf("%s.%s.channels.cluster.local", cName, cNamespace), + }, + Route: []istiov1alpha3.DestinationWeight{{ + Destination: istiov1alpha3.Destination{ + Host: "in-memory-bus-provisioner-clusterbus.knative-eventing.svc.cluster.local", + Port: istiov1alpha3.PortSelector{ + Number: PortNumber, + }, + }}, + }}, + }, + }, + } +} + +func makeVirtualServiceNowOwnedByChannel() *istiov1alpha3.VirtualService { + vs := makeVirtualService() + vs.OwnerReferences = nil + return vs +} + +func errorOnSecondChannelGet() []controllertesting.MockGet { + passThrough := []controllertesting.MockGet{ + func(innerClient client.Client, ctx context.Context, key client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + return controllertesting.Handled, innerClient.Get(ctx, key, obj) + }, + } + return append(passThrough, errorGettingChannel()...) +} + +func errorGettingChannel() []controllertesting.MockGet { + return []controllertesting.MockGet{ + func(_ client.Client, _ context.Context, _ client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*eventingv1alpha1.Channel); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorGettingConfigMap() []controllertesting.MockGet { + return []controllertesting.MockGet{ + func(_ client.Client, _ context.Context, _ client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*corev1.ConfigMap); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorGettingK8sService() []controllertesting.MockGet { + return []controllertesting.MockGet{ + func(_ client.Client, _ context.Context, _ client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*corev1.Service); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorGettingVirtualService() []controllertesting.MockGet { + return []controllertesting.MockGet{ + func(_ client.Client, _ context.Context, _ client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*istiov1alpha3.VirtualService); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorListingChannels() []controllertesting.MockList { + return []controllertesting.MockList{ + func(client.Client, context.Context, *client.ListOptions, runtime.Object) (controllertesting.MockHandled, error) { + return controllertesting.Handled, errors.New(testErrorMessage) + }, + } +} + +func errorCreatingConfigMap() []controllertesting.MockCreate { + return []controllertesting.MockCreate{ + func(_ client.Client, _ context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*corev1.ConfigMap); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorCreatingK8sService() []controllertesting.MockCreate { + return []controllertesting.MockCreate{ + func(_ client.Client, _ context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*corev1.Service); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorCreatingVirtualService() []controllertesting.MockCreate { + return []controllertesting.MockCreate{ + func(_ client.Client, _ context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*istiov1alpha3.VirtualService); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorUpdatingChannel() []controllertesting.MockUpdate { + return []controllertesting.MockUpdate{ + func(_ client.Client, _ context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*eventingv1alpha1.Channel); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +func errorUpdatingConfigMap() []controllertesting.MockUpdate { + return []controllertesting.MockUpdate{ + func(_ client.Client, _ context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*corev1.ConfigMap); ok { + return controllertesting.Handled, errors.New(testErrorMessage) + } + return controllertesting.Unhandled, nil + }, + } +} + +type paginatedChannelsListStruct struct { + channels []eventingv1alpha1.Channel +} + +func (p *paginatedChannelsListStruct) MockLists() []controllertesting.MockList { + return []controllertesting.MockList{ + func(_ client.Client, _ context.Context, _ *client.ListOptions, list runtime.Object) (controllertesting.MockHandled, error) { + if l, ok := list.(*eventingv1alpha1.ChannelList); ok { + + if len(p.channels) > 0 { + c := p.channels[0] + p.channels = p.channels[1:] + l.Continue = "yes" + l.Items = []eventingv1alpha1.Channel{ + c, + } + } + return controllertesting.Handled, nil + } + return controllertesting.Unhandled, nil + }, + } +} + +func verifyConfigMapData() []controllertesting.MockUpdate { + return []controllertesting.MockUpdate{ + func(innerClient client.Client, ctx context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { + if cm, ok := obj.(*corev1.ConfigMap); ok { + s := cm.Data[parse.MultiChannelFanoutConfigKey] + c := multichannelfanout.Config{} + err := json.Unmarshal([]byte(s), &c) + if err != nil { + return controllertesting.Handled, + fmt.Errorf("test is unable to unmarshal ConfigMap data: %v", err) + } + if diff := cmp.Diff(c, channelsConfig); diff != "" { + return controllertesting.Handled, + fmt.Errorf("test got unwanted ChannelsConfig (-want +got) %s", diff) + } + // Verified it is correct, now so that we can verify this actually occurred, swap + // out the data with a known value for later comparison. + cm.Data[parse.MultiChannelFanoutConfigKey] = insertedByVerifyConfigMapData + return controllertesting.Handled, innerClient.Update(ctx, obj) + } + return controllertesting.Unhandled, nil + }, + } } diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go index 128b47bfdb7..168c057c76d 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go @@ -237,8 +237,6 @@ func TestReconcile(t *testing.T) { } recorder := record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) for _, tc := range testCases { - if tc.Name != "Create dispatcher - already exists" { - } c := tc.GetClient() r := &reconciler{ client: c, From 906e8f90fd9255c8aabed1fd6bd2ae098abcbf1c Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Wed, 3 Oct 2018 14:35:46 -0700 Subject: [PATCH 08/22] Rename in-memory-bus-provisioner to in-memory-channel, as requested in PR comments. --- .../in-memory-channel/in-memory-channel.yaml} | 19 +++++-------------- .../in-memory-channel}/tmp/bus-with-subs.yaml | 4 ++-- .../in-memory-channel}/tmp/bus2.yaml | 4 ++-- .../eventing/inmemory/channel/controller.go | 6 +++--- .../eventing/inmemory/channel/reconcile.go | 2 +- .../inmemory/channel/reconcile_test.go | 4 ++-- .../inmemory/clusterprovisioner/controller.go | 4 ++-- .../inmemory/clusterprovisioner/reconcile.go | 10 +++++----- .../clusterprovisioner/reconcile_test.go | 18 +++++++++--------- .../eventing/inmemory/controller/main.go | 12 ++---------- 10 files changed, 33 insertions(+), 50 deletions(-) rename config/{buses/in-memory/in-memory.yaml => provisioners/in-memory-channel/in-memory-channel.yaml} (81%) rename config/{buses/in-memory => provisioners/in-memory-channel}/tmp/bus-with-subs.yaml (96%) rename config/{buses/in-memory => provisioners/in-memory-channel}/tmp/bus2.yaml (91%) diff --git a/config/buses/in-memory/in-memory.yaml b/config/provisioners/in-memory-channel/in-memory-channel.yaml similarity index 81% rename from config/buses/in-memory/in-memory.yaml rename to config/provisioners/in-memory-channel/in-memory-channel.yaml index 3f21e37e785..ca10befe533 100644 --- a/config/buses/in-memory/in-memory.yaml +++ b/config/provisioners/in-memory-channel/in-memory-channel.yaml @@ -15,7 +15,7 @@ apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner metadata: - name: in-memory-bus-provisioner + name: in-memory-channel spec: reconciles: group: eventing.knative.dev/v1alpha1 @@ -26,13 +26,13 @@ spec: apiVersion: apps/v1beta1 kind: Deployment metadata: - name: in-memory-bus-controller + name: in-memory-channel-controller namespace: &namespace knative-eventing spec: replicas: 1 selector: matchLabels: &labels - clusterProvisioner: in-memory-bus-provisioner + clusterProvisioner: in-memory-channel role: controller template: metadata: @@ -42,28 +42,19 @@ spec: containers: - name: controller image: github.com/knative/eventing/pkg/controller/eventing/inmemory/controller - env: - - name: BUS_NAME - value: in-memory - - name: BUS_NAMESPACE - value: *namespace - args: - - -logtostderr - - -stderrthreshold - - INFO --- apiVersion: apps/v1beta1 kind: Deployment metadata: - name: in-memory-bus-dispatcher + name: in-memory-channel-dispatcher namespace: knative-eventing spec: replicas: 1 selector: matchLabels: &labels - clusterProvisioner: in-memory-bus-provisioner + clusterProvisioner: in-memory-channel role: dispatcher template: metadata: diff --git a/config/buses/in-memory/tmp/bus-with-subs.yaml b/config/provisioners/in-memory-channel/tmp/bus-with-subs.yaml similarity index 96% rename from config/buses/in-memory/tmp/bus-with-subs.yaml rename to config/provisioners/in-memory-channel/tmp/bus-with-subs.yaml index e10fa43287c..771d8cc7520 100644 --- a/config/buses/in-memory/tmp/bus-with-subs.yaml +++ b/config/provisioners/in-memory-channel/tmp/bus-with-subs.yaml @@ -15,7 +15,7 @@ spec: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner - name: in-memory-bus-provisioner + name: in-memory-channel --- @@ -28,7 +28,7 @@ spec: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner - name: in-memory-bus-provisioner + name: in-memory-channel --- diff --git a/config/buses/in-memory/tmp/bus2.yaml b/config/provisioners/in-memory-channel/tmp/bus2.yaml similarity index 91% rename from config/buses/in-memory/tmp/bus2.yaml rename to config/provisioners/in-memory-channel/tmp/bus2.yaml index 7ce54d816eb..78d2852e2f2 100644 --- a/config/buses/in-memory/tmp/bus2.yaml +++ b/config/provisioners/in-memory-channel/tmp/bus2.yaml @@ -8,7 +8,7 @@ spec: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner - name: in-memory-bus-provisioner + name: in-memory-channel channelable: subscribers: - sinkableDomain: message-dumper-foo.default.svc.cluster.local # Already exists @@ -26,7 +26,7 @@ spec: ref: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterProvisioner - name: in-memory-bus-provisioner + name: in-memory-channel channelable: subscribers: - sinkableDomain: message-dumper-bar.default.svc.cluster.local # Already exists diff --git a/pkg/controller/eventing/inmemory/channel/controller.go b/pkg/controller/eventing/inmemory/channel/controller.go index 87266ae257a..4e10c199df3 100644 --- a/pkg/controller/eventing/inmemory/channel/controller.go +++ b/pkg/controller/eventing/inmemory/channel/controller.go @@ -30,8 +30,8 @@ import ( const ( // controllerAgentName is the string used by this controller to identify // itself when creating events. - controllerAgentName = "in-memory-bus-channel-controller" - ConfigMapName = "in-memory-bus-dispatcher-config-map" + controllerAgentName = "in-memory-channel-controller" + ConfigMapName = "in-memory-channel-dispatcher-config-map" ) var ( @@ -44,7 +44,7 @@ var ( // ProvideController returns a flow controller. func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, error) { // Setup a new controller to Reconcile Channels that belong to this Cluster Provisioner - // (in-memory buses). + // (in-memory channels). r := &reconciler{ configMapKey: DefaultConfigMapKey, recorder: mgr.GetRecorder(controllerAgentName), diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 442d931b748..5ac3bf95e2f 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -107,7 +107,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } // shouldReconcile determines if this Controller should control (and therefore reconcile) a given -// ClusterProvisioner. This Controller only handles in-memory buses. +// ClusterProvisioner. This Controller only handles in-memory channels. func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { if c.Spec.Provisioner != nil { return cpcontroller.IsControlled(c.Spec.Provisioner) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 44c2d6095e2..e280c8237a5 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -42,7 +42,7 @@ import ( ) const ( - cpName = "in-memory-bus-provisioner" + cpName = "in-memory-channel" cNamespace = "test-namespace" cName = "test-channel" @@ -637,7 +637,7 @@ func makeVirtualService() *istiov1alpha3.VirtualService { }, Route: []istiov1alpha3.DestinationWeight{{ Destination: istiov1alpha3.Destination{ - Host: "in-memory-bus-provisioner-clusterbus.knative-eventing.svc.cluster.local", + Host: "in-memory-channel-clusterbus.knative-eventing.svc.cluster.local", Port: istiov1alpha3.PortSelector{ Number: PortNumber, }, diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go b/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go index 234ce3e14d6..db98ab0d989 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go @@ -28,14 +28,14 @@ import ( const ( // controllerAgentName is the string used by this controller to identify // itself when creating events. - controllerAgentName = "in-memory-bus-cluster-provisioner-controller" + controllerAgentName = "in-memory-channel-controller" ) // ProvideController returns a flow controller. func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, error) { logger = logger.With(zap.String("controller", controllerAgentName)) - // Setup a new controller to Reconcile ClusterProvisioners that are in-memory buses. + // Setup a new controller to Reconcile ClusterProvisioners that are in-memory channels. r := &reconciler{ recorder: mgr.GetRecorder(controllerAgentName), logger: logger, diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go index c933585301d..464870ffe60 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go @@ -36,7 +36,7 @@ import ( ) const ( - cpName = "in-memory-bus-provisioner" + Name = "in-memory-channel" ) type reconciler struct { @@ -105,17 +105,17 @@ func IsControlled(ref *eventingv1alpha1.ProvisionerReference) bool { } // shouldReconcile determines if this Controller should control (and therefore reconcile) a given -// ClusterProvisioner. This Controller only handles in-memory buses. +// ClusterProvisioner. This Controller only handles in-memory channels. func shouldReconcile(namespace, name string) bool { - return namespace == "" && name == cpName + return namespace == "" && name == Name } func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { logger := r.logger.With(zap.Any("clusterProvisioner", cp)) // We are syncing one thing. - // 1. The K8s Service to talk to this in-memory bus. - // - There is a single K8s Service for all requests going to this in-memory bus. + // 1. The K8s Service to talk to all in-memory Channels. + // - There is a single K8s Service for all requests going any in-memory Channel. if cp.DeletionTimestamp != nil { // K8s garbage collection will delete the dispatcher service, once this ClusterProvisioner diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go index 168c057c76d..5ebefefff02 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go @@ -91,7 +91,7 @@ func TestIsControlled(t *testing.T) { ref: &eventingv1alpha1.ProvisionerReference{ Ref: &corev1.ObjectReference{ Namespace: "other", - Name: cpName, + Name: Name, }, }, isControlled: false, @@ -107,7 +107,7 @@ func TestIsControlled(t *testing.T) { "is controlled": { ref: &eventingv1alpha1.ProvisionerReference{ Ref: &corev1.ObjectReference{ - Name: cpName, + Name: Name, }, }, isControlled: true, @@ -143,7 +143,7 @@ func TestReconcile(t *testing.T) { &eventingv1alpha1.ClusterProvisioner{ ObjectMeta: metav1.ObjectMeta{ Namespace: "not empty string", - Name: cpName, + Name: Name, }, }, }, @@ -244,7 +244,7 @@ func TestReconcile(t *testing.T) { logger: zap.NewNop(), } if tc.ReconcileKey == "" { - tc.ReconcileKey = fmt.Sprintf("/%s", cpName) + tc.ReconcileKey = fmt.Sprintf("/%s", Name) } t.Run(tc.Name, tc.Runner(t, r, c)) } @@ -257,7 +257,7 @@ func makeClusterProvisioner() *eventingv1alpha1.ClusterProvisioner { Kind: "ClusterProvisioner", }, ObjectMeta: metav1.ObjectMeta{ - Name: cpName, + Name: Name, UID: cpUid, }, } @@ -288,21 +288,21 @@ func makeK8sService() *corev1.Service { }, ObjectMeta: metav1.ObjectMeta{ Namespace: system.Namespace, - Name: fmt.Sprintf("%s-clusterbus", cpName), + Name: fmt.Sprintf("%s-clusterbus", Name), OwnerReferences: []metav1.OwnerReference{ { APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), Kind: "ClusterProvisioner", - Name: cpName, + Name: Name, UID: cpUid, Controller: &truePointer, BlockOwnerDeletion: &truePointer, }, }, - Labels: dispatcherLabels(cpName), + Labels: dispatcherLabels(Name), }, Spec: corev1.ServiceSpec{ - Selector: dispatcherLabels(cpName), + Selector: dispatcherLabels(Name), Ports: []corev1.ServicePort{ { Name: "http", diff --git a/pkg/controller/eventing/inmemory/controller/main.go b/pkg/controller/eventing/inmemory/controller/main.go index 2ec2cfe8ae2..88a0c0ac745 100644 --- a/pkg/controller/eventing/inmemory/controller/main.go +++ b/pkg/controller/eventing/inmemory/controller/main.go @@ -25,25 +25,17 @@ import ( istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "github.com/knative/pkg/signals" "go.uber.org/zap" - "os" "sigs.k8s.io/controller-runtime/pkg/client/config" "sigs.k8s.io/controller-runtime/pkg/manager" ) func main() { - ref := buses.NewBusReferenceFromNames( - os.Getenv("BUS_NAME"), - os.Getenv("BUS_NAMESPACE"), - ) - logConfig := buses.NewLoggingConfig() logger := buses.NewBusLoggerFromConfig(logConfig) defer logger.Sync() logger = logger.With( - // TODO: probably replace, Bus isn't really a thing anymore. - zap.String("eventing.knative.dev/bus", ref.String()), - zap.String("eventing.knative.dev/busType", "in-memory"), - zap.String("eventing.knative.dev/busComponent", buses.Provisioner), + zap.String("eventing.knative.dev/clusterProvisioner", clusterprovisioner.Name), + zap.String("eventing.knative.dev/clusterProvisionerComponent", "Controller"), ) flag.Parse() From 437d2dfb038faeb1223323840207956dce84078f Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Wed, 3 Oct 2018 15:00:50 -0700 Subject: [PATCH 09/22] Add a README.md. --- .../provisioners/in-memory-channel/README.md | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 config/provisioners/in-memory-channel/README.md diff --git a/config/provisioners/in-memory-channel/README.md b/config/provisioners/in-memory-channel/README.md new file mode 100644 index 00000000000..ac84787ac40 --- /dev/null +++ b/config/provisioners/in-memory-channel/README.md @@ -0,0 +1,63 @@ +# In-Memory Channels + +In-memory channels are a best effort Channel. They should **NOT** be used in Production. They are +useful for development. + +They differ from most Channels in that they have: +* No persistence. + - If a Pod goes down, messages go with it. +* No ordering guarantee. + - There is nothing enforcing an ordering, so two messages that arrive at the same time may + go downstream in any order. + - Different downstream subscribers may see different orders. +* No redelivery attempts. + - If downstream rejects a request, a log message is written, but that request is never sent + again. + + +### Deployment steps: + +1. Setup [Knative Eventing](../../../DEVELOPMENT.md) +1. Apply the 'in-memory-channel' ClusterProvisioner, Controller, and Dispatcher. + ```shell + ko apply -f config/providers/in-memory-channel/in-memory-channel.yaml + ```` +1. Create Channels that reference the 'in-memory-channel'. + + ```yaml + apiVersion: eventing.knative.dev/v1alpha1 + kind: Channel + metadata: + name: foo + spec: + provisioner: + ref: + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterProvisioner + name: in-memory-channel + ``` + +### Components + +The major components are: +* ClusterProvisioner Controller +* Channel Controller +* Channel Dispatcher +* Channel Dispatcher Config Map. + +The ClusterProvisioner Controller and the Channel Controller are colocated in one Pod. +```shell +kubectl get deployment -n knative-eventing in-memory-channel-controller +``` + +The Channel Dispatcher receives and distributes all events. There is a single Dispatcher for all +in-memory Channels. +```shell +kubectl get deployment -n knative-eventing in-memory-channel-dispatcher +``` + +The Channel Dispatcher Config Map is used to send information about Channels and Subscriptions from +the Channel Controller to the Channel Dispatcher. +```shell +kubectl get configmap -n knative-eventing in-memory-channel-dispatcher-config-map +``` From 8dbaeeeba0b8d34cd7ab95c89cc45878279f5f14 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Wed, 3 Oct 2018 15:03:21 -0700 Subject: [PATCH 10/22] Typo. --- config/provisioners/in-memory-channel/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/provisioners/in-memory-channel/README.md b/config/provisioners/in-memory-channel/README.md index ac84787ac40..d895f9b32c4 100644 --- a/config/provisioners/in-memory-channel/README.md +++ b/config/provisioners/in-memory-channel/README.md @@ -17,7 +17,7 @@ They differ from most Channels in that they have: ### Deployment steps: -1. Setup [Knative Eventing](../../../DEVELOPMENT.md) +1. Setup [Knative Eventing](../../../DEVELOPMENT.md). 1. Apply the 'in-memory-channel' ClusterProvisioner, Controller, and Dispatcher. ```shell ko apply -f config/providers/in-memory-channel/in-memory-channel.yaml From 9c1654f8feb1abde45fb794a20ec7cee8f28ad62 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Fri, 5 Oct 2018 09:43:34 -0700 Subject: [PATCH 11/22] Use a service account with specific RBAC permissions. --- .../in-memory-channel/in-memory-channel.yaml | 128 +++++++++++++++++- 1 file changed, 123 insertions(+), 5 deletions(-) diff --git a/config/provisioners/in-memory-channel/in-memory-channel.yaml b/config/provisioners/in-memory-channel/in-memory-channel.yaml index ca10befe533..2f9dfd0c941 100644 --- a/config/provisioners/in-memory-channel/in-memory-channel.yaml +++ b/config/provisioners/in-memory-channel/in-memory-channel.yaml @@ -23,11 +23,88 @@ spec: --- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: in-memory-channel-controller + namespace: knative-eventing + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: in-memory-channel-controller +rules: + - apiGroups: + - eventing.knative.dev + resources: + - channels + - clusterprovisioners + verbs: + - get + - list + - watch + - update + - apiGroups: + - "" # Core API group. + resources: + - services + verbs: + - get + - list + - watch + - create + - apiGroups: + - networking.istio.io + resources: + - virtualservices + verbs: + - get + - list + - watch + - create + - apiGroups: + - "" # Core API Group. + resources: + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - "" # Core API Group. + resources: + - configmaps + resourceNames: + - in-memory-channel-dispatcher-config-map + verbs: + - create + - update + +--- + +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: in-memory-channel-controller + namespace: knative-eventing +subjects: + - kind: ServiceAccount + name: in-memory-channel-controller + namespace: knative-eventing +roleRef: + kind: ClusterRole + name: in-memory-channel-controller + apiGroup: rbac.authorization.k8s.io + +--- + apiVersion: apps/v1beta1 kind: Deployment metadata: name: in-memory-channel-controller - namespace: &namespace knative-eventing + namespace: knative-eventing spec: replicas: 1 selector: @@ -38,10 +115,51 @@ spec: metadata: labels: *labels spec: - serviceAccountName: eventing-controller + serviceAccountName: in-memory-channel-controller containers: - - name: controller - image: github.com/knative/eventing/pkg/controller/eventing/inmemory/controller + - name: controller + image: github.com/knative/eventing/pkg/controller/eventing/inmemory/controller + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: in-memory-channel-dispatcher + namespace: knative-eventing + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: in-memory-channel-dispatcher + namespace: knative-eventing +rules: + - apiGroups: + - "" # Core API group. + resources: + - configmaps + verbs: + - get + - list + - watch + +--- + +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: in-memory-channel-dispatcher + namespace: knative-eventing +subjects: + - kind: ServiceAccount + name: in-memory-channel-dispatcher + namespace: knative-eventing +roleRef: + kind: ClusterRole + name: in-memory-channel-dispatcher + apiGroup: rbac.authorization.k8s.io --- @@ -62,7 +180,7 @@ spec: sidecar.isio.io/inject: "true" labels: *labels spec: - serviceAccountName: eventing-controller # TODO: Change to something much less powerful. + serviceAccountName: in-memory-channel-dispatcher containers: - name: dispatcher image: github.com/knative/eventing/cmd/fanoutsidecar From c1d3082c4f89ccafac55330e291568d1543ef369 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Fri, 5 Oct 2018 09:44:01 -0700 Subject: [PATCH 12/22] hack/update-deps.sh --- Gopkg.lock | 11 + third_party/VENDOR-LICENSE | 34 ++ vendor/github.com/fsnotify/fsnotify/AUTHORS | 52 ++ vendor/github.com/fsnotify/fsnotify/LICENSE | 28 + vendor/github.com/fsnotify/fsnotify/fen.go | 37 ++ .../github.com/fsnotify/fsnotify/fsnotify.go | 66 +++ .../github.com/fsnotify/fsnotify/inotify.go | 337 +++++++++++ .../fsnotify/fsnotify/inotify_poller.go | 187 ++++++ vendor/github.com/fsnotify/fsnotify/kqueue.go | 521 ++++++++++++++++ .../fsnotify/fsnotify/open_mode_bsd.go | 11 + .../fsnotify/fsnotify/open_mode_darwin.go | 12 + .../github.com/fsnotify/fsnotify/windows.go | 561 ++++++++++++++++++ 12 files changed, 1857 insertions(+) create mode 100644 vendor/github.com/fsnotify/fsnotify/AUTHORS create mode 100644 vendor/github.com/fsnotify/fsnotify/LICENSE create mode 100644 vendor/github.com/fsnotify/fsnotify/fen.go create mode 100644 vendor/github.com/fsnotify/fsnotify/fsnotify.go create mode 100644 vendor/github.com/fsnotify/fsnotify/inotify.go create mode 100644 vendor/github.com/fsnotify/fsnotify/inotify_poller.go create mode 100644 vendor/github.com/fsnotify/fsnotify/kqueue.go create mode 100644 vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go create mode 100644 vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go create mode 100644 vendor/github.com/fsnotify/fsnotify/windows.go diff --git a/Gopkg.lock b/Gopkg.lock index 736f006f97b..a2ff0660824 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -71,6 +71,14 @@ revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" version = "v1.1.0" +[[projects]] + digest = "1:1b91ae0dc69a41d4c2ed23ea5cffb721ea63f5037ca4b81e6d6771fbb8f45129" + name = "github.com/fsnotify/fsnotify" + packages = ["."] + pruneopts = "NUT" + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + version = "v1.4.7" + [[projects]] digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756" name = "github.com/ghodss/yaml" @@ -1049,6 +1057,7 @@ "github.com/Shopify/sarama", "github.com/bsm/sarama-cluster", "github.com/davecgh/go-spew/spew", + "github.com/fsnotify/fsnotify", "github.com/golang/glog", "github.com/google/go-cmp/cmp", "github.com/google/go-cmp/cmp/cmpopts", @@ -1070,6 +1079,7 @@ "github.com/knative/serving/pkg/client/clientset/versioned", "github.com/knative/test-infra", "github.com/prometheus/client_golang/prometheus/promhttp", + "go.uber.org/atomic", "go.uber.org/zap", "go.uber.org/zap/zapcore", "golang.org/x/net/context", @@ -1098,6 +1108,7 @@ "k8s.io/apimachinery/pkg/util/sets/types", "k8s.io/apimachinery/pkg/util/validation", "k8s.io/apimachinery/pkg/util/wait", + "k8s.io/apimachinery/pkg/util/yaml", "k8s.io/apimachinery/pkg/watch", "k8s.io/client-go/discovery", "k8s.io/client-go/discovery/fake", diff --git a/third_party/VENDOR-LICENSE b/third_party/VENDOR-LICENSE index d28593bbdba..45ce934d7ba 100644 --- a/third_party/VENDOR-LICENSE +++ b/third_party/VENDOR-LICENSE @@ -254,6 +254,40 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/fsnotify/fsnotify + +Copyright (c) 2012 The Go Authors. All rights reserved. +Copyright (c) 2012 fsnotify Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/ghodss/yaml diff --git a/vendor/github.com/fsnotify/fsnotify/AUTHORS b/vendor/github.com/fsnotify/fsnotify/AUTHORS new file mode 100644 index 00000000000..5ab5d41c547 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/AUTHORS @@ -0,0 +1,52 @@ +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# You can update this list using the following command: +# +# $ git shortlog -se | awk '{print $2 " " $3 " " $4}' + +# Please keep the list sorted. + +Aaron L +Adrien Bustany +Amit Krishnan +Anmol Sethi +Bjørn Erik Pedersen +Bruno Bigras +Caleb Spare +Case Nelson +Chris Howey +Christoffer Buchholz +Daniel Wagner-Hall +Dave Cheney +Evan Phoenix +Francisco Souza +Hari haran +John C Barstow +Kelvin Fo +Ken-ichirou MATSUZAWA +Matt Layher +Nathan Youngman +Nickolai Zeldovich +Patrick +Paul Hammond +Pawel Knap +Pieter Droogendijk +Pursuit92 +Riku Voipio +Rob Figueiredo +Rodrigo Chiossi +Slawek Ligus +Soge Zhang +Tiffany Jernigan +Tilak Sharma +Tom Payne +Travis Cline +Tudor Golubenco +Vahe Khachikyan +Yukang +bronze1man +debrando +henrikedwards +铁哥 diff --git a/vendor/github.com/fsnotify/fsnotify/LICENSE b/vendor/github.com/fsnotify/fsnotify/LICENSE new file mode 100644 index 00000000000..f21e5408009 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. +Copyright (c) 2012 fsnotify Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/fsnotify/fsnotify/fen.go b/vendor/github.com/fsnotify/fsnotify/fen.go new file mode 100644 index 00000000000..ced39cb881e --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/fen.go @@ -0,0 +1,37 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build solaris + +package fsnotify + +import ( + "errors" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct { + Events chan Event + Errors chan error +} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + return nil +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + return nil +} + +// Remove stops watching the the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + return nil +} diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go new file mode 100644 index 00000000000..190bf0de575 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -0,0 +1,66 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9 + +// Package fsnotify provides a platform-independent interface for file system notifications. +package fsnotify + +import ( + "bytes" + "errors" + "fmt" +) + +// Event represents a single file system notification. +type Event struct { + Name string // Relative path to the file or directory. + Op Op // File operation that triggered the event. +} + +// Op describes a set of file operations. +type Op uint32 + +// These are the generalized file operations that can trigger a notification. +const ( + Create Op = 1 << iota + Write + Remove + Rename + Chmod +) + +func (op Op) String() string { + // Use a buffer for efficient string concatenation + var buffer bytes.Buffer + + if op&Create == Create { + buffer.WriteString("|CREATE") + } + if op&Remove == Remove { + buffer.WriteString("|REMOVE") + } + if op&Write == Write { + buffer.WriteString("|WRITE") + } + if op&Rename == Rename { + buffer.WriteString("|RENAME") + } + if op&Chmod == Chmod { + buffer.WriteString("|CHMOD") + } + if buffer.Len() == 0 { + return "" + } + return buffer.String()[1:] // Strip leading pipe +} + +// String returns a string representation of the event in the form +// "file: REMOVE|WRITE|..." +func (e Event) String() string { + return fmt.Sprintf("%q: %s", e.Name, e.Op.String()) +} + +// Common errors that can be reported by a watcher +var ErrEventOverflow = errors.New("fsnotify queue overflow") diff --git a/vendor/github.com/fsnotify/fsnotify/inotify.go b/vendor/github.com/fsnotify/fsnotify/inotify.go new file mode 100644 index 00000000000..d9fd1b88a05 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/inotify.go @@ -0,0 +1,337 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package fsnotify + +import ( + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + "unsafe" + + "golang.org/x/sys/unix" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct { + Events chan Event + Errors chan error + mu sync.Mutex // Map access + fd int + poller *fdPoller + watches map[string]*watch // Map of inotify watches (key: path) + paths map[int]string // Map of watched paths (key: watch descriptor) + done chan struct{} // Channel for sending a "quit message" to the reader goroutine + doneResp chan struct{} // Channel to respond to Close +} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + // Create inotify fd + fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC) + if fd == -1 { + return nil, errno + } + // Create epoll + poller, err := newFdPoller(fd) + if err != nil { + unix.Close(fd) + return nil, err + } + w := &Watcher{ + fd: fd, + poller: poller, + watches: make(map[string]*watch), + paths: make(map[int]string), + Events: make(chan Event), + Errors: make(chan error), + done: make(chan struct{}), + doneResp: make(chan struct{}), + } + + go w.readEvents() + return w, nil +} + +func (w *Watcher) isClosed() bool { + select { + case <-w.done: + return true + default: + return false + } +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + if w.isClosed() { + return nil + } + + // Send 'close' signal to goroutine, and set the Watcher to closed. + close(w.done) + + // Wake up goroutine + w.poller.wake() + + // Wait for goroutine to close + <-w.doneResp + + return nil +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + name = filepath.Clean(name) + if w.isClosed() { + return errors.New("inotify instance already closed") + } + + const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | + unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | + unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF + + var flags uint32 = agnosticEvents + + w.mu.Lock() + defer w.mu.Unlock() + watchEntry := w.watches[name] + if watchEntry != nil { + flags |= watchEntry.flags | unix.IN_MASK_ADD + } + wd, errno := unix.InotifyAddWatch(w.fd, name, flags) + if wd == -1 { + return errno + } + + if watchEntry == nil { + w.watches[name] = &watch{wd: uint32(wd), flags: flags} + w.paths[wd] = name + } else { + watchEntry.wd = uint32(wd) + watchEntry.flags = flags + } + + return nil +} + +// Remove stops watching the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + name = filepath.Clean(name) + + // Fetch the watch. + w.mu.Lock() + defer w.mu.Unlock() + watch, ok := w.watches[name] + + // Remove it from inotify. + if !ok { + return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) + } + + // We successfully removed the watch if InotifyRmWatch doesn't return an + // error, we need to clean up our internal state to ensure it matches + // inotify's kernel state. + delete(w.paths, int(watch.wd)) + delete(w.watches, name) + + // inotify_rm_watch will return EINVAL if the file has been deleted; + // the inotify will already have been removed. + // watches and pathes are deleted in ignoreLinux() implicitly and asynchronously + // by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE + // so that EINVAL means that the wd is being rm_watch()ed or its file removed + // by another thread and we have not received IN_IGNORE event. + success, errno := unix.InotifyRmWatch(w.fd, watch.wd) + if success == -1 { + // TODO: Perhaps it's not helpful to return an error here in every case. + // the only two possible errors are: + // EBADF, which happens when w.fd is not a valid file descriptor of any kind. + // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. + // Watch descriptors are invalidated when they are removed explicitly or implicitly; + // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. + return errno + } + + return nil +} + +type watch struct { + wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) + flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) +} + +// readEvents reads from the inotify file descriptor, converts the +// received events into Event objects and sends them via the Events channel +func (w *Watcher) readEvents() { + var ( + buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events + n int // Number of bytes read with read() + errno error // Syscall errno + ok bool // For poller.wait + ) + + defer close(w.doneResp) + defer close(w.Errors) + defer close(w.Events) + defer unix.Close(w.fd) + defer w.poller.close() + + for { + // See if we have been closed. + if w.isClosed() { + return + } + + ok, errno = w.poller.wait() + if errno != nil { + select { + case w.Errors <- errno: + case <-w.done: + return + } + continue + } + + if !ok { + continue + } + + n, errno = unix.Read(w.fd, buf[:]) + // If a signal interrupted execution, see if we've been asked to close, and try again. + // http://man7.org/linux/man-pages/man7/signal.7.html : + // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" + if errno == unix.EINTR { + continue + } + + // unix.Read might have been woken up by Close. If so, we're done. + if w.isClosed() { + return + } + + if n < unix.SizeofInotifyEvent { + var err error + if n == 0 { + // If EOF is received. This should really never happen. + err = io.EOF + } else if n < 0 { + // If an error occurred while reading. + err = errno + } else { + // Read was too short. + err = errors.New("notify: short read in readEvents()") + } + select { + case w.Errors <- err: + case <-w.done: + return + } + continue + } + + var offset uint32 + // We don't know how many events we just read into the buffer + // While the offset points to at least one whole event... + for offset <= uint32(n-unix.SizeofInotifyEvent) { + // Point "raw" to the event in the buffer + raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) + + mask := uint32(raw.Mask) + nameLen := uint32(raw.Len) + + if mask&unix.IN_Q_OVERFLOW != 0 { + select { + case w.Errors <- ErrEventOverflow: + case <-w.done: + return + } + } + + // If the event happened to the watched directory or the watched file, the kernel + // doesn't append the filename to the event, but we would like to always fill the + // the "Name" field with a valid filename. We retrieve the path of the watch from + // the "paths" map. + w.mu.Lock() + name, ok := w.paths[int(raw.Wd)] + // IN_DELETE_SELF occurs when the file/directory being watched is removed. + // This is a sign to clean up the maps, otherwise we are no longer in sync + // with the inotify kernel state which has already deleted the watch + // automatically. + if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { + delete(w.paths, int(raw.Wd)) + delete(w.watches, name) + } + w.mu.Unlock() + + if nameLen > 0 { + // Point "bytes" at the first byte of the filename + bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent])) + // The filename is padded with NULL bytes. TrimRight() gets rid of those. + name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") + } + + event := newEvent(name, mask) + + // Send the events that are not ignored on the events channel + if !event.ignoreLinux(mask) { + select { + case w.Events <- event: + case <-w.done: + return + } + } + + // Move to the next event in the buffer + offset += unix.SizeofInotifyEvent + nameLen + } + } +} + +// Certain types of events can be "ignored" and not sent over the Events +// channel. Such as events marked ignore by the kernel, or MODIFY events +// against files that do not exist. +func (e *Event) ignoreLinux(mask uint32) bool { + // Ignore anything the inotify API says to ignore + if mask&unix.IN_IGNORED == unix.IN_IGNORED { + return true + } + + // If the event is not a DELETE or RENAME, the file must exist. + // Otherwise the event is ignored. + // *Note*: this was put in place because it was seen that a MODIFY + // event was sent after the DELETE. This ignores that MODIFY and + // assumes a DELETE will come or has come if the file doesn't exist. + if !(e.Op&Remove == Remove || e.Op&Rename == Rename) { + _, statErr := os.Lstat(e.Name) + return os.IsNotExist(statErr) + } + return false +} + +// newEvent returns an platform-independent Event based on an inotify mask. +func newEvent(name string, mask uint32) Event { + e := Event{Name: name} + if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { + e.Op |= Create + } + if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { + e.Op |= Remove + } + if mask&unix.IN_MODIFY == unix.IN_MODIFY { + e.Op |= Write + } + if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { + e.Op |= Rename + } + if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { + e.Op |= Chmod + } + return e +} diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go new file mode 100644 index 00000000000..cc7db4b22ef --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go @@ -0,0 +1,187 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package fsnotify + +import ( + "errors" + + "golang.org/x/sys/unix" +) + +type fdPoller struct { + fd int // File descriptor (as returned by the inotify_init() syscall) + epfd int // Epoll file descriptor + pipe [2]int // Pipe for waking up +} + +func emptyPoller(fd int) *fdPoller { + poller := new(fdPoller) + poller.fd = fd + poller.epfd = -1 + poller.pipe[0] = -1 + poller.pipe[1] = -1 + return poller +} + +// Create a new inotify poller. +// This creates an inotify handler, and an epoll handler. +func newFdPoller(fd int) (*fdPoller, error) { + var errno error + poller := emptyPoller(fd) + defer func() { + if errno != nil { + poller.close() + } + }() + poller.fd = fd + + // Create epoll fd + poller.epfd, errno = unix.EpollCreate1(0) + if poller.epfd == -1 { + return nil, errno + } + // Create pipe; pipe[0] is the read end, pipe[1] the write end. + errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) + if errno != nil { + return nil, errno + } + + // Register inotify fd with epoll + event := unix.EpollEvent{ + Fd: int32(poller.fd), + Events: unix.EPOLLIN, + } + errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event) + if errno != nil { + return nil, errno + } + + // Register pipe fd with epoll + event = unix.EpollEvent{ + Fd: int32(poller.pipe[0]), + Events: unix.EPOLLIN, + } + errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event) + if errno != nil { + return nil, errno + } + + return poller, nil +} + +// Wait using epoll. +// Returns true if something is ready to be read, +// false if there is not. +func (poller *fdPoller) wait() (bool, error) { + // 3 possible events per fd, and 2 fds, makes a maximum of 6 events. + // I don't know whether epoll_wait returns the number of events returned, + // or the total number of events ready. + // I decided to catch both by making the buffer one larger than the maximum. + events := make([]unix.EpollEvent, 7) + for { + n, errno := unix.EpollWait(poller.epfd, events, -1) + if n == -1 { + if errno == unix.EINTR { + continue + } + return false, errno + } + if n == 0 { + // If there are no events, try again. + continue + } + if n > 6 { + // This should never happen. More events were returned than should be possible. + return false, errors.New("epoll_wait returned more events than I know what to do with") + } + ready := events[:n] + epollhup := false + epollerr := false + epollin := false + for _, event := range ready { + if event.Fd == int32(poller.fd) { + if event.Events&unix.EPOLLHUP != 0 { + // This should not happen, but if it does, treat it as a wakeup. + epollhup = true + } + if event.Events&unix.EPOLLERR != 0 { + // If an error is waiting on the file descriptor, we should pretend + // something is ready to read, and let unix.Read pick up the error. + epollerr = true + } + if event.Events&unix.EPOLLIN != 0 { + // There is data to read. + epollin = true + } + } + if event.Fd == int32(poller.pipe[0]) { + if event.Events&unix.EPOLLHUP != 0 { + // Write pipe descriptor was closed, by us. This means we're closing down the + // watcher, and we should wake up. + } + if event.Events&unix.EPOLLERR != 0 { + // If an error is waiting on the pipe file descriptor. + // This is an absolute mystery, and should never ever happen. + return false, errors.New("Error on the pipe descriptor.") + } + if event.Events&unix.EPOLLIN != 0 { + // This is a regular wakeup, so we have to clear the buffer. + err := poller.clearWake() + if err != nil { + return false, err + } + } + } + } + + if epollhup || epollerr || epollin { + return true, nil + } + return false, nil + } +} + +// Close the write end of the poller. +func (poller *fdPoller) wake() error { + buf := make([]byte, 1) + n, errno := unix.Write(poller.pipe[1], buf) + if n == -1 { + if errno == unix.EAGAIN { + // Buffer is full, poller will wake. + return nil + } + return errno + } + return nil +} + +func (poller *fdPoller) clearWake() error { + // You have to be woken up a LOT in order to get to 100! + buf := make([]byte, 100) + n, errno := unix.Read(poller.pipe[0], buf) + if n == -1 { + if errno == unix.EAGAIN { + // Buffer is empty, someone else cleared our wake. + return nil + } + return errno + } + return nil +} + +// Close all poller file descriptors, but not the one passed to it. +func (poller *fdPoller) close() { + if poller.pipe[1] != -1 { + unix.Close(poller.pipe[1]) + } + if poller.pipe[0] != -1 { + unix.Close(poller.pipe[0]) + } + if poller.epfd != -1 { + unix.Close(poller.epfd) + } +} diff --git a/vendor/github.com/fsnotify/fsnotify/kqueue.go b/vendor/github.com/fsnotify/fsnotify/kqueue.go new file mode 100644 index 00000000000..86e76a3d676 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/kqueue.go @@ -0,0 +1,521 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd openbsd netbsd dragonfly darwin + +package fsnotify + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sync" + "time" + + "golang.org/x/sys/unix" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct { + Events chan Event + Errors chan error + done chan struct{} // Channel for sending a "quit message" to the reader goroutine + + kq int // File descriptor (as returned by the kqueue() syscall). + + mu sync.Mutex // Protects access to watcher data + watches map[string]int // Map of watched file descriptors (key: path). + externalWatches map[string]bool // Map of watches added by user of the library. + dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue. + paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events. + fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events). + isClosed bool // Set to true when Close() is first called +} + +type pathInfo struct { + name string + isDir bool +} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + kq, err := kqueue() + if err != nil { + return nil, err + } + + w := &Watcher{ + kq: kq, + watches: make(map[string]int), + dirFlags: make(map[string]uint32), + paths: make(map[int]pathInfo), + fileExists: make(map[string]bool), + externalWatches: make(map[string]bool), + Events: make(chan Event), + Errors: make(chan error), + done: make(chan struct{}), + } + + go w.readEvents() + return w, nil +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + w.mu.Lock() + if w.isClosed { + w.mu.Unlock() + return nil + } + w.isClosed = true + + // copy paths to remove while locked + var pathsToRemove = make([]string, 0, len(w.watches)) + for name := range w.watches { + pathsToRemove = append(pathsToRemove, name) + } + w.mu.Unlock() + // unlock before calling Remove, which also locks + + for _, name := range pathsToRemove { + w.Remove(name) + } + + // send a "quit" message to the reader goroutine + close(w.done) + + return nil +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + w.mu.Lock() + w.externalWatches[name] = true + w.mu.Unlock() + _, err := w.addWatch(name, noteAllEvents) + return err +} + +// Remove stops watching the the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + name = filepath.Clean(name) + w.mu.Lock() + watchfd, ok := w.watches[name] + w.mu.Unlock() + if !ok { + return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) + } + + const registerRemove = unix.EV_DELETE + if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { + return err + } + + unix.Close(watchfd) + + w.mu.Lock() + isDir := w.paths[watchfd].isDir + delete(w.watches, name) + delete(w.paths, watchfd) + delete(w.dirFlags, name) + w.mu.Unlock() + + // Find all watched paths that are in this directory that are not external. + if isDir { + var pathsToRemove []string + w.mu.Lock() + for _, path := range w.paths { + wdir, _ := filepath.Split(path.name) + if filepath.Clean(wdir) == name { + if !w.externalWatches[path.name] { + pathsToRemove = append(pathsToRemove, path.name) + } + } + } + w.mu.Unlock() + for _, name := range pathsToRemove { + // Since these are internal, not much sense in propagating error + // to the user, as that will just confuse them with an error about + // a path they did not explicitly watch themselves. + w.Remove(name) + } + } + + return nil +} + +// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) +const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME + +// keventWaitTime to block on each read from kevent +var keventWaitTime = durationToTimespec(100 * time.Millisecond) + +// addWatch adds name to the watched file set. +// The flags are interpreted as described in kevent(2). +// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks. +func (w *Watcher) addWatch(name string, flags uint32) (string, error) { + var isDir bool + // Make ./name and name equivalent + name = filepath.Clean(name) + + w.mu.Lock() + if w.isClosed { + w.mu.Unlock() + return "", errors.New("kevent instance already closed") + } + watchfd, alreadyWatching := w.watches[name] + // We already have a watch, but we can still override flags. + if alreadyWatching { + isDir = w.paths[watchfd].isDir + } + w.mu.Unlock() + + if !alreadyWatching { + fi, err := os.Lstat(name) + if err != nil { + return "", err + } + + // Don't watch sockets. + if fi.Mode()&os.ModeSocket == os.ModeSocket { + return "", nil + } + + // Don't watch named pipes. + if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { + return "", nil + } + + // Follow Symlinks + // Unfortunately, Linux can add bogus symlinks to watch list without + // issue, and Windows can't do symlinks period (AFAIK). To maintain + // consistency, we will act like everything is fine. There will simply + // be no file events for broken symlinks. + // Hence the returns of nil on errors. + if fi.Mode()&os.ModeSymlink == os.ModeSymlink { + name, err = filepath.EvalSymlinks(name) + if err != nil { + return "", nil + } + + w.mu.Lock() + _, alreadyWatching = w.watches[name] + w.mu.Unlock() + + if alreadyWatching { + return name, nil + } + + fi, err = os.Lstat(name) + if err != nil { + return "", nil + } + } + + watchfd, err = unix.Open(name, openMode, 0700) + if watchfd == -1 { + return "", err + } + + isDir = fi.IsDir() + } + + const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE + if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { + unix.Close(watchfd) + return "", err + } + + if !alreadyWatching { + w.mu.Lock() + w.watches[name] = watchfd + w.paths[watchfd] = pathInfo{name: name, isDir: isDir} + w.mu.Unlock() + } + + if isDir { + // Watch the directory if it has not been watched before, + // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) + w.mu.Lock() + + watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && + (!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) + // Store flags so this watch can be updated later + w.dirFlags[name] = flags + w.mu.Unlock() + + if watchDir { + if err := w.watchDirectoryFiles(name); err != nil { + return "", err + } + } + } + return name, nil +} + +// readEvents reads from kqueue and converts the received kevents into +// Event values that it sends down the Events channel. +func (w *Watcher) readEvents() { + eventBuffer := make([]unix.Kevent_t, 10) + +loop: + for { + // See if there is a message on the "done" channel + select { + case <-w.done: + break loop + default: + } + + // Get new events + kevents, err := read(w.kq, eventBuffer, &keventWaitTime) + // EINTR is okay, the syscall was interrupted before timeout expired. + if err != nil && err != unix.EINTR { + select { + case w.Errors <- err: + case <-w.done: + break loop + } + continue + } + + // Flush the events we received to the Events channel + for len(kevents) > 0 { + kevent := &kevents[0] + watchfd := int(kevent.Ident) + mask := uint32(kevent.Fflags) + w.mu.Lock() + path := w.paths[watchfd] + w.mu.Unlock() + event := newEvent(path.name, mask) + + if path.isDir && !(event.Op&Remove == Remove) { + // Double check to make sure the directory exists. This can happen when + // we do a rm -fr on a recursively watched folders and we receive a + // modification event first but the folder has been deleted and later + // receive the delete event + if _, err := os.Lstat(event.Name); os.IsNotExist(err) { + // mark is as delete event + event.Op |= Remove + } + } + + if event.Op&Rename == Rename || event.Op&Remove == Remove { + w.Remove(event.Name) + w.mu.Lock() + delete(w.fileExists, event.Name) + w.mu.Unlock() + } + + if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { + w.sendDirectoryChangeEvents(event.Name) + } else { + // Send the event on the Events channel. + select { + case w.Events <- event: + case <-w.done: + break loop + } + } + + if event.Op&Remove == Remove { + // Look for a file that may have overwritten this. + // For example, mv f1 f2 will delete f2, then create f2. + if path.isDir { + fileDir := filepath.Clean(event.Name) + w.mu.Lock() + _, found := w.watches[fileDir] + w.mu.Unlock() + if found { + // make sure the directory exists before we watch for changes. When we + // do a recursive watch and perform rm -fr, the parent directory might + // have gone missing, ignore the missing directory and let the + // upcoming delete event remove the watch from the parent directory. + if _, err := os.Lstat(fileDir); err == nil { + w.sendDirectoryChangeEvents(fileDir) + } + } + } else { + filePath := filepath.Clean(event.Name) + if fileInfo, err := os.Lstat(filePath); err == nil { + w.sendFileCreatedEventIfNew(filePath, fileInfo) + } + } + } + + // Move to next event + kevents = kevents[1:] + } + } + + // cleanup + err := unix.Close(w.kq) + if err != nil { + // only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors. + select { + case w.Errors <- err: + default: + } + } + close(w.Events) + close(w.Errors) +} + +// newEvent returns an platform-independent Event based on kqueue Fflags. +func newEvent(name string, mask uint32) Event { + e := Event{Name: name} + if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { + e.Op |= Remove + } + if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { + e.Op |= Write + } + if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { + e.Op |= Rename + } + if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { + e.Op |= Chmod + } + return e +} + +func newCreateEvent(name string) Event { + return Event{Name: name, Op: Create} +} + +// watchDirectoryFiles to mimic inotify when adding a watch on a directory +func (w *Watcher) watchDirectoryFiles(dirPath string) error { + // Get all files + files, err := ioutil.ReadDir(dirPath) + if err != nil { + return err + } + + for _, fileInfo := range files { + filePath := filepath.Join(dirPath, fileInfo.Name()) + filePath, err = w.internalWatch(filePath, fileInfo) + if err != nil { + return err + } + + w.mu.Lock() + w.fileExists[filePath] = true + w.mu.Unlock() + } + + return nil +} + +// sendDirectoryEvents searches the directory for newly created files +// and sends them over the event channel. This functionality is to have +// the BSD version of fsnotify match Linux inotify which provides a +// create event for files created in a watched directory. +func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { + // Get all files + files, err := ioutil.ReadDir(dirPath) + if err != nil { + select { + case w.Errors <- err: + case <-w.done: + return + } + } + + // Search for new files + for _, fileInfo := range files { + filePath := filepath.Join(dirPath, fileInfo.Name()) + err := w.sendFileCreatedEventIfNew(filePath, fileInfo) + + if err != nil { + return + } + } +} + +// sendFileCreatedEvent sends a create event if the file isn't already being tracked. +func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { + w.mu.Lock() + _, doesExist := w.fileExists[filePath] + w.mu.Unlock() + if !doesExist { + // Send create event + select { + case w.Events <- newCreateEvent(filePath): + case <-w.done: + return + } + } + + // like watchDirectoryFiles (but without doing another ReadDir) + filePath, err = w.internalWatch(filePath, fileInfo) + if err != nil { + return err + } + + w.mu.Lock() + w.fileExists[filePath] = true + w.mu.Unlock() + + return nil +} + +func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) { + if fileInfo.IsDir() { + // mimic Linux providing delete events for subdirectories + // but preserve the flags used if currently watching subdirectory + w.mu.Lock() + flags := w.dirFlags[name] + w.mu.Unlock() + + flags |= unix.NOTE_DELETE | unix.NOTE_RENAME + return w.addWatch(name, flags) + } + + // watch file to mimic Linux inotify + return w.addWatch(name, noteAllEvents) +} + +// kqueue creates a new kernel event queue and returns a descriptor. +func kqueue() (kq int, err error) { + kq, err = unix.Kqueue() + if kq == -1 { + return kq, err + } + return kq, nil +} + +// register events with the queue +func register(kq int, fds []int, flags int, fflags uint32) error { + changes := make([]unix.Kevent_t, len(fds)) + + for i, fd := range fds { + // SetKevent converts int to the platform-specific types: + unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) + changes[i].Fflags = fflags + } + + // register the events + success, err := unix.Kevent(kq, changes, nil, nil) + if success == -1 { + return err + } + return nil +} + +// read retrieves pending events, or waits until an event occurs. +// A timeout of nil blocks indefinitely, while 0 polls the queue. +func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) { + n, err := unix.Kevent(kq, nil, events, timeout) + if err != nil { + return nil, err + } + return events[0:n], nil +} + +// durationToTimespec prepares a timeout value +func durationToTimespec(d time.Duration) unix.Timespec { + return unix.NsecToTimespec(d.Nanoseconds()) +} diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go new file mode 100644 index 00000000000..7d8de14513e --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go @@ -0,0 +1,11 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd openbsd netbsd dragonfly + +package fsnotify + +import "golang.org/x/sys/unix" + +const openMode = unix.O_NONBLOCK | unix.O_RDONLY diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go new file mode 100644 index 00000000000..9139e17161b --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go @@ -0,0 +1,12 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package fsnotify + +import "golang.org/x/sys/unix" + +// note: this constant is not defined on BSD +const openMode = unix.O_EVTONLY diff --git a/vendor/github.com/fsnotify/fsnotify/windows.go b/vendor/github.com/fsnotify/fsnotify/windows.go new file mode 100644 index 00000000000..09436f31d82 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/windows.go @@ -0,0 +1,561 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package fsnotify + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "runtime" + "sync" + "syscall" + "unsafe" +) + +// Watcher watches a set of files, delivering events to a channel. +type Watcher struct { + Events chan Event + Errors chan error + isClosed bool // Set to true when Close() is first called + mu sync.Mutex // Map access + port syscall.Handle // Handle to completion port + watches watchMap // Map of watches (key: i-number) + input chan *input // Inputs to the reader are sent on this channel + quit chan chan<- error +} + +// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. +func NewWatcher() (*Watcher, error) { + port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) + if e != nil { + return nil, os.NewSyscallError("CreateIoCompletionPort", e) + } + w := &Watcher{ + port: port, + watches: make(watchMap), + input: make(chan *input, 1), + Events: make(chan Event, 50), + Errors: make(chan error), + quit: make(chan chan<- error, 1), + } + go w.readEvents() + return w, nil +} + +// Close removes all watches and closes the events channel. +func (w *Watcher) Close() error { + if w.isClosed { + return nil + } + w.isClosed = true + + // Send "quit" message to the reader goroutine + ch := make(chan error) + w.quit <- ch + if err := w.wakeupReader(); err != nil { + return err + } + return <-ch +} + +// Add starts watching the named file or directory (non-recursively). +func (w *Watcher) Add(name string) error { + if w.isClosed { + return errors.New("watcher already closed") + } + in := &input{ + op: opAddWatch, + path: filepath.Clean(name), + flags: sysFSALLEVENTS, + reply: make(chan error), + } + w.input <- in + if err := w.wakeupReader(); err != nil { + return err + } + return <-in.reply +} + +// Remove stops watching the the named file or directory (non-recursively). +func (w *Watcher) Remove(name string) error { + in := &input{ + op: opRemoveWatch, + path: filepath.Clean(name), + reply: make(chan error), + } + w.input <- in + if err := w.wakeupReader(); err != nil { + return err + } + return <-in.reply +} + +const ( + // Options for AddWatch + sysFSONESHOT = 0x80000000 + sysFSONLYDIR = 0x1000000 + + // Events + sysFSACCESS = 0x1 + sysFSALLEVENTS = 0xfff + sysFSATTRIB = 0x4 + sysFSCLOSE = 0x18 + sysFSCREATE = 0x100 + sysFSDELETE = 0x200 + sysFSDELETESELF = 0x400 + sysFSMODIFY = 0x2 + sysFSMOVE = 0xc0 + sysFSMOVEDFROM = 0x40 + sysFSMOVEDTO = 0x80 + sysFSMOVESELF = 0x800 + + // Special events + sysFSIGNORED = 0x8000 + sysFSQOVERFLOW = 0x4000 +) + +func newEvent(name string, mask uint32) Event { + e := Event{Name: name} + if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { + e.Op |= Create + } + if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { + e.Op |= Remove + } + if mask&sysFSMODIFY == sysFSMODIFY { + e.Op |= Write + } + if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { + e.Op |= Rename + } + if mask&sysFSATTRIB == sysFSATTRIB { + e.Op |= Chmod + } + return e +} + +const ( + opAddWatch = iota + opRemoveWatch +) + +const ( + provisional uint64 = 1 << (32 + iota) +) + +type input struct { + op int + path string + flags uint32 + reply chan error +} + +type inode struct { + handle syscall.Handle + volume uint32 + index uint64 +} + +type watch struct { + ov syscall.Overlapped + ino *inode // i-number + path string // Directory path + mask uint64 // Directory itself is being watched with these notify flags + names map[string]uint64 // Map of names being watched and their notify flags + rename string // Remembers the old name while renaming a file + buf [4096]byte +} + +type indexMap map[uint64]*watch +type watchMap map[uint32]indexMap + +func (w *Watcher) wakeupReader() error { + e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) + if e != nil { + return os.NewSyscallError("PostQueuedCompletionStatus", e) + } + return nil +} + +func getDir(pathname string) (dir string, err error) { + attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) + if e != nil { + return "", os.NewSyscallError("GetFileAttributes", e) + } + if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + dir = pathname + } else { + dir, _ = filepath.Split(pathname) + dir = filepath.Clean(dir) + } + return +} + +func getIno(path string) (ino *inode, err error) { + h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), + syscall.FILE_LIST_DIRECTORY, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + nil, syscall.OPEN_EXISTING, + syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) + if e != nil { + return nil, os.NewSyscallError("CreateFile", e) + } + var fi syscall.ByHandleFileInformation + if e = syscall.GetFileInformationByHandle(h, &fi); e != nil { + syscall.CloseHandle(h) + return nil, os.NewSyscallError("GetFileInformationByHandle", e) + } + ino = &inode{ + handle: h, + volume: fi.VolumeSerialNumber, + index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), + } + return ino, nil +} + +// Must run within the I/O thread. +func (m watchMap) get(ino *inode) *watch { + if i := m[ino.volume]; i != nil { + return i[ino.index] + } + return nil +} + +// Must run within the I/O thread. +func (m watchMap) set(ino *inode, watch *watch) { + i := m[ino.volume] + if i == nil { + i = make(indexMap) + m[ino.volume] = i + } + i[ino.index] = watch +} + +// Must run within the I/O thread. +func (w *Watcher) addWatch(pathname string, flags uint64) error { + dir, err := getDir(pathname) + if err != nil { + return err + } + if flags&sysFSONLYDIR != 0 && pathname != dir { + return nil + } + ino, err := getIno(dir) + if err != nil { + return err + } + w.mu.Lock() + watchEntry := w.watches.get(ino) + w.mu.Unlock() + if watchEntry == nil { + if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil { + syscall.CloseHandle(ino.handle) + return os.NewSyscallError("CreateIoCompletionPort", e) + } + watchEntry = &watch{ + ino: ino, + path: dir, + names: make(map[string]uint64), + } + w.mu.Lock() + w.watches.set(ino, watchEntry) + w.mu.Unlock() + flags |= provisional + } else { + syscall.CloseHandle(ino.handle) + } + if pathname == dir { + watchEntry.mask |= flags + } else { + watchEntry.names[filepath.Base(pathname)] |= flags + } + if err = w.startRead(watchEntry); err != nil { + return err + } + if pathname == dir { + watchEntry.mask &= ^provisional + } else { + watchEntry.names[filepath.Base(pathname)] &= ^provisional + } + return nil +} + +// Must run within the I/O thread. +func (w *Watcher) remWatch(pathname string) error { + dir, err := getDir(pathname) + if err != nil { + return err + } + ino, err := getIno(dir) + if err != nil { + return err + } + w.mu.Lock() + watch := w.watches.get(ino) + w.mu.Unlock() + if watch == nil { + return fmt.Errorf("can't remove non-existent watch for: %s", pathname) + } + if pathname == dir { + w.sendEvent(watch.path, watch.mask&sysFSIGNORED) + watch.mask = 0 + } else { + name := filepath.Base(pathname) + w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) + delete(watch.names, name) + } + return w.startRead(watch) +} + +// Must run within the I/O thread. +func (w *Watcher) deleteWatch(watch *watch) { + for name, mask := range watch.names { + if mask&provisional == 0 { + w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED) + } + delete(watch.names, name) + } + if watch.mask != 0 { + if watch.mask&provisional == 0 { + w.sendEvent(watch.path, watch.mask&sysFSIGNORED) + } + watch.mask = 0 + } +} + +// Must run within the I/O thread. +func (w *Watcher) startRead(watch *watch) error { + if e := syscall.CancelIo(watch.ino.handle); e != nil { + w.Errors <- os.NewSyscallError("CancelIo", e) + w.deleteWatch(watch) + } + mask := toWindowsFlags(watch.mask) + for _, m := range watch.names { + mask |= toWindowsFlags(m) + } + if mask == 0 { + if e := syscall.CloseHandle(watch.ino.handle); e != nil { + w.Errors <- os.NewSyscallError("CloseHandle", e) + } + w.mu.Lock() + delete(w.watches[watch.ino.volume], watch.ino.index) + w.mu.Unlock() + return nil + } + e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], + uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) + if e != nil { + err := os.NewSyscallError("ReadDirectoryChanges", e) + if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { + // Watched directory was probably removed + if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) { + if watch.mask&sysFSONESHOT != 0 { + watch.mask = 0 + } + } + err = nil + } + w.deleteWatch(watch) + w.startRead(watch) + return err + } + return nil +} + +// readEvents reads from the I/O completion port, converts the +// received events into Event objects and sends them via the Events channel. +// Entry point to the I/O thread. +func (w *Watcher) readEvents() { + var ( + n, key uint32 + ov *syscall.Overlapped + ) + runtime.LockOSThread() + + for { + e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) + watch := (*watch)(unsafe.Pointer(ov)) + + if watch == nil { + select { + case ch := <-w.quit: + w.mu.Lock() + var indexes []indexMap + for _, index := range w.watches { + indexes = append(indexes, index) + } + w.mu.Unlock() + for _, index := range indexes { + for _, watch := range index { + w.deleteWatch(watch) + w.startRead(watch) + } + } + var err error + if e := syscall.CloseHandle(w.port); e != nil { + err = os.NewSyscallError("CloseHandle", e) + } + close(w.Events) + close(w.Errors) + ch <- err + return + case in := <-w.input: + switch in.op { + case opAddWatch: + in.reply <- w.addWatch(in.path, uint64(in.flags)) + case opRemoveWatch: + in.reply <- w.remWatch(in.path) + } + default: + } + continue + } + + switch e { + case syscall.ERROR_MORE_DATA: + if watch == nil { + w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") + } else { + // The i/o succeeded but the buffer is full. + // In theory we should be building up a full packet. + // In practice we can get away with just carrying on. + n = uint32(unsafe.Sizeof(watch.buf)) + } + case syscall.ERROR_ACCESS_DENIED: + // Watched directory was probably removed + w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) + w.deleteWatch(watch) + w.startRead(watch) + continue + case syscall.ERROR_OPERATION_ABORTED: + // CancelIo was called on this handle + continue + default: + w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e) + continue + case nil: + } + + var offset uint32 + for { + if n == 0 { + w.Events <- newEvent("", sysFSQOVERFLOW) + w.Errors <- errors.New("short read in readEvents()") + break + } + + // Point "raw" to the event in the buffer + raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) + buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) + name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) + fullname := filepath.Join(watch.path, name) + + var mask uint64 + switch raw.Action { + case syscall.FILE_ACTION_REMOVED: + mask = sysFSDELETESELF + case syscall.FILE_ACTION_MODIFIED: + mask = sysFSMODIFY + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + watch.rename = name + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + if watch.names[watch.rename] != 0 { + watch.names[name] |= watch.names[watch.rename] + delete(watch.names, watch.rename) + mask = sysFSMOVESELF + } + } + + sendNameEvent := func() { + if w.sendEvent(fullname, watch.names[name]&mask) { + if watch.names[name]&sysFSONESHOT != 0 { + delete(watch.names, name) + } + } + } + if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { + sendNameEvent() + } + if raw.Action == syscall.FILE_ACTION_REMOVED { + w.sendEvent(fullname, watch.names[name]&sysFSIGNORED) + delete(watch.names, name) + } + if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { + if watch.mask&sysFSONESHOT != 0 { + watch.mask = 0 + } + } + if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { + fullname = filepath.Join(watch.path, watch.rename) + sendNameEvent() + } + + // Move to the next event in the buffer + if raw.NextEntryOffset == 0 { + break + } + offset += raw.NextEntryOffset + + // Error! + if offset >= n { + w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") + break + } + } + + if err := w.startRead(watch); err != nil { + w.Errors <- err + } + } +} + +func (w *Watcher) sendEvent(name string, mask uint64) bool { + if mask == 0 { + return false + } + event := newEvent(name, uint32(mask)) + select { + case ch := <-w.quit: + w.quit <- ch + case w.Events <- event: + } + return true +} + +func toWindowsFlags(mask uint64) uint32 { + var m uint32 + if mask&sysFSACCESS != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS + } + if mask&sysFSMODIFY != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE + } + if mask&sysFSATTRIB != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES + } + if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 { + m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME + } + return m +} + +func toFSnotifyFlags(action uint32) uint64 { + switch action { + case syscall.FILE_ACTION_ADDED: + return sysFSCREATE + case syscall.FILE_ACTION_REMOVED: + return sysFSDELETE + case syscall.FILE_ACTION_MODIFIED: + return sysFSMODIFY + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + return sysFSMOVEDFROM + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + return sysFSMOVEDTO + } + return 0 +} From 9e014e9e429b9a8a26ee5cf3961289e35de19557 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Fri, 5 Oct 2018 11:53:47 -0700 Subject: [PATCH 13/22] isio -> istio --- config/provisioners/in-memory-channel/in-memory-channel.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/provisioners/in-memory-channel/in-memory-channel.yaml b/config/provisioners/in-memory-channel/in-memory-channel.yaml index 2f9dfd0c941..b585fccc833 100644 --- a/config/provisioners/in-memory-channel/in-memory-channel.yaml +++ b/config/provisioners/in-memory-channel/in-memory-channel.yaml @@ -177,7 +177,7 @@ spec: template: metadata: annotations: - sidecar.isio.io/inject: "true" + sidecar.istio.io/inject: "true" labels: *labels spec: serviceAccountName: in-memory-channel-dispatcher From 4a8f2644b4c725420e966c4012e7aabb8d2b0ac7 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Tue, 9 Oct 2018 15:36:58 -0700 Subject: [PATCH 14/22] Respond to PR comments. --- .../eventing/inmemory/channel/controller.go | 10 +++++++--- pkg/controller/eventing/inmemory/channel/reconcile.go | 10 +++++----- .../eventing/inmemory/channel/reconcile_test.go | 6 +++--- pkg/controller/eventing/inmemory/controller/main.go | 11 ++++++++++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/controller.go b/pkg/controller/eventing/inmemory/channel/controller.go index 4e10c199df3..e7775e22400 100644 --- a/pkg/controller/eventing/inmemory/channel/controller.go +++ b/pkg/controller/eventing/inmemory/channel/controller.go @@ -31,22 +31,26 @@ const ( // controllerAgentName is the string used by this controller to identify // itself when creating events. controllerAgentName = "in-memory-channel-controller" + + // ConfigMapName is the name of the ConfigMap in the knative-eventing namespace that contains + // the subscription information for all in-memory Channels. The Provisioner writes to it and the + // Dispatcher reads from it. ConfigMapName = "in-memory-channel-dispatcher-config-map" ) var ( - DefaultConfigMapKey = types.NamespacedName{ + defaultConfigMapKey = types.NamespacedName{ Namespace: system.Namespace, Name: ConfigMapName, } ) -// ProvideController returns a flow controller. +// ProvideController returns a Controller that represents the in-memory-channel Provisioner. func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, error) { // Setup a new controller to Reconcile Channels that belong to this Cluster Provisioner // (in-memory channels). r := &reconciler{ - configMapKey: DefaultConfigMapKey, + configMapKey: defaultConfigMapKey, recorder: mgr.GetRecorder(controllerAgentName), logger: logger, } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 5ac3bf95e2f..e072fd7b83b 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -41,8 +41,8 @@ import ( ) const ( - PortName = "http" - PortNumber = 80 + portName = "http" + portNumber = 80 finalizerName = controllerAgentName ) @@ -273,8 +273,8 @@ func newK8sService(c *eventingv1alpha1.Channel) *corev1.Service { Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { - Name: PortName, - Port: PortNumber, + Name: portName, + Port: portNumber, }, }, }, @@ -316,7 +316,7 @@ func newVirtualService(channel *eventingv1alpha1.Channel) *istiov1alpha3.Virtual Destination: istiov1alpha3.Destination{ Host: destinationHost, Port: istiov1alpha3.PortSelector{ - Number: PortNumber, + Number: portNumber, }, }}, }}, diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index e280c8237a5..e522b66b652 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -588,8 +588,8 @@ func makeK8sService() *corev1.Service { Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { - Name: PortName, - Port: PortNumber, + Name: portName, + Port: portNumber, }, }, }, @@ -639,7 +639,7 @@ func makeVirtualService() *istiov1alpha3.VirtualService { Destination: istiov1alpha3.Destination{ Host: "in-memory-channel-clusterbus.knative-eventing.svc.cluster.local", Port: istiov1alpha3.PortSelector{ - Number: PortNumber, + Number: portNumber, }, }}, }}, diff --git a/pkg/controller/eventing/inmemory/controller/main.go b/pkg/controller/eventing/inmemory/controller/main.go index 88a0c0ac745..9b859694756 100644 --- a/pkg/controller/eventing/inmemory/controller/main.go +++ b/pkg/controller/eventing/inmemory/controller/main.go @@ -51,10 +51,19 @@ func main() { // The controllers for both the ClusterProvisioner and the Channels created by that // ClusterProvisioner run in this process. _, err = clusterprovisioner.ProvideController(mgr, logger.Desugar()) + if err != nil { + logger.Fatal("Unable to create Provisioner controller", zap.Error(err)) + } _, err = channel.ProvideController(mgr, logger.Desugar()) + if err != nil { + logger.Fatal("Unable to create Channel controller", zap.Error(err)) + } // set up signals so we handle the first shutdown signal gracefully stopCh := signals.SetupSignalHandler() // Start blocks forever. - mgr.Start(stopCh) + err = mgr.Start(stopCh) + if err != nil { + logger.Fatal("Manager.Start() returned an error", zap.Error(err)) + } } From af00150e3cafde4ae6ada8708f4c7edb17d411ba Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Wed, 10 Oct 2018 14:21:54 -0700 Subject: [PATCH 15/22] ConfigMap create ClusterRole cannot be bound by resourceName. --- .../in-memory-channel/in-memory-channel.yaml | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/config/provisioners/in-memory-channel/in-memory-channel.yaml b/config/provisioners/in-memory-channel/in-memory-channel.yaml index b585fccc833..35531fc57e1 100644 --- a/config/provisioners/in-memory-channel/in-memory-channel.yaml +++ b/config/provisioners/in-memory-channel/in-memory-channel.yaml @@ -47,40 +47,32 @@ rules: - watch - update - apiGroups: - - "" # Core API group. - resources: - - services - verbs: - - get - - list - - watch - - create - - apiGroups: - - networking.istio.io + - "" # Core API group. resources: - - virtualservices + - configmaps + - services verbs: - - get - - list - - watch - - create + - get + - list + - watch + - create - apiGroups: - - "" # Core API Group. + - "" # Core API Group. resources: - - configmaps + - configmaps + resourceNames: + - in-memory-channel-dispatcher-config-map verbs: - - get - - list - - watch + - update - apiGroups: - - "" # Core API Group. + - networking.istio.io resources: - - configmaps - resourceNames: - - in-memory-channel-dispatcher-config-map + - virtualservices verbs: - - create - - update + - get + - list + - watch + - create --- From 1d73bbaaabb5a5185a62440cea84134ffb4ababf Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Wed, 10 Oct 2018 15:26:19 -0700 Subject: [PATCH 16/22] Watch K8s Services and VirtualServices. --- .../eventing/inmemory/channel/controller.go | 23 ++++++++++++++++--- .../inmemory/clusterprovisioner/controller.go | 11 +++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/controller.go b/pkg/controller/eventing/inmemory/channel/controller.go index e7775e22400..b139d622f2a 100644 --- a/pkg/controller/eventing/inmemory/channel/controller.go +++ b/pkg/controller/eventing/inmemory/channel/controller.go @@ -19,7 +19,9 @@ package channel import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/system" + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -35,7 +37,7 @@ const ( // ConfigMapName is the name of the ConfigMap in the knative-eventing namespace that contains // the subscription information for all in-memory Channels. The Provisioner writes to it and the // Dispatcher reads from it. - ConfigMapName = "in-memory-channel-dispatcher-config-map" + ConfigMapName = "in-memory-channel-dispatcher-config-map" ) var ( @@ -71,8 +73,23 @@ func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Cont return nil, err } - // TODO: Should we watch the K8s service and Istio Virtual Service as well? If they change, we - // probably should change it back. + // Watch the K8s Services that are owned by Channels. + err = c.Watch(&source.Kind{ + Type: &corev1.Service{}, + }, &handler.EnqueueRequestForOwner{OwnerType: &eventingv1alpha1.Channel{}, IsController: true}) + if err != nil { + logger.Error("Unable to watch K8s Services.", zap.Error(err)) + return nil, err + } + + // Watch the VirtualServices that are owned by Channels. + err = c.Watch(&source.Kind{ + Type: &istiov1alpha3.VirtualService{}, + }, &handler.EnqueueRequestForOwner{OwnerType: &eventingv1alpha1.Channel{}, IsController: true}) + if err != nil { + logger.Error("Unable to watch VirtualServices.", zap.Error(err)) + return nil, err + } return c, nil } diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go b/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go index db98ab0d989..3296a144733 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go @@ -19,6 +19,7 @@ package clusterprovisioner import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -57,8 +58,14 @@ func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Cont return nil, err } - // TODO: Should we watch the K8s service as well? If it changes, we probably should change it - // back. + // Watch the K8s Services that are owned by ClusterProvisioners. + err = c.Watch(&source.Kind{ + Type: &corev1.Service{}, + }, &handler.EnqueueRequestForOwner{OwnerType: &eventingv1alpha1.ClusterProvisioner{}, IsController: true}) + if err != nil { + logger.Error("Unable to watch K8s Services.", zap.Error(err)) + return nil, err + } return c, nil } From a3aaf5a2582e40bcb69c63301f4013012fe8f429 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 11 Oct 2018 11:00:55 -0700 Subject: [PATCH 17/22] Re-add SerializeConfig (accidentally removed during last merge). --- .../eventing/inmemory/channel/reconcile.go | 19 ++---- .../inmemory/channel/reconcile_test.go | 8 +-- pkg/sidecar/configmap/parse.go | 14 +++++ pkg/sidecar/configmap/parse_test.go | 62 ++++++++++++++++++- 4 files changed, 84 insertions(+), 19 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index e072fd7b83b..4b94cac102f 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -21,7 +21,7 @@ import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/controller" cpcontroller "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterprovisioner" - multichannelfanoutparse "github.com/knative/eventing/pkg/sidecar/configmap/parse" + "github.com/knative/eventing/pkg/sidecar/configmap" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" "github.com/knative/eventing/pkg/system" @@ -370,7 +370,7 @@ func (r *reconciler) syncChannelConfig(ctx context.Context) error { func (r *reconciler) writeConfigMap(ctx context.Context, config *multichannelfanout.Config) error { logger := r.logger.With(zap.Any("configMap", r.configMapKey)) - updated, err := multichannelfanoutparse.SerializeConfig(*config) + updated, err := configmap.SerializeConfig(*config) if err != nil { r.logger.Error("Unable to serialize config", zap.Error(err), zap.Any("config", config)) return err @@ -387,29 +387,22 @@ func (r *reconciler) writeConfigMap(ctx context.Context, config *multichannelfan return err } - cur := cm.Data[multichannelfanoutparse.MultiChannelFanoutConfigKey] - if equality.Semantic.DeepEqual(cur, updated) { + if equality.Semantic.DeepEqual(cm.Data, updated) { // Nothing to update. return nil } - if cm.Data == nil { - cm.Data = map[string]string{} - } - - cm.Data[multichannelfanoutparse.MultiChannelFanoutConfigKey] = updated + cm.Data = updated return r.client.Update(ctx, cm) } -func (r *reconciler) createNewConfigMap(config string) *corev1.ConfigMap { +func (r *reconciler) createNewConfigMap(data map[string]string) *corev1.ConfigMap { return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: r.configMapKey.Namespace, Name: r.configMapKey.Name, }, - Data: map[string]string{ - multichannelfanoutparse.MultiChannelFanoutConfigKey: config, - }, + Data: data, } } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index e522b66b652..e8a8fb3e7b2 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -24,7 +24,7 @@ import ( "github.com/google/go-cmp/cmp" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" controllertesting "github.com/knative/eventing/pkg/controller/testing" - "github.com/knative/eventing/pkg/sidecar/configmap/parse" + "github.com/knative/eventing/pkg/sidecar/configmap" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" @@ -557,7 +557,7 @@ func makeConfigMap() *corev1.ConfigMap { func makeConfigMapWithVerifyConfigMapData() *corev1.ConfigMap { cm := makeConfigMap() cm.Data = map[string]string{} - cm.Data[parse.MultiChannelFanoutConfigKey] = insertedByVerifyConfigMapData + cm.Data[configmap.MultiChannelFanoutConfigKey] = insertedByVerifyConfigMapData return cm } @@ -798,7 +798,7 @@ func verifyConfigMapData() []controllertesting.MockUpdate { return []controllertesting.MockUpdate{ func(innerClient client.Client, ctx context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { if cm, ok := obj.(*corev1.ConfigMap); ok { - s := cm.Data[parse.MultiChannelFanoutConfigKey] + s := cm.Data[configmap.MultiChannelFanoutConfigKey] c := multichannelfanout.Config{} err := json.Unmarshal([]byte(s), &c) if err != nil { @@ -811,7 +811,7 @@ func verifyConfigMapData() []controllertesting.MockUpdate { } // Verified it is correct, now so that we can verify this actually occurred, swap // out the data with a known value for later comparison. - cm.Data[parse.MultiChannelFanoutConfigKey] = insertedByVerifyConfigMapData + cm.Data[configmap.MultiChannelFanoutConfigKey] = insertedByVerifyConfigMapData return controllertesting.Handled, innerClient.Update(ctx, obj) } return controllertesting.Unhandled, nil diff --git a/pkg/sidecar/configmap/parse.go b/pkg/sidecar/configmap/parse.go index 8661dd2bf8e..a6f479fe6a5 100644 --- a/pkg/sidecar/configmap/parse.go +++ b/pkg/sidecar/configmap/parse.go @@ -17,6 +17,7 @@ limitations under the License. package configmap import ( + "encoding/json" "fmt" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" "go.uber.org/zap" @@ -29,6 +30,7 @@ const ( ) // ConfigMapData attempts to parse the config map's data into a multichannelfanout.Config. +// orig == NewFanoutConfig(SerializeConfig(orig)) func NewFanoutConfig(logger *zap.Logger, data map[string]string) (*multichannelfanout.Config, error) { str, present := data[MultiChannelFanoutConfigKey] if !present { @@ -37,3 +39,15 @@ func NewFanoutConfig(logger *zap.Logger, data map[string]string) (*multichannelf } return multichannelfanout.Parse(logger, str) } + +// SerializeConfig takes in a multichannelfanout.Config and generates the ConfigMap equivalent. +// orig == NewFanoutConfig(SerializeConfig(orig)) +func SerializeConfig(config multichannelfanout.Config) (map[string]string, error) { + jb, err := json.Marshal(config) + if err != nil { + return nil, err + } + return map[string]string{ + MultiChannelFanoutConfigKey: string(jb), + }, nil +} diff --git a/pkg/sidecar/configmap/parse_test.go b/pkg/sidecar/configmap/parse_test.go index a621ad510a8..5cddb280b89 100644 --- a/pkg/sidecar/configmap/parse_test.go +++ b/pkg/sidecar/configmap/parse_test.go @@ -38,7 +38,7 @@ func TestNewFanoutConfig(t *testing.T) { expectedErr: true, }, { - name: "invalid YAML", + name: "invalid YAML", config: ` key: - value @@ -57,7 +57,7 @@ func TestNewFanoutConfig(t *testing.T) { expectedErr: true, }, { - name: "valid", + name: "valid", config: ` channelConfigs: - namespace: default @@ -142,6 +142,64 @@ func TestNewFanoutConfig(t *testing.T) { } } +func TestSerializeConfig(t *testing.T) { + testCases := map[string]struct { + config *multichannelfanout.Config + }{ + "empty config": { + config: &multichannelfanout.Config{}, + }, + "full config": { + config: &multichannelfanout.Config{ + ChannelConfigs: []multichannelfanout.ChannelConfig{ + { + Namespace: "default", + Name: "c1", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + { + CallableDomain: "foo.example.com", + SinkableDomain: "bar.example.com", + }, + { + SinkableDomain: "qux.example.com", + }, + { + CallableDomain: "baz.example.com", + }, + {}, + }, + }, + }, + { + Namespace: "other", + Name: "no-subs", + FanoutConfig: fanout.Config{ + Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{}, + }, + }, + }, + }, + }, + } + + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + s, err := SerializeConfig(*tc.config) + if err != nil { + t.Errorf("Unexpected error serializing config: %v", err) + } + rt, err := NewFanoutConfig(zap.NewNop(), s) + if err != nil { + t.Errorf("Unexpected error deserializing: %v", err) + } + if diff := cmp.Diff(tc.config, rt); diff != "" { + t.Errorf("Unexpected error roundtripping the config (-want, +got): %v", diff) + } + }) + } +} + func formatData(config string) map[string]string { data := make(map[string]string) if config != "" { From 0a75ae746406ae94fbab03ae9ac556378b82c003 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 11 Oct 2018 13:32:17 -0700 Subject: [PATCH 18/22] goimports --- pkg/controller/eventing/inmemory/channel/reconcile.go | 1 + pkg/controller/eventing/inmemory/channel/reconcile_test.go | 3 ++- .../eventing/inmemory/clusterprovisioner/reconcile.go | 1 + .../eventing/inmemory/clusterprovisioner/reconcile_test.go | 3 ++- pkg/controller/eventing/inmemory/controller/main.go | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 4b94cac102f..85fe75cfa3a 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -18,6 +18,7 @@ package channel import ( "context" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/controller" cpcontroller "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterprovisioner" diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index e8a8fb3e7b2..2da5a5953fa 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -21,6 +21,8 @@ import ( "encoding/json" "errors" "fmt" + "testing" + "github.com/google/go-cmp/cmp" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" controllertesting "github.com/knative/eventing/pkg/controller/testing" @@ -38,7 +40,6 @@ import ( "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - "testing" ) const ( diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go index 464870ffe60..1fb9eecbba9 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go @@ -18,6 +18,7 @@ package clusterprovisioner import ( "context" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/controller" "github.com/knative/eventing/pkg/system" diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go index 5ebefefff02..c59a755725a 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go @@ -20,6 +20,8 @@ import ( "context" "errors" "fmt" + "testing" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/system" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" @@ -32,7 +34,6 @@ import ( "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - "testing" controllertesting "github.com/knative/eventing/pkg/controller/testing" ) diff --git a/pkg/controller/eventing/inmemory/controller/main.go b/pkg/controller/eventing/inmemory/controller/main.go index 9b859694756..c5485a9e719 100644 --- a/pkg/controller/eventing/inmemory/controller/main.go +++ b/pkg/controller/eventing/inmemory/controller/main.go @@ -18,6 +18,7 @@ package main import ( "flag" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/buses" "github.com/knative/eventing/pkg/controller/eventing/inmemory/channel" From e955a147e1083d5ecc141ea162fa1bece2637f2e Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 11 Oct 2018 14:00:16 -0700 Subject: [PATCH 19/22] Collapse if conditions. --- .../eventing/inmemory/channel/reconcile.go | 22 ++++++++----------- .../inmemory/clusterprovisioner/reconcile.go | 9 +++----- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 85fe75cfa3a..1cd6089bf3e 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -98,8 +98,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // regardless of the error. } - updateStatusErr := r.updateChannel(ctx, c) - if updateStatusErr != nil { + if updateStatusErr := r.updateChannel(ctx, c); updateStatusErr != nil { logger.Info("Error updating Channel Status", zap.Error(updateStatusErr)) return reconcile.Result{}, updateStatusErr } @@ -125,8 +124,7 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) // 3. The configuration of all Channel subscriptions. // We always need to sync the Channel config, so do it first. - err := r.syncChannelConfig(ctx) - if err != nil { + if err := r.syncChannelConfig(ctx); err != nil { logger.Info("Error updating syncing the Channel config", zap.Error(err)) return err } @@ -141,15 +139,14 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) r.addFinalizer(c) r.makeSubscribable(c) - svc, err := r.createK8sService(ctx, c) - if err != nil { + if svc, err := r.createK8sService(ctx, c); err != nil { logger.Info("Error creating the Channel's K8s Service", zap.Error(err)) return err + } else { + r.makeSinkable(c, svc) } - r.makeSinkable(c, svc) - err = r.createVirtualService(ctx, c) - if err != nil { + if err := r.createVirtualService(ctx, c); err != nil { logger.Info("Error creating the Virtual Service for the Channel", zap.Error(err)) return err } @@ -334,10 +331,10 @@ func (r *reconciler) setStatusReady(c *eventingv1alpha1.Channel) { }, } } + func (r *reconciler) updateChannel(ctx context.Context, u *eventingv1alpha1.Channel) error { o := &eventingv1alpha1.Channel{} - err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o) - if err != nil { + if err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o); err != nil { r.logger.Info("Error getting Channel for status update", zap.Error(err), zap.Any("updatedChannel", u)) return err } @@ -441,8 +438,7 @@ func (r *reconciler) listAllChannels(ctx context.Context) ([]eventingv1alpha1.Ch } for { cl := &eventingv1alpha1.ChannelList{} - err := r.client.List(ctx, opts, cl) - if err != nil { + if err := r.client.List(ctx, opts, cl); err != nil { return nil, err } diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go index 1fb9eecbba9..e4a3f1e23fc 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go @@ -89,8 +89,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // regardless of the error. } - updateStatusErr := r.updateClusterProvisionerStatus(ctx, cp) - if updateStatusErr != nil { + if updateStatusErr := r.updateClusterProvisionerStatus(ctx, cp); updateStatusErr != nil { logger.Info("Error updating ClusterProvisioner Status", zap.Error(updateStatusErr)) return reconcile.Result{}, updateStatusErr } @@ -124,8 +123,7 @@ func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.Cluster return nil } - err := r.createDispatcherService(ctx, cp) - if err != nil { + if err := r.createDispatcherService(ctx, cp); err != nil { logger.Info("Error creating the ClusterProvisioner's K8s Service", zap.Error(err)) return err } @@ -171,8 +169,7 @@ func (r *reconciler) setStatusReady(cp *eventingv1alpha1.ClusterProvisioner) { func (r *reconciler) updateClusterProvisionerStatus(ctx context.Context, u *eventingv1alpha1.ClusterProvisioner) error { o := &eventingv1alpha1.ClusterProvisioner{} - err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o) - if err != nil { + if err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o); err != nil { r.logger.Info("Error getting ClusterProvisioner for status update", zap.Error(err), zap.Any("updatedClusterProvisioner", u)) return err } From c79aa4376f41ad1f83cbe077714e4c5e2380a2cd Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 11 Oct 2018 14:20:25 -0700 Subject: [PATCH 20/22] Only reconcile Channels. --- .../eventing/inmemory/channel/reconcile.go | 2 +- .../inmemory/channel/reconcile_test.go | 9 ++++++++ .../inmemory/clusterprovisioner/reconcile.go | 17 ++++++++++---- .../clusterprovisioner/reconcile_test.go | 23 ++++++++++++++++++- 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 1cd6089bf3e..bf178507049 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -110,7 +110,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // ClusterProvisioner. This Controller only handles in-memory channels. func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { if c.Spec.Provisioner != nil { - return cpcontroller.IsControlled(c.Spec.Provisioner) + return cpcontroller.IsControlled(c.Spec.Provisioner, c.Kind) } return false } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 2da5a5953fa..62f942a1c04 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -107,6 +107,9 @@ var ( Namespace: cNamespace, Name: "c1", }, + TypeMeta: metav1.TypeMeta{ + Kind: "Channel", + }, Spec: eventingv1alpha1.ChannelSpec{ Provisioner: &eventingv1alpha1.ProvisionerReference{ Ref: &corev1.ObjectReference{ @@ -134,6 +137,9 @@ var ( Namespace: cNamespace, Name: "c2", }, + TypeMeta: metav1.TypeMeta{ + Kind: "Channel", + }, Spec: eventingv1alpha1.ChannelSpec{ Provisioner: &eventingv1alpha1.ProvisionerReference{ Ref: &corev1.ObjectReference{ @@ -154,6 +160,9 @@ var ( Namespace: cNamespace, Name: "c3", }, + TypeMeta: metav1.TypeMeta{ + Kind: "Channel", + }, Spec: eventingv1alpha1.ChannelSpec{ Provisioner: &eventingv1alpha1.ProvisionerReference{ Ref: &corev1.ObjectReference{ diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go index e4a3f1e23fc..c95e40f95e9 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go @@ -37,7 +37,11 @@ import ( ) const ( + // The name of the in-memory channel ClusterProvisioner. Name = "in-memory-channel" + + // The name of the Channel resource in eventing.knative.dev/v1alpha1. + channel = "Channel" ) type reconciler struct { @@ -76,7 +80,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } // Does this Controller control this ClusterProvisioner? - if !shouldReconcile(cp.Namespace, cp.Name) { + if !shouldReconcile(cp.Namespace, cp.Name, cp.Spec.Reconciles.Kind) { logger.Info("Not reconciling ClusterProvisioner, it is not controlled by this Controller", zap.String("APIVersion", cp.APIVersion), zap.String("Kind", cp.Kind), zap.String("Namespace", cp.Namespace), zap.String("name", cp.Name)) return reconcile.Result{}, nil } @@ -97,17 +101,20 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err return reconcile.Result{}, err } -func IsControlled(ref *eventingv1alpha1.ProvisionerReference) bool { +// IsControlled determines if the in-memory Channel Controller should control (and therefore +// reconcile) a given object, based on that object's ClusterProvisioner reference. kind is the kind +// of that object. +func IsControlled(ref *eventingv1alpha1.ProvisionerReference, kind string) bool { if ref != nil && ref.Ref != nil { - return shouldReconcile(ref.Ref.Namespace, ref.Ref.Name) + return shouldReconcile(ref.Ref.Namespace, ref.Ref.Name, kind) } return false } // shouldReconcile determines if this Controller should control (and therefore reconcile) a given // ClusterProvisioner. This Controller only handles in-memory channels. -func shouldReconcile(namespace, name string) bool { - return namespace == "" && name == Name +func shouldReconcile(namespace, name, kind string) bool { + return namespace == "" && name == Name && kind == channel } func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go index c59a755725a..b557412cbdc 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go @@ -76,16 +76,19 @@ func TestInjectClient(t *testing.T) { func TestIsControlled(t *testing.T) { testCases := map[string]struct { ref *eventingv1alpha1.ProvisionerReference + kind string isControlled bool }{ "nil": { ref: nil, + kind: "Channel", isControlled: false, }, "ref nil": { ref: &eventingv1alpha1.ProvisionerReference{ Ref: nil, }, + kind: "Channel", isControlled: false, }, "wrong namespace": { @@ -95,6 +98,7 @@ func TestIsControlled(t *testing.T) { Name: Name, }, }, + kind: "Channel", isControlled: false, }, "wrong name": { @@ -103,6 +107,16 @@ func TestIsControlled(t *testing.T) { Name: "other-name", }, }, + kind: "Channel", + isControlled: false, + }, + "wrong kind": { + ref: &eventingv1alpha1.ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: Name, + }, + }, + kind: "Source", isControlled: false, }, "is controlled": { @@ -111,12 +125,13 @@ func TestIsControlled(t *testing.T) { Name: Name, }, }, + kind: "Channel", isControlled: true, }, } for n, tc := range testCases { t.Run(n, func(t *testing.T) { - isControlled := IsControlled(tc.ref) + isControlled := IsControlled(tc.ref, tc.kind) if isControlled != tc.isControlled { t.Errorf("Expected: %v. Actual: %v", tc.isControlled, isControlled) } @@ -261,6 +276,12 @@ func makeClusterProvisioner() *eventingv1alpha1.ClusterProvisioner { Name: Name, UID: cpUid, }, + Spec: eventingv1alpha1.ClusterProvisionerSpec{ + Reconciles: metav1.GroupKind{ + Group: "eventing.knative.dev/v1alpha1", + Kind: "Channel", + }, + }, } } From d32d44d191f08754c14307a82f5298b5e1cbb4b1 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 11 Oct 2018 16:01:00 -0700 Subject: [PATCH 21/22] Manipulate Status only via methods on the Status object. --- pkg/apis/eventing/v1alpha1/channel_types.go | 41 ++++- .../eventing/v1alpha1/channel_types_test.go | 151 ++++++++++++++++++ .../v1alpha1/cluster_provisioner_types.go | 14 ++ .../cluster_provisioner_types_test.go | 12 ++ .../eventing/inmemory/channel/reconcile.go | 37 +---- .../inmemory/channel/reconcile_test.go | 21 +-- .../inmemory/clusterprovisioner/reconcile.go | 15 +- .../clusterprovisioner/reconcile_test.go | 1 + 8 files changed, 237 insertions(+), 55 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/channel_types.go b/pkg/apis/eventing/v1alpha1/channel_types.go index 8c69a2124fa..d5122b08344 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types.go +++ b/pkg/apis/eventing/v1alpha1/channel_types.go @@ -20,6 +20,7 @@ import ( "github.com/knative/pkg/apis" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "github.com/knative/pkg/webhook" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -75,7 +76,7 @@ type ChannelSpec struct { Channelable *duckv1alpha1.Channelable `json:"channelable,omitempty"` } -var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned) +var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned, ChannelConditionSinkable, ChannelConditionSubscribable) // ChannelStatus represents the current state of a Channel. type ChannelStatus struct { @@ -110,6 +111,14 @@ const ( // ChannelConditionProvisioned has status True when the Channel's backing // resources have been provisioned. ChannelConditionProvisioned duckv1alpha1.ConditionType = "Provisioned" + + // ChannelConditionSinkable has status true when this Channel meets the Sinkable contract and + // has a non-empty domainInternal. + ChannelConditionSinkable duckv1alpha1.ConditionType = "Sinkable" + + // ChannelConditionSubscribable has status true when this Channel meets the Subscribable + // contract and has a non-empty Channelable object reference. + ChannelConditionSubscribable duckv1alpha1.ConditionType = "Subscribable" ) // GetCondition returns the condition currently associated with the given type, or nil. @@ -132,6 +141,36 @@ func (cs *ChannelStatus) MarkProvisioned() { chanCondSet.Manage(cs).MarkTrue(ChannelConditionProvisioned) } +// SetSubscribable makes this Channel Subscribable, by having it point at itself. The 'name' and +// 'namespace' should be the name and namespace of the Channel this ChannelStatus is on. It also +// sets the ChannelConditionSubscribable to true. +func (cs *ChannelStatus) SetSubscribable(namespace, name string) { + if namespace != "" || name != "" { + cs.Subscribable.Channelable = corev1.ObjectReference{ + Kind: "Channel", + APIVersion: SchemeGroupVersion.String(), + Namespace: namespace, + Name: name, + } + chanCondSet.Manage(cs).MarkTrue(ChannelConditionSubscribable) + } else { + cs.Subscribable.Channelable = corev1.ObjectReference{} + chanCondSet.Manage(cs).MarkFalse(ChannelConditionSubscribable, "notSubscribable", "not Subscribable") + } + +} + +// SetSinkable makes this Channel sinkable by setting the domainInternal. It also sets the +// ChannelConditionSinkable to true. +func (cs *ChannelStatus) SetSinkable(domainInternal string) { + cs.Sinkable.DomainInternal = domainInternal + if domainInternal != "" { + chanCondSet.Manage(cs).MarkTrue(ChannelConditionSinkable) + } else { + chanCondSet.Manage(cs).MarkFalse(ChannelConditionSinkable, "emptyDomainInternal", "domainInternal is the empty string") + } +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // ChannelList is a collection of Channels. diff --git a/pkg/apis/eventing/v1alpha1/channel_types_test.go b/pkg/apis/eventing/v1alpha1/channel_types_test.go index 2f208018207..e2e6cac4104 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_types_test.go @@ -35,6 +35,8 @@ var condUnprovisioned = duckv1alpha1.Condition{ Status: corev1.ConditionFalse, } +var ignoreTransitionTimeMessageAndReason = cmpopts.IgnoreFields(duckv1alpha1.Condition{}, "LastTransitionTime", "Message", "Reason") + func TestChannelGetCondition(t *testing.T) { tests := []struct { name string @@ -97,6 +99,12 @@ func TestChannelInitializeConditions(t *testing.T) { }, { Type: ChannelConditionReady, Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionSinkable, + Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionSubscribable, + Status: corev1.ConditionUnknown, }}, }, }, { @@ -114,6 +122,12 @@ func TestChannelInitializeConditions(t *testing.T) { }, { Type: ChannelConditionReady, Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionSinkable, + Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionSubscribable, + Status: corev1.ConditionUnknown, }}, }, }, { @@ -131,6 +145,12 @@ func TestChannelInitializeConditions(t *testing.T) { }, { Type: ChannelConditionReady, Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionSinkable, + Status: corev1.ConditionUnknown, + }, { + Type: ChannelConditionSubscribable, + Status: corev1.ConditionUnknown, }}}, }, } @@ -150,14 +170,20 @@ func TestChannelIsReady(t *testing.T) { tests := []struct { name string markProvisioned bool + setSubscribable bool + setSinkable bool wantReady bool }{{ name: "all happy", markProvisioned: true, + setSubscribable: true, + setSinkable: true, wantReady: true, }, { name: "one sad", markProvisioned: false, + setSubscribable: true, + setSinkable: true, wantReady: false, }} for _, test := range tests { @@ -166,6 +192,12 @@ func TestChannelIsReady(t *testing.T) { if test.markProvisioned { cs.MarkProvisioned() } + if test.setSubscribable { + cs.SetSubscribable("foo", "bar") + } + if test.setSinkable { + cs.SetSinkable("foo.bar") + } got := cs.IsReady() if test.wantReady != got { t.Errorf("unexpected readiness: want %v, got %v", test.wantReady, got) @@ -173,3 +205,122 @@ func TestChannelIsReady(t *testing.T) { }) } } + +func TestChannelStatus_SetSubscribable(t *testing.T) { + testCases := map[string]struct { + namespace string + name string + want *ChannelStatus + }{ + "empty namespace and name": { + want: &ChannelStatus{ + Conditions: []duckv1alpha1.Condition{ + // Note that Ready is here because when the condition is marked False, duck + // automatically sets Ready to false. + { + Type: ChannelConditionReady, + Status: corev1.ConditionFalse, + }, + { + Type: ChannelConditionSubscribable, + Status: corev1.ConditionFalse, + }, + }, + }, + }, + "empty namespace": { + name: "foobar", + want: &ChannelStatus{ + Subscribable: duckv1alpha1.Subscribable{ + Channelable: corev1.ObjectReference{ + APIVersion: SchemeGroupVersion.String(), + Kind: "Channel", + Name: "foobar", + }, + }, + Conditions: []duckv1alpha1.Condition{ + { + Type: ChannelConditionSubscribable, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + "subscribable": { + namespace: "test-namespace", + name: "test-name", + want: &ChannelStatus{ + Subscribable: duckv1alpha1.Subscribable{ + Channelable: corev1.ObjectReference{ + APIVersion: SchemeGroupVersion.String(), + Kind: "Channel", + Namespace: "test-namespace", + Name: "test-name", + }, + }, + Conditions: []duckv1alpha1.Condition{ + { + Type: ChannelConditionSubscribable, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + cs := &ChannelStatus{} + cs.SetSubscribable(tc.namespace, tc.name) + if diff := cmp.Diff(tc.want, cs, ignoreTransitionTimeMessageAndReason); diff != "" { + t.Errorf("unexpected conditions (-want, +got) = %v", diff) + } + }) + } +} + +func TestChannelStatus_SetSinkable(t *testing.T) { + testCases := map[string]struct { + domainInternal string + want *ChannelStatus + }{ + "empty string": { + want: &ChannelStatus{ + Conditions: []duckv1alpha1.Condition{ + // Note that Ready is here because when the condition is marked False, duck + // automatically sets Ready to false. + { + Type: ChannelConditionReady, + Status: corev1.ConditionFalse, + }, + { + Type: ChannelConditionSinkable, + Status: corev1.ConditionFalse, + }, + }, + }, + }, + "has domain": { + domainInternal: "test-domain", + want: &ChannelStatus{ + Sinkable: duckv1alpha1.Sinkable{ + DomainInternal: "test-domain", + }, + Conditions: []duckv1alpha1.Condition{ + { + Type: ChannelConditionSinkable, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + } + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + cs := &ChannelStatus{} + cs.SetSinkable(tc.domainInternal) + if diff := cmp.Diff(tc.want, cs, ignoreTransitionTimeMessageAndReason); diff != "" { + t.Errorf("unexpected conditions (-want, +got) = %v", diff) + } + }) + } +} diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go b/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go index d093f450208..6a0a32ecb0b 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go +++ b/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go @@ -82,6 +82,12 @@ type ClusterProvisionerStatus struct { ObservedGeneration int64 `json:"observedGeneration,omitempty"` } +const ( + // ClusterProvisionerConditionReady has status True when the Controller reconciling objects + // controlled by it is ready to control them. + ClusterProvisionerConditionReady = duckv1alpha1.ConditionReady +) + // GetCondition returns the condition currently associated with the given type, or nil. func (ps *ClusterProvisionerStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { return cProvCondSet.Manage(ps).GetCondition(t) @@ -97,6 +103,14 @@ func (ps *ClusterProvisionerStatus) InitializeConditions() { cProvCondSet.Manage(ps).InitializeConditions() } +// MarkReady marks this ClusterProvisioner as Ready=true. +// +// Note that this is not the normal pattern for duck conditions, but because there is (currently) +// no other condition on ClusterProvisioners, the normal IsReady() logic doesn't work well. +func (ps *ClusterProvisionerStatus) MarkReady() { + cProvCondSet.Manage(ps).MarkTrue(ClusterProvisionerConditionReady) +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // ClusterProvisionerList is a list of ClusterProvisioner resources diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types_test.go b/pkg/apis/eventing/v1alpha1/cluster_provisioner_types_test.go index 8a9ab747aa0..afb7e8a0ff9 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types_test.go +++ b/pkg/apis/eventing/v1alpha1/cluster_provisioner_types_test.go @@ -116,3 +116,15 @@ func TestClusterProvisionerStatusGetCondition(t *testing.T) { }) } } + +func TestClusterProvisionerStatus_MarkReady(t *testing.T) { + ps := ClusterProvisionerStatus{} + ps.InitializeConditions() + if ps.IsReady() { + t.Errorf("Should not be ready when initialized.") + } + ps.MarkReady() + if !ps.IsReady() { + t.Errorf("Should be ready after MarkReady() was called.") + } +} diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index bf178507049..990d1ab4f3e 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -26,7 +26,6 @@ import ( "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" "github.com/knative/eventing/pkg/system" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" @@ -91,6 +90,9 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } logger.Info("Reconciling Channel") + // Modify a copy, not the original. + c = c.DeepCopy() + err = r.reconcile(ctx, c) if err != nil { logger.Info("Error reconciling Channel", zap.Error(err)) @@ -118,6 +120,8 @@ func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) error { logger := r.logger.With(zap.Any("channel", c)) + c.Status.InitializeConditions() + // We are syncing three things: // 1. The K8s Service to talk to this Channel. // 2. The Istio VirtualService to talk to this Channel. @@ -137,13 +141,13 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) } r.addFinalizer(c) - r.makeSubscribable(c) + c.Status.SetSubscribable(c.Namespace, c.Name) if svc, err := r.createK8sService(ctx, c); err != nil { logger.Info("Error creating the Channel's K8s Service", zap.Error(err)) return err } else { - r.makeSinkable(c, svc) + c.Status.SetSinkable(controller.ServiceHostName(svc.Name, svc.Namespace)) } if err := r.createVirtualService(ctx, c); err != nil { @@ -151,26 +155,10 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) return err } - r.setStatusReady(c) + c.Status.MarkProvisioned() return nil } -// makeSubscribable alters the Channel to conform to the Knative Eventing Subscribable interface. -func (r *reconciler) makeSubscribable(c *eventingv1alpha1.Channel) { - // Point at itself. - c.Status.Subscribable.Channelable = corev1.ObjectReference{ - Kind: "Channel", - APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), - Namespace: c.Namespace, - Name: c.Name, - } -} - -// makeSinkable alters the Channel to conform to the Knative Eventing Sinkable interface. -func (r *reconciler) makeSinkable(c *eventingv1alpha1.Channel, svc *corev1.Service) { - c.Status.Sinkable.DomainInternal = controller.ServiceHostName(svc.Name, svc.Namespace) -} - func (r *reconciler) addFinalizer(c *eventingv1alpha1.Channel) { finalizers := sets.NewString(c.Finalizers...) finalizers.Insert(finalizerName) @@ -323,15 +311,6 @@ func newVirtualService(channel *eventingv1alpha1.Channel) *istiov1alpha3.Virtual } } -func (r *reconciler) setStatusReady(c *eventingv1alpha1.Channel) { - c.Status.Conditions = []duckv1alpha1.Condition{ - { - Type: duckv1alpha1.ConditionReady, - Status: corev1.ConditionTrue, - }, - } -} - func (r *reconciler) updateChannel(ctx context.Context, u *eventingv1alpha1.Channel) error { o := &eventingv1alpha1.Channel{} if err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o); err != nil { diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 62f942a1c04..13466bad4ae 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -455,12 +455,13 @@ func TestReconcile(t *testing.T) { if tc.ReconcileKey == "" { tc.ReconcileKey = fmt.Sprintf("/%s", cName) } + tc.IgnoreTimes = true t.Run(tc.Name, tc.Runner(t, r, c)) } } func makeChannel() *eventingv1alpha1.Channel { - return &eventingv1alpha1.Channel{ + c := &eventingv1alpha1.Channel{ TypeMeta: metav1.TypeMeta{ APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), Kind: "Channel", @@ -478,34 +479,26 @@ func makeChannel() *eventingv1alpha1.Channel { }, }, } + c.Status.InitializeConditions() + return c } func makeChannelWithFinalizerAndSubscribable() *eventingv1alpha1.Channel { c := makeChannelWithFinalizer() - c.Status.Subscribable.Channelable = corev1.ObjectReference{ - Kind: "Channel", - APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), - Namespace: c.Namespace, - Name: c.Name, - } + c.Status.SetSubscribable(c.Namespace, c.Name) return c } func makeChannelWithFinalizerAndSubscribableAndSinkable() *eventingv1alpha1.Channel { c := makeChannelWithFinalizerAndSubscribable() - c.Status.Sinkable.DomainInternal = fmt.Sprintf("%s-channel.%s.svc.cluster.local", cName, cNamespace) + c.Status.SetSinkable(fmt.Sprintf("%s-channel.%s.svc.cluster.local", c.Name, c.Namespace)) return c } func makeReadyChannel() *eventingv1alpha1.Channel { // Ready channels have the finalizer and are Subscribable and Sinkable. c := makeChannelWithFinalizerAndSubscribableAndSinkable() - c.Status.Conditions = []duckv1alpha1.Condition{ - { - Type: duckv1alpha1.ConditionReady, - Status: corev1.ConditionTrue, - }, - } + c.Status.MarkProvisioned() return c } diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go index c95e40f95e9..f1b295843fd 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go @@ -22,7 +22,6 @@ import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/controller" "github.com/knative/eventing/pkg/system" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -86,6 +85,9 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } logger.Info("Reconciling ClusterProvisioner.") + // Modify a copy of this object, rather than the original. + cp = cp.DeepCopy() + err = r.reconcile(ctx, cp) if err != nil { logger.Info("Error reconciling ClusterProvisioner", zap.Error(err)) @@ -135,7 +137,7 @@ func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.Cluster return err } - r.setStatusReady(cp) + cp.Status.MarkReady() return nil } @@ -165,15 +167,6 @@ func (r *reconciler) createDispatcherService(ctx context.Context, cp *eventingv1 return nil } -func (r *reconciler) setStatusReady(cp *eventingv1alpha1.ClusterProvisioner) { - cp.Status.Conditions = []duckv1alpha1.Condition{ - { - Type: duckv1alpha1.ConditionReady, - Status: corev1.ConditionTrue, - }, - } -} - func (r *reconciler) updateClusterProvisionerStatus(ctx context.Context, u *eventingv1alpha1.ClusterProvisioner) error { o := &eventingv1alpha1.ClusterProvisioner{} if err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o); err != nil { diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go index b557412cbdc..f0bd62dbdce 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go @@ -262,6 +262,7 @@ func TestReconcile(t *testing.T) { if tc.ReconcileKey == "" { tc.ReconcileKey = fmt.Sprintf("/%s", Name) } + tc.IgnoreTimes = true t.Run(tc.Name, tc.Runner(t, r, c)) } } From 486a2ba8540311bf3aaaf48d7bf9b028b4635c45 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 11 Oct 2018 16:52:25 -0700 Subject: [PATCH 22/22] Don't rely on the default values for sidecar flags. --- cmd/fanoutsidecar/main.go | 18 +++++++++--------- .../in-memory-channel/in-memory-channel.yaml | 2 ++ .../eventing/inmemory/channel/reconcile.go | 2 +- .../inmemory/clusterprovisioner/reconcile.go | 8 ++++---- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/cmd/fanoutsidecar/main.go b/cmd/fanoutsidecar/main.go index 1ece5804172..629babe71e2 100644 --- a/cmd/fanoutsidecar/main.go +++ b/cmd/fanoutsidecar/main.go @@ -21,9 +21,13 @@ package main import ( "context" - "errors" "flag" "fmt" + "log" + "net/http" + "strings" + "time" + "github.com/knative/eventing/pkg/sidecar/configmap/filesystem" "github.com/knative/eventing/pkg/sidecar/configmap/watcher" "github.com/knative/eventing/pkg/sidecar/swappable" @@ -31,17 +35,13 @@ import ( "go.uber.org/zap" "golang.org/x/sync/errgroup" "k8s.io/client-go/kubernetes" - "log" - "net/http" "sigs.k8s.io/controller-runtime/pkg/client/config" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/runtime/signals" - "strings" - "time" ) const ( - defaultConfigMapName = "in-memory-bus-config" + defaultConfigMapName = "in-memory-channel-dispatcher-config-map" // The following are the only valid values of the config_map_noticer flag. cmnfVolume = "volume" @@ -60,12 +60,12 @@ var ( func init() { flag.IntVar(&port, "sidecar_port", -1, "The port to run the sidecar on.") - flag.StringVar(&configMapNoticer, "config_map_noticer", "", fmt.Sprintf("The system to notice changes to the ConfigMap. Valid values are: %s", configMapNoticerFlags())) + flag.StringVar(&configMapNoticer, "config_map_noticer", "", fmt.Sprintf("The system to notice changes to the ConfigMap. Valid values are: %s", configMapNoticerValues())) flag.StringVar(&configMapNamespace, "config_map_namespace", system.Namespace, "The namespace of the ConfigMap that is watched for configuration.") flag.StringVar(&configMapName, "config_map_name", defaultConfigMapName, "The name of the ConfigMap that is watched for configuration.") } -func configMapNoticerFlags() string { +func configMapNoticerValues() string { return strings.Join([]string{cmnfVolume, cmnfWatcher}, ", ") } @@ -132,7 +132,7 @@ func setupConfigMapNoticer(logger *zap.Logger, configUpdated swappable.UpdateCon case cmnfWatcher: err = setupConfigMapWatcher(logger, mgr, configUpdated) default: - err = errors.New("need to provide the --config_map_noticer flag") + err = fmt.Errorf("need to provide the --config_map_noticer flag (valid values are %s)", configMapNoticerValues()) } if err != nil { return nil, err diff --git a/config/provisioners/in-memory-channel/in-memory-channel.yaml b/config/provisioners/in-memory-channel/in-memory-channel.yaml index 35531fc57e1..44a15c66175 100644 --- a/config/provisioners/in-memory-channel/in-memory-channel.yaml +++ b/config/provisioners/in-memory-channel/in-memory-channel.yaml @@ -179,3 +179,5 @@ spec: args: - --sidecar_port=8080 - --config_map_noticer=watcher + - --config_map_namespace=knative-eventing + - --config_map_name=in-memory-channel-dispatcher-config-map diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 990d1ab4f3e..439361e8978 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -112,7 +112,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // ClusterProvisioner. This Controller only handles in-memory channels. func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { if c.Spec.Provisioner != nil { - return cpcontroller.IsControlled(c.Spec.Provisioner, c.Kind) + return cpcontroller.IsControlled(c.Spec.Provisioner, cpcontroller.Channel) } return false } diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go index f1b295843fd..a11cb53ca2f 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go @@ -36,11 +36,11 @@ import ( ) const ( - // The name of the in-memory channel ClusterProvisioner. + // Name is the name of the in-memory channel ClusterProvisioner. Name = "in-memory-channel" - // The name of the Channel resource in eventing.knative.dev/v1alpha1. - channel = "Channel" + // Channel is the name of the Channel resource in eventing.knative.dev/v1alpha1. + Channel = "Channel" ) type reconciler struct { @@ -116,7 +116,7 @@ func IsControlled(ref *eventingv1alpha1.ProvisionerReference, kind string) bool // shouldReconcile determines if this Controller should control (and therefore reconcile) a given // ClusterProvisioner. This Controller only handles in-memory channels. func shouldReconcile(namespace, name, kind string) bool { - return namespace == "" && name == Name && kind == channel + return namespace == "" && name == Name && kind == Channel } func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error {