Esempio n. 1
0
        public static void ProcessMessageLoop(ref int msgProcessed, ref int msgErrors, int?invisibilityTimeout = null)
        {
            IMessageQueue queue = new AzureMessageQueue();

            while (true)
            {
                try
                {
                    QueueMessage payload = queue.Dequeue(invisibilityTimeout);
                    if (payload == null)
                    {
                        break;
                    }
                    // Right now, success/failure is indicated through a bool
                    // Do we want to surround this with a try/catch and use exceptions instead?
                    if (ProcessMessage(payload, invisibilityTimeout))
                    {
                        queue.DeleteCurrentMessage();
                        msgProcessed++;
                    }
                    else
                    {
                        // Leave it in the queue for retry after invisibility period expires
                        msgErrors++;
                    }
                }
                catch (Exception ex)
                {
                    DashTrace.TraceWarning("Unhandled exception processing async message. Message will be left in queue. Details: {0}", ex);
                    msgErrors++;
                }
            }
        }
Esempio n. 2
0
 public async Task <HttpResponseMessage> OptionsCallAsync()
 {
     return(await DoHandlerAsync("AccountController.OptionsCallAsync", async() =>
     {
         var request = HttpContextFactory.Current.Request;
         HttpResponseMessage response;
         if (request.Headers["Origin"] != null)
         {
             DashTrace.TraceInformation("Forwarding real CORS OPTIONS request");
             Uri forwardUri = ControllerOperations.ForwardUriToNamespace(request);
             var forwardRequest = new HttpRequestMessage(HttpMethod.Options, forwardUri);
             foreach (string key in _corsOptionsHeaders)
             {
                 forwardRequest.Headers.TryAddWithoutValidation(key, request.Headers[key]);
             }
             HttpClient client = new HttpClient();
             response = await client.SendAsync(forwardRequest);
             DashTrace.TraceInformation("CORS OPTIONS response: {0}, {1}", response.StatusCode, response.ReasonPhrase);
         }
         else
         {
             response = this.Request.CreateResponse(HttpStatusCode.OK);
         }
         response.Headers.Add("x-ms-dash-client", "true");
         return response;
     }));
 }
Esempio n. 3
0
        static bool ProcessMessage(QueueMessage message, int?invisibilityTimeout = null)
        {
            var  messageProcessed = false;
            Guid contextId        = message.CorrelationId.HasValue ? message.CorrelationId.Value : Guid.Empty;

            using (var ctx = new CorrelationContext(contextId))
            {
                switch (message.MessageType)
                {
                case MessageTypes.BeginReplicate:
                    messageProcessed = DoReplicateJob(message, invisibilityTimeout);
                    break;

                case MessageTypes.ReplicateProgress:
                    messageProcessed = DoReplicateProgressJob(message, invisibilityTimeout);
                    break;

                case MessageTypes.DeleteReplica:
                    messageProcessed = DoDeleteReplicaJob(message, invisibilityTimeout);
                    break;

                case MessageTypes.Unknown:
                default:
                    DashTrace.TraceWarning("Unable to process unknown message type from async queue [{0}]. Payload: {1}",
                                           message.MessageType,
                                           message.ToString());
                    // Let this message bounce around for a bit - there may be a different version running
                    // on another instance that knows about this message. It will be automatically discarded
                    // after exceeding the deque limit.
                    messageProcessed = false;
                    break;
                }
            }
            return(messageProcessed);
        }
Esempio n. 4
0
        public static bool ProgressBlobReplication(string sourceAccount, string destAccount, string container, string blobName, string copyId, int?waitDelay = null)
        {
            bool       retval   = false;
            ICloudBlob destBlob = null;

            try
            {
                var destContainer = DashConfiguration.GetDataAccountByAccountName(destAccount).CreateCloudBlobClient().GetContainerReference(container);
                destBlob = destContainer.GetBlobReferenceFromServer(blobName);
                retval   = ProcessBlobCopyStatus(destBlob, sourceAccount, copyId, waitDelay);
            }
            catch (StorageException ex)
            {
                // Classify the errors into retryable & non-retryable
                retval = !RecoverReplicationError(ex, destBlob,
                                                  String.Format("Storage error checking replication progress for blob [{0}][{1}] to account [{2}]. Details: {3}",
                                                                sourceAccount,
                                                                PathUtils.CombineContainerAndBlob(container, blobName),
                                                                destAccount,
                                                                ex));
            }
            catch (Exception ex)
            {
                DashTrace.TraceWarning("Error checking replication progress for blob [{0}][{1}] to account [{2}]. Details: {3}",
                                       sourceAccount,
                                       PathUtils.CombineContainerAndBlob(container, blobName),
                                       destAccount,
                                       ex);
            }
            return(retval);
        }
