-
Notifications
You must be signed in to change notification settings - Fork 153
feat(frontend): reject duplicate subnet, NSG, and MRG on cluster create #6004
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 all commits
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 |
|---|---|---|
|
|
@@ -53,6 +53,15 @@ type ClusterAdmissionContext struct { | |
| // ClusterNodePools is the list of node pools belonging to the cluster, used | ||
| // for minor-version skew checks against the desired cluster version. | ||
| ClusterNodePools []ClusterAdmissionNodePool | ||
| // SubscriptionClusters lists cluster documents in the same subscription, used | ||
| // for cross-cluster platform resource uniqueness on CREATE. | ||
| // The list is empty on UPDATE. | ||
| SubscriptionClusters []*api.HCPOpenShiftCluster | ||
| // SubscriptionNodePools lists node pool documents under SubscriptionClusters, | ||
| // used to ensure a cluster subnet is not already assigned to another cluster's | ||
| // node pool on CREATE. | ||
| // The list is empty on UPDATE. | ||
| SubscriptionNodePools []*api.HCPOpenShiftClusterNodePool | ||
|
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. Do we need to check this? don't we check that for nodepools the subnet must be part of the VNet of the cluster? |
||
| } | ||
|
|
||
| // ClusterAdmissionNodePool is a single node pool plus its prefetched service | ||
|
|
@@ -222,6 +231,110 @@ func admitClusterCustomerProperties(ctx context.Context, admissionContext *Clust | |
| errs := field.ErrorList{} | ||
|
|
||
| errs = append(errs, admitClusterVersionProfile(ctx, admissionContext, op, fldPath.Child("version"), &newObj.Version, safe.Field(oldObj, validation.ToClusterCustomerPropertiesVersion))...) | ||
| platformPath := fldPath.Child("platform") | ||
|
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. Move all of this to admitClusterPlatform |
||
| errs = append(errs, admitClusterManagedResourceGroupName(ctx, admissionContext, op, platformPath, &newObj.Platform)...) | ||
| errs = append(errs, admitClusterSubnetResourceID(ctx, admissionContext, op, platformPath, &newObj.Platform)...) | ||
| errs = append(errs, admitClusterNetworkSecurityGroupResourceID(ctx, admissionContext, op, platformPath, &newObj.Platform)...) | ||
|
|
||
| return errs | ||
| } | ||
|
|
||
| // admitClusterManagedResourceGroupName ensures the managed resource group name | ||
| // is unique within the subscription on CREATE. | ||
| // | ||
| // Best-effort only: compares against SubscriptionClusters prefetched before | ||
| // admission runs. Concurrent creates with the same MRG name can both succeed. | ||
| func admitClusterManagedResourceGroupName(_ context.Context, admissionContext *ClusterAdmissionContext, op operation.Operation, fldPath *field.Path, newObj *api.CustomerPlatformProfile) field.ErrorList { | ||
| if op.Type != operation.Create || len(newObj.ManagedResourceGroup) == 0 { | ||
|
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. Can managedresourcegroup be nil at this point? if not, remove the check |
||
| return nil | ||
| } | ||
|
|
||
| subscriptionID := admissionContext.OriginalCluster.ID.SubscriptionID | ||
| var errs field.ErrorList | ||
|
|
||
| for _, existing := range admissionContext.SubscriptionClusters { | ||
| if strings.EqualFold(newObj.ManagedResourceGroup, existing.CustomerProperties.Platform.ManagedResourceGroup) { | ||
| errs = append(errs, field.Invalid( | ||
| fldPath.Child("managedResourceGroup"), | ||
| newObj.ManagedResourceGroup, | ||
| fmt.Sprintf("Cluster with managed resource group name '%s' in subscription '%s' "+ | ||
| "already exists, please provide a unique managed resource group name", | ||
| newObj.ManagedResourceGroup, subscriptionID), | ||
| )) | ||
| break | ||
| } | ||
| } | ||
|
|
||
| return errs | ||
| } | ||
|
|
||
| // admitClusterSubnetResourceID ensures that the subnet ID is not already in use by any other | ||
| // cluster or node pool within the same subscription when creating a new cluster. | ||
| // | ||
| // Best-effort only: compares against SubscriptionClusters and SubscriptionNodePools | ||
| // prefetched before admission runs. Concurrent creates (or a create racing with a | ||
| // node pool create) using the same subnet can both succeed. | ||
| func admitClusterSubnetResourceID(_ context.Context, admissionContext *ClusterAdmissionContext, op operation.Operation, fldPath *field.Path, newObj *api.CustomerPlatformProfile) field.ErrorList { | ||
| if op.Type != operation.Create || newObj.SubnetID == nil { | ||
|
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. can newObj.SubnetID be nil at this point? if not, remove the check |
||
| return nil | ||
| } | ||
|
|
||
| subnetPath := fldPath.Child("subnetId") | ||
| subnetID := newObj.SubnetID.String() | ||
| var errs field.ErrorList | ||
|
|
||
| for _, existing := range admissionContext.SubscriptionClusters { | ||
| existingSubnet := existing.CustomerProperties.Platform.SubnetID | ||
| if strings.EqualFold(subnetID, existingSubnet.String()) { | ||
| errs = append(errs, field.Invalid( | ||
| subnetPath, | ||
| subnetID, | ||
| fmt.Sprintf("Subnet '%s' is already in use by another cluster", subnetID), | ||
| )) | ||
| break | ||
| } | ||
| } | ||
|
Comment on lines
+286
to
+296
|
||
|
|
||
| for _, nodePool := range admissionContext.SubscriptionNodePools { | ||
| nodePoolSubnet := nodePool.Properties.Platform.SubnetID | ||
| if strings.EqualFold(subnetID, nodePoolSubnet.String()) { | ||
| errs = append(errs, field.Invalid( | ||
| subnetPath, | ||
| subnetID, | ||
| fmt.Sprintf("Subnet '%s' is already in use by another cluster", subnetID), | ||
| )) | ||
| break | ||
| } | ||
| } | ||
|
Comment on lines
+298
to
+308
|
||
|
|
||
| return errs | ||
| } | ||
|
|
||
| // admitClusterNetworkSecurityGroupResourceID ensures that the network security group ID is not already in use by any other | ||
| // cluster within the same subscription when creating a new cluster. | ||
| // | ||
| // Best-effort only: compares against SubscriptionClusters prefetched before | ||
| // admission runs. Concurrent creates with the same NSG can both succeed. | ||
| func admitClusterNetworkSecurityGroupResourceID(_ context.Context, admissionContext *ClusterAdmissionContext, op operation.Operation, fldPath *field.Path, newObj *api.CustomerPlatformProfile) field.ErrorList { | ||
| if op.Type != operation.Create || newObj.NetworkSecurityGroupID == nil { | ||
|
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. can newObj.SubnetID be nil at this point? if not, remove the check |
||
| return nil | ||
| } | ||
|
|
||
| nsgPath := fldPath.Child("networkSecurityGroupId") | ||
| nsgID := newObj.NetworkSecurityGroupID.String() | ||
| var errs field.ErrorList | ||
|
|
||
| for _, existing := range admissionContext.SubscriptionClusters { | ||
| existingNSG := existing.CustomerProperties.Platform.NetworkSecurityGroupID | ||
| if strings.EqualFold(nsgID, existingNSG.String()) { | ||
| errs = append(errs, field.Invalid( | ||
| nsgPath, | ||
| nsgID, | ||
| fmt.Sprintf("Network Security Group '%s' is already in use by another cluster", nsgID), | ||
| )) | ||
| break | ||
| } | ||
| } | ||
|
Comment on lines
+327
to
+337
|
||
|
|
||
| return errs | ||
| } | ||
|
|
||
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.
Document whether it includes the cluster being processed itself or not