Skip to content

feat: move cluster creation from frontend to backend - #5120

Closed
Miguel Soriano (miguelsorianod) wants to merge 1 commit into
mainfrom
msoriano-move-cs-cluster-create-to-backend
Closed

feat: move cluster creation from frontend to backend#5120
Miguel Soriano (miguelsorianod) wants to merge 1 commit into
mainfrom
msoriano-move-cs-cluster-create-to-backend

Conversation

@miguelsorianod

Copy link
Copy Markdown
Collaborator

This commit moves cluster creation from frontend to backend. Frontend now just creates the corresponding ARM Operation, with an empty internalID, and it delegates all the actual cluster creation work to backend.

On backend now we check whether internalID is empty or not to assess whether the cluster create request to Clusters Service has been submitted. Once that occurs, we check the state.

The logic that performs cluster creation against Clusters Service has been moved to a backend operationcontroller named DispatchClusterCreate. The controller attempts to be resilient to failures.

This MR cannot be merged until cluster reads have been moved to backend (#4610)

This commit moves cluster creation from frontend to backend. Frontend
now just creates the corresponding ARM Operation, with an empty
internalID, and it delegates all the actual cluster creation
work to backend.

On backend now we check whether internalID is empty or not to assess
whether the cluster create request to Clusters Service has been
submitted. Once that occurs, we check the state.

The logic that performs cluster creation against Clusters Service has
been moved to a backend operationcontroller named DispatchClusterCreate.
The controller attempts to be resilient to failures.
@openshift-ci

openshift-ci Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: miguelsorianod
Once this PR has been reviewed and has the lgtm label, please assign janboll for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci

openshift-ci Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

}

initialClusterProperties := map[string]string{}
if len(f.clusterServiceProvisionShard) != 0 {

@miguelsorianod Miguel Soriano (miguelsorianod) May 5, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The functionality of the CSPropertyProvisionShardID, CSPropertyNoopProvision, CSPropertyNoopDeprovision was added in the beginning of the project. This PR removes the ability to set that completely. No permanent aro-hcp environments leverage it, the helm template of the frontend deployment does not even pass the corresponding frontend CLI flags. Technically some developer could be using it. If for some reason we want to keep that we would need to move it to backend.

Comment thread frontend/cmd/cmd.go

rootCmd.Flags().StringVar(&opts.clustersServiceURL, "clusters-service-url", "https://api.openshift.com", "URL of the OCM API gateway.")
rootCmd.Flags().BoolVar(&opts.insecure, "insecure", false, "Skip validating TLS for clusters-service.")
rootCmd.Flags().StringVar(&opts.clusterServiceProvisionShard, "cluster-service-provision-shard", "", "Manually specify provision shard for all requests to cluster service")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Related to https://github.com/Azure/ARO-HCP/pull/5120/changes#r3187900975. If we decide we don't want to support that anymore the CLI flags can be removed safely from frontend because the helm deployment doesn't pass them.

if mrg == "" {
return utils.TrackError(fmt.Errorf("cluster %s has no managed resource group", cluster.Name))
}
existing, err := c.findAROHCPClusterByAzureInfo(ctx,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

A challenge I found was that it could occur that inbetween the CS create request is received and we store the CS ID in the Cluster doc something fails. Then the challenge is: what do we do at that point? to interact with the CS API you leverage the CS Cluster ID.
What this PR does is to do a list in CS with a search string using the cluster's azure resource information: the azure subscription, tenant id, resource group name, and resource name. This assumes that no two clusters will be created with that information.

Although the PR uses that, CS currently does not support filtering by some of those fields, we would need to add it. Currently CS list only supports filtering by subscription id and resource group"

return utils.TrackError(err)
}

if cluster.ServiceProviderProperties.ActiveOperationID != "" &&

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

is this check needed and returning a non error the correct approach?

b.options.ClustersServiceClient,
activeOperationInformer,
)
dispatchClusterCreateController := operationcontrollers.NewDispatchClusterCreateController(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Although the PR implements create, I wonder whether we need to change something somewhere else. What happens if a delete occurs and the CS ID hasn't been stored for some reason?

if err != nil {
return nil, err
}
if len(matches) > 1 {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If we get more than one result we treat it as an error

// and we need to wire up a fail-safe where if we have no ID and we time out, we report the best failure we can.
return nil
}
// TODO we need to wire up a fail-safe where if we have no ID and we time out, we report the best failure we can.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

See.

Additionally, what happens if for some reason the creation has failed in the CS side? and what if for some reason it's not filled in Cosmos in a timely manner?

database.OperationRequestCreate,
newInternalCluster.ID,
ptr.Deref(newInternalCluster.ServiceProviderProperties.ClusterServiceID, api.InternalID{}),
api.InternalID{},

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

In frontend we now pass an empty internal ID as now the responsibility is fully on backend

return fmt.Errorf("unexpected type %T", resultingUncastInternalCluster)
}

var resultingClusterServiceCluster *arohcpv1alpha1.Cluster // TODO remove this once we moved read from CS

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

temporary to silence the compiler. This depensd on the MR that moves reads out of frontend

if subscription.Properties == nil || subscription.Properties.TenantId == nil || *subscription.Properties.TenantId == "" {
return utils.TrackError(fmt.Errorf("subscription %s has no tenant id", operation.ExternalID.SubscriptionID))
}
tenantID := *subscription.Properties.TenantId

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We get the tenant id from the cluster's subscription. The operation itself has a tenant but on cluster creation I observed the operation id tenant comes from a http header whereas the cluster tenant comes from the subscription. Because of this I retrieved the tenant id using the cluster's subscription (requires one db query more as we need to retrieve the susbcription)

clustersServiceClient ocm.ClusterServiceClientSpec
}

func NewDispatchClusterCreateController(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This dispatches CS cluster creation in a controller. At some point I am guessing we might need to do some resources precreation or calculation in RP itself before sending the call to CS. In that case, how would we coordinate this controller and the others?

@machi1990

Copy link
Copy Markdown
Collaborator

Miguel Soriano (@miguelsorianod) , Jakob Gray (@JakobGray) had created #4821 does this PR supersedes it?

@miguelsorianod

Copy link
Copy Markdown
Collaborator Author

Miguel Soriano (@miguelsorianod) , Jakob Gray (@JakobGray) had created #4821 does this PR supersedes it?

I wasn't aware of that one. It's possible there's some overlap.

@@ -327,6 +327,11 @@ func (b *Backend) runBackendControllersUnderLeaderElection(ctx context.Context,
b.options.ClustersServiceClient,
activeOperationInformer,
)

@miguelsorianod Miguel Soriano (miguelsorianod) May 5, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The introduction of the changes here need to be actually split into two:

  1. First introduce the backend controller. It should never execute because as soon as
  2. Then introduce the frontend removal logic

That way the images can be deployed and potentially rolled back independently.

I think it should be safe to introduce 1 because it will only be executed when the operation exists and it doesn't have an internal id, and until 2 is introduced the cluster create operation should always have an internal id as the operation is created together with it set

@openshift-ci

openshift-ci Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

PR needs rebase.

Details

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-sigs/prow repository.

@miguelsorianod

Copy link
Copy Markdown
Collaborator Author

Superseded by #6082 and #6121. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants