Skip to content
This repository was archived by the owner on Jun 19, 2022. It is now read-only.

Add trigger controller #1094

Merged
merged 12 commits into from
May 30, 2020
Merged

Conversation

liu-cong
Copy link
Contributor

Fixes #1053

I tried to reuse existing code as much as I can to reduce the amount of changes. I will have follow up PRs to do some cleanup and refactoring.

Proposed Changes

  • Add a separate trigger controller that reconciles the retry topic/pull subscription for Triggers
  • The controller reconciles/finalizes triggers if their brokers have the googlecloud brokerclass, or they have the googlecloud finalizer.
  • Changes to targets-config reconciliation logic
    • The controller deletes broker entry if a broker is deleted, previously the entry still exists, which creates garbage
    • Previously the broker entry and each trigger entry is individually updated, now every time a broker is reconciled, the entire broker entry is reconstructed. This has the following benefits:
      • The trigger controller doesn't need to manipulate trigger entries in the configmap
      • It's more reliable to always reconstruct compared to incremental edits

Release Note


Docs

@liu-cong liu-cong requested a review from grantr May 19, 2020 16:48
@knative-prow-robot
Copy link
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

// TODO Maybe get rid of BrokerMutation and add Delete() and Upsert(broker) methods to TargetsConfig. Now we always
// delete or update the entire broker entry and we don't need partial updates per trigger.
// The code can be simplified to r.targetsConfig.Upsert(brokerConfigEntry)
// First delete the broker entry.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had written a targets config reconciler which supports the interface you want here and allows directly constructing the broker config proto rather than working with the mutation interface: #914. Directly constructing the proto is a bit nicer since we are not constrained to using the mutation interface and individual mutations do not need to be threadsafe. I had closed that since the brokercell reconciler was meant to replace this but maybe it is worth reviving after this PR is merged since the brokercell reconciler is not yet ready.

Copy link
Contributor Author

@liu-cong liu-cong May 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we do #1054, we can start moving the target-config reconciliation to brokercell reconciler (assuming a static brokercell).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree that updating proto is better. Reconciler shouldn't worry about the internals of targets config. Again, I'm likely biased but just giving my voice.

Copy link
Contributor Author

@liu-cong liu-cong May 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I follow what do you mean by "internals" of targets config. Currently the UpsertTargets interface already takes in a config.Target object. What I am suggesting here is to add an interface to take in config.Broker because now we don't need to upsert each trigger one by one, it's always all or nothing. If we think the config.Broker proto is "internal", we can always add another abstraction but I don't think it's needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config.Broker is bit different from config.Target IMO. The "internal" I'm referring to is the map in the config.Broker. To construct a config.Broker, you will have to "manually" create the map rather than having the lib handle that. On the other side, config.Target is pure data.

var filterBroker = pkgreconciler.AnnotationFilterFunc(eventingv1beta1.BrokerClassAnnotationKey, brokerv1beta1.BrokerClass, false /*allowUnset*/)

func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
// TODO initialize project ID here via env var or metadata.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to support reconciling across multiple projects?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say no for now unless there are some compelling use cases. Do you have any use case in mind ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't heard of a need for reconciling across multiple projects.

Notes for a future PR: Using an env var here makes tests more complicated. One option for setting project ID in this and other controllers is to add it to the context created in cmd/controller/main.go. Then controllers have a uniform way of getting the global project ID if they need it, and there's only one piece of code responsible for retrieving the project id from metadata or env var.

yolocs
yolocs previously requested changes May 26, 2020
var filterBroker = pkgreconciler.AnnotationFilterFunc(eventingv1beta1.BrokerClassAnnotationKey, brokerv1beta1.BrokerClass, false /*allowUnset*/)

func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
// TODO initialize project ID here via env var or metadata.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't heard of a need for reconciling across multiple projects.

Notes for a future PR: Using an env var here makes tests more complicated. One option for setting project ID in this and other controllers is to add it to the context created in cmd/controller/main.go. Then controllers have a uniform way of getting the global project ID if they need it, and there's only one piece of code responsible for retrieving the project id from metadata or env var.

// Call Finalizer anyway in case the Trigger still holds GCP Broker related resources.
// If a Trigger used to point to a GCP Broker but now has a Broker with a different brokerclass,
// we should clean up resources related to GCP Broker.
return r.FinalizeKind(ctx, t)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a trigger is created for a broker that doesn't have this broker class, will the googlecloud finalizer be added anyway? I think it's automatically added in genreconciler when FinalizeKind is present, before Reconcile is even called. So most likely every Trigger will have the googlecloud finalizer even if they use another broker class.

I think that means that every trigger with a different broker class will be finalized on every update. The unit test of a trigger with a not found broker seems to confirm this (because it finalizes that trigger). Might want to confirm with a real-world test.

If that behavior is confirmed then we should disable the automatic finalizer logic in genreconciler (by renaming FinalizeKind). Then we can add the finalizer only to Triggers that point to a googlecloud broker.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your understanding is correct. I forgot to add a TODO here to explain the finalizer scenario and future improvements.

I think that means that every trigger with a different broker class will be finalized on every update.

Correct. This is unnecessary and it will also cause read operation on the topic and pull subscription.

we should disable the automatic finalizer logic in genreconciler (by renaming FinalizeKind).

