예제 #1
0
            public async Task UsesProvidedMatchETag()
            {
                // Arrange
                AccessCondition  srcAccessCondition  = null;
                AccessCondition  destAccessCondition = null;
                ISimpleCloudBlob srcBlob             = null;

                _destBlobMock
                .Setup(x => x.StartCopyAsync(It.IsAny <ISimpleCloudBlob>(), It.IsAny <AccessCondition>(), It.IsAny <AccessCondition>()))
                .Returns(Task.FromResult(0))
                .Callback <ISimpleCloudBlob, AccessCondition, AccessCondition>((b, s, d) =>
                {
                    srcBlob             = b;
                    srcAccessCondition  = s;
                    destAccessCondition = d;
                    SetDestCopyStatus(CopyStatus.Success);
                });

                // Act
                await _target.CopyFileAsync(
                    _srcFolderName,
                    _srcFileName,
                    _destFolderName,
                    _destFileName,
                    AccessConditionWrapper.GenerateIfMatchCondition("etag!"));

                // Assert
                _destBlobMock.Verify(
                    x => x.StartCopyAsync(It.IsAny <ISimpleCloudBlob>(), It.IsAny <AccessCondition>(), It.IsAny <AccessCondition>()),
                    Times.Once);
                Assert.Equal("etag!", destAccessCondition.IfMatchETag);
                Assert.Null(destAccessCondition.IfNoneMatchETag);
            }
예제 #2
0
            public async Task WillCopyTheFileIfDestinationDoesNotExist()
            {
                // Arrange
                AccessCondition  srcAccessCondition  = null;
                AccessCondition  destAccessCondition = null;
                ISimpleCloudBlob srcBlob             = null;

                _destBlobMock
                .Setup(x => x.StartCopyAsync(It.IsAny <ISimpleCloudBlob>(), It.IsAny <AccessCondition>(), It.IsAny <AccessCondition>()))
                .Returns(Task.FromResult(0))
                .Callback <ISimpleCloudBlob, AccessCondition, AccessCondition>((b, s, d) =>
                {
                    srcBlob             = b;
                    srcAccessCondition  = s;
                    destAccessCondition = d;
                });

                // Act
                await _target.CopyFileAsync(
                    _srcFolderName,
                    _srcFileName,
                    _destFolderName,
                    _destFileName,
                    AccessConditionWrapper.GenerateIfNotExistsCondition());

                // Assert
                _destBlobMock.Verify(
                    x => x.StartCopyAsync(It.IsAny <ISimpleCloudBlob>(), It.IsAny <AccessCondition>(), It.IsAny <AccessCondition>()),
                    Times.Once);
                Assert.Equal(_srcFileName, srcBlob.Name);
                Assert.Equal(_srcETag, srcAccessCondition.IfMatchETag);
                Assert.Equal("*", destAccessCondition.IfNoneMatchETag);
            }
예제 #3
0
            public async Task WillCopyTheFileIfDestinationHasFailedCopy()
            {
                // Arrange
                AccessCondition  srcAccessCondition  = null;
                AccessCondition  destAccessCondition = null;
                ISimpleCloudBlob srcBlob             = null;

                SetDestCopyStatus(CopyStatus.Failed);

                _destBlobMock
                .Setup(x => x.StartCopyAsync(It.IsAny <ISimpleCloudBlob>(), It.IsAny <AccessCondition>(), It.IsAny <AccessCondition>()))
                .Returns(Task.FromResult(0))
                .Callback <ISimpleCloudBlob, AccessCondition, AccessCondition>((b, s, d) =>
                {
                    srcBlob             = b;
                    srcAccessCondition  = s;
                    destAccessCondition = d;
                    SetDestCopyStatus(CopyStatus.Pending);
                });

                _destBlobMock
                .Setup(x => x.ExistsAsync())
                .ReturnsAsync(true);

                _destBlobMock
                .Setup(x => x.FetchAttributesAsync())
                .Returns(Task.FromResult(0))
                .Callback(() => SetDestCopyStatus(CopyStatus.Success));

                // Act
                var srcETag = await _target.CopyFileAsync(
                    _srcFolderName,
                    _srcFileName,
                    _destFolderName,
                    _destFileName,
                    AccessConditionWrapper.GenerateIfNotExistsCondition());

                // Assert
                _destBlobMock.Verify(
                    x => x.StartCopyAsync(It.IsAny <ISimpleCloudBlob>(), It.IsAny <AccessCondition>(), It.IsAny <AccessCondition>()),
                    Times.Once);
                Assert.Equal(_srcETag, srcETag);
                Assert.Equal(_srcFileName, srcBlob.Name);
                Assert.Equal(_srcETag, srcAccessCondition.IfMatchETag);
                Assert.Equal(_destETag, destAccessCondition.IfMatchETag);
            }
