Skip to content
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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

a-hilaly
Copy link
Member

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.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@@ -80,6 +81,7 @@ func NewResourceGroupReconciler(
func (r *ResourceGroupReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.ResourceGroup{}).
WithEventFilter(predicate.GenerationChangedPredicate{}).
Copy link
Member Author

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

@a-hilaly a-hilaly force-pushed the instance-upadte-path branch 3 times, most recently from 9975289 to b9a6dc6 Compare December 23, 2024 20:17
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.
@a-hilaly a-hilaly force-pushed the instance-upadte-path branch from b9a6dc6 to 5d1e755 Compare December 23, 2024 20:18
Copy link
Contributor

@michaelhtm michaelhtm left a 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.

Comment on lines +84 to +88
if annotations, exists := metadata["labels"].(map[string]interface{}); exists {
if len(annotations) == 0 {
delete(metadata, "labels")
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:

Suggested change
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{
Copy link
Contributor

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)
Copy link
Contributor

@michaelhtm michaelhtm Dec 27, 2024

Choose a reason for hiding this comment

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

👍🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants