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); }
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); }
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); }
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; } }
public static CloudFileReference Modified(ISimpleCloudBlob blob, Stream data) { return(new CloudFileReference(data, blob.ETag)); }
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); }
public Task StartCopyAsync(ISimpleCloudBlob source, AccessCondition sourceAccessCondition, AccessCondition destAccessCondition) { throw new NotImplementedException(); }
public static async Task <Stream> OpenWriteAsync(this ISimpleCloudBlob blob, IAccessCondition accessCondition) { return(await blob.OpenWriteAsync(MapAccessCondition(accessCondition))); }