예제 #4
0
        public async Task StartCopyAsync(ISimpleCloudBlob source, AccessCondition sourceAccessCondition, AccessCondition destAccessCondition)
        {
            // To avoid this we would need to somehow abstract away the primary and secondary storage locations. This
            // is not worth the effort right now!
            var sourceWrapper = source as CloudBlobWrapper;

            if (sourceWrapper == null)
            {
                throw new ArgumentException($"The source blob must be a {nameof(CloudBlobWrapper)}.");
            }

            await _blob.StartCopyAsync(
                sourceWrapper._blob,
                sourceAccessCondition : sourceAccessCondition,
                destAccessCondition : destAccessCondition,
                options : null,
                operationContext : null);
        }
 private static async Task WriteBlob(string auditData, string fullPath, ISimpleCloudBlob blob)
 {
     try
     {
         using (var stream = await blob.OpenWriteAsync(AccessCondition.GenerateIfNoneMatchCondition("*")))
             using (var writer = new StreamWriter(stream))
             {
                 await writer.WriteAsync(auditData);
             }
     }
     catch (StorageException ex)
     {
         if (ex.RequestInformation != null && ex.RequestInformation.HttpStatusCode == 409)
         {
             // Blob already existed!
             throw new InvalidOperationException(String.Format(
                                                     CultureInfo.CurrentCulture,
                                                     CoreStrings.CloudAuditingService_DuplicateAuditRecord,
                                                     fullPath), ex);
         }
         throw;
     }
 }
예제 #6
0
 public static CloudFileReference Modified(ISimpleCloudBlob blob, Stream data)
 {
     return(new CloudFileReference(data, blob.ETag));
 }
