ste/xfer-deleteBlob.go (83 lines of code) (raw):

package ste import ( "errors" "fmt" "net/http" "strings" "sync" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror" "github.com/Azure/azure-storage-azcopy/v10/common" ) var explainedSkippedRemoveOnce sync.Once func DeleteBlob(jptm IJobPartTransferMgr, pacer pacer) { // If the transfer was cancelled, then reporting transfer as done and increasing the bytestransferred by the size of the source. if jptm.WasCanceled() { jptm.ReportTransferDone() return } // schedule the work as a chunk, so it will run on the main goroutine pool, instead of the // smaller "transfer initiation pool", where this code runs. id := common.NewChunkID(jptm.Info().Source, 0, 0) cf := createChunkFunc(true, jptm, id, func() { doDeleteBlob(jptm) }) jptm.ScheduleChunks(cf) } func doDeleteBlob(jptm IJobPartTransferMgr) { info := jptm.Info() // Internal function which checks the transfer status and logs the msg respectively. // Sets the transfer status and Report Transfer as Done. // Internal function is created to avoid redundancy of the above steps from several places in the api. transferDone := func(status common.TransferStatus, err error) { if status == common.ETransferStatus.Failed() { jptm.LogError(info.Source, "DELETE ERROR ", err) } else if status == common.ETransferStatus.SkippedBlobHasSnapshots() { explainedSkippedRemoveOnce.Do(func() { common.GetLifecycleMgr().Info("Blobs with snapshots are skipped. Please specify the --delete-snapshots flag for alternative behaviors.") }) // log at error level so that it's clear why the transfer was skipped even when the log level is set to error jptm.Log(common.LogError, fmt.Sprintf("DELETE SKIPPED(blob has snapshots): %s", strings.Split(info.Destination, "?")[0])) } else { jptm.Log(common.LogInfo, fmt.Sprintf("DELETE SUCCESSFUL: %s", strings.Split(info.Destination, "?")[0])) } jptm.SetStatus(status) jptm.ReportTransferDone() } s, err := jptm.SrcServiceClient().BlobServiceClient() if err != nil { transferDone(common.ETransferStatus.Failed(), err) return } // note: if deleteSnapshotsOption is 'only', which means deleting all the snapshots but keep the root blob // we still count this delete operation as successful since we accomplished the desired outcome blobClient := s.NewContainerClient(jptm.Info().SrcContainer).NewBlobClient(jptm.Info().SrcFilePath) if jptm.Info().VersionID != "" { blobClient, err = blobClient.WithVersionID(jptm.Info().VersionID) if err != nil { transferDone(common.ETransferStatus.Failed(), err) return } } else if jptm.Info().SnapshotID != "" { blobClient, err = blobClient.WithSnapshot(jptm.Info().SnapshotID) if err != nil { transferDone(common.ETransferStatus.Failed(), err) return } } _, err = blobClient.Delete(jptm.Context(), &blob.DeleteOptions{ DeleteSnapshots: jptm.DeleteSnapshotsOption().ToDeleteSnapshotsOptionType(), BlobDeleteType: jptm.PermanentDeleteOption().ToPermanentDeleteOptionType(), }) if err != nil { var respErr *azcore.ResponseError if errors.As(err, &respErr) { // if the delete failed with err 404, i.e resource not found, then mark the transfer as success. if respErr.StatusCode == http.StatusNotFound { transferDone(common.ETransferStatus.Success(), nil) return } // if the delete failed because the blob has snapshots, then skip it if respErr.StatusCode == http.StatusConflict && respErr.ErrorCode == string(bloberror.SnapshotsPresent) { transferDone(common.ETransferStatus.SkippedBlobHasSnapshots(), nil) return } // If the status code was 403, it means there was an authentication error and we exit. // User can resume the job if completely ordered with a new sas. if respErr.StatusCode == http.StatusForbidden { errMsg := fmt.Sprintf("Authentication Failed. The SAS is not correct or expired or does not have the correct permission %s", err.Error()) jptm.Log(common.LogError, errMsg) common.GetLifecycleMgr().Error(errMsg) } } // in all other cases, make the transfer as failed transferDone(common.ETransferStatus.Failed(), err) } else { transferDone(common.ETransferStatus.Success(), nil) } }