Esempio n. 5
0
            public async Task UpdateStatus(string message, States newState, TraceLevel traceLevel)
            {
                this.Message = message;
                if (newState != States.Unknown)
                {
                    this.State = newState;
                }
                await AccountStatus.UpdateAccountStatus(this);

                switch (traceLevel)
                {
                case TraceLevel.Error:
                    DashTrace.TraceError(message);
                    break;

                case TraceLevel.Warning:
                    DashTrace.TraceWarning(message);
                    break;

                case TraceLevel.Info:
                case TraceLevel.Verbose:
                    DashTrace.TraceInformation(message);
                    break;
                }
            }
Esempio n. 6
0
 public QueueMessage Dequeue(int?invisibilityTimeout = null)
 {
     while (true)
     {
         invisibilityTimeout = invisibilityTimeout ?? _timeout;
         if (invisibilityTimeout.Value < 1)
         {
             invisibilityTimeout = 1;
         }
         this.CurrentMessage = Queue.GetMessage(new TimeSpan(0, 0, invisibilityTimeout.Value));
         if (this.CurrentMessage != null)
         {
             if (this.CurrentMessage.DequeueCount >= _dequeLimit)
             {
                 DashTrace.TraceWarning("Discarding message after exceeding deque limit of {0}. Message details: {1}",
                                        this.CurrentMessage.DequeueCount,
                                        this.CurrentMessage.AsString);
                 DeleteCurrentMessage();
             }
             else
             {
                 return(JsonConvert.DeserializeObject <QueueMessage>(this.CurrentMessage.AsString));
             }
         }
         else
         {
             break;
         }
     }
     return(null);
 }
Esempio n. 7
0
        public IEnumerable <T> Values <T>(string itemName) where T : IConvertible
        {
            IList <string> values = null;

            if (_items.TryGetValue(itemName, out values))
            {
                return(values
                       .Select(value =>
                {
                    try
                    {
                        if (typeof(T).IsEnum)
                        {
                            return (T)Enum.Parse(typeof(T), value);
                        }
                        return (T)Convert.ChangeType(value, typeof(T));
                    }
                    catch (Exception ex)
                    {
                        DashTrace.TraceWarning(new TraceMessage
                        {
                            Operation = "General Exception",
                            Message = String.Format("Failure converting item type. Item: {0}, new type: {1}", value, typeof(T).Name),
                            ErrorDetails = new DashErrorInformation {
                                ErrorMessage = ex.ToString()
                            },
                        });
                    }
                    return default(T);
                }));
            }
            return(Enumerable.Empty <T>());
        }
Esempio n. 8
0
 // TODO: Consider storage abstraction if we want to switch between XTable & Redis for this info
 static AccountStatus()
 {
     try
     {
         GetAccountStatusTable().CreateIfNotExists();
     }
     catch (Exception ex)
     {
         DashTrace.TraceWarning("Error creating/referencing account status table: {0}. Details: {1}", StatusTableName, ex);
     }
 }
Esempio n. 9
0
 private static async Task <TableResult> ExecuteAsync(TableOperation operation)
 {
     try
     {
         return(await GetAccountStatusTable().ExecuteAsync(operation));
     }
     catch (Exception ex)
     {
         DashTrace.TraceWarning("Error interacting with account status. Details: {0}", ex);
     }
     return(null);
 }
Esempio n. 10
0
        protected void Application_Start()
        {
            AzureUtils.AddAzureDiagnosticsListener();
            DashTrace.TraceInformation("Starting application instance");
            GlobalConfiguration.Configure(WebApiConfig.Register);
            // Import any statically configured accounts
            var importAccounts = DashConfiguration.ImportAccounts;

            if (importAccounts.Any())
            {
                Task.Factory.StartNew(() => AccountManager.ImportAccounts(importAccounts));
            }
        }
Esempio n. 11
0
        public static string GenerateSignature(Func <string> stringToSignFactory, byte[] accountKey)
        {
            string stringToSign = stringToSignFactory();

            DashTrace.TraceInformation("Authentication signing string: {0}", stringToSign);

            var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

            using (var hmac = new HMACSHA256(accountKey))
            {
                return(Convert.ToBase64String(hmac.ComputeHash(bytesToSign)));
            }
        }
Esempio n. 12
0
        static void Main(string[] args)
        {
            if (!Trace.Listeners.OfType <ConsoleTraceListener>().Any())
            {
                Trace.Listeners.Add(new ConsoleTraceListener());
            }
            AzureUtils.AddAzureDiagnosticsListener();
            DashTrace.TraceInformation("DashAsync (version: {0}): Asynchronous worker starting up.", Assembly.GetEntryAssembly().GetName().Version);

            int msgProcessed = 0, msgErrors = 0;

            MessageProcessor.ProcessMessageLoop(ref msgProcessed, ref msgErrors);
            DashTrace.TraceInformation("DashAsync completed. Messages processed [{0}], messages unprocessed [{1}]", msgProcessed, msgErrors);
        }
Esempio n. 13
0
 static bool CleanupAbortedBlobReplication(NamespaceBlob namespaceBlob, ICloudBlob destBlob)
 {
     try
     {
         destBlob.DeleteIfExists();
     }
     catch (Exception ex1)
     {
         DashTrace.TraceWarning("Error deleting aborted replication target [{0}][{1}]. Details: {2}",
                                destBlob.ServiceClient.Credentials.AccountName,
                                destBlob.Name,
                                ex1);
     }
     return(namespaceBlob.RemoveDataAccount(destBlob.ServiceClient.Credentials.AccountName));
 }
Esempio n. 14
0
        public static async Task EnqueueBlobReplicationAsync(NamespaceBlob namespaceBlob, bool deleteReplica, bool saveNamespaceEntry = true)
        {
            if (!await namespaceBlob.ExistsAsync())
            {
                return;
            }
            // Trim down the namespace replication list to the first 'master' item. This is sufficient to ensure that the
            // orphaned blobs are not effectively in the account. The master blob will be replicated over the top of the
            // orphaned blobs.
            string primaryAccount = namespaceBlob.PrimaryAccountName;

            if (namespaceBlob.IsReplicated)
            {
                namespaceBlob.PrimaryAccountName = primaryAccount;
                if (saveNamespaceEntry)
                {
                    await namespaceBlob.SaveAsync();
                }
            }
            // This rest of this method does not block. Enqueueing the replication is a completely async process
            var task = Task.Factory.StartNew(() =>
            {
                var queue = new AzureMessageQueue();
                var tasks = DashConfiguration.DataAccounts
                            .Where(dataAccount => !dataAccount.Credentials.AccountName.Equals(primaryAccount, StringComparison.OrdinalIgnoreCase))
                            .Select(async dataAccount => await queue.EnqueueAsync(ConstructReplicationMessage(deleteReplica,
                                                                                                              primaryAccount,
                                                                                                              dataAccount.Credentials.AccountName,
                                                                                                              namespaceBlob.Container,
                                                                                                              namespaceBlob.BlobName,
                                                                                                              deleteReplica ? await GetBlobETagAsync(dataAccount, namespaceBlob.Container, namespaceBlob.BlobName) : null)));
                Task.WhenAll(tasks)
                .ContinueWith(antecedent =>
                {
                    if (antecedent.Exception != null)
                    {
                        DashTrace.TraceWarning("Error queueing replication message for blob: {0}. Details: {1}",
                                               PathUtils.CombineContainerAndBlob(namespaceBlob.Container, namespaceBlob.BlobName),
                                               antecedent.Exception.Flatten());
                    }
                    else
                    {
                        DashTrace.TraceInformation("Blob: {0} has been enqueued for replication.",
                                                   PathUtils.CombineContainerAndBlob(namespaceBlob.Container, namespaceBlob.BlobName));
                    }
                });
            });
        }
Esempio n. 15
0
        static async Task <string> GetBlobETagAsync(CloudStorageAccount dataAccount, string containerName, string blobName)
        {
            try
            {
                var container = dataAccount.CreateCloudBlobClient().GetContainerReference(containerName);
                var blob      = await container.GetBlobReferenceFromServerAsync(blobName);

                return(blob.Properties.ETag);
            }
            catch (StorageException ex)
            {
                DashTrace.TraceWarning("Exception attempting to retrieve ETag value for data blob [{0}/{1}/{2}]. Details: {3}",
                                       dataAccount.Credentials.AccountName, containerName, blobName, ex);
            }
            return(String.Empty);
        }
Esempio n. 16
0
        static bool RecoverReplicationError(StorageException ex, ICloudBlob destBlob, string exceptionMessage)
        {
            bool retval = true;

            switch ((HttpStatusCode)ex.RequestInformation.HttpStatusCode)
            {
            case HttpStatusCode.Conflict:
            case HttpStatusCode.PreconditionFailed:
                // Destination has been modified - no retry
                if (destBlob != null)
                {
                    DashTrace.TraceInformation("A pre-condition was not met attempting to replicate or cleanup blob [{0}] in account [{1}]. This operation will be aborted",
                                               destBlob.Name,
                                               destBlob.ServiceClient.Credentials.AccountName);
                }
                else
                {
                    DashTrace.TraceWarning("A pre-condition was not met attempting to replicate or cleanup an unknown blob. This operation will be aborted.");
                }
                retval = false;
                break;

            case HttpStatusCode.NotFound:
                // The source blob could not be found - delete the target to prevent orphaning
                if (destBlob != null)
                {
                    DashTrace.TraceInformation("Replication of blob [{0}] to account [{1}] cannot be completed because the source blob does not exist.",
                                               destBlob.Name,
                                               destBlob.ServiceClient.Credentials.AccountName);
                    CleanupAbortedBlobReplication(destBlob);
                }
                else
                {
                    DashTrace.TraceWarning("Replication of unknown blob cannot be completed because the source blob does not exist.");
                }
                retval = false;
                break;

            default:
                // Unexpected exceptions are warnings
                DashTrace.TraceWarning(exceptionMessage);
                break;
            }
            return(retval);
        }
Esempio n. 17
0
 static bool FinalizeBlobReplication(string dataAccount, string container, string blobName, bool deleteReplica, ICloudBlob destBlob = null)
 {
     return(NamespaceHandler.PerformNamespaceOperation(container, blobName, async(namespaceBlob) =>
     {
         bool exists = await namespaceBlob.ExistsAsync();
         if (!exists || namespaceBlob.IsMarkedForDeletion)
         {
             // It's ok for a deleted replica not to have a corresponding namespace blob
             if (!deleteReplica)
             {
                 DashTrace.TraceWarning("Replication of blob [{0}] to account [{1}] cannot be completed because the namespace blob either does not exist or is marked for deletion.",
                                        PathUtils.CombineContainerAndBlob(container, blobName),
                                        dataAccount);
                 // Attempt to not leave the replica orphaned
                 if (CleanupAbortedBlobReplication(namespaceBlob, destBlob))
                 {
                     await namespaceBlob.SaveAsync();
                 }
             }
             // Do not attempt retry in this state
             return true;
         }
         string message = "replicated to";
         bool nsDirty = false;
         if (deleteReplica)
         {
             nsDirty = namespaceBlob.RemoveDataAccount(dataAccount);
             message = "dereplicated from";
         }
         else
         {
             nsDirty = namespaceBlob.AddDataAccount(dataAccount);
         }
         if (nsDirty)
         {
             await namespaceBlob.SaveAsync();
             DashTrace.TraceInformation("Blob [{0}] has been successfully {1} account [{2}].",
                                        PathUtils.CombineContainerAndBlob(container, blobName),
                                        message,
                                        dataAccount);
         }
         return true;
     }).Result);
 }
Esempio n. 18
0
 static DashConfigurationSource()
 {
     _dataAccounts       = new Lazy <IList <CloudStorageAccount> >(() => GetDataStorageAccountsFromConfig().ToList(), LazyThreadSafetyMode.PublicationOnly);
     _dataAccountsByName = new Lazy <IDictionary <string, CloudStorageAccount> >(() => DashConfiguration.DataAccounts
                                                                                 .Where(account => account != null)
                                                                                 .ToDictionary(account => account.Credentials.AccountName, StringComparer.OrdinalIgnoreCase), LazyThreadSafetyMode.PublicationOnly);
     _namespaceAccount = new Lazy <CloudStorageAccount>(() =>
     {
         CloudStorageAccount account;
         string connectString = AzureUtils.GetConfigSetting("StorageConnectionStringMaster", "");
         if (!CloudStorageAccount.TryParse(connectString, out account))
         {
             DashTrace.TraceError("Error reading namespace account connection string from configuration. Details: {0}", connectString);
             return(null);
         }
         ServicePointManager.FindServicePoint(account.BlobEndpoint).ConnectionLimit = int.MaxValue;
         return(account);
     }, LazyThreadSafetyMode.PublicationOnly);
 }
Esempio n. 19
0
 static IEnumerable <CloudStorageAccount> GetDataStorageAccountsFromConfig()
 {
     for (int accountIndex = 0; true; accountIndex++)
     {
         var connectString = AzureUtils.GetConfigSetting("ScaleoutStorage" + accountIndex.ToString(), "");
         if (String.IsNullOrWhiteSpace(connectString))
         {
             yield break;
         }
         CloudStorageAccount account;
         if (CloudStorageAccount.TryParse(connectString, out account))
         {
             ServicePointManager.FindServicePoint(account.BlobEndpoint).ConnectionLimit = int.MaxValue;
             yield return(account);
         }
         else
         {
             DashTrace.TraceWarning("Error reading data account connection string from configuration. Configuration details: {0}:{1}", accountIndex, connectString);
         }
     }
 }
