diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs b/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs index a10410408c..e2def6dfc0 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; +using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Raven.Client.Documents; @@ -25,6 +27,7 @@ public async Task Execute(IDocumentStore documentStore, CancellationToken cancel await CreateIndexes(documentStore, configuration.EnableFullTextSearch, cancellationToken); + await LicenseStatusCheck.WaitForLicenseOrThrow(configuration, cancellationToken); await ConfigureExpiration(documentStore, cancellationToken); } diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs new file mode 100644 index 0000000000..9aee22a425 --- /dev/null +++ b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs @@ -0,0 +1,41 @@ +namespace ServiceControl.Audit.Persistence.RavenDB; + +using System; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading; +using System.Threading.Tasks; + +static class LicenseStatusCheck +{ + record LicenseStatusFragment(string Id, string LicensedTo, string Status, bool Expired); + + public static async Task WaitForLicenseOrThrow(DatabaseConfiguration configuration, CancellationToken cancellationToken) + { + using var client = new HttpClient + { + BaseAddress = new Uri(configuration.ServerConfiguration.ConnectionString ?? configuration.ServerConfiguration.ServerUrl) + }; + + // Not linking to the incoming cancellationToken to ensure no OperationCancelledException prevents the last InvalidOperationException to be thrown + using var cts = new CancellationTokenSource(30_000); + while (!cts.IsCancellationRequested) + { + var httpResponse = await client.GetAsync("license/status", cancellationToken); + var licenseStatus = await httpResponse.Content.ReadFromJsonAsync(cancellationToken); + if (licenseStatus.Expired) + { + throw new InvalidOperationException("The current RavenDB license is expired. Please, contact support"); + } + + if (licenseStatus.LicensedTo != null && licenseStatus.Id != null) + { + return; + } + + await Task.Delay(200, cancellationToken); + } + + throw new InvalidOperationException("Cannot validate the current RavenDB license. Please, contact support"); + } +} \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs b/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs index 89ea46ec3a..c77342946b 100644 --- a/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs +++ b/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs @@ -23,6 +23,7 @@ public async Task Execute(CancellationToken cancellationToken) await IndexCreation.CreateIndexesAsync(typeof(DatabaseSetup).Assembly, documentStore, null, null, cancellationToken); + await LicenseStatusCheck.WaitForLicenseOrThrow(settings, cancellationToken); await ConfigureExpiration(settings, cancellationToken); } diff --git a/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs new file mode 100644 index 0000000000..deb1956cc6 --- /dev/null +++ b/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs @@ -0,0 +1,41 @@ +namespace ServiceControl.Persistence.RavenDB; + +using System; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading; +using System.Threading.Tasks; + +static class LicenseStatusCheck +{ + record LicenseStatusFragment(string Id, string LicensedTo, string Status, bool Expired); + + public static async Task WaitForLicenseOrThrow(RavenPersisterSettings configuration, CancellationToken cancellationToken) + { + using var client = new HttpClient + { + BaseAddress = new Uri(configuration.ConnectionString ?? configuration.ServerUrl) + }; + + // Not linking to the incoming cancellationToken to ensure no OperationCancelledException prevents the last InvalidOperationException to be thrown + using var cts = new CancellationTokenSource(30_000); + while (!cts.IsCancellationRequested) + { + var httpResponse = await client.GetAsync("license/status", cancellationToken); + var licenseStatus = await httpResponse.Content.ReadFromJsonAsync(cancellationToken); + if (licenseStatus.Expired) + { + throw new InvalidOperationException("The current RavenDB license is expired. Please, contact support"); + } + + if (licenseStatus.LicensedTo != null && licenseStatus.Id != null) + { + return; + } + + await Task.Delay(200, cancellationToken); + } + + throw new InvalidOperationException("Cannot validate the current RavenDB license. Please, contact support"); + } +} \ No newline at end of file