-
Notifications
You must be signed in to change notification settings - Fork 37
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
feat(core): track and apply resource changes through deep diffing #173
base: main
Are you sure you want to change the base?
Conversation
@@ -80,6 +81,7 @@ func NewResourceGroupReconciler( | |||
func (r *ResourceGroupReconciler) SetupWithManager(mgr ctrl.Manager) error { | |||
return ctrl.NewControllerManagedBy(mgr). | |||
For(&v1alpha1.ResourceGroup{}). | |||
WithEventFilter(predicate.GenerationChangedPredicate{}). |
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've observed some weird infinite-loops during integration test logs... While i know that predicate event filters can fix it, i'm still slightly hesitant, it should be needed..
Need to do some careful sleuthing here
9975289
to
b9a6dc6
Compare
Up until now, we weren't really doing proper diffing when deciding whether to update managed resources - we just marked everything as "SYNCED". This patch adds a new delta package that does a proper deep comparison between desired and observed resource states, being careful about all the k8s metadata fields that we should ignore during comparisons (e.g `metadata.generation`, `metadta.revision` etc...) A key design aspect of this implementation is that `kro` only diffs fields that are explicitly defined in `ResourceGroup` - any field that is defaulted by other controllers or mutating webhooks are ignored. This ensures `kro` coexists seamlessly with other reconciliation systems without fighting over field ownership. The delta `Compare` function takes the desired and observed `unstructured.Unstructured` objects and returns a list of structured "differences", where each difference contains the full path to the changed field and its desired/observed values. It recursively walks both object trees in parallel, building pathstring like `spec.containers[0].image` to precisely identify where values diverge. The comparison handles type mismatches, nil vs empty maps/slices, value differences etc... Also patch also adds integration tests in suites/core for generic resource updates and in suites/ackekscluster for EKS-clutser-specific version updates. note that in both tests we mainly rely on the `metadata.revision` to validate that indeed the controller did make an spec update call.
b9a6dc6
to
5d1e755
Compare
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.
Great Job @a-hilaly 🔥. I left a few comments.
if annotations, exists := metadata["labels"].(map[string]interface{}); exists { | ||
if len(annotations) == 0 { | ||
delete(metadata, "labels") | ||
} | ||
} |
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:
if annotations, exists := metadata["labels"].(map[string]interface{}); exists { | |
if len(annotations) == 0 { | |
delete(metadata, "labels") | |
} | |
} | |
if labels, exists := metadata["labels"].(map[string]interface{}); exists { | |
if len(labels) == 0 { | |
delete(metadata, "labels") | |
} | |
} |
} | ||
|
||
// ignoredFields are Kubernetes metadata fields that should not trigger updates. | ||
var ignoredFields = []string{ |
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.
Will we add more fields to ignore here? or just the ones in metadata?
Because instead i'm thinking we can call it ignoredMetadataFields...
} | ||
|
||
for _, field := range ignoredFields { | ||
delete(metadata, field) |
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.
👍🏼
Up until now, we weren't really doing proper diffing when deciding whether to update
managed resources - we just marked everything as "SYNCED". This patch adds a new delta
package that does a proper deep comparison between desired and observed resource
states, being careful about all the k8s metadata fields that we should ignore during
comparisons (e.g
metadata.generation
,metadta.revision
etc...)A key design aspect of this implementation is that
kro
only diffs fields that areexplicitly defined in
ResourceGroup
- any field that is defaulted by othercontrollers or mutating webhooks are ignored. This ensures
kro
coexists seamlesslywith other reconciliation systems without fighting over field ownership.
The delta
Compare
function takes the desired and observedunstructured.Unstructured
objects and returns a list of structured "differences", where each difference contains
the full path to the changed field and its desired/observed values. It recursively
walks both object trees in parallel, building pathstring like
spec.containers[0].image
to precisely identify where values diverge. The comparison handles type mismatches,
nil vs empty maps/slices, value differences etc...
Also patch also adds integration tests in suites/core for generic resource
updates and in suites/ackekscluster for EKS-clutser-specific version updates. note that
in both tests we mainly rely on the
metadata.revision
to validate that indeed thecontroller did make an spec update call.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.