diff --git a/api-runtime/common-runtime/ClusterManager.go b/api-runtime/common-runtime/ClusterManager.go
index ea05bfc2e..e1d198728 100644
--- a/api-runtime/common-runtime/ClusterManager.go
+++ b/api-runtime/common-runtime/ClusterManager.go
@@ -960,8 +960,11 @@ func ListCluster(connectionName string, rsType string, kubeconfigType string) ([
if err != nil {
clusterSPLock.RUnlock(connectionName, iidInfo.NameId)
if checkNotFoundError(err) {
- cblog.Error(err)
- info = cres.ClusterInfo{IId: cres.IID{NameId: iidInfo.NameId, SystemId: iidInfo.SystemId}}
+ cblog.Infof("Cluster '%s' not found on CSP, marking as NotFound", iidInfo.NameId)
+ info = cres.ClusterInfo{
+ IId: cres.IID{NameId: iidInfo.NameId, SystemId: iidInfo.SystemId},
+ Status: cres.ClusterNotFound,
+ }
infoList2 = append(infoList2, &info)
continue
}
@@ -1078,6 +1081,14 @@ func GetCluster(connectionName string, rsType string, clusterName string, kubeco
// (2) get resource(SystemId)
info, err := handler.GetCluster(getDriverIID(cres.IID{NameId: iidInfo.NameId, SystemId: iidInfo.SystemId}))
if err != nil {
+ if checkNotFoundError(err) {
+ cblog.Info("Cluster not found on CSP, returning NotFound status")
+ notFoundInfo := cres.ClusterInfo{
+ IId: getUserIID(cres.IID{NameId: iidInfo.NameId, SystemId: iidInfo.SystemId}),
+ Status: cres.ClusterNotFound,
+ }
+ return ¬FoundInfo, nil
+ }
cblog.Error(err)
return nil, err
}
@@ -1985,17 +1996,102 @@ func DeleteCluster(connectionName string, rsType string, nameID string, force st
}
}
- // (3) delete IID
- _, err = infostore.DeleteByConditions(&ClusterIIDInfo{}, CONNECTION_NAME_COLUMN, iidInfo.ConnectionName, NAME_ID_COLUMN, nameID)
+ return result, nil
+}
+
+// FinalizeDeleteCluster deletes the Spider meta information for a Cluster
+// only when the Cluster no longer exists on the CSP.
+// (1) Check the Cluster existence in MetaDB
+// (2) Check the Cluster existence on CSP via GetCluster()
+// (3) If the Cluster does not exist on CSP, delete the meta information
+func FinalizeDeleteCluster(connectionName string, rsType string, nameID string) (bool, error) {
+ cblog.Info("call FinalizeDeleteCluster()")
+
+ // check empty and trim user inputs
+ connectionName, err := EmptyCheckAndTrim("connectionName", connectionName)
if err != nil {
cblog.Error(err)
- if force != "true" {
+ return false, err
+ }
+
+ if err := checkCapability(connectionName, CLUSTER_HANDLER); err != nil {
+ return false, err
+ }
+
+ nameID, err = EmptyCheckAndTrim("nameID", nameID)
+ if err != nil {
+ cblog.Error(err)
+ return false, err
+ }
+
+ cldConn, err := ccm.GetCloudConnection(connectionName)
+ if err != nil {
+ cblog.Error(err)
+ return false, err
+ }
+
+ handler, err := cldConn.CreateClusterHandler()
+ if err != nil {
+ cblog.Error(err)
+ return false, err
+ }
+
+ clusterSPLock.Lock(connectionName, nameID)
+ defer clusterSPLock.Unlock(connectionName, nameID)
+
+ // (1) get spiderIID for creating driverIID
+ var iidInfo *ClusterIIDInfo
+ var iidInfoList []*ClusterIIDInfo
+ if os.Getenv("PERMISSION_BASED_CONTROL_MODE") != "" {
+ err = getAuthIIDInfoList(connectionName, &iidInfoList)
+ if err != nil {
+ cblog.Error(err)
return false, err
}
+ } else {
+ err = infostore.ListByCondition(&iidInfoList, CONNECTION_NAME_COLUMN, connectionName)
+ if err != nil {
+ cblog.Error(err)
+ return false, err
+ }
+ }
+ var found = false
+ for _, OneIIdInfo := range iidInfoList {
+ if OneIIdInfo.NameId == nameID {
+ iidInfo = OneIIdInfo
+ found = true
+ break
+ }
+ }
+ if !found {
+ err := fmt.Errorf("%s '%s' does not exist in Spider's MetaDB for connection '%s'", RSTypeString(rsType), nameID, connectionName)
+ cblog.Error(err)
+ return false, err
}
- // for NodeGroup list
- // delete all nodegroups of target Cluster
+ // (2) Check the Cluster existence on CSP via GetCluster()
+ driverIId := getDriverIID(cres.IID{NameId: iidInfo.NameId, SystemId: iidInfo.SystemId})
+ _, err = handler.(cres.ClusterHandler).GetCluster(driverIId)
+ if err != nil {
+ if !checkNotFoundError(err) {
+ // unexpected error from CSP
+ cblog.Error(err)
+ return false, fmt.Errorf("failed to check Cluster existence on CSP: %w", err)
+ }
+ // Cluster does not exist on CSP => proceed to finalize
+ } else {
+ // Cluster still exists on CSP => cannot finalize
+ return false, fmt.Errorf("cannot finalize: Cluster '%s' still exists on CSP (status is not %s)", nameID, cres.ClusterNotFound)
+ }
+
+ // (3) delete IID from MetaDB
+ _, err = infostore.DeleteByConditions(&ClusterIIDInfo{}, CONNECTION_NAME_COLUMN, iidInfo.ConnectionName, NAME_ID_COLUMN, nameID)
+ if err != nil {
+ cblog.Error(err)
+ return false, err
+ }
+
+ // delete all nodegroups of target Cluster from MetaDB
_, err = infostore.DeleteByConditions(&NodeGroupIIDInfo{}, CONNECTION_NAME_COLUMN, iidInfo.ConnectionName,
OWNER_CLUSTER_NAME_COLUMN, iidInfo.NameId)
if err != nil {
@@ -2003,7 +2099,7 @@ func DeleteCluster(connectionName string, rsType string, nameID string, force st
return false, err
}
- return result, nil
+ return true, nil
}
func CountAllClusters() (int64, error) {
diff --git a/api-runtime/rest-runtime/CBSpiderRuntime.go b/api-runtime/rest-runtime/CBSpiderRuntime.go
index 8a51b07d1..35be7d2ac 100644
--- a/api-runtime/rest-runtime/CBSpiderRuntime.go
+++ b/api-runtime/rest-runtime/CBSpiderRuntime.go
@@ -473,6 +473,7 @@ func getRoutes() []route {
{"GET", "/cluster", ListCluster},
{"GET", "/cluster/:Name", GetCluster},
{"DELETE", "/cluster/:Name", DeleteCluster},
+ {"DELETE", "/cluster/:Name/finalize", FinalizeDeleteCluster},
{"GET", "/cluster/:Name/token", GetClusterToken},
//-- for NodeGroup
{"POST", "/cluster/:Name/nodegroup", AddNodeGroup},
diff --git a/api-runtime/rest-runtime/ClusterRest.go b/api-runtime/rest-runtime/ClusterRest.go
index a206f16ca..e7e3cd160 100644
--- a/api-runtime/rest-runtime/ClusterRest.go
+++ b/api-runtime/rest-runtime/ClusterRest.go
@@ -549,7 +549,7 @@ func ChangeNodeGroupScaling(c echo.Context) error {
// deleteCluster godoc
// @ID delete-cluster
// @Summary Delete Cluster
-// @Description Delete a specified Cluster.
+// @Description Delete a specified Cluster from the CSP. This API only deletes the CSP resource and does not remove Spider meta information. After deletion, call **DELETE /cluster/{Name}/finalize** to clean up Spider's internal metadata once the CSP resource no longer exists.
// @Tags [Cluster Management]
// @Accept json
// @Produce json
@@ -585,6 +585,46 @@ func DeleteCluster(c echo.Context) error {
return c.JSON(http.StatusOK, &resultInfo)
}
+// finalizeDeleteCluster godoc
+// @ID finalize-delete-cluster
+// @Summary Finalize Delete Cluster
+// @Description Finalize the deletion of a Cluster by removing its Spider meta information.
+// @Description This API only succeeds when the Cluster no longer exists on the CSP.
+// @Description Use this after DeleteCluster to clean up Spider's internal metadata.
+// @Tags [Cluster Management]
+// @Accept json
+// @Produce json
+// @Param ConnectionRequest body restruntime.ConnectionRequest true "Request body for finalizing Cluster deletion"
+// @Param Name path string true "The name of the Cluster to finalize deletion"
+// @Success 200 {object} BooleanInfo "Result of the finalize delete operation"
+// @Failure 400 {object} SimpleMsg "Bad Request, possibly due to invalid JSON structure or missing fields"
+// @Failure 404 {object} SimpleMsg "Resource Not Found"
+// @Failure 500 {object} SimpleMsg "Internal Server Error"
+// @Router /cluster/{Name}/finalize [delete]
+func FinalizeDeleteCluster(c echo.Context) error {
+ cblog.Info("call FinalizeDeleteCluster()")
+
+ var req ConnectionRequest
+
+ if err := c.Bind(&req); err != nil {
+ return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+
+ clusterName := c.Param("Name")
+
+ // Call common-runtime API
+ result, err := cmrt.FinalizeDeleteCluster(req.ConnectionName, CLUSTER, clusterName)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ }
+
+ resultInfo := BooleanInfo{
+ Result: strconv.FormatBool(result),
+ }
+
+ return c.JSON(http.StatusOK, &resultInfo)
+}
+
// deleteCSPCluster godoc
// @ID delete-csp-cluster
// @Summary Delete CSP Cluster
diff --git a/api-runtime/rest-runtime/admin-web/html/cluster.html b/api-runtime/rest-runtime/admin-web/html/cluster.html
index e806649b0..e3842e53f 100644
--- a/api-runtime/rest-runtime/admin-web/html/cluster.html
+++ b/api-runtime/rest-runtime/admin-web/html/cluster.html
@@ -630,8 +630,11 @@
Cluster Management
-
- Delete
+
+ Delete
+ |
+
+ Finalize
@@ -655,7 +658,9 @@ Cluster Management
NodeGroups Info
Tags
Misc
-
+
+
+
{{range $index, $cluster := .Clusters}}
@@ -666,11 +671,11 @@ Cluster Management
{{$cluster.IId.NameId}} • {{$cluster.IId.SystemId}}
- {{$cluster.Version}}
+ {{if ne (printf "%s" $cluster.Status) "NotFound"}}{{$cluster.Version}}{{end}}
{{$cluster.Status}}
- {{if $cluster.Status}}
+ {{if and $cluster.Status (ne (printf "%s" $cluster.Status) "NotFound")}}
- {{if $cluster.Status}}
+ {{if and $cluster.Status (ne (printf "%s" $cluster.Status) "NotFound")}}
{{if $cluster.Addons.KeyValueList}}
{{len $cluster.Addons.KeyValueList}} items
View Details
@@ -694,7 +699,7 @@ Cluster Management
- {{if $cluster.Status}}
+ {{if and $cluster.Status (ne (printf "%s" $cluster.Status) "NotFound")}}
- {{if $cluster.Status}}
+ {{if and $cluster.Status (ne (printf "%s" $cluster.Status) "NotFound")}}
Name
@@ -731,7 +736,7 @@ Cluster Management
- {{if $cluster.Status}}
+ {{if and $cluster.Status (ne (printf "%s" $cluster.Status) "NotFound")}}
{{range $tag := $cluster.TagList}}
{{$tag.Key}}: {{$tag.Value}}
{{end}}
@@ -742,6 +747,7 @@ Cluster Management
+ {{if ne (printf "%s" $cluster.Status) "NotFound"}}
{{range $kv := $cluster.KeyValueList}}
{{$kv.Key}}: {{$kv.Value}}
@@ -750,9 +756,10 @@
Cluster Management
{{if $cluster.KeyValueList}}
more...
{{end}}
+ {{end}}
-
+
{{end}}
{{if not .Clusters}}
@@ -1107,12 +1114,155 @@ System ID (Managed by CSP)
}
- function toggleSelectAll(selectAllCheckbox) {
+ function toggleSelectAllDelete(selectAllCheckbox) {
+ // Select only non-NotFound clusters
+ clearAllSelections();
const checkboxes = document.querySelectorAll('input[name="deleteCheckbox"]');
-
checkboxes.forEach((checkbox) => {
- checkbox.checked = selectAllCheckbox.checked;
+ if (checkbox.dataset.status !== 'NotFound') {
+ checkbox.checked = selectAllCheckbox.checked;
+ } else {
+ checkbox.checked = false;
+ }
});
+ // Uncheck the finalize select-all
+ document.getElementById('selectAllFinalize').checked = false;
+ document.getElementById('selectAllColumn').checked = false;
+ updateCheckboxStates();
+ }
+
+ function toggleSelectAllFinalize(selectAllCheckbox) {
+ // Select only NotFound clusters
+ clearAllSelections();
+ const checkboxes = document.querySelectorAll('input[name="deleteCheckbox"]');
+ checkboxes.forEach((checkbox) => {
+ if (checkbox.dataset.status === 'NotFound') {
+ checkbox.checked = selectAllCheckbox.checked;
+ } else {
+ checkbox.checked = false;
+ }
+ });
+ // Uncheck the delete select-all
+ document.getElementById('selectAllDelete').checked = false;
+ document.getElementById('selectAllColumn').checked = false;
+ updateCheckboxStates();
+ }
+
+ function toggleSelectAllColumn(selectAllCheckbox) {
+ // Determine current selection mode from already checked items
+ const checked = document.querySelectorAll('input[name="deleteCheckbox"]:checked');
+ if (checked.length === 0) {
+ // No selection yet: select all non-NotFound (delete mode) by default
+ const checkboxes = document.querySelectorAll('input[name="deleteCheckbox"]');
+ checkboxes.forEach((checkbox) => {
+ if (checkbox.dataset.status !== 'NotFound') {
+ checkbox.checked = selectAllCheckbox.checked;
+ }
+ });
+ } else {
+ // Follow current mode
+ const mode = getSelectionMode();
+ const checkboxes = document.querySelectorAll('input[name="deleteCheckbox"]');
+ checkboxes.forEach((checkbox) => {
+ if (mode === 'notfound' && checkbox.dataset.status === 'NotFound') {
+ checkbox.checked = selectAllCheckbox.checked;
+ } else if (mode === 'normal' && checkbox.dataset.status !== 'NotFound') {
+ checkbox.checked = selectAllCheckbox.checked;
+ }
+ });
+ }
+ updateCheckboxStates();
+ }
+
+ function clearAllSelections() {
+ const checkboxes = document.querySelectorAll('input[name="deleteCheckbox"]');
+ checkboxes.forEach((checkbox) => {
+ checkbox.checked = false;
+ });
+ }
+
+ // Returns 'notfound', 'normal', or null
+ function getSelectionMode() {
+ const checked = document.querySelectorAll('input[name="deleteCheckbox"]:checked');
+ if (checked.length === 0) return null;
+ const firstStatus = checked[0].dataset.status;
+ return firstStatus === 'NotFound' ? 'notfound' : 'normal';
+ }
+
+ function onClusterCheckboxChange(changedCheckbox) {
+ updateCheckboxStates();
+ }
+
+ function updateCheckboxStates() {
+ const mode = getSelectionMode();
+ const checkboxes = document.querySelectorAll('input[name="deleteCheckbox"]');
+ const btnDelete = document.getElementById('btnDelete');
+ const btnFinalize = document.getElementById('btnFinalize');
+
+ checkboxes.forEach((checkbox) => {
+ const isNotFound = checkbox.dataset.status === 'NotFound';
+ const row = checkbox.closest('tr');
+
+ if (mode === null) {
+ // No selection: all enabled
+ checkbox.disabled = false;
+ row.style.opacity = '1';
+ row.title = '';
+ } else if (mode === 'notfound') {
+ if (isNotFound) {
+ checkbox.disabled = false;
+ row.style.opacity = '1';
+ row.title = '';
+ } else {
+ checkbox.disabled = true;
+ checkbox.checked = false;
+ row.style.opacity = '0.45';
+ row.title = 'Cannot mix: NotFound clusters are selected. Deselect them first to select active clusters.';
+ }
+ } else {
+ // mode === 'normal'
+ if (!isNotFound) {
+ checkbox.disabled = false;
+ row.style.opacity = '1';
+ row.title = '';
+ } else {
+ checkbox.disabled = true;
+ checkbox.checked = false;
+ row.style.opacity = '0.45';
+ row.title = 'Cannot mix: Active clusters are selected. Deselect them first to select NotFound clusters.';
+ }
+ }
+ });
+
+ // Enable/disable header buttons based on selection mode
+ if (mode === 'notfound') {
+ btnDelete.disabled = true;
+ btnDelete.style.opacity = '0.4';
+ btnDelete.style.cursor = 'not-allowed';
+ btnFinalize.disabled = false;
+ btnFinalize.style.opacity = '1';
+ btnFinalize.style.cursor = 'pointer';
+ } else if (mode === 'normal') {
+ btnDelete.disabled = false;
+ btnDelete.style.opacity = '1';
+ btnDelete.style.cursor = 'pointer';
+ btnFinalize.disabled = true;
+ btnFinalize.style.opacity = '0.4';
+ btnFinalize.style.cursor = 'not-allowed';
+ } else {
+ // No selection: both enabled
+ btnDelete.disabled = false;
+ btnDelete.style.opacity = '1';
+ btnDelete.style.cursor = 'pointer';
+ btnFinalize.disabled = false;
+ btnFinalize.style.opacity = '1';
+ btnFinalize.style.cursor = 'pointer';
+ }
+ }
+
+ // Legacy wrapper kept for table header checkbox
+ function toggleSelectAll(selectAllCheckbox) {
+ toggleSelectAllColumn(selectAllCheckbox);
}
@@ -1151,6 +1301,41 @@ System ID (Managed by CSP)
});
}
+ function finalizeSelectedClusters() {
+ const checkboxes = document.querySelectorAll('input[name="deleteCheckbox"]:checked');
+ if (checkboxes.length === 0) {
+ alert("Please select clusters to finalize.");
+ return;
+ }
+
+ if (!confirm("Are you sure you want to finalize the selected clusters?\n\nThis will remove Spider meta information only if the cluster no longer exists on the CSP.")) {
+ return;
+ }
+
+ const finalizePromises = Array.from(checkboxes).map(checkbox => {
+ const clusterName = checkbox.value;
+
+ return fetchWithProgress(`/spider/cluster/${clusterName}/finalize`, {
+ method: 'DELETE',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ ConnectionName: "{{.ConnectionConfig}}" })
+ }).then(response => {
+ if (!response.ok) {
+ return response.json().then(error => { throw new Error(error.message); });
+ }
+ return response.json();
+ });
+ });
+
+ Promise.all(finalizePromises)
+ .then(() => {
+ location.reload();
+ })
+ .catch(error => {
+ alert("Error finalizing clusters: " + error.message);
+ });
+ }
+
function showClusterTagOverlay(event, tag, resourceType, resourceName) {
event.stopPropagation();
@@ -1377,7 +1562,7 @@ System ID (Managed by CSP)
switch (providerName.toUpperCase()) {
case 'AZURE':
- versionInput.value = '1.30.3';
+ versionInput.value = '1.34.3';
vmSpecInput.value = 'Standard_B2s';
break;
case 'NHNCLOUD':
@@ -1542,7 +1727,7 @@ System ID (Managed by CSP)
function showError(message, title) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
- const formattedMessage = message.split('.').join('. ');
+ const formattedMessage = message.split('. ').join('. ');
errorDiv.innerHTML = `${title}
${formattedMessage}
OK `;
document.body.appendChild(errorDiv);
document.addEventListener('keydown', handleEscError);
diff --git a/api-runtime/rest-runtime/admin-web/html/vm.html b/api-runtime/rest-runtime/admin-web/html/vm.html
index db6a6a53a..3a5631e52 100644
--- a/api-runtime/rest-runtime/admin-web/html/vm.html
+++ b/api-runtime/rest-runtime/admin-web/html/vm.html
@@ -3866,12 +3866,6 @@ MC-Insight API Token Req
// Get current connection info and set filters
getConnectionInfo().then(info => {
- if (!info.zone) {
- alert('Cannot determine Zone from selected Subnet. Please select a valid Subnet.');
- closeImageSelectionOverlay();
- return;
- }
-
// For OpenStack and MOCK, skip MC-Insight token check and load filters directly
if (info.csp.toLowerCase() === 'openstack' || info.csp.toLowerCase() === 'mock') {
loadImageFilterOptions(info.csp, info.region, info.zone);
@@ -4042,6 +4036,7 @@ MC-Insight API Token Req
// Populate Region/Zone (cloud_hierarchy)
if (data.cloud_hierarchy && data.cloud_hierarchy[actualCsp]) {
const regions = Object.keys(data.cloud_hierarchy[actualCsp]);
+ let regionFound = false;
regions.forEach(region => {
const option = document.createElement('option');
@@ -4049,6 +4044,7 @@ MC-Insight API Token Req
option.textContent = region;
if (region === currentRegion) {
option.selected = true;
+ regionFound = true;
}
regionSelect.appendChild(option);
});
@@ -4056,6 +4052,10 @@ MC-Insight API Token Req
regionSelect.disabled = true;
regionSelect.style.backgroundColor = '#f0f0f0';
+ if (!regionFound) {
+ alert(`MC-Insight does not have image data for the current region: ${currentRegion}`);
+ }
+
const zones = data.cloud_hierarchy[actualCsp][currentRegion];
if (zones && zones.length > 0) {
@@ -4767,12 +4767,6 @@ MC-Insight API Token Req
// Get current connection info and set filters
getConnectionInfo().then(info => {
- if (!info.zone) {
- alert('Cannot determine Zone from selected Subnet. Please select a valid Subnet.');
- closeSpecSelectionOverlay();
- return;
- }
-
// For OpenStack and MOCK, skip MC-Insight token check and load filters directly
if (info.csp.toLowerCase() === 'openstack' || info.csp.toLowerCase() === 'mock') {
loadSpecFilterOptions(info.csp, info.region, info.zone);
@@ -5264,6 +5258,7 @@ MC-Insight API Token Req
// Populate Region/Zone (cloud_hierarchy)
if (data.cloud_hierarchy && data.cloud_hierarchy[actualCsp]) {
const regions = Object.keys(data.cloud_hierarchy[actualCsp]);
+ let regionFound = false;
regions.forEach(region => {
const option = document.createElement('option');
@@ -5271,6 +5266,7 @@ MC-Insight API Token Req
option.textContent = region;
if (region === currentRegion) {
option.selected = true;
+ regionFound = true;
}
regionSelect.appendChild(option);
});
@@ -5278,6 +5274,10 @@ MC-Insight API Token Req
regionSelect.disabled = true;
regionSelect.style.backgroundColor = '#f0f0f0';
+ if (!regionFound) {
+ alert(`MC-Insight does not have spec data for the current region: ${currentRegion}`);
+ }
+
const zones = data.cloud_hierarchy[actualCsp][currentRegion];
if (zones && zones.length > 0) {
diff --git a/api-runtime/rest-runtime/admin-web/html/vpc-subnet.html b/api-runtime/rest-runtime/admin-web/html/vpc-subnet.html
index 30b054f96..1fe3ca879 100644
--- a/api-runtime/rest-runtime/admin-web/html/vpc-subnet.html
+++ b/api-runtime/rest-runtime/admin-web/html/vpc-subnet.html
@@ -1050,10 +1050,11 @@ System ID (Managed by CSP)
const vpcCIDR = document.getElementById('vpcCIDR').value;
const subnetName = document.getElementById('subnetName').value;
const subnetCIDR = document.getElementById('subnetCIDR').value;
- const subnetZone = document.getElementById('subnetZone').value;
+ const subnetZoneEl = document.getElementById('subnetZone');
+ const subnetZone = subnetZoneEl.value;
const vpcCount = document.getElementById('vpcCount').value;
- if (!vpcName || !vpcCIDR || !subnetName || !subnetCIDR || !subnetZone || !vpcCount) {
+ if (!vpcName || !vpcCIDR || !subnetName || !subnetCIDR || !vpcCount || (subnetZoneEl.hasAttribute('required') && !subnetZone)) {
alert("Please fill in all the fields.");
return false;
}
@@ -1875,7 +1876,18 @@ System ID (Managed by CSP)
.then(response => response.json())
.then(data => {
zoneSelect.innerHTML = '';
- data.ZoneList.forEach(zone => {
+ const zones = data.ZoneList || [];
+ if (zones.length === 0) {
+ zoneSelect.removeAttribute('required');
+ const option = document.createElement('option');
+ option.value = '';
+ option.textContent = 'No Zone Available';
+ option.selected = true;
+ zoneSelect.appendChild(option);
+ return;
+ }
+ zoneSelect.setAttribute('required', 'required');
+ zones.forEach(zone => {
const option = document.createElement('option');
option.value = zone.Name;
option.textContent = `${zone.Name} (${zone.DisplayName})`;
diff --git a/api/docs.go b/api/docs.go
index 17ae4aba3..d496e4196 100644
--- a/api/docs.go
+++ b/api/docs.go
@@ -1174,7 +1174,7 @@ const docTemplate = `{
}
},
"delete": {
- "description": "Delete a specified Cluster.",
+ "description": "Delete a specified Cluster from the CSP. \u003cbr\u003e This API only deletes the CSP resource and does not remove Spider meta information. \u003cbr\u003e After deletion, call **DELETE /cluster/{Name}/finalize** to clean up Spider's internal metadata once the CSP resource no longer exists.",
"consumes": [
"application/json"
],
@@ -1238,6 +1238,66 @@ const docTemplate = `{
}
}
},
+ "/cluster/{Name}/finalize": {
+ "delete": {
+ "description": "Finalize the deletion of a Cluster by removing its Spider meta information.\nThis API only succeeds when the Cluster no longer exists on the CSP.\nUse this after DeleteCluster to clean up Spider's internal metadata.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "[Cluster Management]"
+ ],
+ "summary": "Finalize Delete Cluster",
+ "operationId": "finalize-delete-cluster",
+ "parameters": [
+ {
+ "description": "Request body for finalizing Cluster deletion",
+ "name": "ConnectionRequest",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/spider.ConnectionRequest"
+ }
+ },
+ {
+ "type": "string",
+ "description": "The name of the Cluster to finalize deletion",
+ "name": "Name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Result of the finalize delete operation",
+ "schema": {
+ "$ref": "#/definitions/spider.BooleanInfo"
+ }
+ },
+ "400": {
+ "description": "Bad Request, possibly due to invalid JSON structure or missing fields",
+ "schema": {
+ "$ref": "#/definitions/spider.SimpleMsg"
+ }
+ },
+ "404": {
+ "description": "Resource Not Found",
+ "schema": {
+ "$ref": "#/definitions/spider.SimpleMsg"
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "schema": {
+ "$ref": "#/definitions/spider.SimpleMsg"
+ }
+ }
+ }
+ }
+ },
"/cluster/{Name}/nodegroup": {
"post": {
"description": "Add a new Node Group to an existing Cluster.",
@@ -11315,14 +11375,16 @@ const docTemplate = `{
"Active",
"Inactive",
"Updating",
- "Deleting"
+ "Deleting",
+ "NotFound"
],
"x-enum-varnames": [
"ClusterCreating",
"ClusterActive",
"ClusterInactive",
"ClusterUpdating",
- "ClusterDeleting"
+ "ClusterDeleting",
+ "ClusterNotFound"
]
},
"spider.CronSchedule": {
diff --git a/api/swagger.json b/api/swagger.json
index b1ff1d94d..010bdd21a 100644
--- a/api/swagger.json
+++ b/api/swagger.json
@@ -1171,7 +1171,7 @@
}
},
"delete": {
- "description": "Delete a specified Cluster.",
+ "description": "Delete a specified Cluster from the CSP. \u003cbr\u003e This API only deletes the CSP resource and does not remove Spider meta information. \u003cbr\u003e After deletion, call **DELETE /cluster/{Name}/finalize** to clean up Spider's internal metadata once the CSP resource no longer exists.",
"consumes": [
"application/json"
],
@@ -1235,6 +1235,66 @@
}
}
},
+ "/cluster/{Name}/finalize": {
+ "delete": {
+ "description": "Finalize the deletion of a Cluster by removing its Spider meta information.\nThis API only succeeds when the Cluster no longer exists on the CSP.\nUse this after DeleteCluster to clean up Spider's internal metadata.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "[Cluster Management]"
+ ],
+ "summary": "Finalize Delete Cluster",
+ "operationId": "finalize-delete-cluster",
+ "parameters": [
+ {
+ "description": "Request body for finalizing Cluster deletion",
+ "name": "ConnectionRequest",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/spider.ConnectionRequest"
+ }
+ },
+ {
+ "type": "string",
+ "description": "The name of the Cluster to finalize deletion",
+ "name": "Name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Result of the finalize delete operation",
+ "schema": {
+ "$ref": "#/definitions/spider.BooleanInfo"
+ }
+ },
+ "400": {
+ "description": "Bad Request, possibly due to invalid JSON structure or missing fields",
+ "schema": {
+ "$ref": "#/definitions/spider.SimpleMsg"
+ }
+ },
+ "404": {
+ "description": "Resource Not Found",
+ "schema": {
+ "$ref": "#/definitions/spider.SimpleMsg"
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "schema": {
+ "$ref": "#/definitions/spider.SimpleMsg"
+ }
+ }
+ }
+ }
+ },
"/cluster/{Name}/nodegroup": {
"post": {
"description": "Add a new Node Group to an existing Cluster.",
@@ -11312,14 +11372,16 @@
"Active",
"Inactive",
"Updating",
- "Deleting"
+ "Deleting",
+ "NotFound"
],
"x-enum-varnames": [
"ClusterCreating",
"ClusterActive",
"ClusterInactive",
"ClusterUpdating",
- "ClusterDeleting"
+ "ClusterDeleting",
+ "ClusterNotFound"
]
},
"spider.CronSchedule": {
diff --git a/api/swagger.yaml b/api/swagger.yaml
index 70a9fbc44..5dc5559cf 100644
--- a/api/swagger.yaml
+++ b/api/swagger.yaml
@@ -316,6 +316,7 @@ definitions:
- Inactive
- Updating
- Deleting
+ - NotFound
type: string
x-enum-varnames:
- ClusterCreating
@@ -323,6 +324,7 @@ definitions:
- ClusterInactive
- ClusterUpdating
- ClusterDeleting
+ - ClusterNotFound
spider.CronSchedule:
properties:
DayOfMonth:
@@ -4245,7 +4247,10 @@ paths:
delete:
consumes:
- application/json
- description: Delete a specified Cluster.
+ description: Delete a specified Cluster from the CSP. This API only deletes
+ the CSP resource and does not remove Spider meta information. After deletion,
+ call **DELETE /cluster/{Name}/finalize** to clean up Spider's internal metadata
+ once the CSP resource no longer exists.
operationId: delete-cluster
parameters:
- description: Request body for deleting a Cluster
@@ -4332,6 +4337,50 @@ paths:
summary: Get Cluster
tags:
- '[Cluster Management]'
+ /cluster/{Name}/finalize:
+ delete:
+ consumes:
+ - application/json
+ description: |-
+ Finalize the deletion of a Cluster by removing its Spider meta information.
+ This API only succeeds when the Cluster no longer exists on the CSP.
+ Use this after DeleteCluster to clean up Spider's internal metadata.
+ operationId: finalize-delete-cluster
+ parameters:
+ - description: Request body for finalizing Cluster deletion
+ in: body
+ name: ConnectionRequest
+ required: true
+ schema:
+ $ref: '#/definitions/spider.ConnectionRequest'
+ - description: The name of the Cluster to finalize deletion
+ in: path
+ name: Name
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: Result of the finalize delete operation
+ schema:
+ $ref: '#/definitions/spider.BooleanInfo'
+ "400":
+ description: Bad Request, possibly due to invalid JSON structure or missing
+ fields
+ schema:
+ $ref: '#/definitions/spider.SimpleMsg'
+ "404":
+ description: Resource Not Found
+ schema:
+ $ref: '#/definitions/spider.SimpleMsg'
+ "500":
+ description: Internal Server Error
+ schema:
+ $ref: '#/definitions/spider.SimpleMsg'
+ summary: Finalize Delete Cluster
+ tags:
+ - '[Cluster Management]'
/cluster/{Name}/nodegroup:
post:
consumes:
diff --git a/cloud-control-manager/cloud-driver/drivers/aws/resources/ClusterHandler.go b/cloud-control-manager/cloud-driver/drivers/aws/resources/ClusterHandler.go
index 40caabbba..efe44b839 100644
--- a/cloud-control-manager/cloud-driver/drivers/aws/resources/ClusterHandler.go
+++ b/cloud-control-manager/cloud-driver/drivers/aws/resources/ClusterHandler.go
@@ -646,7 +646,7 @@ func (ClusterHandler *AwsClusterHandler) GetCluster(clusterIID irs.IID) (irs.Clu
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case eks.ErrCodeResourceNotFoundException:
- cblogger.Error(eks.ErrCodeResourceNotFoundException, aerr.Error())
+ cblogger.Info(eks.ErrCodeResourceNotFoundException, aerr.Error())
case eks.ErrCodeClientException:
cblogger.Error(eks.ErrCodeClientException, aerr.Error())
case eks.ErrCodeServerException:
diff --git a/cloud-control-manager/cloud-driver/drivers/azure/AzureDriver.go b/cloud-control-manager/cloud-driver/drivers/azure/AzureDriver.go
index bab529a48..83d958c6b 100644
--- a/cloud-control-manager/cloud-driver/drivers/azure/AzureDriver.go
+++ b/cloud-control-manager/cloud-driver/drivers/azure/AzureDriver.go
@@ -15,8 +15,6 @@ import (
"os"
"time"
- "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns"
- "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
@@ -26,6 +24,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
+ "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription"
cblogger "github.com/cloud-barista/cb-log"
"github.com/sirupsen/logrus"
@@ -258,10 +257,6 @@ func (driver *AzureDriver) ConnectCloud(connectionInfo idrv.ConnectionInfo) (ico
if err != nil {
return nil, err
}
- Ctx, dnsZoneClient, err := getDnsZoneClient(connectionInfo.CredentialInfo)
- if err != nil {
- return nil, err
- }
Ctx, fileShareClient, err := getFileShareClient(connectionInfo.CredentialInfo)
if err != nil {
return nil, err
@@ -300,7 +295,6 @@ func (driver *AzureDriver) ConnectCloud(connectionInfo idrv.ConnectionInfo) (ico
ResourceGroupsClient: resourceGroupsClient,
TagsClient: tagsClient,
ResourceSKUsClient: resourceSKUsClient,
- DnsZoneClient: dnsZoneClient,
FileShareClient: fileShareClient,
AccountsClient: accountsClient,
}
@@ -698,21 +692,6 @@ func getTagsClient(credential idrv.CredentialInfo) (context.Context, *armresourc
return ctx, tagsClient, nil
}
-func getDnsZoneClient(credential idrv.CredentialInfo) (context.Context, *armdns.ZonesClient, error) {
- cred, err := getCred(credential)
- if err != nil {
- return nil, nil, err
- }
-
- dnsZoneClient, err := armdns.NewZonesClient(credential.SubscriptionId, cred, nil)
- if err != nil {
- return nil, nil, err
- }
-
- ctx, _ := context.WithTimeout(context.Background(), cspTimeout*time.Second)
- return ctx, dnsZoneClient, nil
-}
-
func getFileShareClient(credential idrv.CredentialInfo) (context.Context, *armstorage.FileSharesClient, error) {
cred, err := getCred(credential)
if err != nil {
diff --git a/cloud-control-manager/cloud-driver/drivers/azure/connect/Azure_CloudConnection.go b/cloud-control-manager/cloud-driver/drivers/azure/connect/Azure_CloudConnection.go
index 26e941a8c..e7b87c181 100644
--- a/cloud-control-manager/cloud-driver/drivers/azure/connect/Azure_CloudConnection.go
+++ b/cloud-control-manager/cloud-driver/drivers/azure/connect/Azure_CloudConnection.go
@@ -17,7 +17,6 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6"
- "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
@@ -67,7 +66,6 @@ type AzureCloudConnection struct {
ResourceGroupsClient *armresources.ResourceGroupsClient
ResourceSKUsClient *armcompute.ResourceSKUsClient
TagsClient *armresources.TagsClient
- DnsZoneClient *armdns.ZonesClient
FileShareClient *armstorage.FileSharesClient
AccountsClient *armstorage.AccountsClient
}
@@ -257,7 +255,6 @@ func (cloudConn *AzureCloudConnection) CreateClusterHandler() (irs.ClusterHandle
SecurityRulesClient: cloudConn.SecurityGroupRuleClient,
VirtualMachineSizesClient: cloudConn.VmSpecClient,
SSHPublicKeysClient: cloudConn.SshKeyClient,
- DnsZonesClient: cloudConn.DnsZoneClient,
}
return &clusterHandler, nil
}
diff --git a/cloud-control-manager/cloud-driver/drivers/azure/resources/ClusterHandler.go b/cloud-control-manager/cloud-driver/drivers/azure/resources/ClusterHandler.go
index 2c1253029..e16d8e018 100644
--- a/cloud-control-manager/cloud-driver/drivers/azure/resources/ClusterHandler.go
+++ b/cloud-control-manager/cloud-driver/drivers/azure/resources/ClusterHandler.go
@@ -17,7 +17,7 @@ import (
"sync"
"time"
- "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns"
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6"
@@ -52,7 +52,6 @@ type AzureClusterHandler struct {
SecurityRulesClient *armnetwork.SecurityRulesClient
VirtualMachineSizesClient *armcompute.VirtualMachineSizesClient
SSHPublicKeysClient *armcompute.SSHPublicKeysClient
- DnsZonesClient *armdns.ZonesClient
}
type auth struct {
@@ -201,43 +200,16 @@ func (ac *AzureClusterHandler) CreateCluster(clusterReqInfo irs.ClusterInfo) (in
LoggingError(hiscallInfo, createErr)
return irs.ClusterInfo{}, createErr
}
- defer func() {
- if createErr != nil {
- if err := ac.CleanCluster(clusterReqInfo.IId.NameId); err != nil {
- cblogger.Error(fmt.Sprintf("failed to clean up cluster %q: %s", clusterReqInfo.IId.NameId, err))
- }
- }
- }()
- baseSecurityGroup, err := waitingClusterBaseSecurityGroup(irs.IID{NameId: clusterReqInfo.IId.NameId}, ac.ManagedClustersClient, ac.SecurityGroupsClient, ac.Ctx, ac.CredentialInfo, ac.Region)
- if err != nil {
- createErr = errors.New(fmt.Sprintf("Failed to Create Cluster. err = %s", err))
- cblogger.Error(createErr.Error())
- LoggingError(hiscallInfo, createErr)
- return irs.ClusterInfo{}, createErr
- }
- for _, sg := range clusterReqInfo.Network.SecurityGroupIIDs {
- err = applySecurityGroup(irs.IID{NameId: clusterReqInfo.IId.NameId}, irs.IID{NameId: sg.NameId}, baseSecurityGroup, ac.ManagedClustersClient, ac.SecurityGroupsClient, ac.SecurityRulesClient, ac.Ctx, ac.CredentialInfo, ac.Region)
- if err != nil {
- createErr = errors.New(fmt.Sprintf("Failed to Create Cluster. err = %s", err))
- cblogger.Error(createErr.Error())
- LoggingError(hiscallInfo, createErr)
- return irs.ClusterInfo{}, createErr
- }
- }
- cluster, err := getRawCluster(clusterReqInfo.IId, ac.ManagedClustersClient, ac.Ctx, ac.CredentialInfo, ac.Region)
- if err != nil {
- createErr = errors.New(fmt.Sprintf("Failed to Create Cluster. err = %s", err))
- cblogger.Error(createErr.Error())
- LoggingError(hiscallInfo, createErr)
- return irs.ClusterInfo{}, createErr
- }
- info, err = setterClusterInfo(&cluster, ac.ManagedClustersClient, ac.SecurityGroupsClient, ac.VirtualNetworksClient, ac.AgentPoolsClient, ac.VirtualMachineScaleSetsClient, ac.VirtualMachineScaleSetVMsClient, ac.CredentialInfo, ac.Region, ac.Ctx)
- if err != nil {
- createErr = errors.New(fmt.Sprintf("Failed to Create Cluster. err = %s", err))
- cblogger.Error(createErr.Error())
- LoggingError(hiscallInfo, createErr)
- return irs.ClusterInfo{}, createErr
+
+ // Async: Return immediately with Creating status (like AWS EKS pattern)
+ // The cluster is being provisioned in the background by Azure.
+ info = irs.ClusterInfo{
+ IId: clusterReqInfo.IId,
+ Version: clusterReqInfo.Version,
+ Network: clusterReqInfo.Network,
+ Status: irs.ClusterCreating,
}
+
LoggingInfo(hiscallInfo, start)
return info, nil
}
@@ -281,6 +253,12 @@ func (ac *AzureClusterHandler) GetCluster(clusterIID irs.IID) (info irs.ClusterI
cluster, err := getRawCluster(clusterIID, ac.ManagedClustersClient, ac.Ctx, ac.CredentialInfo, ac.Region)
if err != nil {
+ // Check if the error is ResourceNotFound (e.g., async creation not yet visible)
+ var respErr *azcore.ResponseError
+ if errors.As(err, &respErr) && respErr.ErrorCode == "ResourceNotFound" {
+ cblogger.Infof("Cluster '%s' not found on CSP: %s", clusterIID.NameId, err.Error())
+ return irs.ClusterInfo{}, err
+ }
getErr = errors.New(fmt.Sprintf("Failed to Get Cluster. err = %s", err))
cblogger.Error(getErr.Error())
LoggingError(hiscallInfo, getErr)
@@ -1098,30 +1076,6 @@ func checkValidationNodeGroups(nodeGroups []irs.NodeGroupInfo, virtualMachineSiz
return nil
}
-func (ac *AzureClusterHandler) generateDnsZone(clusterName string) (string, error) {
- rg := ac.Region.Region
- zoneName := fmt.Sprintf("%s.com", clusterName)
- globalLoc := "global"
-
- resp, err := ac.DnsZonesClient.CreateOrUpdate(
- ac.Ctx,
- rg,
- zoneName,
- armdns.Zone{
- Location: &globalLoc,
- },
- nil,
- )
- if err != nil {
- return "", fmt.Errorf("failed to create/update DNS zone %q: %w", zoneName, err)
- }
-
- if resp.Zone.ID == nil {
- return "", fmt.Errorf("DNS zone %q created but returned ID is nil", zoneName)
- }
- return *resp.Zone.ID, nil
-}
-
func checkValidationCreateCluster(clusterReqInfo irs.ClusterInfo, virtualMachineSizesClient *armcompute.VirtualMachineSizesClient, regionInfo idrv.RegionInfo, ctx context.Context) error {
// nodegroup 확인
err := checkValidationNodeGroups(clusterReqInfo.NodeGroupList, virtualMachineSizesClient, regionInfo, ctx)
@@ -1161,13 +1115,6 @@ func createCluster(clusterReqInfo irs.ClusterInfo, ac *AzureClusterHandler) erro
return err
}
- zoneID, err := ac.generateDnsZone(clusterReqInfo.IId.NameId)
- if err != nil {
- return fmt.Errorf("failed to create dns zone: %v", err)
- }
-
- ingressProfile := generateIngressProfile(&zoneID)
-
clusterCreateOpts := armcontainerservice.ManagedCluster{
Location: toStrPtr(ac.Region.Region),
SKU: &armcontainerservice.ManagedClusterSKU{
@@ -1186,7 +1133,6 @@ func createCluster(clusterReqInfo irs.ClusterInfo, ac *AzureClusterHandler) erro
AgentPoolProfiles: agentPoolProfiles,
NetworkProfile: &networkProfile,
LinuxProfile: &linuxProfileSSH,
- IngressProfile: ingressProfile,
},
}
if clusterReqInfo.TagList != nil {
@@ -1208,23 +1154,12 @@ func createCluster(clusterReqInfo irs.ClusterInfo, ac *AzureClusterHandler) erro
}
func (ac *AzureClusterHandler) CleanCluster(clusterName string) error {
- delPoller, err := ac.ManagedClustersClient.BeginDelete(ac.Ctx, ac.Region.Region, clusterName, nil)
+ // Async: Begin delete and return immediately (like AWS EKS pattern).
+ // The cluster deletion is processed in the background by Azure.
+ _, err := ac.ManagedClustersClient.BeginDelete(ac.Ctx, ac.Region.Region, clusterName, nil)
if err != nil {
return fmt.Errorf("failed to begin deleting cluster %q: %w", clusterName, err)
}
- if _, err := delPoller.PollUntilDone(ac.Ctx, nil); err != nil {
- return fmt.Errorf("failed to delete cluster %q: %w", clusterName, err)
- }
-
- zoneName := fmt.Sprintf("%s.com", clusterName)
-
- dnsPoller, err := ac.DnsZonesClient.BeginDelete(ac.Ctx, ac.Region.Region, zoneName, nil)
- if err != nil {
- return fmt.Errorf("failed to begin deleting DNS zone %q: %w", zoneName, err)
- }
- if _, err := dnsPoller.PollUntilDone(ac.Ctx, nil); err != nil {
- return fmt.Errorf("failed to delete DNS zone %q: %w", zoneName, err)
- }
return nil
}
@@ -1402,15 +1337,6 @@ func getSSHKeyIIDByNodeGroups(NodeGroupInfos []irs.NodeGroupInfo) (irs.IID, erro
return *key, nil
}
-func generateIngressProfile(dnsZoneID *string) *armcontainerservice.ManagedClusterIngressProfile {
- return &armcontainerservice.ManagedClusterIngressProfile{
- WebAppRouting: &armcontainerservice.ManagedClusterIngressProfileWebAppRouting{
- Enabled: toBoolPtr(true),
- DNSZoneResourceIDs: []*string{dnsZoneID},
- },
- }
-}
-
func generateManagedClusterLinuxProfileSSH(clusterReqInfo irs.ClusterInfo, sshPublicKeysClient *armcompute.SSHPublicKeysClient, resourceGroup string, ctx context.Context) (armcontainerservice.LinuxProfile, armcompute.SSHPublicKeyResource, error) {
sshkeyId, err := getSSHKeyIIDByNodeGroups(clusterReqInfo.NodeGroupList)
if err != nil {
diff --git a/cloud-control-manager/cloud-driver/interfaces/resources/ClusterHandler.go b/cloud-control-manager/cloud-driver/interfaces/resources/ClusterHandler.go
index f31057bc5..c640b203a 100644
--- a/cloud-control-manager/cloud-driver/interfaces/resources/ClusterHandler.go
+++ b/cloud-control-manager/cloud-driver/interfaces/resources/ClusterHandler.go
@@ -21,6 +21,7 @@ const (
ClusterInactive ClusterStatus = "Inactive"
ClusterUpdating ClusterStatus = "Updating"
ClusterDeleting ClusterStatus = "Deleting"
+ ClusterNotFound ClusterStatus = "NotFound"
)
type NodeGroupStatus string
diff --git a/go.mod b/go.mod
index 6aa0c30ea..caa55215e 100644
--- a/go.mod
+++ b/go.mod
@@ -47,7 +47,6 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery v1.1.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.0.0
- github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0
diff --git a/go.sum b/go.sum
index d8b2d4bf1..d3b6bd0ac 100644
--- a/go.sum
+++ b/go.sum
@@ -77,8 +77,6 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontai
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0/go.mod h1:HcZY0PHPo/7d75p99lB6lK0qYOP4vLRJUBpiehYXtLQ=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.0.0 h1:EK0ZY1qKWzaWyRNFDsrwRfgVBMGbs+m71yie+y11+Tc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.0.0/go.mod h1:drbnYtukMoZqUQq9hJASf41w3RB4VoTJPoPpe+XDHPU=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw=