Beispiel #1
0
            public async Task IfTheSourceDoesNotHaveMoreElementsTheExecutionWillStop()
            {
                // Arrange
                // Set a source that returns 1 element to process
                var uri             = "https://dummy/foo.gz";
                var cb              = new CloudBlob(new Uri(uri));
                var _logSource      = new Mock <ILogSource>();
                var dummyLockResult = new AzureBlobLockResult(cb, false, "leaseid", CancellationToken.None);

                _logSource.Setup(lS => lS.GetFilesAsync(It.IsAny <int>(), It.IsAny <CancellationToken>(), It.IsAny <string>())).ReturnsAsync(new Uri[] { new Uri(uri) });
                _logSource.Setup(lS => lS.TakeLockAsync(It.IsAny <Uri>(), It.IsAny <CancellationToken>())).ReturnsAsync(dummyLockResult);

                // set a number larger than 1
                int maxElementsToProcess = 2;
                var _logDestination      = new Mock <ILogDestination>();
                var sanitizerList        = new List <ISanitizer>();
                var logger = new Mock <ILogger <Processor> >();

                using (var cancellationTokenSource = new CancellationTokenSource())
                {
                    var processor = new Processor(_logSource.Object, _logDestination.Object, maxElementsToProcess, sanitizerList, logger.Object);

                    // Act
                    await processor.ProcessAsync(cancellationTokenSource.Token);

                    // Assert
                    _logSource.Verify(logS => logS.GetFilesAsync(It.IsAny <int>(), It.IsAny <CancellationToken>(), It.IsAny <string>()), Times.Once);
                    _logSource.Verify(logS => logS.TakeLockAsync(It.IsAny <Uri>(), It.IsAny <CancellationToken>()), Times.Once);
                }
            }