Unfortunately if we do this, the FinalizeKind logic won't be called when the trigger object is deleted. The genreconciler will do nothing because it thinks there is no finalizer to call if the reconciler doesn't implement FinalizeKind.

I have thought about this and came up with a few options:

  1. The current behavior. Pros: least effort; Cons: unnecessary FinalizeKind calls for non-GCP broker.
  2. Use a custom controller instead of genreconciler. This essentially means we need to copy most of the genreconciler boil plate code and make some modifications.
  3. Upstream genreconciler changes to make it more flexible, specifically, add a FilterKind interface to let the genreconciler skip a trigger if it doesn't point to a GCP broker. I have proposed this interface before genreconciler should support Filtering knative/pkg#1149

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally prefer Option 3. If this sounds good to you, I will add a TODO for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dumb question: why would we reconcile such a trigger (trigger not pointing a gcp broker) in the first place? Broker in trigger spec is immutable.

Also for option 2, there might not be that much boilerplate code from just the FinalizeKind. We can still use the majority of the genreconciler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would we reconcile such a trigger (trigger not pointing a gcp broker) in the first place? Broker in trigger spec is immutable.

Consider this scenario. A trigger was pointed to a gcp broker. Then the broker was updated to use a different brokerclass. Now we want to tear down trigger pubsub resources. We don't know if users will actually do this. But in theory they can.

We could do option 2 but I really don't like the trigger controller to be "special".
I don't think it's very high priority because: 1. users are likely to only use gcp broker. 2. even if they use a different broker, this results in a couple of unnecessary calls to pubsub. not great but doesn't really hurt too much.
For the above reasons I prefer to have a clean solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A broker could be deleted and replaced with a broker with a different class. From the trigger controller's perspective, this will be the same as a broker changing class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right it's immutable. But you can still delete a broker and recreate it with a different brokerclass.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fine to defer this for later. I'd love to end up at option 3, but realistically I'd probably attempt option 2 first, then move the new code upstream later. It will be easier to justify as an upstream option once we have a demonstrated solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG. Created an issue to follow up #1164

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. Just an idea: if we can (or already?) label the trigger with the initial broker class, then even if the broker got recreated with a different class we can still handle that deterministically, right?

@liu-cong liu-cong requested a review from yolocs May 27, 2020 20:39
Copy link
Member

@yolocs yolocs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a couple of questions


r.Logger.Info("Setting up event handlers")

triggerInformer.Informer().AddEventHandlerWithResyncPeriod(controller.HandleAll(impl.Enqueue), reconciler.DefaultResyncPeriod)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to filter on trigger label eventing.BrokerLabelKey?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because we need to be able to reconcile orphaned triggers without a broker.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would that happen? It seems trigger spec broker and broker class are both immutable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry let me clarify. Do mean filter on the existence of the label or actually check the broker is GCP broker?

Let me assume you meant the latter . If you delete a broker, then its triggers become orphaned. We'd like to be able to still delete the triggers (in this case reconcile is a noop).

// Call Finalizer anyway in case the Trigger still holds GCP Broker related resources.
// If a Trigger used to point to a GCP Broker but now has a Broker with a different brokerclass,
// we should clean up resources related to GCP Broker.
return r.FinalizeKind(ctx, t)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dumb question: why would we reconcile such a trigger (trigger not pointing a gcp broker) in the first place? Broker in trigger spec is immutable.

Also for option 2, there might not be that much boilerplate code from just the FinalizeKind. We can still use the majority of the genreconciler.

Copy link
Contributor

@grantr grantr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@liu-cong
Copy link
Contributor Author

/hold for @yolocs to take a look

@liu-cong
Copy link
Contributor Author

Thanks @yolocs and @grantr for the review. There are many tricky bits and thanks for your comments.

@knative-metrics-robot
Copy link

The following is the coverage report on the affected files.
Say /test pull-google-knative-gcp-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/reconciler/broker/broker.go 66.3% 63.5% -2.8
pkg/reconciler/broker/controller.go 81.6% 78.1% -3.5
pkg/reconciler/trigger/controller.go Do not exist 69.7%
pkg/reconciler/trigger/trigger.go Do not exist 59.2%

@liu-cong
Copy link
Contributor Author

/retest

Copy link
Contributor

@grantr grantr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@knative-prow-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: grantr, liu-cong

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@yolocs
Copy link
Member

yolocs commented May 29, 2020

/lgtm

@grantr grantr dismissed yolocs’s stale review May 29, 2020 18:38

Dismissing request changes after lgtm

@liu-cong
Copy link
Contributor Author

/unhold

@liu-cong
Copy link
Contributor Author

/retest

@knative-test-reporter-robot

The following jobs failed:

Test name Triggers Retries
pull-google-knative-gcp-integration-tests pull-google-knative-gcp-integration-tests
pull-google-knative-gcp-integration-tests
pull-google-knative-gcp-integration-tests
pull-google-knative-gcp-integration-tests
pull-google-knative-gcp-integration-tests
3/3

Job pull-google-knative-gcp-integration-tests expended all 3 retries without success.

@grantr
Copy link
Contributor

grantr commented May 29, 2020

/retest

@knative-prow-robot knative-prow-robot merged commit 123a9fe into google:master May 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
approved cla: yes (override cla status due to multiple authors bug) lgtm size/XXL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow triggers to be reconciled without a broker
8 participants