Beispiel #1
0
 /// <summary>
 /// Generic function to redirect a put request for properties of a blob
 /// </summary>
 public static async Task <HandlerResult> BasicBlobAsync(IHttpRequestWrapper requestWrapper, string container, string blob, bool servePrimaryOnly, bool operationCanReplicateBlob)
 {
     return(await WebOperationRunner.DoHandlerAsync("BlobHandler.BasicBlobAsync", async() =>
     {
         var namespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(container, blob);
         if (!await namespaceBlob.ExistsAsync())
         {
             return new HandlerResult
             {
                 StatusCode = HttpStatusCode.NotFound,
             };
         }
         string accountName = namespaceBlob.SelectDataAccount(servePrimaryOnly);
         if (operationCanReplicateBlob)
         {
             if (namespaceBlob.IsReplicated ||
                 BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, container, blob))
             {
                 accountName = namespaceBlob.PrimaryAccountName;
                 await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false);
             }
         }
         Uri redirect = ControllerOperations.GetRedirectUri(HttpContextFactory.Current.Request,
                                                            DashConfiguration.GetDataAccountByAccountName(accountName),
                                                            namespaceBlob.Container,
                                                            namespaceBlob.BlobName,
                                                            false);
         return HandlerResult.Redirect(requestWrapper, redirect);
     }));
 }
Beispiel #2
0
        public async Task <HttpResponseMessage> DeleteBlob(string container, string blob, string snapshot = null)
        {
            var  requestWrapper  = DashHttpRequestWrapper.Create(this.Request);
            var  headers         = requestWrapper.Headers;
            var  queryParams     = requestWrapper.QueryParameters;
            bool dataBlobDeleted = false;

            return(await DoHandlerAsync(String.Format("BlobController.DeleteBlob: {0}/{1}", container, blob),
                                        async() => await NamespaceHandler.PerformNamespaceOperation(container, blob, async(namespaceBlob) =>
            {
                // We only need to delete the actual blob. We are leaving the namespace entry alone as a sort of cache.
                if (!(await namespaceBlob.ExistsAsync()) || namespaceBlob.IsMarkedForDeletion)
                {
                    return this.CreateResponse(HttpStatusCode.NotFound, headers);
                }
                // Delete the real data blob by forwarding the request onto the data account
                if (!dataBlobDeleted)
                {
                    var forwardedResponse = await ForwardRequestHandler(namespaceBlob, StorageOperationTypes.DeleteBlob);
                    if (!forwardedResponse.IsSuccessStatusCode)
                    {
                        return forwardedResponse;
                    }
                    dataBlobDeleted = true;
                }
                // See if we need to delete any replicas
                if (namespaceBlob.IsReplicated)
                {
                    await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, true, false);
                }
                // Mark the namespace blob for deletion
                await namespaceBlob.MarkForDeletionAsync();
                return this.CreateResponse(HttpStatusCode.Accepted, headers);
            })));
        }
Beispiel #3
0
 private async Task <HttpResponseMessage> PutBlobHandler(string container, string blob, IHttpRequestWrapper requestWrapper, StorageOperationTypes operation)
 {
     return(await DoHandlerAsync(String.Format("BlobController.PutBlobHandler: {0} {1}/{2}", operation, container, blob),
                                 async() =>
     {
         var namespaceBlob = await NamespaceHandler.CreateNamespaceBlobAsync(container, blob);
         if (BlobReplicationOperations.DoesOperationTriggerReplication(operation) &&
             BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, namespaceBlob))
         {
             await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false);
         }
         return await ForwardRequestHandler(namespaceBlob, operation);
     }));
 }
Beispiel #4
0
 /// <summary>
 /// Generic function to forward blob request. Target blob must already exist.
 /// </summary>
 private async Task <HttpResponseMessage> BasicBlobHandler(string container, string blob, IHttpRequestWrapper requestWrapper, StorageOperationTypes operation)
 {
     return(await DoHandlerAsync(String.Format("BlobController.BasicBlobHandler: {0} {1}/{2}", operation, container, blob),
                                 async() =>
     {
         var namespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(container, blob);
         if (!await namespaceBlob.ExistsAsync())
         {
             return this.CreateResponse(HttpStatusCode.NotFound, (RequestHeaders)null);
         }
         if (BlobReplicationOperations.DoesOperationTriggerReplication(operation) &&
             (namespaceBlob.IsReplicated ||
              BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, namespaceBlob)))
         {
             await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false);
         }
         return await ForwardRequestHandler(namespaceBlob, operation);
     }));
 }
Beispiel #5
0
 public static async Task <HandlerResult> PutBlobAsync(IHttpRequestWrapper requestWrapper, string container, string blob, bool operationCanReplicateBlob)
 {
     return(await WebOperationRunner.DoHandlerAsync("BlobHandler.PutBlobAsync", async() =>
     {
         var namespaceBlob = await NamespaceHandler.CreateNamespaceBlobAsync(container, blob);
         if (operationCanReplicateBlob)
         {
             if (BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, container, blob))
             {
                 await BlobReplicationHandler.EnqueueBlobReplicationAsync(namespaceBlob, false);
             }
         }
         Uri redirect = ControllerOperations.GetRedirectUri(HttpContextFactory.Current.Request,
                                                            DashConfiguration.GetDataAccountByAccountName(namespaceBlob.PrimaryAccountName),
                                                            container,
                                                            blob,
                                                            false);
         return HandlerResult.Redirect(requestWrapper, redirect);
     }));
 }
Beispiel #6
0
 public static async Task <HandlerResult> CopyBlobAsync(IHttpRequestWrapper requestWrapper, string destContainer, string destBlob, string source)
 {
     return(await WebOperationRunner.DoHandlerAsync(String.Format("BlobHandler.CopyBlobAsync: {0}/{1} from {2}", destContainer, destBlob, source), async() =>
     {
         // source is a naked URI supplied by client
         Uri sourceUri;
         if (Uri.TryCreate(source, UriKind.RelativeOrAbsolute, out sourceUri))
         {
             string sourceContainer = String.Empty;
             string sourceBlobName = String.Empty;
             string sourceQuery = String.Empty;
             BlobType sourceBlobType = BlobType.BlockBlob;
             var requestVersion = requestWrapper.Headers.Value("x-ms-version", StorageServiceVersions.Version_2009_09_19);
             bool processRelativeSource = false;
             if (!sourceUri.IsAbsoluteUri)
             {
                 if (requestVersion >= StorageServiceVersions.Version_2012_02_12)
                 {
                     // 2012-02-12 onwards doesn't accept relative URIs
                     return new HandlerResult
                     {
                         StatusCode = HttpStatusCode.BadRequest,
                     };
                 }
                 // Make sourceUri absolute here because a bunch of Uri functionality fails for relative URIs
                 sourceUri = new Uri(new Uri("http://dummyhost"), sourceUri);
                 processRelativeSource = true;
             }
             if (processRelativeSource ||
                 (String.Equals(sourceUri.Host, requestWrapper.Url.Host, StringComparison.OrdinalIgnoreCase) &&
                  ((sourceUri.IsDefaultPort && requestWrapper.Url.IsDefaultPort) || (sourceUri.Port == requestWrapper.Url.Port))))
             {
                 var segments = PathUtils.GetPathSegments(sourceUri.AbsolutePath);
                 if (processRelativeSource)
                 {
                     // Blob in named container: /accountName/containerName/blobName
                     // Snapshot in named container: /accountName/containerName/blobName?snapshot=<DateTime>
                     // Blob in root container: /accountName/blobName
                     // Snapshot in root container: /accountName/blobName?snapshot=<DateTime>
                     if (!String.Equals(segments.FirstOrDefault(), DashConfiguration.AccountName))
                     {
                         return new HandlerResult
                         {
                             StatusCode = HttpStatusCode.BadRequest,
                             ErrorInformation = new DashErrorInformation
                             {
                                 ErrorCode = "CopyAcrossAccountsNotSupported",
                                 ErrorMessage = "The copy source account and destination account must be the same.",
                             },
                         };
                     }
                     if (segments.Count() == 2)
                     {
                         sourceContainer = "root";
                         sourceBlobName = segments[1];
                     }
                     else if (segments.Count() > 2)
                     {
                         sourceContainer = segments[1];
                         sourceBlobName = PathUtils.CombinePathSegments(segments.Skip(2));
                     }
                 }
                 else
                 {
                     sourceContainer = segments.FirstOrDefault();
                     sourceBlobName = PathUtils.CombinePathSegments(segments.Skip(1));
                 }
             }
             var destNamespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(destContainer, destBlob);
             string destAccount = String.Empty;
             if (!String.IsNullOrEmpty(sourceContainer) && !String.IsNullOrEmpty(sourceBlobName))
             {
                 var sourceQueryParams = HttpUtility.ParseQueryString(sourceUri.Query);
                 var sourceNamespaceBlob = await NamespaceHandler.FetchNamespaceBlobAsync(sourceContainer, sourceBlobName, sourceQueryParams["snapshot"]);
                 if (!await sourceNamespaceBlob.ExistsAsync())
                 {
                     // This isn't actually documented (what happens when the source doesn't exist), but by obervation the service emits 404
                     return new HandlerResult
                     {
                         StatusCode = HttpStatusCode.NotFound,
                     };
                 }
                 var sourceCloudContainer = NamespaceHandler.GetContainerByName(
                     DashConfiguration.GetDataAccountByAccountName(sourceNamespaceBlob.PrimaryAccountName), sourceContainer);
                 sourceBlobType = sourceCloudContainer.GetBlobReferenceFromServer(sourceBlobName).BlobType;
                 // This is effectively an intra-account copy which is expected to be atomic. Therefore, even if the destination already
                 // exists, we need to place the destination in the same data account as the source.
                 // If the destination blob already exists, we delete it below to prevent an orphaned data blob
                 destAccount = sourceNamespaceBlob.PrimaryAccountName;
                 var sourceUriBuilder = ControllerOperations.GetRedirectUriBuilder("GET",
                                                                                   requestWrapper.Url.Scheme,
                                                                                   DashConfiguration.GetDataAccountByAccountName(destAccount),
                                                                                   sourceContainer,
                                                                                   sourceBlobName,
                                                                                   false,
                                                                                   String.Empty);
                 sourceUri = sourceUriBuilder.Uri;
             }
             else if (await destNamespaceBlob.ExistsAsync())
             {
                 destAccount = destNamespaceBlob.PrimaryAccountName;
             }
             else
             {
                 destAccount = NamespaceHandler.GetDataStorageAccountForBlob(destBlob).Credentials.AccountName;
             }
             bool replicateDestination = false;
             if (await destNamespaceBlob.ExistsAsync() && destNamespaceBlob.PrimaryAccountName != destAccount)
             {
                 // Delete the existing blob to prevent orphaning it
                 replicateDestination = destNamespaceBlob.IsReplicated;
                 var dataBlob = NamespaceHandler.GetBlobByName(
                     DashConfiguration.GetDataAccountByAccountName(destNamespaceBlob.PrimaryAccountName), destContainer, destBlob);
                 await dataBlob.DeleteIfExistsAsync();
             }
             destNamespaceBlob.PrimaryAccountName = destAccount;
             destNamespaceBlob.Container = destContainer;
             destNamespaceBlob.BlobName = destBlob;
             destNamespaceBlob.IsMarkedForDeletion = false;
             await destNamespaceBlob.SaveAsync();
             // Now that we've got the metadata tucked away - do the actual copy
             var destCloudContainer = NamespaceHandler.GetContainerByName(DashConfiguration.GetDataAccountByAccountName(destAccount), destContainer);
             ICloudBlob destCloudBlob = null;
             if (sourceBlobType == BlobType.PageBlob)
             {
                 destCloudBlob = destCloudContainer.GetPageBlobReference(destBlob);
             }
             else
             {
                 destCloudBlob = destCloudContainer.GetBlockBlobReference(destBlob);
             }
             // Storage client will retry failed copy. Let our clients decide that.
             var copyId = await destCloudBlob.StartCopyFromBlobAsync(sourceUri,
                                                                     AccessCondition.GenerateEmptyCondition(),
                                                                     AccessCondition.GenerateEmptyCondition(),
                                                                     new BlobRequestOptions
             {
                 RetryPolicy = new NoRetry(),
             },
                                                                     new OperationContext());
             // Check if we should replicate the copied destination blob
             if (replicateDestination || BlobReplicationHandler.ShouldReplicateBlob(requestWrapper.Headers, destContainer, destBlob))
             {
                 await BlobReplicationHandler.EnqueueBlobReplicationAsync(destNamespaceBlob, false);
             }
             return new HandlerResult
             {
                 StatusCode = requestVersion >= StorageServiceVersions.Version_2012_02_12 ? HttpStatusCode.Accepted : HttpStatusCode.Created,
                 Headers = new ResponseHeaders(new[]
                 {
                     new KeyValuePair <string, string>("x-ms-copy-id", copyId),
                     new KeyValuePair <string, string>("x-ms-copy-status", destCloudBlob.CopyState.Status == CopyStatus.Success ? "success" : "pending"),
                 })
             };
         }
         return new HandlerResult
         {
             StatusCode = HttpStatusCode.BadRequest,
         };
     }));
 }
Beispiel #7
0
        public static bool BeginBlobReplication(string sourceAccount, string destAccount, string container, string blobName, int?waitDelay = null)
        {
            bool       retval   = false;
            ICloudBlob destBlob = null;

            try
            {
                // Process is:
                //  - start the copy
                //  - wait around for a little while to see if it finishes - if so, we're done - update the namespace
                //  - if the copy is still in progress, enqueue a ReplicateProgress message to revisit the progress & update the namespace

                // Attempt to acquire a reference to the specified destination first, because if the source no longer exists we must cleanup this orphaned replica
                var destContainer = DashConfiguration.GetDataAccountByAccountName(destAccount).CreateCloudBlobClient().GetContainerReference(container);
                try
                {
                    destBlob = destContainer.GetBlobReferenceFromServer(blobName);
                }
                catch
                {
                    destBlob = null;
                }
                var sourceClient = DashConfiguration.GetDataAccountByAccountName(sourceAccount).CreateCloudBlobClient();
                var sourceBlob   = sourceClient.GetContainerReference(container).GetBlobReferenceFromServer(blobName);
                if (destBlob == null)
                {
                    if (sourceBlob.BlobType == BlobType.PageBlob)
                    {
                        destBlob = destContainer.GetPageBlobReference(blobName);
                    }
                    else
                    {
                        destBlob = destContainer.GetBlockBlobReference(blobName);
                    }
                }
                DashTrace.TraceInformation("Replicating blob [{0}] to account [{1}]", sourceBlob.Uri, destAccount);
                // If the source is still being copied to (we kicked off the replication as the result of a copy operation), then just recycle
                // this message (don't return false as that may ultimately cause us to give up replicating).
                if (sourceBlob.CopyState != null && sourceBlob.CopyState.Status == CopyStatus.Pending)
                {
                    DashTrace.TraceInformation("Waiting for replication source [{0}] to complete copy.", sourceBlob.Uri);
                    new AzureMessageQueue().Enqueue(
                        BlobReplicationHandler.ConstructReplicationMessage(
                            false,
                            sourceAccount,
                            destAccount,
                            container,
                            blobName,
                            String.Empty));
                    return(true);
                }
                var sasUri = new UriBuilder(sourceBlob.Uri);
                sasUri.Query = sourceBlob.GetSharedAccessSignature(new SharedAccessBlobPolicy
                {
                    SharedAccessStartTime  = DateTime.UtcNow.AddMinutes(-5),
                    SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(60),
                    Permissions            = SharedAccessBlobPermissions.Read,
                }).TrimStart('?');
                string   copyId = destBlob.StartCopyFromBlob(sasUri.Uri);
                DateTime waitForCompleteGiveUp = DateTime.UtcNow.AddSeconds(waitDelay ?? (DashConfiguration.AsyncWorkerTimeout / 2));
                while (DateTime.UtcNow < waitForCompleteGiveUp)
                {
                    destBlob.FetchAttributes();
                    if (destBlob.CopyState.CopyId != copyId || destBlob.CopyState.Status != CopyStatus.Pending)
                    {
                        break;
                    }
                    Thread.Sleep(1000);
                }
                retval = ProcessBlobCopyStatus(destBlob, sourceAccount, copyId, waitDelay);
            }
            catch (StorageException ex)
            {
                // Classify the errors into retryable & non-retryable
                retval = !RecoverReplicationError(ex, destBlob,
                                                  String.Format("Storage error initiating replication for blob [{0}][{1}] to account [{2}]. Details: {3}",
                                                                sourceAccount,
                                                                PathUtils.CombineContainerAndBlob(container, blobName),
                                                                destAccount,
                                                                ex));
            }
            catch (Exception ex)
            {
                DashTrace.TraceWarning("Error initiating replication for blob [{0}][{1}] to account [{2}]. Details: {3}",
                                       sourceAccount,
                                       PathUtils.CombineContainerAndBlob(container, blobName),
                                       destAccount,
                                       ex);
            }
            return(retval);
        }