-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: create separate reconciler for control flow Stage #2848
Conversation
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
✅ Deploy Preview for docs-kargo-io ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2848 +/- ##
==========================================
+ Coverage 48.82% 50.01% +1.18%
==========================================
Files 270 274 +4
Lines 23941 24386 +445
==========================================
+ Hits 11690 12197 +507
+ Misses 11620 11539 -81
- Partials 631 650 +19 ☔ View full report in Codecov by Sentry. |
// This index is used to find all Freight that are directly available from | ||
// a Warehouse. It is used to find Freight that can be sourced directly from | ||
// the Warehouse for the control flow Stage. | ||
if err := mgr.GetFieldIndexer().IndexField( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a matter of personal preference, but I never liked the way the registration was factored out because it hid two important things from me when I wanted to know what a reconciler cared about:
- The index field that's important to the reconciler.
- The actual thing that decides how the value of this field is determined.
Given this, I took the liberty of not using the abstraction but rather doing the registration by hand here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent rationale. 👍
// SetupWithManager sets up the control flow Stage reconciler with the given | ||
// controller manager. It registers the reconciler with the manager and sets up | ||
// watches on the required objects. | ||
func (r *ControlFlowStageReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if I like to be idiomatic and puristic at times, this is how controller-runtime projects do it — and would fit more into what we have set out for ourselves with #1479.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Far from being a hill I would die on, I think the advantage of the other approach was that a caller in another package could obtain a fully-initialized reconciler after a single function call. With this approach, it requires two, which creates the possibility of obtaining a partially-initialized reconciler.
As I said, it's not a hill I would die on. Merely explaining the rationale behind the prior approach.
) | ||
// If the Stage is a control flow Stage, there is no need to reconcile it. | ||
if stage.IsControlFlow() { | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, this would always result in a reconciliation — even if there wasn't any actual interest.
@@ -478,6 +513,11 @@ func (p *phaseChangedAnalysisRunHandler[T]) Update( | |||
) | |||
} | |||
for _, stage := range stages.Items { | |||
// If the Stage is a control flow Stage, there is no need to reconcile it. | |||
if stage.IsControlFlow() { | |||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, this would always result in a reconciliation — even if there wasn't any actual interest.
} | ||
return ctrl.Result{}, fmt.Errorf("failed to update Stage status: %w", err) | ||
} | ||
return ctrl.Result{}, reconcileErr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to test this more thoroughly, but I believe there is no actual reason for us to requeue on an interval here because we should be informed by a watcher or change event.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested this, and what's expected from it is triggered just fine without it.
d7b4ca4
to
33a6bce
Compare
97b6bfd
to
c963f03
Compare
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
c963f03
to
fbc5658
Compare
Add a `SharedFieldIndexer` wrapper around `client.FieldIndexer` to prevent multiple reconcilers from creating duplicate indices for the same fields. This avoids errors by ensuring each field is only indexed once, while still allowing reconcilers to manage their indices independently. Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
2d4af35
to
405409d
Compare
The main problem with the Stage reconciler at present is the level of complexity, which makes it hard to reason about behavior due to the sheer number of responsibilities it handles, mixing both regular Stage and control flow Stage reconciliation logic into a single controller.
This PR is the first step in a larger refactoring effort aimed at improving the Stage reconciliation logic. The effort consists of two phases:
Splitting the reconciler into two separate controllers:
ControlFlowStageReconciler
that handles Stages without promotion templates (i.e. "control flow" Stages)This PR implements the first part by extracting control flow Stage logic into a dedicated controller.
Rewriting the remaining Stage reconciler to follow the same patterns established in this PR:
This will be addressed in a follow-up PR.
The end goal is to have two well-structured controllers that each handle a specific type of Stage, making the codebase easier to understand, test, and modify.