Esempio n. 20
0
        public static bool DeleteReplica(string accountName, string container, string blobName, string eTag)
        {
            bool       retval      = false;
            ICloudBlob replicaBlob = null;

            try
            {
                DashTrace.TraceInformation("Deleting replica blob [{0}] from account [{1}]", PathUtils.CombineContainerAndBlob(container, blobName), accountName);
                var blobContainer = DashConfiguration.GetDataAccountByAccountName(accountName).CreateCloudBlobClient().GetContainerReference(container);
                replicaBlob = blobContainer.GetBlobReferenceFromServer(blobName);
                AccessCondition accessCondition = null;
                if (!String.IsNullOrWhiteSpace(eTag))
                {
                    accessCondition = AccessCondition.GenerateIfMatchCondition(eTag);
                }
                replicaBlob.Delete(DeleteSnapshotsOption.IncludeSnapshots, accessCondition);
                FinalizeBlobReplication(accountName, container, blobName, true);
                retval = true;
            }
            catch (StorageException ex)
            {
                // Classify the errors into retryable & non-retryable
                retval = !RecoverReplicationError(ex, replicaBlob,
                                                  String.Format("Storage error deleting replica blob [{0}] from account [{1}]. Details: {2}",
                                                                PathUtils.CombineContainerAndBlob(container, blobName),
                                                                accountName,
                                                                ex));
            }
            catch (Exception ex)
            {
                DashTrace.TraceWarning("Error deleting replica blob [{0}] from account [{1}]. Details: {2}",
                                       PathUtils.CombineContainerAndBlob(container, blobName),
                                       accountName,
                                       ex);
            }
            return(retval);
        }
Esempio n. 21
0
        public static async Task <HandlerResult> HandlePrePipelineOperationAsync(IHttpRequestWrapper requestWrapper)
        {
            string containerName = requestWrapper.UriParts.Container;
            string blobName      = requestWrapper.UriParts.BlobName;
            StorageOperationTypes  requestOperation = StorageOperations.GetBlobOperation(requestWrapper);
            DashClientCapabilities client           = DashClientDetector.DetectClient(requestWrapper);
            HandlerResult          result           = null;
            bool servePrimaryOnly = false;

            switch (requestOperation)
            {
            case StorageOperationTypes.GetBlobProperties:
            case StorageOperationTypes.SetBlobProperties:
            case StorageOperationTypes.GetBlobMetadata:
            case StorageOperationTypes.SetBlobMetadata:
            case StorageOperationTypes.LeaseBlob:
            case StorageOperationTypes.GetBlockList:
                servePrimaryOnly = true;
                // Fall through
                goto case StorageOperationTypes.GetBlob;

            case StorageOperationTypes.GetBlob:
            case StorageOperationTypes.SnapshotBlob:
            case StorageOperationTypes.GetPageRanges:
                if (client.HasFlag(DashClientCapabilities.FollowRedirects))
                {
                    // If the client has specified a concurrent operation, then we always serve the primary account
                    // as that is where the concurrency controls exist
                    if (!servePrimaryOnly && requestWrapper.Headers.IsConcurrentRequest())
                    {
                        servePrimaryOnly = true;
                    }
                    result = await BlobHandler.BasicBlobAsync(requestWrapper,
                                                              containerName,
                                                              blobName,
                                                              servePrimaryOnly,
                                                              BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation));
                }
                break;

            case StorageOperationTypes.PutPage:
                if (client.HasFlag(DashClientCapabilities.NoPayloadToDash))
                {
                    result = await BlobHandler.BasicBlobAsync(requestWrapper,
                                                              containerName,
                                                              blobName,
                                                              true,
                                                              BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation));
                }
                break;

            case StorageOperationTypes.PutBlob:
            case StorageOperationTypes.PutBlock:
            case StorageOperationTypes.PutBlockList:
                if (client.HasFlag(DashClientCapabilities.NoPayloadToDash))
                {
                    result = await BlobHandler.PutBlobAsync(requestWrapper,
                                                            containerName,
                                                            blobName,
                                                            BlobReplicationOperations.DoesOperationTriggerReplication(requestOperation));
                }
                break;

            default:
                // All other operations flow through to the controller action
                break;
            }
            DashTrace.TraceInformation("Operation: {0}, client capability: {1}, action: {2}",
                                       requestOperation,
                                       client,
                                       result == null ? "Forward" : "Redirect");
            return(result);
        }
Esempio n. 22
0
        public static bool IsAuthorized(IHttpRequestWrapper request, RequestHeaders headers, RequestQueryParameters queryParams, bool ignoreRequestAge)
        {
            // Quick request age check
            var    authHeader        = headers.Value <string>("Authorization");
            string requestDateHeader = headers.Value <string>("x-ms-date");
            string dateHeader        = String.Empty;

            if (String.IsNullOrWhiteSpace(requestDateHeader))
            {
                requestDateHeader = headers.Value <string>("Date");
                dateHeader        = requestDateHeader;
            }
            if (String.IsNullOrWhiteSpace(requestDateHeader))
            {
                // One of the date headers is mandatory
                return(false);
            }
            if (!ignoreRequestAge)
            {
                DateTime requestDate;
                if (!DateTime.TryParse(requestDateHeader, out requestDate))
                {
                    return(false);
                }
                else if (requestDate < DateTime.Now.AddMinutes(-15))
                {
                    return(false);
                }
            }
            var parts = authHeader.Split(' ', ':');

            if (parts.Length != 3)
            {
                return(false);
            }
            else if (parts[0] != SharedKeySignature.AlgorithmSharedKey && parts[0] != SharedKeySignature.AlgorithmSharedKeyLite)
            {
                return(false);
            }
            var account   = parts[1];
            var signature = parts[2];

            if (!String.Equals(account, SharedKeySignature.AccountName, StringComparison.OrdinalIgnoreCase))
            {
                return(false);
            }
            // We have to deal with multiple encodings (the spec is a bit ambiguous on what an 'encoded' path actually is).
            // Only run the validation if the encodings result in different strings
            var requestUriParts = request.UriParts;
            var pathsToCheck    = new List <string>()
            {
                requestUriParts.OriginalUriPath
            };
            var unencodedPath = requestUriParts.PublicUriPath;

            if (unencodedPath != pathsToCheck[0])
            {
                pathsToCheck.Add(unencodedPath);
            }
            var alternateEncodingPaths = AlternateEncodeString(pathsToCheck[0]);

            if (alternateEncodingPaths != null)
            {
                pathsToCheck.AddRange(alternateEncodingPaths);
            }
            // For some verbs we can't tell if the Content-Length header was specified as 0 or that IIS/UrlRewrite/ASP.NET has constructed
            // the header value for us. The difference is significant to the signature as content length is included for SharedKey
            bool   fullKeyAlgorithm = parts[0] == SharedKeySignature.AlgorithmSharedKey;
            bool   runBlankContentLengthComparison = false;
            string method        = request.HttpMethod.ToUpper();
            var    contentLength = headers.Value("Content-Length", String.Empty);

            if (fullKeyAlgorithm)
            {
                int length;
                if (!int.TryParse(contentLength, out length) || length <= 0)
                {
                    // Preserve a Content-Length: 0 header for PUT methods
                    runBlankContentLengthComparison = !method.Equals(WebRequestMethods.Http.Put, StringComparison.OrdinalIgnoreCase);
                }
            }
            var validationChecks = pathsToCheck.SelectMany(uriPath => new[] {
                runBlankContentLengthComparison?Tuple.Create(true, uriPath, String.Empty) : null,
                    Tuple.Create(true, uriPath, contentLength),
                    runBlankContentLengthComparison ? Tuple.Create(false, uriPath, String.Empty) : null,
                    Tuple.Create(false, uriPath, contentLength),
            })
                                   .Where(check => check != null)
                                   .ToArray();
            var evaluationResult = validationChecks
                                   .FirstOrDefault(validatationCheck =>
                                                   VerifyRequestAuthorization(signature, validatationCheck.Item1, !fullKeyAlgorithm, method, validatationCheck.Item2, headers, queryParams, dateHeader, validatationCheck.Item3));

            if (evaluationResult != null)
            {
                // Remember the Auth Scheme & Key for when we have to sign the response
                request.AuthenticationScheme = parts[0];
                request.AuthenticationKey    = evaluationResult.Item1 ? SharedKeySignature.PrimaryAccountKey : SharedKeySignature.SecondaryAccountKey;
                return(true);
            }
            DashTrace.TraceWarning("Failed to authenticate SharedKey request: {0}:{1}:{2}:{3}:{4}", parts[0], account, method, request.Url, signature);

            return(false);
        }
