diff --git a/BuildingBlocks/src/BuildingBlocks.Infrastructure/BuildingBlocks.Infrastructure.csproj b/BuildingBlocks/src/BuildingBlocks.Infrastructure/BuildingBlocks.Infrastructure.csproj index bd7e3e2076..cfddde7c78 100644 --- a/BuildingBlocks/src/BuildingBlocks.Infrastructure/BuildingBlocks.Infrastructure.csproj +++ b/BuildingBlocks/src/BuildingBlocks.Infrastructure/BuildingBlocks.Infrastructure.csproj @@ -11,6 +11,7 @@ + diff --git a/BuildingBlocks/src/BuildingBlocks.Infrastructure/Persistence/BlobStorage/BlobStorageHealthCheck.cs b/BuildingBlocks/src/BuildingBlocks.Infrastructure/Persistence/BlobStorage/BlobStorageHealthCheck.cs new file mode 100644 index 0000000000..b6cf815560 --- /dev/null +++ b/BuildingBlocks/src/BuildingBlocks.Infrastructure/Persistence/BlobStorage/BlobStorageHealthCheck.cs @@ -0,0 +1,82 @@ +using System.Text; +using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.Persistence.BlobStorage; +using Backbone.Tooling.Extensions; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage; + +public class BlobStorageHealthCheck : IHealthCheck +{ + private const string FILE_TEXT = "healthcheck"; + private const int MAX_NUMBER_OF_TRIES = 5; + + private static bool? _isHealthy; + private static int _numberOfTries; + + private readonly IBlobStorage _storage; + private readonly string _bucketName; + + public BlobStorageHealthCheck(IBlobStorage storage, string bucketName) + { + _storage = storage; + _bucketName = bucketName; + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + if (!_isHealthy.HasValue || (_isHealthy == false && _numberOfTries < MAX_NUMBER_OF_TRIES)) + { + var filename = Guid.NewGuid().ToString(); + var isUploadPossible = await IsUploadPossible(filename); + var isDownloadPossible = await IsDownloadPossible(filename); + var isDeletionPossible = await IsDeletionPossible(filename); + + _isHealthy = isUploadPossible && isDownloadPossible && isDeletionPossible; + _numberOfTries++; + } + + return _isHealthy.Value ? HealthCheckResult.Healthy() : HealthCheckResult.Unhealthy(); + } + + private async Task IsUploadPossible(string filename) + { + try + { + _storage.Add(_bucketName, filename, FILE_TEXT.GetBytes()); + await _storage.SaveAsync(); + return true; + } + catch (Exception) + { + return false; + } + } + + private async Task IsDownloadPossible(string filename) + { + try + { + var downloadBytes = await _storage.FindAsync(_bucketName, filename); + var downloadedString = Encoding.UTF8.GetString(downloadBytes); + return downloadedString == FILE_TEXT; + } + catch (Exception) + { + return false; + } + } + + private async Task IsDeletionPossible(string filename) + { + try + { + _storage.Remove(_bucketName, filename); + await _storage.SaveAsync(); + return true; + } + catch (Exception) + { + return false; + } + } +} diff --git a/BuildingBlocks/src/BuildingBlocks.Infrastructure/Persistence/BlobStorage/BlobStorageServiceCollectionExtensions.cs b/BuildingBlocks/src/BuildingBlocks.Infrastructure/Persistence/BlobStorage/BlobStorageServiceCollectionExtensions.cs index e6ece37294..f6c9c85826 100644 --- a/BuildingBlocks/src/BuildingBlocks.Infrastructure/Persistence/BlobStorage/BlobStorageServiceCollectionExtensions.cs +++ b/BuildingBlocks/src/BuildingBlocks.Infrastructure/Persistence/BlobStorage/BlobStorageServiceCollectionExtensions.cs @@ -1,7 +1,9 @@ +using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.Persistence.BlobStorage; using Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage.AzureStorageAccount; using Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage.GoogleCloudStorage; using Backbone.Tooling.Extensions; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; namespace Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage; @@ -41,6 +43,14 @@ public static void AddBlobStorage(this IServiceCollection services, BlobStorageO $"{options.CloudProvider} is not a currently supported cloud provider."); } } + + services.AddHealthChecks().Add( + new HealthCheckRegistration( + "blob_storage", + sp => new BlobStorageHealthCheck(sp.GetRequiredService(), options.Container), + HealthStatus.Unhealthy, null + ) + ); } }