-
Notifications
You must be signed in to change notification settings - Fork 171
Gate SPC deletion + delete-operation on removal of all remaining ApplyDesires #6661
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
972714b
dd56f9b
14ef6f4
75a66f4
c54c328
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -271,6 +271,26 @@ func (c *clusterChildResourcesCleanupController) extraDeleteGateShouldDeleteServ | |
| return false, nil | ||
| } | ||
|
|
||
| // Check that the ClusterResourcesController has removed all the ApplyDesires it | ||
| // owns. That controller is responsible for deleting its own desires during | ||
| // cluster deletion; we only remove the ServiceProviderCluster document (which | ||
| // carries the ManagementClusterResourceID needed to reach them) once they are gone. | ||
| applyDesiresGone, err := c.clusterResourceApplyDesiresGone( | ||
| ctx, | ||
| spc, | ||
| serviceProviderClusterResourceID.SubscriptionID, | ||
| serviceProviderClusterResourceID.ResourceGroupName, | ||
| clusterName, | ||
| ) | ||
| if err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("failed to check ClusterResourcesController ApplyDesire precondition: %w", err)) | ||
| } | ||
| if !applyDesiresGone { | ||
| logger.Info("waiting for ClusterResourcesController to delete its ApplyDesires before removing ServiceProviderCluster", | ||
| "serviceProviderClusterResourceID", spc.ResourceID.String()) | ||
| return false, nil | ||
| } | ||
|
|
||
| // Check if there are any cluster-scoped kube-applier *Desire documents remaining. | ||
| if spc.Status.ManagementClusterResourceID != nil { | ||
| kaClient := c.kubeApplierDBClients.For(ctx, spc.Status.ManagementClusterResourceID) | ||
|
|
@@ -307,6 +327,59 @@ func (c *clusterChildResourcesCleanupController) extraDeleteGateShouldDeleteServ | |
| return true, nil | ||
| } | ||
|
|
||
| // clusterResourceApplyDesiresGone reports whether all ApplyDesires tagged by the | ||
| // ClusterResourcesController have been removed for the cluster owning the given | ||
| // ServiceProviderCluster. The ClusterResourcesController is responsible for | ||
| // deleting its own desires during cluster deletion; this controller only verifies | ||
| // they are gone before removing the ServiceProviderCluster document (which carries | ||
| // the ManagementClusterResourceID needed to reach them). A nil management cluster | ||
| // reference or unavailable kube-applier client is treated as gone, consistent with | ||
| // the best-effort behavior elsewhere in this file. | ||
| func (c *clusterChildResourcesCleanupController) clusterResourceApplyDesiresGone(ctx context.Context, spc *coreapi.ServiceProviderCluster, subscriptionID, resourceGroupName, clusterName string) (bool, error) { | ||
| logger := utils.LoggerFromContext(ctx) | ||
|
|
||
| if spc == nil || spc.Status.ManagementClusterResourceID == nil { | ||
| return true, nil | ||
| } | ||
|
|
||
| managementClusterID := spc.Status.ManagementClusterResourceID | ||
| kubeApplierDBClient := c.kubeApplierDBClients.For(ctx, managementClusterID) | ||
| if kubeApplierDBClient == nil { | ||
| logger.Info("no kube-applier client for management cluster; treating ClusterResourcesController ApplyDesires as gone", | ||
| "managementClusterResourceID", managementClusterID.String()) | ||
| return true, nil | ||
| } | ||
|
|
||
| applyDesireCRUD, err := kubeApplierDBClient.ApplyDesiresForCluster(subscriptionID, resourceGroupName, clusterName) | ||
| if err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("failed to get kube-applier CRUD for ApplyDesire precondition: %w", err)) | ||
| } | ||
|
|
||
| applyDesireIterator, err := applyDesireCRUD.List(ctx, &cosmosstorageutils.DBClientListResourceDocsOptions{}) | ||
| if err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("failed to list ApplyDesire documents for precondition check: %w", err)) | ||
| } | ||
|
|
||
| for _, desire := range applyDesireIterator.Items(ctx) { | ||
| if desire.Tags == nil { | ||
| continue | ||
| } | ||
| if desire.Tags[kubeapplierapi.TagControllerName] == kubeapplierapi.ClusterResourcesControllerName { | ||
| // A tagged ApplyDesire still exists; the gate is not satisfied. Surface | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, count how many we have for each controller and log a message saying we're waiting on X many for each controller to be cleaned up.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in AI-generated. Review for accuracy. |
||
| // any iteration error observed so far before short-circuiting. | ||
| if err := applyDesireIterator.GetError(); err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("error iterating ApplyDesires for precondition check: %w", err)) | ||
| } | ||
| return false, nil | ||
| } | ||
| } | ||
| if err := applyDesireIterator.GetError(); err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("error iterating ApplyDesires for precondition check: %w", err)) | ||
| } | ||
|
|
||
| return true, nil | ||
| } | ||
|
|
||
| // ensureClusterScopedKubeApplierResourcesDeleted ensures that the cluster-scoped *Desire documents are deleted | ||
| // from the database. *Desire documents on non-cluster scoped resources are deleted by their corresponding deletion controllers. | ||
| func (c *clusterChildResourcesCleanupController) ensureClusterScopedKubeApplierResourcesDeleted(ctx context.Context, clusterResourceID *azcorearm.ResourceID) error { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -179,6 +179,18 @@ func (c *operationClusterDelete) SynchronizeOperation(ctx context.Context, key c | |
| return nil | ||
| } | ||
|
|
||
| // Hold the delete operation non-terminal until the ClusterResourcesController has | ||
| // removed all the ApplyDesires it owns. Placed after the deadline check above so | ||
| // the timeout-failure path still fires if this cleanup stalls. | ||
| applyDesiresGone, err := c.clusterResourceApplyDesiresGone(ctx, cluster) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, do it like hostedClusterDeletionStatus so we can have a message for the operation about how many applydesires are remaining for each controller
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in AI-generated. Review for accuracy. |
||
| if err != nil { | ||
| return utils.TrackError(fmt.Errorf("failed to check ClusterResourcesController ApplyDesire precondition: %w", err)) | ||
| } | ||
| if !applyDesiresGone { | ||
| logger.Info("waiting for ClusterResourcesController to delete its ApplyDesires before completing delete operation") | ||
| return nil | ||
| } | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The AI-generated. Review for accuracy. AI-generated. Review for accuracy.
Comment on lines
+182
to
+193
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the PR title and description to match: the gate intentionally blocks while any ApplyDesire remains for the cluster (not only AI-generated. Review for accuracy.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aligned in AI-generated. Review for accuracy. AI-generated. Review for accuracy.
Comment on lines
+190
to
+193
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in logger.Info("waiting for ApplyDesires to be deleted before completing delete operation", "remaining", remainingApplyDesires, "breakdown", applyDesireBreakdown)The cleanup controller's SPC-deletion gate log builds the count + breakdown into the message via AI-generated. Review for accuracy. AI-generated. Review for accuracy. |
||
|
|
||
| if !c.shouldReconcileOperationAndResourceStatus(cluster) { | ||
| return nil | ||
| } | ||
|
|
@@ -196,6 +208,59 @@ func (c *operationClusterDelete) shouldReconcileOperationAndResourceStatus(clust | |
| cluster.ServiceProviderProperties.ClusterServiceID != nil | ||
| } | ||
|
|
||
| // clusterResourceApplyDesiresGone reports whether all ApplyDesires tagged by the | ||
| // ClusterResourcesController have been removed for the given cluster. The | ||
| // ClusterResourcesController deletes its own desires during cluster deletion; this | ||
| // controller holds the delete operation non-terminal until they are gone. A missing | ||
| // ServiceProviderCluster, a nil ManagementClusterResourceID, or an unavailable | ||
| // kube-applier client is treated as gone. | ||
| func (c *operationClusterDelete) clusterResourceApplyDesiresGone(ctx context.Context, cluster *coreapi.HCPOpenShiftCluster) (bool, error) { | ||
| spc, err := c.resourcesDBClient.ServiceProviderClusters(cluster.ID.SubscriptionID, cluster.ID.ResourceGroupName, cluster.ID.Name).Get(ctx, coreapi.ServiceProviderClusterResourceName) | ||
| if cosmosstorageutils.IsNotFoundError(err) { | ||
| return true, nil | ||
| } | ||
| if err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("failed to get ServiceProviderCluster: %w", err)) | ||
| } | ||
| if spc.Status.ManagementClusterResourceID == nil { | ||
| return true, nil | ||
| } | ||
|
|
||
| kubeApplierDBClient := c.kubeApplierDBClients.For(ctx, spc.Status.ManagementClusterResourceID) | ||
| if kubeApplierDBClient == nil { | ||
| return true, nil | ||
| } | ||
|
|
||
| applyDesireCRUD, err := kubeApplierDBClient.ApplyDesiresForCluster(cluster.ID.SubscriptionID, cluster.ID.ResourceGroupName, cluster.ID.Name) | ||
| if err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("failed to get kube-applier CRUD for ApplyDesire precondition: %w", err)) | ||
| } | ||
|
|
||
| applyDesireIterator, err := applyDesireCRUD.List(ctx, &cosmosstorageutils.DBClientListResourceDocsOptions{}) | ||
| if err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("failed to list ApplyDesire documents for precondition check: %w", err)) | ||
| } | ||
|
|
||
| for _, desire := range applyDesireIterator.Items(ctx) { | ||
| if desire.Tags == nil { | ||
| continue | ||
| } | ||
| if desire.Tags[kubeapplierapi.TagControllerName] == kubeapplierapi.ClusterResourcesControllerName { | ||
| // A tagged ApplyDesire still exists; the gate is not satisfied. Surface | ||
| // any iteration error observed so far before short-circuiting. | ||
| if err := applyDesireIterator.GetError(); err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("error iterating ApplyDesires for precondition check: %w", err)) | ||
| } | ||
| return false, nil | ||
| } | ||
| } | ||
| if err := applyDesireIterator.GetError(); err != nil { | ||
| return false, utils.TrackError(fmt.Errorf("error iterating ApplyDesires for precondition check: %w", err)) | ||
| } | ||
|
|
||
| return true, nil | ||
| } | ||
|
|
||
| func (c *operationClusterDelete) reconcileOperationAndResourceStatus(ctx context.Context, operation *coreapi.Operation, cluster *coreapi.HCPOpenShiftCluster) error { | ||
| logger := utils.LoggerFromContext(ctx) | ||
|
|
||
|
|
||
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.
The
ClusterChildResourcesCleanupControllersection ofdocs/cosmos-data-flow.mdreflects this extra precondition and the added reads — SPC read forStatus.ManagementClusterResourceID, ApplyDesire list grouped byTags[ControllerName], with SPC deletion blocked while any remain. Accurate for the current commit.AI-generated. Review for accuracy.
AI-generated. Review for accuracy.