feat: move cluster creation from frontend to backend - #5120
feat: move cluster creation from frontend to backend#5120Miguel Soriano (miguelsorianod) wants to merge 1 commit into
Conversation
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.
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: miguelsorianod The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
Skipping CI for Draft Pull Request. |
| } | ||
|
|
||
| initialClusterProperties := map[string]string{} | ||
| if len(f.clusterServiceProvisionShard) != 0 { |
There was a problem hiding this comment.
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.
|
|
||
| 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") |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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 != "" && |
There was a problem hiding this comment.
is this check needed and returning a non error the correct approach?
| b.options.ClustersServiceClient, | ||
| activeOperationInformer, | ||
| ) | ||
| dispatchClusterCreateController := operationcontrollers.NewDispatchClusterCreateController( |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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{}, |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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?
|
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, | |||
| ) | |||
There was a problem hiding this comment.
The introduction of the changes here need to be actually split into two:
- First introduce the backend controller. It should never execute because as soon as
- 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
|
PR needs rebase. DetailsInstructions 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. |
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)