Beispiel #2
0
            public async Task WhenLockIsNotTakenTheExecutionDoesNotProceed()
            {
                // Arrange
                int maxElementsToProcess = 1;
                var _logSource           = new Mock <ILogSource>();
                var dummyLockResult      = new AzureBlobLockResult(new CloudBlob(new Uri("https://dummy/foo.gz")), false, "leaseid", CancellationToken.None);

                _logSource.Setup(lS => lS.GetFilesAsync(It.IsAny <int>(), It.IsAny <CancellationToken>(), It.IsAny <string>()))
                .ReturnsAsync(new Uri[] { new Uri("https://dummy/foo.gz"), new Uri("https://dummy/foo2.gz") });
                _logSource.Setup(lS => lS.TakeLockAsync(It.IsAny <Uri>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(dummyLockResult);
                var _logDestination = new Mock <ILogDestination>();
                var sanitizerList   = new List <ISanitizer>();
                var logger          = new Mock <ILogger <Processor> >();

                using (var cancellationTokenSource = new CancellationTokenSource())
                {
                    var processor = new Processor(_logSource.Object, _logDestination.Object, maxElementsToProcess, sanitizerList, logger.Object);

                    // Act
                    await processor.ProcessAsync(cancellationTokenSource.Token);

                    // Assert
                    // This will be invoked only once because the continuation should not continue after
                    _logSource.Verify(logS => logS.GetFilesAsync(It.IsAny <int>(), It.IsAny <CancellationToken>(), It.IsAny <string>()), Times.Once);
                    _logSource.Verify(logS => logS.TakeLockAsync(It.IsAny <Uri>(), It.IsAny <CancellationToken>()), Times.Exactly(2));
                    _logSource.Verify(logS => logS.OpenReadAsync(It.IsAny <Uri>(), ContentType.GZip, It.IsAny <CancellationToken>()), Times.Never);
                }
            }
Beispiel #3
0
            public async Task TheProcessOfABlobIsStoppedOnCancellation()
            {
                // Arrange
                // Set a source that returns 1 element to process
                var _logSource      = new Mock <ILogSource>();
                var dummyLockResult = new AzureBlobLockResult(new CloudBlob(new Uri("https://dummy/foo.gz")), true, "leaseid", CancellationToken.None);

                dummyLockResult.BlobOperationToken.Cancel();
                _logSource.Setup(lS => lS.GetFilesAsync(It.IsAny <int>(), It.IsAny <CancellationToken>(), It.IsAny <string>()))
                .ReturnsAsync(new Uri[] { new Uri("https://dummy") });
                _logSource.Setup(lS => lS.TakeLockAsync(It.IsAny <Uri>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(dummyLockResult);

                // set a number larger than 1
                int maxElementsToProcess = 2;
                var _logDestination      = new Mock <ILogDestination>();
                var sanitizerList        = new List <ISanitizer>();
                var logger = new Mock <ILogger <Processor> >();

                using (var cancellationTokenSource = new CancellationTokenSource())
                {
                    var processor = new Processor(_logSource.Object, _logDestination.Object, maxElementsToProcess, sanitizerList, logger.Object);

                    // Act
                    await processor.ProcessAsync(cancellationTokenSource.Token);

                    // Assert
                    _logSource.Verify(logS => logS.GetFilesAsync(It.IsAny <int>(), It.IsAny <CancellationToken>(), It.IsAny <string>()), Times.Once);
                    _logSource.Verify(logS => logS.TakeLockAsync(It.IsAny <Uri>(), It.IsAny <CancellationToken>()), Times.Once);
                    _logSource.Verify(logS => logS.OpenReadAsync(It.IsAny <Uri>(), ContentType.GZip, It.IsAny <CancellationToken>()), Times.Never);
                }
            }
Beispiel #4
0
        /// <summary>
        /// Take the lease on the blob.
        /// The lease will be renewed at every 60 seconds. In order to stop the renew of the lease:
        /// 1. Call ReleaseLockAsync
        ///     or
        /// 2.Cancel the cancellation token
        /// </summary>
        /// <param name="blobUri">The blob uri.</param>
        /// <param name="token">A token to be used for cancellation.</param>
        /// <returns>True if the lock was taken. False otherwise. And the task that renews the lock overtime.</returns>
        public async Task <AzureBlobLockResult> TakeLockAsync(Uri blobUri, CancellationToken token)
        {
            var blob = await GetBlobAsync(blobUri);

            if (blob == null)
            {
                return(AzureBlobLockResult.FailedLockResult(blob));
            }
            return(_blobLeaseManager.AcquireLease(blob, token));
        }
Beispiel #5
0
        public void VerifyThatTheTokenRespectsExternalCancellation()
        {
            // Arrange
            var cts = new CancellationTokenSource();
            var testAzureBlobLockResult = new AzureBlobLockResult(blob: new CloudBlob(new Uri("https://test")), lockIsTaken: false, leaseId: string.Empty, linkToken: cts.Token);

            // Act
            cts.Cancel();

            // Verify
            Assert.True(testAzureBlobLockResult.BlobOperationToken.IsCancellationRequested);
        }
Beispiel #6
0
 /// <summary>
 /// Release the lock on the blob.
 /// </summary>
 /// <param name="blobLock">The blobLock as was taken at the begining of the operation.</param>
 /// <param name="token">A token to be used for cancellation. For this implemention the token is ignored.</param>
 /// <returns>True if the lease was released or the blob does not exist.</returns>
 public async Task <AsyncOperationResult> TryReleaseLockAsync(AzureBlobLockResult blobLock, CancellationToken token)
 {
     if (token.IsCancellationRequested)
     {
         _logger.LogInformation("TryReleaseLockAsync: The operation was cancelled. BlobUri {BlobUri}.", blobLock.Blob.Uri);
         new AsyncOperationResult(false, new OperationCanceledException(token));
     }
     if (await blobLock.Blob.ExistsAsync())
     {
         return(await _blobLeaseManager.TryReleaseLockAsync(blobLock));
     }
     return(new AsyncOperationResult(false, null));
 }
Beispiel #7
0
        public void VerifyThatTheTokenCancellationDoesNotAffectExternalLinkedToken()
        {
            // Arrange
            var cts                     = new CancellationTokenSource();
            var externalToken           = cts.Token;
            var testAzureBlobLockResult = new AzureBlobLockResult(blob: new CloudBlob(new Uri("https://test")), lockIsTaken: false, leaseId: string.Empty, linkToken: externalToken);

            // Act
            testAzureBlobLockResult.BlobOperationToken.Cancel();

            // Verify
            Assert.False(externalToken.IsCancellationRequested);
        }
Beispiel #8
0
        /// <summary>
        /// Copy the blob from souurce to the destination
        /// </summary>
        /// <param name="sourceBlobInformation">The source blobLock as was taken at the begining of the operation.</param>
        /// <param name="destinationContainer">The destination Container.</param>
        /// <returns></returns>
        private async Task <bool> CopyBlobToContainerAsync(AzureBlobLockResult sourceBlobInformation, CloudBlobContainer destinationContainer, CancellationToken token)
        {
            if (token.IsCancellationRequested)
            {
                _logger.LogInformation("CopyBlobToContainerAsync: The operation was cancelled.");
                return(false);
            }

            //just get a reference to the future blob
            var destinationBlob = destinationContainer.GetBlobReference(GetBlobNameFromUri(sourceBlobInformation.Blob.Uri));

            try
            {
                if (!await destinationBlob.ExistsAsync(token))
                {
                    return(await TryCopyInternalAsync(sourceBlobInformation.Blob.Uri, destinationBlob, destinationContainer));
                }
                else
                {
                    _logger.LogInformation("CopyBlobToContainerAsync: Blob already exists DestinationUri {DestinationUri}.", destinationBlob.Uri);

                    // Overwrite
                    var lease = destinationBlob.AcquireLease(TimeSpan.FromSeconds(CopyBlobLeaseTimeInSeconds), sourceBlobInformation.LeaseId);
                    var destinationAccessCondition = new AccessCondition {
                        LeaseId = lease
                    };
                    await destinationBlob.DeleteAsync(deleteSnapshotsOption : DeleteSnapshotsOption.IncludeSnapshots, accessCondition : destinationAccessCondition, options : null, operationContext : null);

                    var result = await TryCopyInternalAsync(sourceBlobInformation.Blob.Uri, destinationBlob, destinationContainer, destinationAccessCondition : destinationAccessCondition);

                    try
                    {
                        destinationBlob.ReleaseLease(destinationAccessCondition);
                    }
                    catch (StorageException)
                    {
                        // do not do anything the lease will be released anyway
                    }
                    return(result);
                }
            }
            catch (StorageException exception)
            {
                _logger.LogCritical(LogEvents.FailedBlobCopy, exception, "CopyBlobToContainerAsync: Blob Copy Failed. SourceUri: {SourceUri}. DestinationUri {DestinationUri}", sourceBlobInformation.Blob.Uri, destinationBlob.Uri);
                return(false);
            }
        }
Beispiel #9
0
        /// <summary>
        /// It will perform the clean up steps.
        /// In this case it will copy the blob in the archive or dead-letter and delete the blob.
        /// This method will not throw an exception.
        /// Use the <see cref="AsyncOperationResult.OperationException"/> for any exception during this execution.
        /// </summary>
        /// <param name="blobLock">The <see cref="AzureBlobLockResult"/> for the blob that needs clean-up.</param>
        /// <param name="onError">Flag to indicate if the cleanup is done because an error or not. </param>
        /// <param name="token">A token to be used for cancellation.</param>
        /// <returns>True is the cleanup was successful. If the blob does not exist the return value is false.</returns>
        public async Task <AsyncOperationResult> TryCleanAsync(AzureBlobLockResult blobLock, bool onError, CancellationToken token)
        {
            _logger.LogInformation("CleanAsync:Start cleanup for {Blob}", blobLock.Blob.Uri);
            if (token.IsCancellationRequested)
            {
                _logger.LogInformation("CleanAsync: The operation was cancelled. BlobUri {BlobUri}.", blobLock.Blob.Uri);
                new AsyncOperationResult(false, new OperationCanceledException(token));
            }
            try
            {
                var sourceBlob = await GetBlobAsync(blobLock.Blob.Uri);

                if (sourceBlob == null)
                {
                    _logger.LogError("CleanAsync: The blob {Blob} was not found.", blobLock.Blob.Uri);
                    return(new AsyncOperationResult(false, null));
                }
                string archiveTargetContainerName = onError ? _deadletterContainerName : _archiveContainerName;
                _logger.LogInformation("CleanAsync: Blob {Blob} will be copied to container {Container}", blobLock.Blob.Uri, archiveTargetContainerName);
                var archiveTargetContainer = await CreateContainerAsync(archiveTargetContainerName);

                if (await CopyBlobToContainerAsync(blobLock, archiveTargetContainer, token))
                {
                    _logger.LogInformation("CleanAsync: Blob {Blob} was copied to container {Container}", blobLock.Blob.Uri, archiveTargetContainerName);
                    var accessCondition = new AccessCondition {
                        LeaseId = blobLock.LeaseId
                    };
                    // The operation will throw if the lease does not match
                    bool deleteResult = await sourceBlob.DeleteIfExistsAsync(deleteSnapshotsOption : DeleteSnapshotsOption.IncludeSnapshots,
                                                                             accessCondition : accessCondition,
                                                                             options : _blobRequestOptions,
                                                                             operationContext : null,
                                                                             cancellationToken : token);

                    _logger.LogInformation("CleanAsync: Blob {Blob} was deleted {DeletedResult}. The leaseId: {LeaseId}", blobLock.Blob.Uri, deleteResult, blobLock.LeaseId);
                    return(new AsyncOperationResult(deleteResult, null));
                }
                _logger.LogWarning("CleanAsync: Blob {Blob} failed to be copied to container {Container}", blobLock.Blob.Uri, archiveTargetContainerName);
                return(new AsyncOperationResult(false, null));
            }
            catch (Exception exception)
            {
                _logger.LogCritical(LogEvents.FailedBlobDelete, exception, "CleanAsync: Blob {Blob} failed the clean-up. The current leaseId: {LeaseId}", blobLock.Blob.Uri, blobLock.LeaseId);
                return(new AsyncOperationResult(null, exception));
            }
        }
Beispiel #10
0
            public async Task VerifyCleanAsyncCallOnErrorIsInvokedWhenWriteAsyncThrows(bool writeAsyncThrows)
            {
                // Arrange
                // Set a source that returns 1 element to process
                var _logSource      = new Mock <ILogSource>();
                var dummyLockResult = new AzureBlobLockResult(new CloudBlob(new Uri("https://dummy/foo.gz")), true, "leaseid", CancellationToken.None);

                _logSource.Setup(lS => lS.GetFilesAsync(It.IsAny <int>(), It.IsAny <CancellationToken>(), It.IsAny <string>()))
                .ReturnsAsync(new Uri[] { new Uri("https://dummy") });
                _logSource.Setup(lS => lS.TakeLockAsync(It.IsAny <Uri>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(dummyLockResult);
                _logSource.Setup(lS => lS.OpenReadAsync(It.IsAny <Uri>(), ContentType.GZip, It.IsAny <CancellationToken>()))
                .ReturnsAsync(new MemoryStream());

                // set a number larger than 1
                int maxElementsToProcess = 2;
                var _logDestination      = new Mock <ILogDestination>();
                var writeResult          = writeAsyncThrows ? new AsyncOperationResult(null, new Exception("Boo")) : new AsyncOperationResult(true, null);

                _logDestination.Setup(lD => lD.TryWriteAsync(It.IsAny <Stream>(), It.IsAny <Action <Stream, Stream> >(), It.IsAny <string>(), ContentType.GZip, It.IsAny <CancellationToken>()))
                .ReturnsAsync(writeResult);
                var sanitizerList = new List <ISanitizer>();
                var logger        = new Mock <ILogger <Processor> >();

                using (var cancellationTokenSource = new CancellationTokenSource())
                {
                    var processor = new Processor(_logSource.Object, _logDestination.Object, maxElementsToProcess, sanitizerList, logger.Object);

                    // Act
                    await processor.ProcessAsync(cancellationTokenSource.Token);

                    // Assert
                    _logSource.Verify(logS => logS.GetFilesAsync(It.IsAny <int>(), It.IsAny <CancellationToken>(), It.IsAny <string>()), Times.Once);
                    _logSource.Verify(logS => logS.TakeLockAsync(It.IsAny <Uri>(), It.IsAny <CancellationToken>()), Times.Once);
                    _logSource.Verify(logS => logS.TryCleanAsync(dummyLockResult, writeAsyncThrows, It.IsAny <CancellationToken>()), Times.Once);
                    _logSource.Verify(logS => logS.TryReleaseLockAsync(dummyLockResult, It.IsAny <CancellationToken>()), Times.Once);
                }
            }