-
Notifications
You must be signed in to change notification settings - Fork 15
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
propogate labels from limitador to deployment template #183
base: main
Are you sure you want to change the base?
Conversation
d496720
to
6530652
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #183 +/- ##
==========================================
- Coverage 83.47% 82.23% -1.24%
==========================================
Files 18 18
Lines 1210 1233 +23
==========================================
+ Hits 1010 1014 +4
- Misses 152 165 +13
- Partials 48 54 +6
Flags with carried forward coverage won't be shown. Click here to find out more.
|
c77999c
to
d142de1
Compare
bd6f1d8
to
04eaa19
Compare
04eaa19
to
39ae954
Compare
@@ -37,6 +37,16 @@ func DeploymentMutator(opts ...DeploymentMutateFn) MutateFn { | |||
} | |||
} | |||
|
|||
func DeploymentTemplateLabelMutator(desired, existing *appsv1.Deployment) bool { |
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.
@eguzki @KevFan From (Kuadrant/authorino-operator#236 (comment)) point
Keep any other user added labels already added to the deployment pod template.
is not fully addressed here. This current iteration checks if the labels from limitador are present in the template labels and only if not does a update trigger. However any of the Mutator functions trigger an update, in this current state any manually added labels to the spec.template.labels will get lost.
for example a user that has followed this guide -> https://github.com/Kuadrant/kuadrant-operator/blob/main/doc/install/mtls-configuration.md will lose the configuration. that would be the current state.
Is the intention that that point should be addressed as part of this change or is it sufficient that
- Known hardcoded labels take precedence
- Merge Authorino CR labels
are now covered?
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.
Let me propose implementation to covert any manually added labels:
In some helper.go (or whatever)
package helper
func MergeMapStringString(modified *bool, existing *map[string]string, desired map[string]string)
if *existing == nil {
*existing = map[string]string{}
}
for k, v := range desired {
if existingVal, ok := (*existing)[k]; !ok || v != existingVal {
(*existing)[k] = v
*modified = true
}
}
}
here
import (
"github.com/kuadrant/limitador-operator/pkg/helper" (or whatever)
)
func DeploymentTemplateLabelMutator(desired, existing *appsv1.Deployment) bool {
updated := false
helper.MergeMapStringString(&updated, &existing.Spec.Template.Labels, desired.Spec.Template.Labels)
return 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.
@eguzki Nice yes I like that. About the question above.
Even with this helper method, we would still overwrite any user added labels to the deployment due to the fact that we don't have the cluster copy of the deployment here -> https://github.com/Kuadrant/limitador-operator/pull/183/files#diff-41181542228742e869e5d34e69e5c72a4bc8f1a5ddc93c6199470479fc3b73fbR55
We build the deployment and then do a create or update using that object. As we wouldn't have access to any existing user added labels (or any other configuration) that would get lost.
My question around that is, are we fine with that? aka do we tell users that everytime they update the limitador cr, any objects owned by that are at risk of losing user added configuration. I think that's the stance now. But labels haven't been a trigger up to now so are we still fine with that?
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.
Personally, I think this doesn’t have to be part of this PR if we want to discuss it further, as I see it as an additional enhancement of behavior. With this PR, labels are hardened, and the Limitador CR becomes a source of truth for labels. The Limitador CR is the only place where a user should add or update labels to ensure they are not removed during a reconciliation event. Previous to this, labels are not reconciled by the controller from the CR, so users would be adding labels via the deployment instead 🤔
One thing to note, after reflecting on this, is the consequence of "keeping user-added labels directly in the deployment pod template." If a user adds labels to the Limitador CR and later removes them from the CR, these labels will still persist in the deployment pod template. This happens because the controller cannot differentiate between labels added by a previous CR event and those added manually to the deployment by the user. As a result, a user must remove the label from both the CR and the deployment pod template manually to fully remove 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.
@eguzki Nice yes I like that. About the question above. Even with this helper method, we would still overwrite any user added labels to the deployment due to the fact that we don't have the cluster copy of the deployment here -> https://github.com/Kuadrant/limitador-operator/pull/183/files#diff-41181542228742e869e5d34e69e5c72a4bc8f1a5ddc93c6199470479fc3b73fbR55 We build the deployment and then do a create or update using that object. As we wouldn't have access to any existing user added labels (or any other configuration) that would get lost. My question around that is, are we fine with that? aka do we tell users that everytime they update the limitador cr, any objects owned by that are at risk of losing user added configuration. I think that's the stance now. But labels haven't been a trigger up to now so are we still fine with that?
On this, with Eguzki's suggestion, the update / keeping of the existing labels happens as part of the MergeMapStringString
function and DeploymentTemplateLabelMutator(desired, existing *appsv1.Deployment)
mutator which has access to the existing and desired deployements also
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.
Good points @KevFan
So, what is what we want to do? My idea was to implement what I explained Kuadrant/authorino-operator#236 (comment)
Basically, labels from Limitador CR are being propagated to deployment and pods. Quoting: If deployment object exist and there are more labels than those existing in the authorino CR, those are accepted and not removed. If authorino CR changes labels, the new labels need to be copied to the deployment labels.
If a user adds labels to the Limitador CR and later removes them from the CR, these labels will still persist in the deployment pod template
Right. I knew about that and I think it is a reasonable downside to have. There are ways to have better management of labels and removing those if those are removed from the Limitador CR. But the complexity to implement those does not payoff IMO. I have implemented those for volumes/volumemounts in other projects successfully (running in prod), so I can give more details.
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.
Allowing manually adding labels to deployment is desirable because users usually end up adding custom labels for reasons we cannot foresee. And usually, adding labels is not harmful. So having the operator removing them is usually frustrating for the user.
If you think the operator should be more strict, whatever the majority decides 🤷
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.
Ok. I think I misunderstood the modify functions. I understood that it only checks whether it should trigger update. But I see the existing and desired are pointers so we can do some modification in there? aka merge the map to include the user added lables and add the expected ones also. am i understanding that correctly now?
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.
Allowing manually adding labels to deployment is desirable because users usually end up adding custom labels for reasons we cannot foresee. And usually, adding labels is not harmful. So having the operator removing them is usually frustrating for the user.
If you think the operator should be more strict, whatever the majority decides 🤷
I don't feel too strongly about making it more strict, but I just wanted to call out whether this is an intended behavior or consequence. Though having some documentation explaining how labels are managed by the operator would be helpful for users to understand this I think (as part of this PR if implemented or as a follow up)
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.
Ok. I think I misunderstood the modify functions. I understood that it only checks whether it should trigger update. But I see the existing and desired are pointers so we can do some modification in there? aka merge the map to include the user added lables and add the expected ones also. am i understanding that correctly now?
Yes, it is so called mutator pattern. You get the desired and existing objects, you basically compute the difference (aka delta) and if delta is not empty, update (existing + delta) is applied to the cluster. I find it very intuitive and very flexible to implement any business logic you want.
b792015
to
2f46bc6
Compare
Propagate Limitador CR labels to deployment labels as well here https://github.com/Kuadrant/limitador-operator/blob/main/pkg/limitador/k8s_objects.go#L77 |
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.
Looking very nice
@@ -37,6 +37,16 @@ func DeploymentMutator(opts ...DeploymentMutateFn) MutateFn { | |||
} | |||
} | |||
|
|||
func DeploymentTemplateLabelMutator(desired, existing *appsv1.Deployment) bool { |
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.
Let me propose implementation to covert any manually added labels:
In some helper.go (or whatever)
package helper
func MergeMapStringString(modified *bool, existing *map[string]string, desired map[string]string)
if *existing == nil {
*existing = map[string]string{}
}
for k, v := range desired {
if existingVal, ok := (*existing)[k]; !ok || v != existingVal {
(*existing)[k] = v
*modified = true
}
}
}
here
import (
"github.com/kuadrant/limitador-operator/pkg/helper" (or whatever)
)
func DeploymentTemplateLabelMutator(desired, existing *appsv1.Deployment) bool {
updated := false
helper.MergeMapStringString(&updated, &existing.Spec.Template.Labels, desired.Spec.Template.Labels)
return updated
}
2f46bc6
to
c84accc
Compare
18b0c08
to
8f40569
Compare
97168ec
to
519d065
Compare
519d065
to
10cf420
Compare
pkg/helpers/helpers.go
Outdated
func MergeMapStringString(modified *bool, existing map[string]string, desired *map[string]string) { | ||
if existing == nil { | ||
existing = map[string]string{} | ||
} | ||
|
||
// for each desired key value set, e.g. labels | ||
// check if it's present in existing. if not add it to existing. | ||
// e.g. preserving existing labels while adding those that are in the desired set. | ||
for desiredKey, desiredValue := range *desired { | ||
if existingValue, exists := (existing)[desiredKey]; !exists || existingValue != desiredValue { | ||
(*desired)[desiredKey] = existingValue | ||
*modified = true | ||
} | ||
} |
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.
nit: Go maps are by default passed by reference, I don't think we need deference desired *map[string]string
typically? Would make the function a bit simpler 🤔
func MergeMapStringString(modified *bool, existing map[string]string, desired *map[string]string) { | |
if existing == nil { | |
existing = map[string]string{} | |
} | |
// for each desired key value set, e.g. labels | |
// check if it's present in existing. if not add it to existing. | |
// e.g. preserving existing labels while adding those that are in the desired set. | |
for desiredKey, desiredValue := range *desired { | |
if existingValue, exists := (existing)[desiredKey]; !exists || existingValue != desiredValue { | |
(*desired)[desiredKey] = existingValue | |
*modified = true | |
} | |
} | |
func MergeMapStringString(modified *bool, existing map[string]string, desired map[string]string) { | |
if existing == nil { | |
existing = map[string]string{} | |
} | |
// for each desired key value set, e.g. labels | |
// check if it's present in existing. if not add it to existing. | |
// e.g. preserving existing labels while adding those that are in the desired set. | |
for desiredKey, desiredValue := range desired { | |
if existingValue, exists := existing[desiredKey]; !exists || existingValue != desiredValue { | |
desired[desiredKey] = existingValue | |
*modified = true | |
} | |
} |
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.
If you check my proposal, it is the existing map which is passed as a pointer, because it could be empty and it is being initialized within the function. The desired map is read-only so, no need to be passed as a pointer. a reference is enough.
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.
And it is the existing map which needs to be updated. The desired represents an idea, a willing. The actual thing is the existing.
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 @eguzki I though I had addressed it, i updated the comment but not the assignment. Should be good now. 👍
Signed-off-by: Laura Fitzgerald <[email protected]>
10cf420
to
9b6b9d0
Compare
Closes #176
Verification
make run
kubectl edit deployment
spec.template.labels
something likeuseradded: label
kubectl edit limitador limitador-sample
metadata.labels
something likelimitdatorcradded: label
spec.template.labels
includes the following labels