Esempio n. 23
0
        public static async Task ImportAccountAsync(string accountName)
        {
            await OperationRunner.DoActionAsync(String.Format("Importing data account: {0}", accountName), async() =>
            {
                // This method will only import the blobs into the namespace. A future task may be
                // to redistribute the blobs to balance the entire virtual account.
                var account = DashConfiguration.GetDataAccountByAccountName(accountName);
                if (account == null)
                {
                    DashTrace.TraceWarning("Failure importing storage account: {0}. The storage account has not been configured as part of this virtual account",
                                           accountName);
                    return;
                }
                // Check if we've already imported this account
                var accountClient     = account.CreateCloudBlobClient();
                var namespaceClient   = DashConfiguration.NamespaceAccount.CreateCloudBlobClient();
                var accountContainers = await ListContainersAsync(accountClient);
                var status            = await AccountStatus.GetAccountStatus(accountName);
                await status.UpdateStatusInformation(AccountStatus.States.Healthy, "Importing storage account: {0} into virtual account", accountName);
                bool alreadyImported = false;
                await GetAccountBlobs(accountClient, async(blobItem) =>
                {
                    var blob          = (ICloudBlob)blobItem;
                    var namespaceBlob = await NamespaceBlob.FetchForBlobAsync(
                        (CloudBlockBlob)NamespaceHandler.GetBlobByName(DashConfiguration.NamespaceAccount, blob.Container.Name, blob.Name, blob.IsSnapshot ? blob.SnapshotTime.ToString() : String.Empty));
                    alreadyImported = await namespaceBlob.ExistsAsync(true) &&
                                      namespaceBlob.DataAccounts.Contains(accountName, StringComparer.OrdinalIgnoreCase);
                    return(false);
                });
                if (alreadyImported)
                {
                    await status.UpdateStatusWarning("Importing storage account: {0} has already been imported. This account cannot be imported again.", accountName);
                    return;
                }
                // Sync the container structure first - add containers in the imported account to the virtual account
                await status.UpdateStatusInformation("Importing storage account: {0}. Synchronizing container structure", accountName);
                int containersAddedCount = 0, containersWarningCount = 0;
                var namespaceContainers  = await ListContainersAsync(namespaceClient);
                await ProcessContainerDifferencesAsync(accountContainers, namespaceContainers, async(newContainerName, accountContainer) =>
                {
                    var createContainerResult = await ContainerHandler.DoForAllContainersAsync(newContainerName,
                                                                                               HttpStatusCode.Created,
                                                                                               async newContainer => await CopyContainer(accountContainer, newContainer),
                                                                                               true,
                                                                                               new[] { account });
                    if (createContainerResult.StatusCode < HttpStatusCode.OK || createContainerResult.StatusCode >= HttpStatusCode.Ambiguous)
                    {
                        await status.UpdateStatusWarning("Importing storage account: {0}. Failed to create container: {1} in virtual account. Details: {2}, {3}",
                                                         accountName,
                                                         newContainerName,
                                                         createContainerResult.StatusCode.ToString(),
                                                         createContainerResult.ReasonPhrase);
                        containersWarningCount++;
                    }
                    else
                    {
                        containersAddedCount++;
                    }
                },
                                                       (newContainerName, ex) =>
                {
                    status.UpdateStatusWarning("Importing storage account: {0}. Error processing container {1}. Details: {2}",
                                               accountName,
                                               newContainerName,
                                               ex.ToString()).Wait();
                    containersWarningCount++;
                });
                // Sync the other way
                await ProcessContainerDifferencesAsync(namespaceContainers, accountContainers, async(newContainerName, namespaceContainer) =>
                {
                    await CopyContainer(namespaceContainer, accountClient.GetContainerReference(newContainerName));
                },
                                                       (newContainerName, ex) =>
                {
                    status.UpdateStatusWarning("Importing storage account: {0}. Error replicating container {1} to imported account. Details: {2}",
                                               accountName,
                                               newContainerName,
                                               ex.ToString()).Wait();
                    containersWarningCount++;
                });
                DashTrace.TraceInformation("Importing storage account: {0}. Synchronized containers structure. {1} containers added to virtual account. {2} failures/warnings.",
                                           accountName,
                                           containersAddedCount,
                                           containersWarningCount);

                // Start importing namespace entries
                await status.UpdateStatusInformation("Importing storage account: {0}. Adding blob entries to namespace", accountName);
                int blobsAddedCount = 0, warningCount = 0, duplicateCount = 0;
                await GetAccountBlobs(accountClient, async(blobItem) =>
                {
                    var blob = (ICloudBlob)blobItem;
                    try
                    {
                        var namespaceBlob = await NamespaceBlob.FetchForBlobAsync(
                            (CloudBlockBlob)NamespaceHandler.GetBlobByName(DashConfiguration.NamespaceAccount, blob.Container.Name, blob.Name, blob.IsSnapshot ? blob.SnapshotTime.ToString() : String.Empty));
                        if (await namespaceBlob.ExistsAsync())
                        {
                            if (!String.Equals(namespaceBlob.PrimaryAccountName, accountName, StringComparison.OrdinalIgnoreCase))
                            {
                                await status.UpdateStatusWarning("Importing storage account: {0}. Adding blob: {1}/{2} would result in a duplicate blob entry. This blob will NOT be imported into the virtual account. Manually add the contents of this blob to the virtual account.",
                                                                 accountName,
                                                                 blob.Container.Name,
                                                                 blob.Name);
                                duplicateCount++;
                            }
                        }
                        else
                        {
                            namespaceBlob.PrimaryAccountName  = accountName;
                            namespaceBlob.Container           = blob.Container.Name;
                            namespaceBlob.BlobName            = blob.Name;
                            namespaceBlob.IsMarkedForDeletion = false;
                            await namespaceBlob.SaveAsync();
                            blobsAddedCount++;
                        }
                    }
                    catch (StorageException ex)
                    {
                        status.UpdateStatusWarning("Importing storage account: {0}. Error importing blob: {0}/{1} into virtual namespace. Details: {3}",
                                                   accountName,
                                                   blob.Container.Name,
                                                   blob.Name,
                                                   ex.ToString()).Wait();
                        warningCount++;
                    }
                    return(true);
                });

                if (status.State < AccountStatus.States.Warning)
                {
                    await status.UpdateStatus(String.Empty, AccountStatus.States.Unknown, TraceLevel.Off);
                }
                DashTrace.TraceInformation("Successfully imported the contents of storage account: '{0}' into the virtual namespace. Blobs added: {1}, duplicates detected: {2}, errors encountered: {3}",
                                           accountName, blobsAddedCount, duplicateCount, warningCount);
            },
                                                ex =>
            {
                var status = AccountStatus.GetAccountStatus(accountName).Result;
                status.UpdateStatusWarning("Error importing storage account: {0} into virtual account. Details: {1}", accountName, ex.ToString()).Wait();
            }, false, true);
        }
