-
Notifications
You must be signed in to change notification settings - Fork 103
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
Clean up instance controller #380
Conversation
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.
Lgtm, though, I believe there is at least one breaking change with the default plan (see comments)
if !planFound { | ||
// The parameter doesn't have a trigger, try to find the corresponding default plan. | ||
names := []string{"update", "deploy"} | ||
for _, n := range names { |
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.
Since we're refactoring this code - wdyt about extracting this loop into a separate function:
findPlan := func(names []string) (string, boolean) {
for _, n := range names {
if plan, found = fv.Spec.Plans[n]; found {
return plan, found
}
return "", false
}
and then calling it here (and above) with:
planName, planFound := findPlan([]string{"update", "deploy"})
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 personally think that trying to understand the code for the first time would have to do an extra context switch to see what findPlan()
actually does and then come back.
This would be justified if we'd findPlan()
very often, but we only use it twice, so my personal opinion is that at this point extracting it into a separate function would only hurt readability.
My opinion on this matter is not very strong, so if you feel stronger about this, I can totally extract it =).
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 would probably also extract it but I don't have strong opinion here :)
planName = n | ||
planFound = true | ||
break | ||
} |
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.
Wait, previously (line 140 on the left) there is a default fallback planName = "deploy"
if no plan was found. This is not the case on the right, correct?
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.
The original line #140 isn't setting planName=deploy
as a default fallback — it's actually only executed if ok == true
, which means that deploy
was found.
Line #151 might be an attempt to use deploy
as a default fallback plan, but if that line is ever executed, ok
will be false
, so the PlanExecution
will never be created.
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.
You're right. But since we spent quite some time understanding all the ifs and elses
of the logic, I believe we need to extract this portion of code into separate methods and provide a few tests for it (similar to what I did for parameterDifference
. Wdyt?
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.
To be honest I think that instead going further with the refactoring of the predicate function, I think we should get rid of it and move most of the logic to the Reconcile
function, see my writeup on #422 for more details.
Instead of writing low level unit tests for something that we should change soon, I would rather start working on #422 or write a few unit tests to prepare the ground for #422.
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.
BTW it looks like the original intention was to create a PlanExecution
for deploy
even if no changes were detected.
@runyontr recently created a PR that includes a change to make this happen: https://github.com/kudobuilder/kudo/pull/367/files#r297890298
planName = "update" | ||
|
||
if planFound { | ||
// FIXME: this can be misleading, the plan will only be used if this is the last parameter returned by `parameterDifference`. |
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.
// FIXME: this can be misleading, the plan will only be used if this is the last parameter returned by `parameterDifference`. | |
// FIXME: this can be misleading, the plan will only be used if this is the first parameter returned by `parameterDifference`. |
afterward, planName
(on the left) or planFound
(on the right) would be set and we won't land 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.
I think that neither my original comment nor your suggestion are completely accurate 😱.
This is all nested within the loop that starts on line 124 on the right. If for any changed param a trigger was defined (param.trigger != ""
), then lines 142-143 will overwrite planName
and planFound
. This will happen regardless of whether we had previously logged (and "decided") that we were going to use a default plan. This means that the plan that we logged might actually not be used, even when it corresponds to the first parameter returned by parameterDifference
.
I think that right now we don't have a test verifying which plan will be executed if parameters with different triggers have changed. We probably also haven't documented this. We should define what we want to happen... and then clean up the code a little more to reflect that and write a test to make sure that the code is correct.
I added this FIXME here to spark this discussion... but I forgot to add a comment when posting the PR. Thanks for noticing this =).
So now my question is: which plan should we run if multiple parameters have been changed triggering different triggers? cc/ @gerred @alenkacz @runyontr
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.
We have another issue to discuss this one, let's not change this here. There's a lot of options and most of them will require a validating admission hook.
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.
@@ -164,7 +175,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { | |||
if current.Status.State == kudov1alpha1.PhaseStateComplete { | |||
log.Println("InstanceController: Current Plan for Instance is already done, won't change the Suspend flag.") | |||
} else { | |||
log.Println("InstanceController: Setting PlanExecution to Suspend") | |||
log.Println("InstanceController: Suspending the PlanExecution") |
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.
log.Println("InstanceController: Suspending the PlanExecution") | |
log.Printf("InstanceController: Suspending the PlanExecution for instance %v", new.Name) |
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.
Note: this has to be log.Printf(...)
instead of log.Println(...)
, I made the same mistake a few times during this refactoring 😳.
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.
LGTM. Although doing any kind of refactoring while not backed by tests feels very ... dangerous :D I think we should invest in tests next time we're touching any code
planName = "deploy" | ||
} else { | ||
planName = "update" | ||
// Its an upgrade! |
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.
It's
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.
Thanks for catching the typo!
// Find the right parameter in the FV | ||
// Find the spec of the updated parameter. | ||
// | ||
// TODO: what should we do if more than one parameter was updated? |
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.
that should be addressed in #354
if !planFound { | ||
// The parameter doesn't have a trigger, try to find the corresponding default plan. | ||
names := []string{"update", "deploy"} | ||
for _, n := range names { |
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 would probably also extract it but I don't have strong opinion 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.
I echo the thoughts here. Let's do a separate extraction PR but also make sure all of that is guarded as testable code so we can do these refactorings safely.
planName = "update" | ||
|
||
if planFound { | ||
// FIXME: this can be misleading, the plan will only be used if this is the last parameter returned by `parameterDifference`. |
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.
We have another issue to discuss this one, let's not change this here. There's a lot of options and most of them will require a validating admission hook.
After having spent a considerable amount of time trying to clean up this predicate function, I found a few fundamental issues that I describe in #422. My proposal on #422 is to get rid of this predicate and to add more logic to the However if you think that this PR is valuable, we can merge it. This should make the current implementation of |
Yeah, I agree. #422 looks like the right way to go. I'll retract my request for change and leave the decision of whether to merge it or not up to you ;) |
@gkleiman this is based on an older version... we do not reference Frameworks anymore |
NOTE: The update predicate function still inspects the status attribute and has side-effects. This is an anti-pattern that should be solved in a different patch, see #422.
3bb67e6
to
0fb3be7
Compare
I rebased this PR and verified that the integration tests introduced by #504 pass =). |
What type of PR is this?
What this PR does / why we need it:
This PR cleans up and improves the readability of the instances controller. It improves comments, includes some refactoring code, and addresses an existing TODO.
Special notes for your reviewer:
I think that most of the code touched here should actually moved to the
Reconcile
method.Does this PR introduce a user-facing change?: