Fixed: Various HealthCheck Improvements

- Align Radarr and Sonarr HealthChecks
- Backport various Radarr changes
- Add ProviderAddedEvent
- Cleanup usings
- Add HealthCheck Wiki Fragments
- Add filterBlockedClients to Download Clients

based on Radarr cae4faae61fe47ee6dacf334d8c4e4d483f5e5c8

Co-authored-by: Qstick <qstick@gmail.com>
This commit is contained in:
Bakerboy448 2023-04-29 11:49:29 -05:00
parent f56d504816
commit 1c2359e66e
23 changed files with 75 additions and 47 deletions

View File

@ -29,7 +29,7 @@ namespace NzbDrone.Core.Test.Download
_downloadClients = new List<IDownloadClient>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(v => v.GetDownloadClients())
.Setup(v => v.GetDownloadClients(It.IsAny<bool>()))
.Returns(_downloadClients);
Mocker.GetMock<IProvideDownloadClient>()

View File

@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.HealthCheck.Checks
{
@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_warning_when_download_client_has_not_been_configured()
{
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(Array.Empty<IDownloadClient>());
Subject.Check().ShouldBeWarning();
@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Throws<Exception>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeError();
@ -46,7 +46,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(new List<DownloadClientItem>());
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeOk();

View File

@ -44,7 +44,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IDiskProvider>()

View File

@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { _downloadClient.Object });
}

View File

@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients())
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IConfigService>()

View File

@ -10,7 +10,7 @@ namespace NzbDrone.Core.Download
public interface IProvideDownloadClient
{
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol, int indexerId = 0);
IEnumerable<IDownloadClient> GetDownloadClients();
IEnumerable<IDownloadClient> GetDownloadClients(bool filterBlockedClients = false);
IDownloadClient Get(int id);
}
@ -86,14 +86,39 @@ namespace NzbDrone.Core.Download
return provider;
}
public IEnumerable<IDownloadClient> GetDownloadClients()
public IEnumerable<IDownloadClient> GetDownloadClients(bool filterBlockedClients = false)
{
return _downloadClientFactory.GetAvailableProviders();
var enabledClients = _downloadClientFactory.GetAvailableProviders();
if (filterBlockedClients)
{
return FilterBlockedIndexers(enabledClients).ToList();
}
return enabledClients;
}
public IDownloadClient Get(int id)
{
return _downloadClientFactory.GetAvailableProviders().Single(d => d.Definition.Id == id);
}
private IEnumerable<IDownloadClient> FilterBlockedIndexers(IEnumerable<IDownloadClient> clients)
{
var blockedClients = _downloadClientStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
foreach (var client in clients)
{
DownloadClientStatus blockedClientStatus;
if (blockedClients.TryGetValue(client.Definition.Id, out blockedClientStatus))
{
_logger.Debug("Temporarily ignoring client {0} till {1} due to recent failures.", client.Definition.Name, blockedClientStatus.DisabledTill.Value.ToLocalTime());
continue;
}
yield return client;
}
}
}
}

View File

@ -1,6 +1,5 @@
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
@ -18,7 +17,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (_appFolderInfo.StartUpFolder.IsParentPath(_appFolderInfo.AppDataFolder) ||
_appFolderInfo.StartUpFolder.PathEquals(_appFolderInfo.AppDataFolder))
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Updating will not be possible to prevent deleting AppData on Update");
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Updating will not be possible to prevent deleting AppData on Update", "#updating-will-not-be-possible-to-prevent-deleting-appdata-on-update");
}
return new HealthCheck(GetType());

View File

@ -6,6 +6,7 @@ using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderAddedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderStatusChangedEvent<IDownloadClient>))]
@ -26,7 +27,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (!downloadClients.Any())
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No download client is available");
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No download client is available", "#no-download-client-is-available");
}
foreach (var downloadClient in downloadClients)

View File

@ -12,6 +12,7 @@ using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderAddedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
[CheckOn(typeof(ModelEvent<RootFolder>))]
@ -35,7 +36,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
public override HealthCheck Check()
{
// Only check clients not in failure status, those get another message
var clients = _downloadClientProvider.GetDownloadClients();
var clients = _downloadClientProvider.GetDownloadClients(true);
var rootFolders = _rootFolderService.All();
foreach (var client in clients)

View File

@ -1,5 +1,4 @@
using System;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;

View File

@ -1,4 +1,3 @@
using System;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Download;

View File

@ -1,14 +1,15 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.ImportLists;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Tv;
using NzbDrone.Core.ThingiProvider.Events;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderUpdatedEvent<IImportList>))]
[CheckOn(typeof(SeriesDeletedEvent))]
[CheckOn(typeof(SeriesMovedEvent))]
[CheckOn(typeof(EpisodeImportedEvent), CheckOnCondition.FailedOnly)]
@ -40,7 +41,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
continue;
}
if (!_diskProvider.FolderExists(rootFolderPath))
if (rootFolderPath.IsNullOrWhiteSpace() || !_diskProvider.FolderExists(rootFolderPath))
{
missingRootFolders.Add(rootFolderPath, new List<ImportListDefinition> { importList });
}

View File

@ -1,4 +1,3 @@
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.ThingiProvider.Events;
@ -30,7 +29,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (active.Empty())
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, "All rss-capable indexers are temporarily unavailable due to recent indexer errors");
return new HealthCheck(GetType(), HealthCheckResult.Warning, "All rss-capable indexers are temporarily unavailable due to recent indexer errors");
}
return new HealthCheck(GetType());

View File

@ -1,10 +1,10 @@
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderAddedEvent<IIndexer>))]
[CheckOn(typeof(ProviderUpdatedEvent<IIndexer>))]
[CheckOn(typeof(ProviderDeletedEvent<IIndexer>))]
[CheckOn(typeof(ProviderStatusChangedEvent<IIndexer>))]
@ -23,21 +23,21 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (automaticSearchEnabled.Empty())
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No indexers available with Automatic Search enabled, Sonarr will not provide any automatic search results");
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No indexers available with Automatic Search enabled, Sonarr will not provide any automatic search results", "#no-indexers-available-with-automatic-search-enabled-sonarr-will-not-provide-any-automatic-search-results");
}
var interactiveSearchEnabled = _indexerFactory.InteractiveSearchEnabled(false);
if (interactiveSearchEnabled.Empty())
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No indexers available with Interactive Search enabled, Sonarr will not provide any interactive search results");
return new HealthCheck(GetType(), HealthCheckResult.Warning, "No indexers available with Interactive Search enabled, Sonarr will not provide any interactive search results", "#no-indexers-available-with-interactive-search-enabled");
}
var active = _indexerFactory.AutomaticSearchEnabled(true);
if (active.Empty())
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, "All search-capable indexers are temporarily unavailable due to recent indexer errors");
return new HealthCheck(GetType(), HealthCheckResult.Warning, "All search-capable indexers are temporarily unavailable due to recent indexer errors", "#indexers-are-unavailable-due-to-failures");
}
return new HealthCheck(GetType());

View File

@ -1,6 +1,5 @@
using System.Linq;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.HealthCheck.Checks

View File

@ -1,11 +1,5 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{

View File

@ -34,7 +34,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
var addresses = Dns.GetHostAddresses(_configService.ProxyHostname);
if (!addresses.Any())
{
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format("Failed to resolve the IP Address for the Configured Proxy Host {0}", _configService.ProxyHostname));
return new HealthCheck(GetType(), HealthCheckResult.Error, string.Format("Failed to resolve the IP Address for the Configured Proxy Host {0}", _configService.ProxyHostname), "#proxy-failed-resolve-ip");
}
var request = _cloudRequestBuilder.Create()
@ -49,13 +49,13 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (response.StatusCode == HttpStatusCode.BadRequest)
{
_logger.Error("Proxy Health Check failed: {0}", response.StatusCode);
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Failed to test proxy. StatusCode: {response.StatusCode}");
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Failed to test proxy. StatusCode: {response.StatusCode}", "#proxy-failed-test");
}
}
catch (Exception ex)
{
_logger.Error(ex, "Proxy Health Check failed");
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Failed to test proxy: {request.Url}");
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Failed to test proxy: {request.Url}", "#proxy-failed-test");
}
}

View File

@ -29,7 +29,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
if (!_diskProvider.FolderWritable(recycleBin))
{
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Unable to write to configured recycling bin folder: {recycleBin}. Ensure this path exists and is writable by the user running Sonarr");
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Unable to write to configured recycling bin folder: {recycleBin}. Ensure this path exists and is writable by the user running Sonarr", "#cannot-write-recycle-bin");
}
return new HealthCheck(GetType());

View File

@ -13,13 +13,16 @@ using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.ThingiProvider.Events;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderAddedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderUpdatedEvent<IDownloadClient>))]
[CheckOn(typeof(ProviderDeletedEvent<IDownloadClient>))]
[CheckOn(typeof(ModelEvent<RemotePathMapping>))]
[CheckOn(typeof(EpisodeImportFailedEvent), CheckOnCondition.SuccessfulOnly)]
[CheckOn(typeof(SeriesImportedEvent), CheckOnCondition.SuccessfulOnly)]
public class RemotePathMappingCheck : HealthCheckBase, IProvideHealthCheck
{
private readonly IDiskProvider _diskProvider;
@ -143,7 +146,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
// If the previous case did not match then the failure occured in DownloadedEpisodeImportService,
// while trying to locate the files reported by the download client
// Only check clients not in failure status, those get another message
var client = _downloadClientProvider.GetDownloadClients().FirstOrDefault(x => x.Definition.Name == failureMessage.DownloadClientInfo.Name);
var client = _downloadClientProvider.GetDownloadClients(true).FirstOrDefault(x => x.Definition.Name == failureMessage.DownloadClientInfo.Name);
if (client == null)
{

View File

@ -1,6 +1,4 @@
using System.Diagnostics;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.RootFolders;

View File

@ -65,12 +65,9 @@ namespace NzbDrone.Core.HealthCheck.Checks
}
}
if (BuildInfo.BuildDateTime < DateTime.UtcNow.AddDays(-14))
if (BuildInfo.BuildDateTime < DateTime.UtcNow.AddDays(-14) && _checkUpdateService.AvailableUpdate() != null)
{
if (_checkUpdateService.AvailableUpdate() != null)
{
return new HealthCheck(GetType(), HealthCheckResult.Warning, "New update is available");
}
return new HealthCheck(GetType(), HealthCheckResult.Warning, "New update is available");
}
return new HealthCheck(GetType());

View File

@ -0,0 +1,14 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.ThingiProvider.Events
{
public class ProviderAddedEvent<TProvider> : IEvent
{
public ProviderDefinition Definition { get; private set; }
public ProviderAddedEvent(ProviderDefinition definition)
{
Definition = definition;
}
}
}

View File

@ -110,8 +110,7 @@ namespace NzbDrone.Core.ThingiProvider
public virtual TProviderDefinition Create(TProviderDefinition definition)
{
var result = _providerRepository.Insert(definition);
_eventAggregator.PublishEvent(new ProviderUpdatedEvent<TProvider>(result));
_eventAggregator.PublishEvent(new ProviderAddedEvent<TProvider>(definition));
return result;
}