Browse Source

health check fixes (#763)

pull/765/head
Jason Dove 3 years ago committed by GitHub
parent
commit
7687278b80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      CHANGELOG.md
  2. 1
      ErsatzTV.Core/Health/IHealthCheck.cs
  3. 2
      ErsatzTV.Infrastructure/Health/Checks/BaseHealthCheck.cs
  4. 2
      ErsatzTV.Infrastructure/Health/Checks/EpisodeMetadataHealthCheck.cs
  5. 2
      ErsatzTV.Infrastructure/Health/Checks/ErrorReportsHealthCheck.cs
  6. 2
      ErsatzTV.Infrastructure/Health/Checks/FFmpegReportsHealthCheck.cs
  7. 2
      ErsatzTV.Infrastructure/Health/Checks/FFmpegVersionHealthCheck.cs
  8. 10
      ErsatzTV.Infrastructure/Health/Checks/FileNotFoundHealthCheck.cs
  9. 2
      ErsatzTV.Infrastructure/Health/Checks/HardwareAccelerationHealthCheck.cs
  10. 2
      ErsatzTV.Infrastructure/Health/Checks/MovieMetadataHealthCheck.cs
  11. 10
      ErsatzTV.Infrastructure/Health/Checks/UnavailableHealthCheck.cs
  12. 2
      ErsatzTV.Infrastructure/Health/Checks/VaapiDriverHealthCheck.cs
  13. 2
      ErsatzTV.Infrastructure/Health/Checks/ZeroDurationHealthCheck.cs
  14. 27
      ErsatzTV.Infrastructure/Health/HealthCheckService.cs

4
CHANGELOG.md

@ -5,7 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -5,7 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Fixed
- Cleanly stop local library scan when service termination is requested
- Cleanly stop all library scans when service termination is requested
- Fix health check crash when trash contains a show or a season
- Fix ability of health check crash to crash home page
### Changed
- Update Plex, Jellyfin and Emby movie library scanners to share a significant amount of code

1
ErsatzTV.Core/Health/IHealthCheck.cs

@ -2,5 +2,6 @@ @@ -2,5 +2,6 @@
public interface IHealthCheck
{
string Title { get; }
Task<HealthCheckResult> Check(CancellationToken cancellationToken);
}

2
ErsatzTV.Infrastructure/Health/Checks/BaseHealthCheck.cs

@ -7,7 +7,7 @@ namespace ErsatzTV.Infrastructure.Health.Checks; @@ -7,7 +7,7 @@ namespace ErsatzTV.Infrastructure.Health.Checks;
public abstract class BaseHealthCheck
{
protected abstract string Title { get; }
public abstract string Title { get; }
protected HealthCheckResult Result(HealthCheckStatus status, string message) =>
new(Title, status, message, None);

2
ErsatzTV.Infrastructure/Health/Checks/EpisodeMetadataHealthCheck.cs

@ -13,7 +13,7 @@ public class EpisodeMetadataHealthCheck : BaseHealthCheck, IEpisodeMetadataHealt @@ -13,7 +13,7 @@ public class EpisodeMetadataHealthCheck : BaseHealthCheck, IEpisodeMetadataHealt
public EpisodeMetadataHealthCheck(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
protected override string Title => "Episode Metadata";
public override string Title => "Episode Metadata";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{

2
ErsatzTV.Infrastructure/Health/Checks/ErrorReportsHealthCheck.cs

@ -12,7 +12,7 @@ public class ErrorReportsHealthCheck : BaseHealthCheck, IErrorReportsHealthCheck @@ -12,7 +12,7 @@ public class ErrorReportsHealthCheck : BaseHealthCheck, IErrorReportsHealthCheck
public ErrorReportsHealthCheck(IOptions<BugsnagConfiguration> bugsnagConfiguration) =>
_bugsnagConfiguration = bugsnagConfiguration;
protected override string Title => "Error Reports";
public override string Title => "Error Reports";
public Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{

2
ErsatzTV.Infrastructure/Health/Checks/FFmpegReportsHealthCheck.cs

@ -12,7 +12,7 @@ public class FFmpegReportsHealthCheck : BaseHealthCheck, IFFmpegReportsHealthChe @@ -12,7 +12,7 @@ public class FFmpegReportsHealthCheck : BaseHealthCheck, IFFmpegReportsHealthChe
public FFmpegReportsHealthCheck(IConfigElementRepository configElementRepository) =>
_configElementRepository = configElementRepository;
protected override string Title => "FFmpeg Reports";
public override string Title => "FFmpeg Reports";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{

2
ErsatzTV.Infrastructure/Health/Checks/FFmpegVersionHealthCheck.cs

@ -14,7 +14,7 @@ public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthChe @@ -14,7 +14,7 @@ public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthChe
public FFmpegVersionHealthCheck(IConfigElementRepository configElementRepository) =>
_configElementRepository = configElementRepository;
protected override string Title => "FFmpeg Version";
public override string Title => "FFmpeg Version";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{

10
ErsatzTV.Infrastructure/Health/Checks/FileNotFoundHealthCheck.cs

@ -14,7 +14,7 @@ public class FileNotFoundHealthCheck : BaseHealthCheck, IFileNotFoundHealthCheck @@ -14,7 +14,7 @@ public class FileNotFoundHealthCheck : BaseHealthCheck, IFileNotFoundHealthCheck
public FileNotFoundHealthCheck(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
protected override string Title => "File Not Found";
public override string Title => "File Not Found";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{
@ -34,11 +34,15 @@ public class FileNotFoundHealthCheck : BaseHealthCheck, IFileNotFoundHealthCheck @@ -34,11 +34,15 @@ public class FileNotFoundHealthCheck : BaseHealthCheck, IFileNotFoundHealthCheck
.ThenInclude(mv => mv.MediaFiles);
List<MediaItem> five = await mediaItems
// shows and seasons don't have paths to display
.Filter(mi => !(mi is Show))
.Filter(mi => !(mi is Season))
.OrderBy(mi => mi.Id)
.Take(5)
.ToListAsync(cancellationToken);
if (five.Any())
if (mediaItems.Any())
{
IEnumerable<string> paths = five.Map(mi => mi.GetHeadVersion().MediaFiles.Head().Path);
@ -47,7 +51,7 @@ public class FileNotFoundHealthCheck : BaseHealthCheck, IFileNotFoundHealthCheck @@ -47,7 +51,7 @@ public class FileNotFoundHealthCheck : BaseHealthCheck, IFileNotFoundHealthCheck
int count = await mediaItems.CountAsync(cancellationToken);
return WarningResult(
$"There are {count} files that do not exist on disk, including the following: {files}",
$"There are {count} items that do not exist on disk, including the following: {files}",
"/media/trash");
}

2
ErsatzTV.Infrastructure/Health/Checks/HardwareAccelerationHealthCheck.cs

@ -22,7 +22,7 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler @@ -22,7 +22,7 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler
_configElementRepository = configElementRepository;
}
protected override string Title => "Hardware Acceleration";
public override string Title => "Hardware Acceleration";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{

2
ErsatzTV.Infrastructure/Health/Checks/MovieMetadataHealthCheck.cs

@ -13,7 +13,7 @@ public class MovieMetadataHealthCheck : BaseHealthCheck, IMovieMetadataHealthChe @@ -13,7 +13,7 @@ public class MovieMetadataHealthCheck : BaseHealthCheck, IMovieMetadataHealthChe
public MovieMetadataHealthCheck(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
protected override string Title => "Movie Metadata";
public override string Title => "Movie Metadata";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{

10
ErsatzTV.Infrastructure/Health/Checks/UnavailableHealthCheck.cs

@ -29,7 +29,7 @@ public class UnavailableHealthCheck : BaseHealthCheck, IUnavailableHealthCheck @@ -29,7 +29,7 @@ public class UnavailableHealthCheck : BaseHealthCheck, IUnavailableHealthCheck
_embyPathReplacementService = embyPathReplacementService;
}
protected override string Title => "Unavailable";
public override string Title => "Unavailable";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{
@ -49,11 +49,15 @@ public class UnavailableHealthCheck : BaseHealthCheck, IUnavailableHealthCheck @@ -49,11 +49,15 @@ public class UnavailableHealthCheck : BaseHealthCheck, IUnavailableHealthCheck
.ThenInclude(mv => mv.MediaFiles);
List<MediaItem> five = await mediaItems
// shows and seasons don't have paths to display
.Filter(mi => !(mi is Show))
.Filter(mi => !(mi is Season))
.OrderBy(mi => mi.Id)
.Take(5)
.ToListAsync(cancellationToken);
if (five.Any())
if (mediaItems.Any())
{
var paths = new List<string>();
@ -73,7 +77,7 @@ public class UnavailableHealthCheck : BaseHealthCheck, IUnavailableHealthCheck @@ -73,7 +77,7 @@ public class UnavailableHealthCheck : BaseHealthCheck, IUnavailableHealthCheck
int count = await mediaItems.CountAsync(cancellationToken);
return WarningResult(
$"There are {count} files that are unavailable because ErsatzTV cannot find them on disk, including the following: {files}",
$"There are {count} items that are unavailable because ErsatzTV cannot find them on disk, including the following: {files}",
"/search?query=state%3aUnavailable");
}

2
ErsatzTV.Infrastructure/Health/Checks/VaapiDriverHealthCheck.cs

@ -14,7 +14,7 @@ public class VaapiDriverHealthCheck : BaseHealthCheck, IVaapiDriverHealthCheck @@ -14,7 +14,7 @@ public class VaapiDriverHealthCheck : BaseHealthCheck, IVaapiDriverHealthCheck
public VaapiDriverHealthCheck(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
protected override string Title => "VAAPI Driver";
public override string Title => "VAAPI Driver";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{

2
ErsatzTV.Infrastructure/Health/Checks/ZeroDurationHealthCheck.cs

@ -14,7 +14,7 @@ public class ZeroDurationHealthCheck : BaseHealthCheck, IZeroDurationHealthCheck @@ -14,7 +14,7 @@ public class ZeroDurationHealthCheck : BaseHealthCheck, IZeroDurationHealthCheck
public ZeroDurationHealthCheck(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
protected override string Title => "Zero Duration";
public override string Title => "Zero Duration";
public async Task<HealthCheckResult> Check(CancellationToken cancellationToken)
{

27
ErsatzTV.Infrastructure/Health/HealthCheckService.cs

@ -1,11 +1,13 @@ @@ -1,11 +1,13 @@
using ErsatzTV.Core.Health;
using ErsatzTV.Core.Health.Checks;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.Infrastructure.Health;
public class HealthCheckService : IHealthCheckService
{
private readonly List<IHealthCheck> _checks; // ReSharper disable SuggestBaseTypeForParameterInConstructor
private readonly ILogger<HealthCheckService> _logger;
public HealthCheckService(
IFFmpegVersionHealthCheck ffmpegVersionHealthCheck,
@ -17,7 +19,10 @@ public class HealthCheckService : IHealthCheckService @@ -17,7 +19,10 @@ public class HealthCheckService : IHealthCheckService
IFileNotFoundHealthCheck fileNotFoundHealthCheck,
IUnavailableHealthCheck unavailableHealthCheck,
IVaapiDriverHealthCheck vaapiDriverHealthCheck,
IErrorReportsHealthCheck errorReportsHealthCheck) =>
IErrorReportsHealthCheck errorReportsHealthCheck,
ILogger<HealthCheckService> logger)
{
_logger = logger;
_checks = new List<IHealthCheck>
{
ffmpegVersionHealthCheck,
@ -31,7 +36,25 @@ public class HealthCheckService : IHealthCheckService @@ -31,7 +36,25 @@ public class HealthCheckService : IHealthCheckService
vaapiDriverHealthCheck,
errorReportsHealthCheck
};
}
public Task<List<HealthCheckResult>> PerformHealthChecks(CancellationToken cancellationToken) =>
_checks.Map(c => c.Check(cancellationToken)).SequenceParallel().Map(results => results.ToList());
_checks.Map(
c =>
{
var failedResult = new HealthCheckResult(
c.Title,
HealthCheckStatus.Fail,
"Health check failure; see logs",
None);
return TryAsync(() => c.Check(cancellationToken)).IfFail(ex => LogAndReturn(ex, failedResult));
})
.SequenceParallel()
.Map(results => results.ToList());
private HealthCheckResult LogAndReturn(Exception ex, HealthCheckResult failedResult)
{
_logger.LogWarning(ex, "Failed to run health check {Title}", failedResult.Title);
return failedResult;
}
}

Loading…
Cancel
Save