From 99d9ccc7dde8c6e3c25c83c7a11da3e193f6e01d Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 17 Feb 2020 14:26:47 +0000 Subject: [PATCH] Added health checks --- .../HealthCheckExtensions.cs | 16 ++++++ .../Ombi.HealthChecks.csproj | 15 ++++++ src/Ombi.HealthChecks/PlexHealthCheck.cs | 50 ++++++++++++++++++ src/Ombi.sln | 9 +++- src/Ombi/Extensions/DatabaseExtensions.cs | 38 +++++++++++-- src/Ombi/Ombi.csproj | 5 ++ src/Ombi/Program.cs | 2 +- src/Ombi/Startup.cs | 29 ++++++---- src/Ombi/healthchecksdb | Bin 0 -> 45056 bytes 9 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 src/Ombi.HealthChecks/HealthCheckExtensions.cs create mode 100644 src/Ombi.HealthChecks/Ombi.HealthChecks.csproj create mode 100644 src/Ombi.HealthChecks/PlexHealthCheck.cs create mode 100644 src/Ombi/healthchecksdb diff --git a/src/Ombi.HealthChecks/HealthCheckExtensions.cs b/src/Ombi.HealthChecks/HealthCheckExtensions.cs new file mode 100644 index 000000000..89a350cfe --- /dev/null +++ b/src/Ombi.HealthChecks/HealthCheckExtensions.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace Ombi.HealthChecks +{ + public static class HealthCheckExtensions + { + + public static IHealthChecksBuilder AddOmbiHealthChecks(this IHealthChecksBuilder builder) + { + builder.AddCheck("Plex", tags: new string[] { "MediaServer" }); + + return builder; + } + } +} diff --git a/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj b/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj new file mode 100644 index 000000000..70b4dd959 --- /dev/null +++ b/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.1 + + + + + + + + + + + diff --git a/src/Ombi.HealthChecks/PlexHealthCheck.cs b/src/Ombi.HealthChecks/PlexHealthCheck.cs new file mode 100644 index 000000000..7015d9965 --- /dev/null +++ b/src/Ombi.HealthChecks/PlexHealthCheck.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Ombi.Api.Plex; +using Ombi.Api.Plex.Models.Status; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Ombi.HealthChecks +{ + public class PlexHealthCheck : IHealthCheck + { + private readonly IPlexApi _plexApi; + private readonly ISettingsService _settings; + public PlexHealthCheck(IPlexApi plexApi, ISettingsService settings) + { + _plexApi = plexApi; + _settings = settings; + } + public async Task CheckHealthAsync( + HealthCheckContext context, + CancellationToken cancellationToken = default(CancellationToken)) + { + var settings = await _settings.GetSettingsAsync(); + if (settings == null) + { + return HealthCheckResult.Healthy("Plex is not confiured."); + } + + var taskResult = new List>(); + foreach (var server in settings.Servers) + { + taskResult.Add(_plexApi.GetStatus(server.PlexAuthToken, server.FullUri)); + } + + try + { + var result = await Task.WhenAll(taskResult.ToArray()); + return HealthCheckResult.Healthy(); + } + catch (Exception e) + { + return HealthCheckResult.Unhealthy("Could not communicate with Plex", e); + } + } + } +} diff --git a/src/Ombi.sln b/src/Ombi.sln index 78d3898ac..9602d5fd8 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -110,7 +110,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.GroupMe", "Ombi.Ap EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.MusicBrainz", "Ombi.Api.MusicBrainz\Ombi.Api.MusicBrainz.csproj", "{C5C1769B-4197-4410-A160-0EEF39EDDC98}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Twilio", "Ombi.Api.Twilio\Ombi.Api.Twilio.csproj", "{34E5DD1A-6A90-448B-9E71-64D1ACD6C1A3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Twilio", "Ombi.Api.Twilio\Ombi.Api.Twilio.csproj", "{34E5DD1A-6A90-448B-9E71-64D1ACD6C1A3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.HealthChecks", "Ombi.HealthChecks\Ombi.HealthChecks.csproj", "{59D19538-0496-44EE-936E-EBBC22CF7B27}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -298,6 +300,10 @@ Global {34E5DD1A-6A90-448B-9E71-64D1ACD6C1A3}.Debug|Any CPU.Build.0 = Debug|Any CPU {34E5DD1A-6A90-448B-9E71-64D1ACD6C1A3}.Release|Any CPU.ActiveCfg = Release|Any CPU {34E5DD1A-6A90-448B-9E71-64D1ACD6C1A3}.Release|Any CPU.Build.0 = Release|Any CPU + {59D19538-0496-44EE-936E-EBBC22CF7B27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59D19538-0496-44EE-936E-EBBC22CF7B27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59D19538-0496-44EE-936E-EBBC22CF7B27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59D19538-0496-44EE-936E-EBBC22CF7B27}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -341,6 +347,7 @@ Global {9266403C-B04D-4C0F-AC39-82F12C781949} = {9293CA11-360A-4C20-A674-B9E794431BF5} {C5C1769B-4197-4410-A160-0EEF39EDDC98} = {9293CA11-360A-4C20-A674-B9E794431BF5} {34E5DD1A-6A90-448B-9E71-64D1ACD6C1A3} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {59D19538-0496-44EE-936E-EBBC22CF7B27} = {410F36CF-9C60-428A-B191-6FD90610991A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869} diff --git a/src/Ombi/Extensions/DatabaseExtensions.cs b/src/Ombi/Extensions/DatabaseExtensions.cs index 2c6d13d06..21680f8b9 100644 --- a/src/Ombi/Extensions/DatabaseExtensions.cs +++ b/src/Ombi/Extensions/DatabaseExtensions.cs @@ -2,6 +2,7 @@ using System.IO; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Newtonsoft.Json; using Ombi.Helpers; using Ombi.Store.Context; @@ -17,7 +18,7 @@ namespace Ombi.Extensions public const string SqliteDatabase = "Sqlite"; public const string MySqlDatabase = "MySQL"; - public static void ConfigureDatabases(this IServiceCollection services) + public static void ConfigureDatabases(this IServiceCollection services, IHealthChecksBuilder hcBuilder) { var configuration = GetDatabaseConfiguration(); @@ -26,9 +27,11 @@ namespace Ombi.Extensions { case var type when type.Equals(SqliteDatabase, StringComparison.InvariantCultureIgnoreCase): services.AddDbContext(x => ConfigureSqlite(x, configuration.OmbiDatabase)); + AddSqliteHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase); break; case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase): services.AddDbContext(x => ConfigureMySql(x, configuration.OmbiDatabase)); + AddMySqlHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase); break; } @@ -36,9 +39,11 @@ namespace Ombi.Extensions { case var type when type.Equals(SqliteDatabase, StringComparison.InvariantCultureIgnoreCase): services.AddDbContext(x => ConfigureSqlite(x, configuration.ExternalDatabase)); + AddSqliteHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase); break; case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase): services.AddDbContext(x => ConfigureMySql(x, configuration.ExternalDatabase)); + AddMySqlHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase); break; } @@ -46,9 +51,11 @@ namespace Ombi.Extensions { case var type when type.Equals(SqliteDatabase, StringComparison.InvariantCultureIgnoreCase): services.AddDbContext(x => ConfigureSqlite(x, configuration.SettingsDatabase)); + AddSqliteHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase); break; case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase): services.AddDbContext(x => ConfigureMySql(x, configuration.SettingsDatabase)); + AddMySqlHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase); break; } } @@ -62,7 +69,7 @@ namespace Ombi.Extensions } var databaseFileLocation = Path.Combine(i.StoragePath, "database.json"); - + var configuration = new DatabaseConfiguration(i.StoragePath); if (File.Exists(databaseFileLocation)) { @@ -78,11 +85,36 @@ namespace Ombi.Extensions return configuration; } + + private static void AddSqliteHealthCheck(IHealthChecksBuilder builder, string dbName, PerDatabaseConfiguration config) + { + if (builder != null) + { + builder.AddSqlite( + sqliteConnectionString: config.ConnectionString, + name: dbName, + failureStatus: HealthStatus.Unhealthy, + tags: new string[] { "db" }); + } + } + + private static void AddMySqlHealthCheck(IHealthChecksBuilder builder, string dbName, PerDatabaseConfiguration config) + { + if (builder != null) + { + builder.AddMySql( + connectionString: config.ConnectionString, + name: dbName, + failureStatus: HealthStatus.Unhealthy, + tags: new string[] { "db" } + ); + } + } + public static void ConfigureSqlite(DbContextOptionsBuilder options, PerDatabaseConfiguration config) { SQLitePCL.Batteries.Init(); SQLitePCL.raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD); - options.UseSqlite(config.ConnectionString); } diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 125325b0b..4f80b91c9 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -56,6 +56,10 @@ + + + + @@ -85,6 +89,7 @@ + diff --git a/src/Ombi/Program.cs b/src/Ombi/Program.cs index ac9c5bea0..6ddce97ae 100644 --- a/src/Ombi/Program.cs +++ b/src/Ombi/Program.cs @@ -59,7 +59,7 @@ namespace Ombi //CheckAndMigrate(); var services = new ServiceCollection(); - services.ConfigureDatabases(); + services.ConfigureDatabases(null); using (var provider = services.BuildServiceProvider()) { var settingsDb = provider.GetRequiredService(); diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 44d37509d..57f72be0e 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -28,6 +28,9 @@ using Microsoft.AspNetCore.StaticFiles.Infrastructure; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using ILogger = Serilog.ILogger; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Ombi.HealthChecks; namespace Ombi { @@ -73,8 +76,13 @@ namespace Ombi options.User.AllowedUserNameCharacters = string.Empty; }); - services.ConfigureDatabases(); - services.AddHealthChecks(); + var hcBuilder = services.AddHealthChecks(); + hcBuilder.AddOmbiHealthChecks(); + services.ConfigureDatabases(hcBuilder); + services.AddHealthChecksUI(setupSettings: setup => + { + setup.AddHealthCheckEndpoint("Ombi", "http://localhost:3577/healthz"); + }); services.AddMemoryCache(); services.AddJwtAuthentication(Configuration); @@ -205,17 +213,20 @@ namespace Ombi endpoints.MapControllers(); endpoints.MapHub("/hubs/notification"); endpoints.MapHealthChecks("/health"); + endpoints.MapHealthChecks("/healthz", new HealthCheckOptions + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + endpoints.MapHealthChecksUI(); }); app.UseSpa(spa => { -#if DEBUG - //if (env.IsDevelopment()) - //{ - spa.Options.SourcePath = "ClientApp"; - spa.UseProxyToSpaDevelopmentServer("http://localhost:3578"); - //} -#endif +//#if DEBUG +// spa.Options.SourcePath = "ClientApp"; +// spa.UseProxyToSpaDevelopmentServer("http://localhost:3578"); +//#endif }); } diff --git a/src/Ombi/healthchecksdb b/src/Ombi/healthchecksdb new file mode 100644 index 0000000000000000000000000000000000000000..8b2c7dd717f6a6481c1253ae1b95bcc66b489187 GIT binary patch literal 45056 zcmeI*&vM#E90%|f!j{1$Y7ZW!Gflf@(lktBEb+$}PYw82|9g=o_y+bdgv`Z^wJ)B=nBN&2#C|^)K0!T{1MXbO8eQL7Rwr> z%`fu0rO~~5<50C|jJ!)c9`YHbgb1%8z5(FRsflDoLqWOj3H(Bm zq43FlpAdYU;Z6hN+Nl`U1zYKq@V`X<_gq7#8(1N_QOd2S zOFQ(QvP08bIk)Tn(aMAwQXC6_IPqs^k^6sn0n)AL zLg6vgmy?2EdxAZ+-W1~@foEXZIGq;Y?#%`%;7`n0l@~FV} zkkkB>5S*Xq&Z@1Z_jat_)bwEUdkZcG&|hvM!K4r_8g>qCtx-Q5(RQ1sy~>e1TMd0g z;W6y1x>>D1(i%@TwZb@($D(8eDj>&KfQ|vBUlY%ad(R2qu%< zX?yUlRFt)KeZM_8neA`VC$2L+gAT5FdK}$4?NMxCHyZv%A5l@CP3PNmqfy^&R;@3! zhS~Q$?d+W_Omd#-;Qb^=T7R55U9UE+dh2t=bv+f?b+&zMZy(Q-0DE%cFN8giAOHaf zKmY;|fB*y_009U<00I!Wq5>asd@2}~}T!b#NP;e zAVB~E5P$##AOHafKmY;|fB*y_aHR#_=TkoSe*(16|DO}_{7N?v8xH{pKmY;|fB*y_ z009U<00IzrD+O+HQzSb_4lL_vWpS}qud20!x@oP%63OJ^LF>n>Ul$J_==SkHFaAix z7vj(C4G97efB*y_009U<00Izz00bZafy*ZF5zlA1zRP}eua)6kAH^H+@bkXzT=PAS z5BmnM(a=8s|D1@=FI(fVJrIBZ1Rwwb2tWV=5P$##AOL~4MxesY5H6EuXYxH!DJm^U z(FHk4<@ic8wi1^k><9gcs2m$wlvt7F$kK9rIhA^)^Z)Fk|40yk00bZa0SG_<0uX=z z1Rwwb2)w}p_W6IWs1fm3@dxocHh}~I2tWV=5P$##AOHafKmY;|fB<0!$|OF=r51b} zHSO4Azfj$zM%|)S`yPwz2B5nUNm{WVu}Eq;5sfcJ!(M?ba%91`saY1grKU+&Rd!oO z)zmyrRw*LMQc6xpVcyBA99vq$P;0JPqp78s{IN$MpV+n9zvFkyCdtdvQv3r>Aai!M z)&}?1C#9rRO0tjtasD5H4FL#100Izz00bZa0SG_<0uXq!1?>C(dBqn*{9F7}{DVy( zK>z{}fB*y_009U<00Izz00ba#wFSJqkMr6uy#3S