diff --git a/src/core/infra/provisioning.go b/src/core/infra/provisioning.go index 8e88fabb..b6cc1de4 100644 --- a/src/core/infra/provisioning.go +++ b/src/core/infra/provisioning.go @@ -957,30 +957,62 @@ func CreateMciDynamic(reqID string, nsId string, req *model.TbMciDynamicReq, dep } //If not, generate default resources dynamically. + // Parallel processing of VM requests + var wg sync.WaitGroup + var mutex sync.Mutex + errChan := make(chan error, len(vmRequest)) // Error channel to collect errors + for _, k := range vmRequest { - vmReq, err := getVmReqFromDynamicReq(reqID, nsId, &k) + wg.Add(1) + + // Launch a goroutine for each VM request + go func(vmReq model.TbVmDynamicReq) { + defer wg.Done() + + req, err := getVmReqFromDynamicReq(reqID, nsId, &vmReq) + if err != nil { + log.Error().Err(err).Msg("Failed to prepare resources for dynamic MCI creation") + errChan <- err + return + } + + // Safely append to the shared mciReq.Vm slice + mutex.Lock() + mciReq.Vm = append(mciReq.Vm, *req) + mutex.Unlock() + }(k) + } + + // Wait for all goroutines to complete + wg.Wait() + close(errChan) // Close the error channel after processing + + // Check for any errors from the goroutines + for err := range errChan { if err != nil { - log.Error().Err(err).Msg("Failed to prefare resources for dynamic MCI creation") - // Rollback created default resources + // Rollback if any error occurs + log.Info().Msg("Rolling back created default resources") time.Sleep(5 * time.Second) - log.Info().Msg("Try rollback created default resources") - rollbackResult, rollbackErr := resource.DelAllSharedResources(nsId) - if rollbackErr != nil { - err = fmt.Errorf("Failed in rollback operation: %w", rollbackErr) + if rollbackResult, rollbackErr := resource.DelAllSharedResources(nsId); rollbackErr != nil { + return emptyMci, fmt.Errorf("failed in rollback operation: %w", rollbackErr) } else { ids := strings.Join(rollbackResult.IdList, ", ") - err = fmt.Errorf("Rollback results [%s]: %w", ids, err) + return emptyMci, fmt.Errorf("rollback results [%s]: %w", ids, err) } - return emptyMci, err } - mciReq.Vm = append(mciReq.Vm, *vmReq) } + // Log the prepared MCI request and update the progress common.PrintJsonPretty(mciReq) - common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Prepared all resources for provisioning MCI:" + mciReq.Name, Info: mciReq, Time: time.Now()}) - common.UpdateRequestProgress(reqID, common.ProgressInfo{Title: "Start provisioning", Time: time.Now()}) - - // Run create MCI with the generated MCI request (option != register) + common.UpdateRequestProgress(reqID, common.ProgressInfo{ + Title: "Prepared all resources for provisioning MCI: " + mciReq.Name, + Info: mciReq, Time: time.Now(), + }) + common.UpdateRequestProgress(reqID, common.ProgressInfo{ + Title: "Start instance provisioning", Time: time.Now(), + }) + + // Run create MCI with the generated MCI request option := "create" if deployOption == "hold" { option = "hold" diff --git a/src/core/resource/common.go b/src/core/resource/common.go index 1f14ecb4..adbbff6e 100644 --- a/src/core/resource/common.go +++ b/src/core/resource/common.go @@ -71,11 +71,11 @@ func init() { validate.RegisterStructValidation(TbVNetReqStructLevelValidation, model.TbVNetReq{}) } -// DelAllResources deletes all TB Resource object of given resourceType +// DelAllResources deletes all TB Resource objects of the given resourceType. func DelAllResources(nsId string, resourceType string, subString string, forceFlag string) (model.IdList, error) { - deletedResources := model.IdList{} - deleteStatus := "" + var mutex sync.Mutex // Protect shared slice access + var wg sync.WaitGroup // Synchronize all goroutines err := common.CheckString(nsId) if err != nil { @@ -89,27 +89,59 @@ func DelAllResources(nsId string, resourceType string, subString string, forceFl } if len(resourceIdList) == 0 { - errString := "There is no " + resourceType + " resource in " + nsId + errString := fmt.Sprintf("There is no %s resource in %s", resourceType, nsId) err := fmt.Errorf(errString) log.Error().Err(err).Msg("") return deletedResources, err } + // Channel to capture errors + errChan := make(chan error, len(resourceIdList)) + + // Process each resourceId concurrently for _, v := range resourceIdList { - // if subString is provided, check the resourceId contains the subString. - if subString == "" || strings.Contains(v, subString) { + // Increment WaitGroup counter + wg.Add(1) + + // Launch a goroutine for each resource deletion + go func(resourceId string) { + defer wg.Done() + common.RandomSleep(0, len(resourceIdList)/10) + + // Check if the resourceId matches the subString criteria + if subString != "" && !strings.Contains(resourceId, subString) { + return + } - deleteStatus = "[Done] " + // Attempt to delete the resource + deleteStatus := "[Done] " errString := "" - err := DelResource(nsId, resourceType, v, forceFlag) + err := DelResource(nsId, resourceType, resourceId, forceFlag) if err != nil { deleteStatus = "[Failed] " errString = " (" + err.Error() + ")" + errChan <- err // Send error to the error channel } - deletedResources.IdList = append(deletedResources.IdList, deleteStatus+resourceType+": "+v+errString) + + // Safely append the result to deletedResources.IdList using mutex + mutex.Lock() + deletedResources.IdList = append(deletedResources.IdList, deleteStatus+resourceType+": "+resourceId+errString) + mutex.Unlock() + }(v) // Pass loop variable as an argument to avoid race conditions + } + + // Wait for all goroutines to complete + wg.Wait() + close(errChan) // Close the error channel + + // Collect any errors from the error channel + for err := range errChan { + if err != nil { + log.Info().Err(err).Msg("error deleting resource") } } + return deletedResources, nil }