/// <summary> /// Deletes a single file revision from the Azure cloud storage provider. /// </summary> /// <param name="file"><c>BackupFile</c></param> /// <param name="sourceLocation"><c>SourceLocation</c></param> /// <param name="directory"><c>DirectoryMapItem</c></param> /// <param name="cancelToken">The canellation token.</param> public async Task DeleteFileRevisionAsync(BackupFile file, SourceLocation sourceLocation, DirectoryMapItem directory, CancellationToken cancelToken) { int maxAttempts = 3; var sasBlobUri = ProviderUtilities.GetFileUri(AzureStorage.Credentials.AccountName, directory.GetRemoteContainerName(StorageProviderTypes.Azure), file.GetRemoteFileName(StorageProviderTypes.Azure)); for (int currentAttempt = 1; currentAttempt <= maxAttempts; currentAttempt++) { cancelToken.ThrowIfCancellationRequested(); try { CloudBlockBlob blob = new CloudBlockBlob(new Uri(sasBlobUri), AzureStorage.Credentials); await blob.DeleteIfExistsAsync(cancelToken).ConfigureAwait(false); Logger.WriteTraceMessage("File revision successfully cleaned up."); break; } catch (StorageException ex) { if (ex.InnerException != null && ex.InnerException is TimeoutException) { if (currentAttempt == maxAttempts) { // retries exhausted throw; } // special case handling: // a windows azure storage timeout has occurred. give it a minute and try again. Logger.WriteTraceMessage("An Azure storage timeout exception has occurred while trying to check (or delete) the blob. Waiting a minute before trying again."); await Task.Delay(TimeSpan.FromSeconds(60), cancelToken).ConfigureAwait(false); } else { throw; } } } }
/// <summary> /// Uploads a single block of a file to Azure cloud storage. /// </summary> /// <remarks> /// If this file is the only (or final) block in the file, the file should be committed and/or the transaction finalized. /// </remarks> /// <param name="file"><c>BackupFile</c></param> /// <param name="sourceLocation"><c>SourceLocation</c></param> /// <param name="directory"><c>DirectoryMapItem</c></param> /// <param name="data">A byte array stream of file contents/data.</param> /// <param name="currentBlockIndex">The block number associated with the specified data.</param> /// <param name="totalBlocks">The total number of blocks that this file is made of.</param> /// <param name="cancelToken">The cancellation token.</param> public async Task UploadFileBlockAsync(BackupFile file, SourceLocation sourceLocation, DirectoryMapItem directory, byte[] data, int currentBlockIndex, int totalBlocks, CancellationToken cancelToken) { string containerName = null; if (sourceLocation.Priority == FileBackupPriority.Meta) { // special handling for meta/reserved folders containerName = sourceLocation.DestinationContainerName; } else { containerName = directory.GetRemoteContainerName(StorageProviderTypes.Azure); } var containerUri = ProviderUtilities.GetContainerUri(AzureStorage.Credentials.AccountName, containerName); var currentBlockNumber = currentBlockIndex + 1; var isFirstBlock = (currentBlockNumber == 1); var isLastBlock = (currentBlockNumber == totalBlocks); if (isFirstBlock) { await CreateBlobContainerIfMissingWithRetryAsync(containerName, containerUri, directory, cancelToken).ConfigureAwait(false); } cancelToken.ThrowIfCancellationRequested(); string blobName = null; string sasBlobUri = null; if (sourceLocation.Priority == FileBackupPriority.Meta) { // special handling for meta/reserved folders blobName = file.Filename.ToLower(); sasBlobUri = ProviderUtilities.GetFileUri(AzureStorage.Credentials.AccountName, sourceLocation.DestinationContainerName, blobName); } else { blobName = file.GetRemoteFileName(StorageProviderTypes.Azure); sasBlobUri = ProviderUtilities.GetFileUri(AzureStorage.Credentials.AccountName, containerName, blobName); } if (isFirstBlock) { Logger.WriteTraceMessage(string.Format("Azure destination: {0}/{1}", containerName, blobName)); } Logger.WriteTraceMessage(string.Format("Uploading file block ({0} of {1}) to Azure storage.", currentBlockNumber, totalBlocks)); // get a current reference to the blob and it's attributes. // upload the block. CloudBlockBlob blob = new CloudBlockBlob(new Uri(sasBlobUri), AzureStorage.Credentials); await UploadFileBlockWithRetryAsync(file, blob, data, currentBlockIndex, cancelToken).ConfigureAwait(false); // is this the first block or the last block? then run the block commit. // >> we need to commit once at the beginning to create the blob, which allows us to set metadata. // >> we need to commit at the end/final block as well to commit the complete block list. if (isFirstBlock || isLastBlock) { await CommitBlocksWithRetryAsync(file, currentBlockIndex, blob, cancelToken).ConfigureAwait(false); } // set the metadata (all situations). await SetBlobMetadataWithRetryAsync(file, currentBlockIndex, totalBlocks, blob, cancelToken).ConfigureAwait(false); if (isLastBlock) { if (!blob.Properties.StandardBlobTier.HasValue || blob.Properties.StandardBlobTier.Value != StandardBlobTier.Archive) { // set blob tier access. // we only need to set this one time, at the time the upload is completed. if (sourceLocation.Priority == FileBackupPriority.Meta) { await blob.SetStandardBlobTierAsync(StandardBlobTier.Cool, null, MetaDataRequestOptions, null).ConfigureAwait(false); } else { await blob.SetStandardBlobTierAsync(StandardBlobTier.Archive, null, MetaDataRequestOptions, null).ConfigureAwait(false); } } Logger.WriteTraceMessage("File successfully uploaded to Azure storage: " + file.FullSourcePath); } }
/// <summary> /// Returns the status of a file as it exists (or doesn't) in Azure cloud storage. /// </summary> /// <param name="file"><c>BackupFile</c></param> /// <param name="sourceLocation"><c>SourceLocation</c></param> /// <param name="directory"><c>DirectoryMapItem</c></param> /// <returns><c>ProviderFileStatus</c></returns> public async Task <StorageProviderFileStatus> GetFileStatusAsync(BackupFile file, SourceLocation sourceLocation, DirectoryMapItem directory) { // calculate my uri string sasBlobUri = null; if (sourceLocation.Priority == FileBackupPriority.Meta) { // file has a specific destination container. this is reserved for meta folders. // use that specific container and filename instead of a guid-based uri. sasBlobUri = ProviderUtilities.GetFileUri(AzureStorage.Credentials.AccountName, sourceLocation.DestinationContainerName, file.Filename.ToLower()); } else { sasBlobUri = ProviderUtilities.GetFileUri(AzureStorage.Credentials.AccountName, directory.GetRemoteContainerName(StorageProviderTypes.Azure), file.GetRemoteFileName(StorageProviderTypes.Azure)); } // the default state for a freshly initialized file status object is unsynced. // if the blob doesn't exist, the file is unsynced. CloudBlockBlob blob = new CloudBlockBlob(new Uri(sasBlobUri), AzureStorage.Credentials); var fileStatus = new StorageProviderFileStatus(StorageProviderTypes.Azure); // does the file exist at the specified uri? if (await blob.ExistsAsync(MetaDataRequestOptions, null).ConfigureAwait(false)) { // -- query metadata // -- determine state from metadata await blob.FetchAttributesAsync(null, MetaDataRequestOptions, null).ConfigureAwait(false); var allPropsAndMetadata = new Dictionary <string, string>(blob.Metadata); allPropsAndMetadata.Add(ProviderMetadata.HydrationStateKeyName, ProviderUtilities.GetHydrationStatusFromAzureState(blob.Properties.RehydrationStatus)); fileStatus.ApplyMetadataToState(allPropsAndMetadata); } return(fileStatus); }