Esempio n. 24
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);
        }
Esempio n. 25
0
        static bool ProcessBlobCopyStatus(ICloudBlob destBlob, string sourceAccount, string copyId, int?waitDelay = null)
        {
            bool   retval      = false;
            string destAccount = destBlob.ServiceClient.Credentials.AccountName;
            Uri    sourceUri   = destBlob.CopyState.Source;

            // If the blob has moved on to another copy, just assume that it overrode our copy
            if (destBlob.CopyState.CopyId == copyId)
            {
                switch (destBlob.CopyState.Status)
                {
                case CopyStatus.Aborted:
                    // Copy has been abandoned - we don't automatically come back from here
                    DashTrace.TraceWarning("Replicating blob [{0}] to account [{1}]. Copy Id [{3}] has been aborted.",
                                           sourceUri, destAccount, copyId);
                    // Make sure we don't orphan the replica
                    CleanupAbortedBlobReplication(destBlob);
                    retval = true;
                    break;

                case CopyStatus.Failed:
                case CopyStatus.Invalid:
                    // Possibly temporaral issues - allow the message to retry after a period
                    DashTrace.TraceWarning("Replicating blob [{0}] to account [{1}]. Copy Id [{3}] has been failed or is invalid.",
                                           sourceUri, destAccount, copyId);
                    // Make sure we don't orphan the replica
                    CleanupAbortedBlobReplication(destBlob);
                    retval = false;
                    break;

                case CopyStatus.Pending:
                    // Enqueue a new message to check on the copy status
                    DashTrace.TraceInformation("Replicating blob [{0}] to account [{1}]. Copy Id [{2}] is pending. Copied [{3}]/[{4}] bytes. Enqueing progress message.",
                                               sourceUri, destBlob.ServiceClient.Credentials.AccountName, copyId, destBlob.CopyState.BytesCopied, destBlob.CopyState.TotalBytes);
                    new AzureMessageQueue().Enqueue(new QueueMessage(MessageTypes.ReplicateProgress,
                                                                     new Dictionary <string, string>
                    {
                        { ReplicateProgressPayload.Source, sourceAccount },
                        { ReplicateProgressPayload.Destination, destAccount },
                        { ReplicateProgressPayload.Container, destBlob.Container.Name },
                        { ReplicateProgressPayload.BlobName, destBlob.Name },
                        { ReplicateProgressPayload.CopyID, copyId },
                    },
                                                                     DashTrace.CorrelationId),
                                                    waitDelay ?? (DashConfiguration.WorkerQueueInitialDelay + 10));
                    retval = true;
                    break;

                case CopyStatus.Success:
                    retval = FinalizeBlobReplication(destAccount, destBlob.Container.Name, destBlob.Name, false, destBlob);
                    break;
                }
            }
            else
            {
                DashTrace.TraceInformation("Replication of blob [{0}] to account [{1}] has been aborted as the destination blob has a different copy id. Expected [{2}], actual [{3}]",
                                           sourceUri, destAccount, copyId, destBlob.CopyState.CopyId);
                // Return true to indicate that this operation shouldn't be retried
                retval = true;
            }
            return(retval);
        }