eventing: Create a default Broker upon Trigger creation by default

This echoes a thread started on Slack (#eventing, April 10th)

Problem

We’ve had multiple discussion, especially in knative/docs#2325 and knative/eventing#2946, about making the automatic injection of a (MT-)Broker per namespace the default.

To sum up the current state of the discussion:

  • pro Creating a MT-Broker is super cheap and makes the developer’s experience simpler, because at the end of the days people want to use Triggers, and Triggers are only useful with an existing Broker.
  • contra A cluster is rarely composed of Eventing namespaces only, and users don’t expect objects to be magically injected everywhere on their behalf.

I think we all agree we’d rather not be creating Brokers manually whenever we want to use some Triggers. We want to keep the developer experience as simple as possible by letting users focus on Triggers, unless they explicitly express the desire to manage Brokers on their own.

Therefore I would like to suggest that a Trigger can be created without explicitly referencing a Broker and without the need to define any extra label, in which case the mutation webhook injects broker: default inside the Trigger’s spec and the Trigger reconciler creates that default Broker if it’s missing.

@grantr pointed out this feature already exists since #2034, so we would simply have to reverse the requirement on the knative-eventing-injection label to make it opt-out instead of opt-in.

Persona:

Event consumer (developer)

Exit Criteria

A developer can create a Trigger without explicitly creating a Broker beforehand. This action should cascade into the creation of a default Broker with sane defaults within the same namespace as the Trigger.

Time Estimate (optional):

A day.

Additional context (optional)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (18 by maintainers)

Most upvoted comments

I believe auto-creating a Broker upon Trigger creation sets a precedent that goes against the fine-grained level-triggered reconciliation model of Kubernetes, leading to a more coarse-grained transactional view of a collection of collaborating resources.

Kubernetes resource reconciliation often involves cascading creation of lower-level dependency resources which are owned/managed and/or delegated to (e.g. Deployment creates ReplicaSet, ReplicaSet creates Pods), but not to create collaborating “sibling” - or worse, “upstream” - resources as a matter of convenience. Instead, the status of a resource should reflect that it’s not ready due to lack of any of its independently-reconciled collaborating resources. That fine-grained, independent approach also promotes flexibility in terms of dynamic configuration, e.g. a Trigger could be updated to refer to a different Broker dynamically, without having to delete and recreate the resources. Also, users should be aware of the semantics of the Broker, especially as more Broker implementations become available, and the associated configuration parameters are exposed (durability, delivery guarantees, etc), and of course users need to be aware of the Broker resource instance as the addressable for publishing events. Having to determine the latter via the Trigger is counter to the goal of decoupling producers and consumers.

All that said, in https://github.com/knative/docs/pull/2325#issuecomment-608078448 @mattmoor referred to default ServiceAccounts as a parallel, but I view the motivation for default ServiceAccounts as not being analogous for several reasons:

  1. plays a bootstrapping role for a Pod when secrets are needed to pull images for the containers in the Pod
  2. provides access to the API Server if necessary (when token mounting is enabled), with the default ServiceAccount promoting the least-privilege principle
  3. immutable due to the nature of those 2 features, e.g. updating a ServiceAccount with imagePullSecrets would be irrelevant/misleading once past the bootstrapping phase
  4. falling back to the default ServiceAccount is managed by an admission controller due to the bootstrapping/immutability
  5. ServiceAccount has no runtime footprint

Finally, it seems as though the very idea of creating a default Broker was motivated by the complexity of Channel-based Brokers, as the Channel/Subscription model does not itself satisfy the Broker/Trigger requirements, thereby requiring several additional components, especially for filter-matching. When instead dealing with a Broker implementation that simply delegates to a messaging system that does satisfy the requirements, that motivating complexity is no longer an issue. So, I personally think it would be better to reconsider the role of Channels in the implementation of a Broker (especially as a default) than to set this precedent that doesn’t follow typical Kubernetes resource reconciliation idioms.

I’ve opened https://github.com/knative/eventing/issues/3138 and tried to keep the initial “goal” free of my biases. I will follow up with my own thoughts, but I’d encourage folks to weigh in there. Please feel free to loop in others, but I’ve seeded it with several folks involved in recent convos.

@grantr I was not proposing on dropping features, just being able to select which pieces of functionality we provide and where 😃 And agree on other stakeholders, just hoping to get some consensus amongst the group on what we think we want and what are the contentious bits. But opening a new issue to discuss seems like a good idea 😃 Until it forks lol 😃

I agree that this is an inappropriate place to discuss this, but alas I was linked here from a PR that was removing an unrelated feature that we use 😉

I’ll open another issue to discuss this because it keeps getting smeared across other issues, PRs and slack threads.

@mattmoor here’s what I think is a better parallel based on something I just happened to do:

I created a PingSource and its sink is my “default” Broker. I didn’t create the Broker yet, but the PingSource reconciler doesn’t therefore try to go create it. Instead its status simply reflects that its sink is unavailable. As soon as I create the Broker and its ingress is ready, that status changes. The same thing happens again if I delete and recreate the Broker. That feels like an idiomatic Kubernetes reconciliation experience. Why should it be different for the relationship between Trigger and Broker?