예제 #7
0
        private async Task <string> CopyFileAsync(
            ISimpleCloudBlob srcBlob,
            string destFolderName,
            string destFileName,
            IAccessCondition destAccessCondition)
        {
            if (destFolderName == null)
            {
                throw new ArgumentNullException(nameof(destFolderName));
            }

            if (destFileName == null)
            {
                throw new ArgumentNullException(nameof(destFileName));
            }

            var destContainer = await GetContainerAsync(destFolderName);

            var destBlob = destContainer.GetBlobReference(destFileName);

            destAccessCondition = destAccessCondition ?? AccessConditionWrapper.GenerateIfNotExistsCondition();
            var mappedDestAccessCondition = new AccessCondition
            {
                IfNoneMatchETag = destAccessCondition.IfNoneMatchETag,
                IfMatchETag     = destAccessCondition.IfMatchETag,
            };

            if (!await srcBlob.ExistsAsync())
            {
                _trace.TraceEvent(
                    TraceEventType.Warning,
                    id: 0,
                    message: $"Before calling FetchAttributesAsync(), the source blob '{srcBlob.Name}' does not exist.");
            }

            // Determine the source blob etag.
            await srcBlob.FetchAttributesAsync();

            var srcAccessCondition = AccessCondition.GenerateIfMatchCondition(srcBlob.ETag);

            // Check if the destination blob already exists and fetch attributes.
            if (await destBlob.ExistsAsync())
            {
                if (destBlob.CopyState?.Status == CopyStatus.Failed)
                {
                    // If the last copy failed, allow this copy to occur no matter what the caller's destination
                    // condition is. This is because the source blob is preferable over a failed copy. We use the etag
                    // of the failed blob to avoid inadvertently replacing a blob that is now valid (i.e. has a
                    // successful copy status).
                    _trace.TraceEvent(
                        TraceEventType.Information,
                        id: 0,
                        message: $"Destination blob '{destFolderName}/{destFileName}' already exists but has a " +
                        $"failed copy status. This blob will be replaced if the etag matches '{destBlob.ETag}'.");

                    mappedDestAccessCondition = AccessCondition.GenerateIfMatchCondition(destBlob.ETag);
                }
                else if ((srcBlob.Properties.ContentMD5 != null &&
                          srcBlob.Properties.ContentMD5 == destBlob.Properties.ContentMD5 &&
                          srcBlob.Properties.Length == destBlob.Properties.Length))
                {
                    // If the blob hash is the same and the length is the same, no-op the copy.
                    _trace.TraceEvent(
                        TraceEventType.Information,
                        id: 0,
                        message: $"Destination blob '{destFolderName}/{destFileName}' already has hash " +
                        $"'{destBlob.Properties.ContentMD5}' and length '{destBlob.Properties.Length}'. The copy " +
                        $"will be skipped.");

                    return(srcBlob.ETag);
                }
            }

            _trace.TraceEvent(
                TraceEventType.Information,
                id: 0,
                message: $"Copying of source blob '{srcBlob.Uri}' to '{destFolderName}/{destFileName}' with source " +
                $"access condition {Log(srcAccessCondition)} and destination access condition " +
                $"{Log(mappedDestAccessCondition)}.");

            // Start the server-side copy and wait for it to complete. If "If-None-Match: *" was specified and the
            // destination already exists, HTTP 409 is thrown. If "If-Match: ETAG" was specified and the destination
            // has changed, HTTP 412 is thrown.
            try
            {
                await destBlob.StartCopyAsync(
                    srcBlob,
                    srcAccessCondition,
                    mappedDestAccessCondition);
            }
            catch (StorageException ex) when(ex.IsFileAlreadyExistsException())
            {
                throw new FileAlreadyExistsException(
                          String.Format(
                              CultureInfo.CurrentCulture,
                              "There is already a blob with name {0} in container {1}.",
                              destFileName,
                              destFolderName),
                          ex);
            }

            var stopwatch = Stopwatch.StartNew();

            while (destBlob.CopyState.Status == CopyStatus.Pending &&
                   stopwatch.Elapsed < MaxCopyDuration)
            {
                if (!await destBlob.ExistsAsync())
                {
                    _trace.TraceEvent(
                        TraceEventType.Warning,
                        id: 0,
                        message: $"Before calling FetchAttributesAsync(), the destination blob '{destBlob.Name}' does not exist.");
                }

                await destBlob.FetchAttributesAsync();

                await Task.Delay(CopyPollFrequency);
            }

            if (destBlob.CopyState.Status == CopyStatus.Pending)
            {
                throw new TimeoutException($"Waiting for the blob copy operation to complete timed out after {MaxCopyDuration.TotalSeconds} seconds.");
            }
            else if (destBlob.CopyState.Status != CopyStatus.Success)
            {
                throw new StorageException($"The blob copy operation had copy status {destBlob.CopyState.Status} ({destBlob.CopyState.StatusDescription}).");
            }

            return(srcBlob.ETag);
        }
 public static CloudFileReference Modified(ISimpleCloudBlob blob, Stream data)
 {
     return new CloudFileReference(data, blob.ETag);
 }
예제 #9
0
 public Task StartCopyAsync(ISimpleCloudBlob source, AccessCondition sourceAccessCondition, AccessCondition destAccessCondition)
 {
     throw new NotImplementedException();
 }
예제 #10
0
 public static async Task <Stream> OpenWriteAsync(this ISimpleCloudBlob blob, IAccessCondition accessCondition)
 {
     return(await blob.OpenWriteAsync(MapAccessCondition(accessCondition)));
 }