-
Notifications
You must be signed in to change notification settings - Fork 128
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: allow updating entities' IDs while keeping their names #918
Conversation
6daf13e
to
fa38b2b
Compare
35dadc6
to
b8abb2c
Compare
It's ready for initial review - I'd like to validate whether the path I'm following here is sane and if there's no simpler way to achieve the possibility of changing entities' IDs without changing their names. I'd much appreciate input from @hbagdi who authored most of the code I was touching here. I was doing my best to not break existing scenarios after reading your comment on ec56add commit saying:
|
8954f6f
to
c1594cb
Compare
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #918 +/- ##
==========================================
+ Coverage 35.57% 35.59% +0.01%
==========================================
Files 92 92
Lines 11279 11343 +64
==========================================
+ Hits 4013 4038 +25
- Misses 6873 6912 +39
Partials 393 393
☔ View full report in Codecov by Sentry. |
@czeslavo I am currently testing this impl. Looks like there is a regression (sad that no test catched that too). It seems that updating an entity (with a definition that contains the id) is failing now.
deck sync .... then modify a field and try to sync again. you should get:
Note that I am testing on konnect and that patch is not supported. However the main branch is doing a put properly... so there is probably something to double check. |
9df8832
to
ab5e07a
Compare
@aboudreault Cool! It's great you were able to catch it in manual tests. Indeed I made this change unnecessarily: + updatedService, err := s.client.Services.Update(ctx, &service.Service)
- updatedService, err := s.client.Services.Create(ctx, &service.Service) I added a test case that covers updates of the entities with explicitly assigned IDs and fixed the regression. |
ab5e07a
to
9daed7b
Compare
9daed7b
to
7e6a860
Compare
7e6a860
to
8418219
Compare
thanks @czeslavo! I will test this again on Monday. |
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.
Nice job here @czeslavo! I spend some time to review this change, did some additional testing and overall it seems to work well now. One thing I noticed is that this "bug" also exists with all other entities that have more than the ID as unique constraint (e.g. Upstream). We should create a follow-up ticket for these ones.
types/postProcess.go
Outdated
// Delete all plugins associated with this service as that's the implicit behavior of Kong (cascade delete). | ||
plugins, err := crud.currentState.Plugins.GetAllByServiceID(serviceID) | ||
if err != nil { | ||
return nil, fmt.Errorf("error looking up plugins for service '%v': %v", serviceID, err) |
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.
return nil, fmt.Errorf("error looking up plugins for service '%v': %v", serviceID, err) | |
return nil, fmt.Errorf("error looking up plugins for service '%v': %w", serviceID, err) |
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.
Hi !
Adding errorlint linter to golangci-lint could help you ensure that accross the project.
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.
+1, will look to add this one
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.
ref: #923
types/postProcess.go
Outdated
for _, plugin := range plugins { | ||
err = crud.currentState.Plugins.Delete(*plugin.ID) | ||
if err != nil { | ||
return nil, fmt.Errorf("error deleting plugin '%v' for service '%v': %v", *plugin.ID, serviceID, err) |
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.
return nil, fmt.Errorf("error deleting plugin '%v' for service '%v': %v", *plugin.ID, serviceID, err) | |
return nil, fmt.Errorf("error deleting plugin '%v' for service '%v': %w", *plugin.ID, serviceID, err) |
types/postProcess.go
Outdated
for _, plugin := range plugins { | ||
err = crud.currentState.Plugins.Delete(*plugin.ID) | ||
if err != nil { | ||
return nil, fmt.Errorf("error deleting plugin '%v' for route '%v': %v", *plugin.ID, routeID, err) |
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.
return nil, fmt.Errorf("error deleting plugin '%v' for route '%v': %v", *plugin.ID, routeID, err) | |
return nil, fmt.Errorf("error deleting plugin '%v' for route '%v': %w", *plugin.ID, routeID, err) |
types/postProcess.go
Outdated
// Delete all plugins associated with this route as that's the implicit behavior of Kong (cascade delete). | ||
plugins, err := crud.currentState.Plugins.GetAllByRouteID(routeID) | ||
if err != nil { | ||
return nil, fmt.Errorf("error looking up plugins for route '%v': %v", routeID, err) |
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.
return nil, fmt.Errorf("error looking up plugins for route '%v': %v", routeID, err) | |
return nil, fmt.Errorf("error looking up plugins for route '%v': %w", routeID, err) |
types/postProcess.go
Outdated
// Delete all plugins associated with this consumer as that's the implicit behavior of Kong (cascade delete). | ||
plugins, err := crud.currentState.Plugins.GetAllByConsumerID(consumerID) | ||
if err != nil { | ||
return nil, fmt.Errorf("error looking up plugins for consumer '%v': %v", consumerID, err) |
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.
return nil, fmt.Errorf("error looking up plugins for consumer '%v': %v", consumerID, err) | |
return nil, fmt.Errorf("error looking up plugins for consumer '%v': %w", consumerID, err) |
types/postProcess.go
Outdated
} | ||
for _, plugin := range plugins { | ||
if err := crud.currentState.Plugins.Delete(*plugin.ID); err != nil { | ||
return nil, fmt.Errorf("error deleting plugin '%v' for consumer '%v': %v", *plugin.ID, consumerID, err) |
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.
return nil, fmt.Errorf("error deleting plugin '%v' for consumer '%v': %v", *plugin.ID, consumerID, err) | |
return nil, fmt.Errorf("error deleting plugin '%v' for consumer '%v': %w", *plugin.ID, consumerID, err) |
@aboudreault I addressed all your suggestions and added a changelog entry, also created a tracking issue to cover the other entities (#922). |
This adds an additional step to the
diff.Syncer
'sdiff
producer -deleteDuplicates
. Its purpose is to clean up the entities in the current state that have duplicates with the same names in the target state before proceeding with updates/creates and regular deletes. Without that additional step, it's not possible to perform sync in which the IDs of entities are changed while their names are kept between syncs.An actual use-case that is the motivation for this change is Kong Ingress Controller where we recently started assigning entities' IDs explicitly. Without this change, existing setups break when KIC is upgraded from the version not setting the IDs to the new one because of breaking the uniqueness constraints of Services, Routes, and Consumers.
The added test cases represent the complete use case with all the affected entities: Services, Routes, and Consumers. We change their IDs, keeping the names and we expect the final state on the Kong side to be using the new IDs.
The problematic part of the implementation is how Services are handled - we cannot safely delete them as they might be referred by Routes. When they do, deletes fail as there's no cascade delete for them configured and foreign key constraint is violated. Because of that, in the Service's
deleteDuplicateService
implementation, we have to manually generate delete events for the associated Routes and, in consequence, their Plugins. It is to make sure they'll be recreated in the later stage of the diff command -createUpdate
.That gives us more headache as that means we generate events that shouldn't be handled in parallel in the case of Service. Because of that, higher level events rearranging needs to happen to make sure they are all handled in the expected
reverseOrder
, just as the regulardelete
events.It's a prerequisite for solving Kong/kubernetes-ingress-controller#4025.