-
Notifications
You must be signed in to change notification settings - Fork 262
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
🐛 Fix random instance port deletion #1753
Conversation
|
✅ Deploy Preview for kubernetes-sigs-cluster-api-openstack ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Welcome @zioc! |
Hi @zioc. Thanks for your PR. I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
/ok-to-test |
@mdbooth maybe need your help on this to see fit for your original idea |
6376101
to
efb7b61
Compare
I revisited to patch to remove the polling as suggested in the issue. As a consequence, reconcileBastion has to be revisited to requeue until bastion server gets ready. If the approach looks good to you I'll work on adding some tests to controllers. As instance status check is now moved to the main reconcile loop while the instance is in BUILDING state, its status will only be checked every minute, whereas retryInterval was 10s in CreateInstance polling. I was wondering if it would be acceptable to requeue sooner (in 10s like before for example) while the instance is still in BUILDING state? |
c940ccd
to
e185368
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.
This looks great: I'm very much in favour of this!
The e2e test failure looks real, though. Something odd is going on with Bastion creation. It looks like we end up creating bastions continuously until devstack runs out of quota.
To take a look, have a look at the e2e job failure in the PR. At the top you'll see 'Artifacts' which link to everything we dump out of the test run. Some interesting logs:
There's one other factor to consider here from a design POV: we need to handle the case where we fail to write the machine/bastion ID to the status. It turns out it is surprisingly easy for this to happen due to the way we patch objects, but we should also consider that brief outages of the k8s control plane are expected from time to time.
My solution to this to date has been that if the ID is not stored we always check by name, and adopt any resource we find. This solution has its own edge cases, but I think it's a lesser evil.
One more thing, and I mention this only because it looks like you know what you're doing and appreciate clean controllers. Feel free to ignore this entirely if the scope creep is too much:
I would really like to move machine creation out of the cluster controller, but we haven't gotten around to it yet. Instead of calling directly into CreateInstance, I would like to see the cluster controller create a Machine/OpenStackMachine for the Bastion and watch it. That would eliminate a bunch of cruft and duplication, and enable more robustification of the machine cleanup flow.
Again, only if you're up for it. It would make this cleanup simpler, but I'm pretty sure the total work involved would be greater.
@@ -379,6 +383,9 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope | |||
scope.Logger().Info("Machine instance state is DELETED, no actions") | |||
conditions.MarkFalse(openStackMachine, infrav1.InstanceReadyCondition, infrav1.InstanceDeletedReason, clusterv1.ConditionSeverityError, "") | |||
return ctrl.Result{}, nil | |||
case infrav1.InstanceStateBuilding: | |||
scope.Logger().Info("Waiting for instance to become ACTIVE", "id", instanceStatus.ID(), "status", instanceStatus.State()) | |||
return ctrl.Result{RequeueAfter: waitForBuildingInstanceToReconcile}, nil |
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: I think it's sensible to have a different requeue after here: this is an expected state which under normal circumstances should resolve quickly. The fall-through below is 'random other'. We don't want to poll at 10 second intervals for a server that's shutoff.
e185368
to
d207046
Compare
Thanks a lot for the feedback and suggestions @mdbooth , as well as tips to understand e2e failures, they were very helpful. It is indeed much safer to try to get instance by name when there is no ID saved in resource, I've added that fallback in various places. While reworking the reconcileBastion method to fix the observed issue, I figured out that current implementation is not really reconcilling bastion floating IP as it exits as soon as instance hash has been saved in annotations. With current implementation, if floating IP fails to be attached to the instance (it can be the case if we provide a cluster network without router), it will be creating floating IPs in loop (similarly to what has been described in this issue. I tried to revisit that part to make it more robust and save allocated floatingIP in cluster status. The proposed patch is functional in most cases, but there is a race condition that sometimes leads to the creation of two floating IPs. It is very likely to be caused by "the way we patch objects" as you said: when bastion is created, its object is patched before the status subresource as we are adding the bastionHash annotation. Consequently cluster may reconcile without updated status and create a new floatingIP. Regarding your suggestion to use Machine/OpenstackMachine for bastion, it seems very interesting at first glance, but there are several points that remains unclear to me:
|
d207046
to
398e443
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.
Finally I couldn't manage to avoid an important refactoring of reconcileBastion part. The race condition I was referring to in my previous message is fixed by checking if bastion already has a floating IP attached. (The race may still occur if floating IP failed to be attached, so we may create 2 floating IP in some cases, but it's already much better than current code that will create as many floating IP as it can if it fails to attach them for some reason).
@@ -116,7 +116,6 @@ func (is *InstanceStatus) BastionStatus(openStackCluster *infrav1.OpenStackClust | |||
|
|||
clusterNetwork := openStackCluster.Status.Network.Name | |||
i.IP = ns.IP(clusterNetwork) | |||
i.FloatingIP = ns.FloatingIP(clusterNetwork) |
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.
Here we were persisting the floating IP in status only if it was attached to the bastion VM. In order to properly track the created floating IP, it is now saved independently in reconcileBastion
// InstanceStateBuilding is the string representing an instance in a building state. | ||
InstanceStateBuilding = InstanceState("BUILDING") | ||
// InstanceStateBuild is the string representing an instance in a build state. | ||
InstanceStateBuild = InstanceState("BUILD") |
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.
While it is being created Nova server status is BUILD and not BUILDING. As this variable was not used elsewhere I felt it was ok to change it, please let me know it you foresee any issue to do 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.
not sure whether there are some consistent
https://github.com/openstack/nova/blob/master/nova/objects/fields.py#L969
seems indicate it's BUILDING
but in the CLI it's BUILD
which makes me a little bit confused, maybe something updated in the nova client?
| ddd0059f-f71e-4b07-9b28-f7344496a713 | amphora-ed18d7f4-e0b8-4f30-98fa-889f4aa0771a | BUILD | spawning | NOSTATE | |
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.
VM state seems to be derived from status, and the correspondance is not staightforward, for BUILDING state, we have a BUILD status
// InstanceStateBuilding is the string representing an instance in a building state. | ||
InstanceStateBuilding = InstanceState("BUILDING") | ||
// InstanceStateBuild is the string representing an instance in a build state. | ||
InstanceStateBuild = InstanceState("BUILD") |
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.
not sure whether there are some consistent
https://github.com/openstack/nova/blob/master/nova/objects/fields.py#L969
seems indicate it's BUILDING
but in the CLI it's BUILD
which makes me a little bit confused, maybe something updated in the nova client?
| ddd0059f-f71e-4b07-9b28-f7344496a713 | amphora-ed18d7f4-e0b8-4f30-98fa-889f4aa0771a | BUILD | spawning | NOSTATE | |
if err != nil { | ||
return err | ||
if openStackCluster.Status.Bastion != nil && openStackCluster.Status.Bastion.FloatingIP != "" { | ||
// Floating IP could have been created but not associated to instance, attempt to delete it from saved status first |
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 add some log here as it's useful info
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.
Actually this is the regular case (floating IP is stored in status, and deleted here), I haven't removed the other block that removes floating IP attached to bastion, but it will we be useful only if floating IP is was not properly saved in status for some reason. Maybe I should reword the comment to make it clearer.
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.
As the removal of floating IP will already be logged when it will be removed at this point, it does not seem very meaningfull to add another log. I changed my comment, hoping that it will be more explicit. Let me know if you want me to revisit that.
398e443
to
19a6cb5
Compare
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: mdbooth, zioc The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/test pull-cluster-api-provider-openstack-e2e-full-test |
b509efa
to
6c4f075
Compare
Thanks for the review @mdbooth I've updated the tests following your suggestion, and rebased on top of latest changes as there were merge conflicts. @jichenjc could you please have a look too? It'd be really great to get rid of this issue! |
/test pull-cluster-api-provider-openstack-e2e-full-test |
@zioc can you help address above comments and solve teh conflict ? Thanks |
Under certain circumstances, when they are non-responsive cells, nova may return a partial result instead of an error. (this is controlled by list_records_by_skipping_down_cells parameter that is true by default - no error will be thrown) When it faces this issue, capo controller will try to create a new server for the machine, resulting in deleting ports of existing one. In order avoid this issue, use GET instead of LIST with server uuid stored in machine spec when it is created, and store server ID in machine spec immediately. For that purpose server status polling that used to take place in CreateMachine has to be moved to main reconcile loop. ReconcileBastion also needed to be revisited to properly support requeuing.
6c4f075
to
75ffe73
Compare
@jichenjc I've rebased on top of latest change. The comments have already been addressed, and the proposed PR is in line with the outcome of various discussions to my opinion. However, I didn't feel comfortable clicking on "Resolve conversation" as I wasn't the one who initiated them, should I? |
FWIW, all of my comments are addressed 👍 |
/lgtm looks good to me ,thanks~ |
thanks! /retest |
/test pull-cluster-api-provider-openstack-e2e-test |
/cherry-pick release-0.9 |
/cherry-pick release-0.8 |
@mnaser: #1753 failed to apply on top of branch "release-0.9":
In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
@mnaser: #1753 failed to apply on top of branch "release-0.8":
In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
In this PR,
I have no idea how our CI didn't catch that 🤷 but for sure I'll take a look. |
What this PR does / why we need it:
Under certain circumstances, when they are non-responsive cells, nova may return a partial result instead of an error.
(this is controlled by list_records_by_skipping_down_cells parameter that is true by default - no error will be thrown)
When it faces this issue, capo controller will try to create a new server for the machine, resulting in deleting ports of existing one.
In order avoid this issue, use GET instead of LIST to retrieve server using its uuid. For that purpose we must ensure that uuid is stored in machine spec as soon as the server is created, even if server is in error state.
Fixes: #1612
Special notes for your reviewer:
Yes
TODOs:
/hold