diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index bc941f3dd..000000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "src/ExternalModules/CurlSharp"] - path = src/ExternalModules/CurlSharp - url = https://github.com/Sonarr/CurlSharp.git - branch = master diff --git a/build.sh b/build.sh index f88ddd6b3..fad531c87 100755 --- a/build.sh +++ b/build.sh @@ -171,9 +171,6 @@ PackageMono() rm -f $outputFolderLinux/sqlite3.* rm -f $outputFolderLinux/fpcalc* - echo "Adding CurlSharp.dll.config (for dllmap)" - cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $outputFolderLinux - echo "Renaming Lidarr.Console.exe to Lidarr.exe" rm $outputFolderLinux/Lidarr.exe* for file in $outputFolderLinux/Lidarr.Console.exe*; do @@ -252,12 +249,6 @@ PackageTests() CleanFolder $testPackageFolder true - echo "Adding CurlSharp.dll.config (for dllmap)" - cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $testPackageFolder - - echo "Copying CurlSharp libraries" - cp $sourceFolder/ExternalModules/CurlSharp/libs/i386/* $testPackageFolder - echo "Adding sqlite dylibs" cp $sourceFolder/Libraries/Sqlite/*.dylib $testPackageFolder diff --git a/frontend/src/System/Status/Health/Health.js b/frontend/src/System/Status/Health/Health.js index dc28033a5..b99075704 100644 --- a/frontend/src/System/Status/Health/Health.js +++ b/frontend/src/System/Status/Health/Health.js @@ -155,12 +155,26 @@ class Health extends Component { const internalLink = getInternalLink(item.source); const testLink = getTestLink(item.source, this.props); + let kind = kinds.WARNING; + switch (item.type.toLowerCase()) { + case 'error': + kind = kinds.DANGER; + break; + default: + case 'warning': + kind = kinds.WARNING; + break; + case 'notice': + kind = kinds.INFO; + break; + } + return ( diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 322b4ac30..b3d5c08be 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -20,8 +20,10 @@ + Release - $(LidarrRootDir)_temp\obj\$(Configuration)\$(MSBuildProjectName)\ + $(LidarrRootDir)_temp\obj\$(MSBuildProjectName)\ + $(LidarrRootDir)_temp\obj\$(MSBuildProjectName)\$(Configuration)\ $(LidarrRootDir)_temp\bin\$(Configuration)\$(MSBuildProjectName)\ @@ -30,6 +32,7 @@ $(LidarrRootDir)_output\Lidarr.Update\ + $([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(BaseIntermediateOutputPath)')) $([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)')) $([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(OutputPath)')) diff --git a/src/ExternalModules/CurlSharp b/src/ExternalModules/CurlSharp deleted file mode 160000 index cfdbbbd9c..000000000 --- a/src/ExternalModules/CurlSharp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cfdbbbd9c6b9612c2756245049a8234ce87dc576 diff --git a/src/Lidarr.sln b/src/Lidarr.sln index 1afb16fd1..a2322292b 100644 --- a/src/Lidarr.sln +++ b/src/Lidarr.sln @@ -72,8 +72,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Mono.Test", "NzbDr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoTorrent", "MonoTorrent\MonoTorrent.csproj", "{411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CurlSharp", "ExternalModules\CurlSharp\CurlSharp\CurlSharp.csproj", "{74420A79-CC16-442C-8B1E-7C1B913844F0}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lidarr.Http", "Lidarr.Http\Lidarr.Http.csproj", "{5370BFF7-1BD7-46BC-AF06-7D9EA5CDA1D6}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lidarr.Api.V1", "Lidarr.Api.V1\Lidarr.Api.V1.csproj", "{7140FF1F-79BE-492F-9188-B21A050BF708}" @@ -228,12 +226,6 @@ Global {411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Mono|x86.Build.0 = Release|x86 {411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Release|x86.ActiveCfg = Release|x86 {411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8}.Release|x86.Build.0 = Release|x86 - {74420A79-CC16-442C-8B1E-7C1B913844F0}.Debug|x86.ActiveCfg = Debug|Any CPU - {74420A79-CC16-442C-8B1E-7C1B913844F0}.Debug|x86.Build.0 = Debug|Any CPU - {74420A79-CC16-442C-8B1E-7C1B913844F0}.Mono|x86.ActiveCfg = Release|Any CPU - {74420A79-CC16-442C-8B1E-7C1B913844F0}.Mono|x86.Build.0 = Release|Any CPU - {74420A79-CC16-442C-8B1E-7C1B913844F0}.Release|x86.ActiveCfg = Release|Any CPU - {74420A79-CC16-442C-8B1E-7C1B913844F0}.Release|x86.Build.0 = Release|Any CPU {5370BFF7-1BD7-46BC-AF06-7D9EA5CDA1D6}.Debug|x86.ActiveCfg = Debug|x86 {5370BFF7-1BD7-46BC-AF06-7D9EA5CDA1D6}.Debug|x86.Build.0 = Debug|x86 {5370BFF7-1BD7-46BC-AF06-7D9EA5CDA1D6}.Mono|x86.ActiveCfg = Release|x86 @@ -274,7 +266,6 @@ Global {80B51429-7A0E-46D6-BEE3-C80DCB1C4EAA} = {4EACDBBC-BCD7-4765-A57B-3E08331E4749} {40D72824-7D02-4A77-9106-8FE0EEA2B997} = {4EACDBBC-BCD7-4765-A57B-3E08331E4749} {411A9E0E-FDC6-4E25-828A-0C2CD1CD96F8} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA} - {74420A79-CC16-442C-8B1E-7C1B913844F0} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2C047BC5-490F-4DCE-962F-141370D23765} diff --git a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs index cbd303147..1eab71277 100644 --- a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs @@ -21,7 +21,6 @@ namespace NzbDrone.Common.Test.Http { [IntegrationTest] [TestFixture(typeof(ManagedHttpDispatcher))] - [TestFixture(typeof(CurlHttpDispatcher))] public class HttpClientFixture : TestBase where TDispatcher : IHttpDispatcher { private static string[] _httpBinHosts = new[] { "eu.httpbin.org", "httpbin.org" }; diff --git a/src/NzbDrone.Common.Test/Lidarr.Common.Test.csproj b/src/NzbDrone.Common.Test/Lidarr.Common.Test.csproj index e535b3304..b5db0b483 100644 --- a/src/NzbDrone.Common.Test/Lidarr.Common.Test.csproj +++ b/src/NzbDrone.Common.Test/Lidarr.Common.Test.csproj @@ -11,9 +11,4 @@ - - - PreserveNewest - - diff --git a/src/NzbDrone.Common/CurlSharp.dll.config b/src/NzbDrone.Common/CurlSharp.dll.config deleted file mode 100644 index eadeb9ad7..000000000 --- a/src/NzbDrone.Common/CurlSharp.dll.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/NzbDrone.Common/Http/Dispatchers/CurlHttpDispatcher.cs b/src/NzbDrone.Common/Http/Dispatchers/CurlHttpDispatcher.cs deleted file mode 100644 index 17574982d..000000000 --- a/src/NzbDrone.Common/Http/Dispatchers/CurlHttpDispatcher.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.RegularExpressions; -using CurlSharp; -using NLog; -using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Extensions; -using NzbDrone.Common.Http.Proxy; - -namespace NzbDrone.Common.Http.Dispatchers -{ - public class CurlHttpDispatcher : IHttpDispatcher - { - private static readonly Regex ExpiryDate = new Regex(@"(expires=)([^;]+)", RegexOptions.IgnoreCase | RegexOptions.Compiled); - - private readonly IHttpProxySettingsProvider _proxySettingsProvider; - private readonly IUserAgentBuilder _userAgentBuilder; - private readonly Logger _logger; - - private const string _caBundleFileName = "curl-ca-bundle.crt"; - private static readonly string _caBundleFilePath; - - static CurlHttpDispatcher() - { - if (Assembly.GetExecutingAssembly().Location.IsNotNullOrWhiteSpace()) - { - _caBundleFilePath = Path.Combine(Assembly.GetExecutingAssembly().Location, "..", _caBundleFileName); - } - else - { - _caBundleFilePath = _caBundleFileName; - } - } - - public CurlHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, IUserAgentBuilder userAgentBuilder, Logger logger) - { - _proxySettingsProvider = proxySettingsProvider; - _userAgentBuilder = userAgentBuilder; - _logger = logger; - } - - public bool CheckAvailability() - { - try - { - return CurlGlobalHandle.Instance.Initialize(); - } - catch (Exception ex) - { - _logger.Trace(ex, "Initializing curl failed"); - return false; - } - } - - public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies) - { - if (!CheckAvailability()) - { - throw new ApplicationException("Curl failed to initialize."); - } - - lock (CurlGlobalHandle.Instance) - { - Stream responseStream = new MemoryStream(); - Stream headerStream = new MemoryStream(); - - using (var curlEasy = new CurlEasy()) - { - curlEasy.AutoReferer = false; - curlEasy.WriteFunction = (b, s, n, o) => - { - responseStream.Write(b, 0, s * n); - return s * n; - }; - curlEasy.HeaderFunction = (b, s, n, o) => - { - headerStream.Write(b, 0, s * n); - return s * n; - }; - - AddProxy(curlEasy, request); - - curlEasy.Url = request.Url.FullUri; - - switch (request.Method) - { - case HttpMethod.GET: - curlEasy.HttpGet = true; - break; - - case HttpMethod.POST: - curlEasy.Post = true; - break; - - case HttpMethod.PUT: - curlEasy.Put = true; - break; - - default: - throw new NotSupportedException($"HttpCurl method {request.Method} not supported"); - } - curlEasy.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent); - curlEasy.FollowLocation = false; - - if (request.RequestTimeout != TimeSpan.Zero) - { - curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds); - } - - if (OsInfo.IsWindows) - { - curlEasy.CaInfo = _caBundleFilePath; - } - - if (cookies != null) - { - curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url); - } - - if (request.ContentData != null) - { - curlEasy.PostFieldSize = request.ContentData.Length; - curlEasy.SetOpt(CurlOption.CopyPostFields, new string(Array.ConvertAll(request.ContentData, v => (char)v))); - } - - // Yes, we have to keep a ref to the object to prevent corrupting the unmanaged state - using (var httpRequestHeaders = SerializeHeaders(request)) - { - curlEasy.HttpHeader = httpRequestHeaders; - - var result = curlEasy.Perform(); - - if (result != CurlCode.Ok) - { - switch (result) - { - case CurlCode.SslCaCert: - case (CurlCode)77: - throw new WebException(string.Format("Curl Error {0} for Url {1}, issues with your operating system SSL Root Certificate Bundle (ca-bundle).", result, curlEasy.Url)); - default: - throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url)); - - } - } - } - - var webHeaderCollection = ProcessHeaderStream(request, cookies, headerStream); - var responseData = ProcessResponseStream(request, responseStream, webHeaderCollection); - - var httpHeader = new HttpHeader(webHeaderCollection); - - return new HttpResponse(request, httpHeader, responseData, (HttpStatusCode)curlEasy.ResponseCode); - } - } - } - - private void AddProxy(CurlEasy curlEasy, HttpRequest request) - { - var proxySettings = _proxySettingsProvider.GetProxySettings(request); - if (proxySettings != null) - - { - switch (proxySettings.Type) - { - case ProxyType.Http: - curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Http); - curlEasy.SetOpt(CurlOption.ProxyAuth, CurlHttpAuth.Basic); - curlEasy.SetOpt(CurlOption.ProxyUserPwd, proxySettings.Username + ":" + proxySettings.Password.ToString()); - break; - case ProxyType.Socks4: - curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Socks4); - curlEasy.SetOpt(CurlOption.ProxyUsername, proxySettings.Username); - curlEasy.SetOpt(CurlOption.ProxyPassword, proxySettings.Password); - break; - case ProxyType.Socks5: - curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Socks5); - curlEasy.SetOpt(CurlOption.ProxyUsername, proxySettings.Username); - curlEasy.SetOpt(CurlOption.ProxyPassword, proxySettings.Password); - break; - } - curlEasy.SetOpt(CurlOption.Proxy, proxySettings.Host + ":" + proxySettings.Port.ToString()); - } - } - - private CurlSlist SerializeHeaders(HttpRequest request) - { - if (!request.Headers.ContainsKey("Accept-Encoding")) - { - request.Headers.Add("Accept-Encoding", "gzip"); - } - - if (request.Headers.ContentType == null) - { - request.Headers.ContentType = string.Empty; - } - - var curlHeaders = new CurlSlist(); - foreach (var header in request.Headers) - { - curlHeaders.Append(header.Key + ": " + header.Value.ToString()); - } - - return curlHeaders; - } - - private WebHeaderCollection ProcessHeaderStream(HttpRequest request, CookieContainer cookies, Stream headerStream) - { - headerStream.Position = 0; - var headerData = headerStream.ToBytes(); - var headerString = Encoding.ASCII.GetString(headerData); - - var webHeaderCollection = new WebHeaderCollection(); - - // following a redirect we could have two sets of headers, so only process the last one - foreach (var header in headerString.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Reverse()) - { - if (!header.Contains(":")) break; - webHeaderCollection.Add(header); - } - - var setCookie = webHeaderCollection.Get("Set-Cookie"); - if (setCookie != null && setCookie.Length > 0 && cookies != null) - { - try - { - cookies.SetCookies((Uri)request.Url, FixSetCookieHeader(setCookie)); - } - catch (CookieException ex) - { - _logger.Debug("Rejected cookie {0}: {1}", ex.InnerException.Message, setCookie); - } - } - - return webHeaderCollection; - } - - private string FixSetCookieHeader(string setCookie) - { - // fix up the date if it was malformed - var setCookieClean = ExpiryDate.Replace(setCookie, delegate(Match match) - { - string shortFormat = "ddd, dd-MMM-yy HH:mm:ss"; - string longFormat = "ddd, dd-MMM-yyyy HH:mm:ss"; - DateTime dt; - if (DateTime.TryParseExact(match.Groups[2].Value, longFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) || - DateTime.TryParseExact(match.Groups[2].Value, shortFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) || - DateTime.TryParse(match.Groups[2].Value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt)) - return match.Groups[1].Value + dt.ToUniversalTime().ToString(longFormat, CultureInfo.InvariantCulture) + " GMT"; - else - return match.Value; - }); - return setCookieClean; - } - - private byte[] ProcessResponseStream(HttpRequest request, Stream responseStream, WebHeaderCollection webHeaderCollection) - { - responseStream.Position = 0; - - if (responseStream.Length != 0) - { - var encoding = webHeaderCollection["Content-Encoding"]; - if (encoding != null) - { - if (encoding.IndexOf("gzip") != -1) - { - responseStream = new GZipStream(responseStream, CompressionMode.Decompress); - - webHeaderCollection.Remove("Content-Encoding"); - } - else if (encoding.IndexOf("deflate") != -1) - { - responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); - - webHeaderCollection.Remove("Content-Encoding"); - } - } - } - - return responseStream.ToBytes(); - - } - } - - internal sealed class CurlGlobalHandle : SafeHandle - { - public static readonly CurlGlobalHandle Instance = new CurlGlobalHandle(); - - private bool _initialized; - private bool _available; - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - } - - private CurlGlobalHandle() - : base(IntPtr.Zero, true) - { - - } - - public bool Initialize() - { - lock (CurlGlobalHandle.Instance) - { - if (_initialized) - return _available; - - _initialized = true; - _available = Curl.GlobalInit(CurlInitFlag.All) == CurlCode.Ok; - - return _available; - } - } - - protected override bool ReleaseHandle() - { - if (_initialized && _available) - { - Curl.GlobalCleanup(); - _available = false; - } - return true; - } - - public override bool IsInvalid => !_initialized || !_available; - } -} diff --git a/src/NzbDrone.Common/Http/Dispatchers/FallbackHttpDispatcher.cs b/src/NzbDrone.Common/Http/Dispatchers/FallbackHttpDispatcher.cs deleted file mode 100644 index 01b60e012..000000000 --- a/src/NzbDrone.Common/Http/Dispatchers/FallbackHttpDispatcher.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Net; -using NLog; -using NzbDrone.Common.Cache; -using NzbDrone.Common.EnvironmentInfo; - -namespace NzbDrone.Common.Http.Dispatchers -{ - public class FallbackHttpDispatcher : IHttpDispatcher - { - private readonly ManagedHttpDispatcher _managedDispatcher; - private readonly CurlHttpDispatcher _curlDispatcher; - private readonly IPlatformInfo _platformInfo; - private readonly Logger _logger; - - private readonly ICached _curlTLSFallbackCache; - - public FallbackHttpDispatcher(ManagedHttpDispatcher managedDispatcher, CurlHttpDispatcher curlDispatcher, ICacheManager cacheManager, IPlatformInfo platformInfo, Logger logger) - { - _managedDispatcher = managedDispatcher; - _curlDispatcher = curlDispatcher; - _platformInfo = platformInfo; - _curlTLSFallbackCache = cacheManager.GetCache(GetType(), "curlTLSFallback"); - _logger = logger; - } - - public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies) - { - if (PlatformInfo.IsMono && request.Url.Scheme == "https") - { - if (!_curlTLSFallbackCache.Find(request.Url.Host)) - { - try - { - return _managedDispatcher.GetResponse(request, cookies); - } - catch (TlsFailureException) - { - _logger.Debug("https request failed in tls error for {0}, trying curl fallback.", request.Url.Host); - - _curlTLSFallbackCache.Set(request.Url.Host, true); - } - } - - if (_curlDispatcher.CheckAvailability()) - { - return _curlDispatcher.GetResponse(request, cookies); - } - - _logger.Trace("Curl not available, using default WebClient."); - } - - return _managedDispatcher.GetResponse(request, cookies); - } - } -} diff --git a/src/NzbDrone.Common/Http/TlsFailureException.cs b/src/NzbDrone.Common/Http/TlsFailureException.cs index c1dcdd991..cb1b5f93a 100644 --- a/src/NzbDrone.Common/Http/TlsFailureException.cs +++ b/src/NzbDrone.Common/Http/TlsFailureException.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Common.Http public class TlsFailureException : WebException { public TlsFailureException(WebRequest request, WebException innerException) - : base("Failed to establish secure https connection to '" + request.RequestUri + "', libcurl fallback might be unavailable.", innerException, WebExceptionStatus.SecureChannelFailure, innerException.Response) + : base("Failed to establish secure https connection to '" + request.RequestUri + "'.", innerException, WebExceptionStatus.SecureChannelFailure, innerException.Response) { } diff --git a/src/NzbDrone.Common/Lidarr.Common.csproj b/src/NzbDrone.Common/Lidarr.Common.csproj index d6341de66..412138c97 100644 --- a/src/NzbDrone.Common/Lidarr.Common.csproj +++ b/src/NzbDrone.Common/Lidarr.Common.csproj @@ -12,9 +12,6 @@ - - - diff --git a/src/NzbDrone.Core.Test/Framework/CoreTest.cs b/src/NzbDrone.Core.Test/Framework/CoreTest.cs index 98cadc6cd..9a27d0c4e 100644 --- a/src/NzbDrone.Core.Test/Framework/CoreTest.cs +++ b/src/NzbDrone.Core.Test/Framework/CoreTest.cs @@ -24,10 +24,9 @@ namespace NzbDrone.Core.Test.Framework Mocker.SetConstant(new HttpProxySettingsProvider(Mocker.Resolve())); Mocker.SetConstant(new ManagedWebProxyFactory(Mocker.Resolve())); - Mocker.SetConstant(new ManagedHttpDispatcher(Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve(), TestLogger)); - Mocker.SetConstant(new CurlHttpDispatcher(Mocker.Resolve(), Mocker.Resolve(), TestLogger)); + Mocker.SetConstant(new ManagedHttpDispatcher(Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve(), TestLogger)); Mocker.SetConstant(new HttpProvider(TestLogger)); - Mocker.SetConstant(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve(), TestLogger)); + Mocker.SetConstant(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve(), Mocker.Resolve(), TestLogger)); Mocker.SetConstant(new LidarrCloudRequestBuilder()); Mocker.SetConstant(Mocker.Resolve()); } diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/DotnetVersionCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/DotnetVersionCheckFixture.cs new file mode 100644 index 000000000..b797a1ee9 --- /dev/null +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/DotnetVersionCheckFixture.cs @@ -0,0 +1,57 @@ +using System; +using NUnit.Framework; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Core.HealthCheck.Checks; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.HealthCheck.Checks +{ + [TestFixture] + public class DotnetVersionCheckFixture : CoreTest + { + private void GivenOutput(string version) + { + WindowsOnly(); + + Mocker.GetMock() + .SetupGet(s => s.Version) + .Returns(new Version(version)); + } + + [TestCase("4.7.2")] + [TestCase("4.8")] + public void should_return_ok(string version) + { + GivenOutput(version); + + Subject.Check().ShouldBeOk(); + } + + [TestCase("4.6.2")] + [TestCase("4.7")] + [TestCase("4.7.1")] + public void should_return_notice(string version) + { + GivenOutput(version); + + Subject.Check().ShouldBeNotice(); + } + + public void should_return_warning(string version) + { + GivenOutput(version); + + Subject.Check().ShouldBeWarning(); + } + + [TestCase("4.5")] + [TestCase("4.5.2")] + [TestCase("4.6.1")] + public void should_return_error(string version) + { + GivenOutput(version); + + Subject.Check().ShouldBeError(); + } + } +} diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/HealthCheckFixtureExtensions.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/HealthCheckFixtureExtensions.cs index 9bc851fe4..0eb927426 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/HealthCheckFixtureExtensions.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/HealthCheckFixtureExtensions.cs @@ -11,6 +11,16 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks result.Type.Should().Be(HealthCheckResult.Ok); } + public static void ShouldBeNotice(this Core.HealthCheck.HealthCheck result, string message = null) + { + result.Type.Should().Be(HealthCheckResult.Notice); + + if (message.IsNotNullOrWhiteSpace()) + { + result.Message.Should().Contain(message); + } + } + public static void ShouldBeWarning(this Core.HealthCheck.HealthCheck result, string message = null) { result.Type.Should().Be(HealthCheckResult.Warning); diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/MonoVersionCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/MonoVersionCheckFixture.cs index 269b46d87..e33402e41 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/MonoVersionCheckFixture.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/MonoVersionCheckFixture.cs @@ -18,12 +18,8 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks .Returns(new Version(version)); } - [TestCase("4.6")] - [TestCase("4.4.2")] - [TestCase("4.8")] - [TestCase("5.0")] - [TestCase("5.2")] - [TestCase("5.4")] + [TestCase("5.18")] + [TestCase("5.20")] public void should_return_ok(string version) { GivenOutput(version); @@ -31,6 +27,23 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks Subject.Check().ShouldBeOk(); } + [TestCase("5.16")] + public void should_return_notice(string version) + { + GivenOutput(version); + + Subject.Check().ShouldBeNotice(); + } + + [TestCase("5.4")] + [TestCase("5.8")] + public void should_return_warning(string version) + { + GivenOutput(version); + + Subject.Check().ShouldBeWarning(); + } + [TestCase("2.10.2")] [TestCase("2.10.8.1")] [TestCase("3.0.0.1")] @@ -42,14 +55,6 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks [TestCase("3.10")] [TestCase("4.0.0.0")] [TestCase("4.2")] - public void should_return_warning(string version) - { - GivenOutput(version); - - Subject.Check().ShouldBeWarning(); - } - - [TestCase("4.4.0")] [TestCase("4.4.1")] public void should_return_error(string version) diff --git a/src/NzbDrone.Core/HealthCheck/Checks/DotnetVersionCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/DotnetVersionCheck.cs new file mode 100644 index 000000000..d238d7bee --- /dev/null +++ b/src/NzbDrone.Core/HealthCheck/Checks/DotnetVersionCheck.cs @@ -0,0 +1,52 @@ +using System; +using NLog; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Core.HealthCheck.Checks +{ + public class DotnetVersionCheck : HealthCheckBase + { + private readonly IPlatformInfo _platformInfo; + private readonly Logger _logger; + + public DotnetVersionCheck(IPlatformInfo platformInfo, Logger logger) + { + _platformInfo = platformInfo; + _logger = logger; + } + + public override HealthCheck Check() + { + if (!PlatformInfo.IsDotNet) + { + return new HealthCheck(GetType()); + } + + var dotnetVersion = _platformInfo.Version; + + // Target .Net version, which would allow us to increase our target framework + var targetVersion = new Version("4.7.2"); + if (dotnetVersion >= targetVersion) + { + _logger.Debug("Dotnet version is {0} or better: {1}", targetVersion, dotnetVersion); + return new HealthCheck(GetType()); + } + + // Supported .net version but below our desired target + var stableVersion = new Version("4.6.2"); + if (dotnetVersion >= stableVersion) + { + _logger.Debug("Dotnet version is {0} or better: {1}", stableVersion, dotnetVersion); + return new HealthCheck(GetType(), HealthCheckResult.Notice, + $"Currently installed .Net Framework {dotnetVersion} is supported but we recommend upgrading to at least {targetVersion}.", + "#currently-installed-net-framework-is-supported-but-upgrading-is-recommended"); + } + + return new HealthCheck(GetType(), HealthCheckResult.Error, + $"Currently installed .Net Framework {dotnetVersion} is old and unsupported. Please upgrade the .Net Framework to at least {targetVersion}.", + "#currently-installed-net-framework-is-old-and-unsupported"); + } + + public override bool CheckOnSchedule => false; + } +} diff --git a/src/NzbDrone.Core/HealthCheck/Checks/MonoTlsCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/MonoTlsCheck.cs index 589791099..01e690ac9 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/MonoTlsCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/MonoTlsCheck.cs @@ -2,7 +2,9 @@ using System; using System.Linq; using System.Reflection; using NLog; +using NLog.Fluent; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation.Extensions; namespace NzbDrone.Core.HealthCheck.Checks { @@ -26,11 +28,14 @@ namespace NzbDrone.Core.HealthCheck.Checks var monoVersion = _platformInfo.Version; - if (monoVersion >= new Version("5.0.0") && Environment.GetEnvironmentVariable("MONO_TLS_PROVIDER") == "legacy") + if (monoVersion >= new Version("5.8.0") && Environment.GetEnvironmentVariable("MONO_TLS_PROVIDER") == "legacy") { - // Mono 5.0 still has issues in combination with libmediainfo, so disabling this check for now. - //_logger.Debug("Mono version 5.0.0 or higher and legacy TLS provider is selected, recommending user to switch to btls."); - //return new HealthCheck(GetType(), HealthCheckResult.Warning, "Lidarr now supports Mono 5.x with btls enabled, consider removing MONO_TLS_PROVIDER=legacy option"); + _logger.Debug() + .Message("Mono version {0} and legacy TLS provider is selected, recommending user to switch to btls.", monoVersion) + .WriteSentryDebug("LegacyTlsProvider", monoVersion.ToString()) + .Write(); + + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Lidarr Mono 4.x tls workaround still enabled, consider removing MONO_TLS_PROVIDER=legacy environment option"); } return new HealthCheck(GetType()); diff --git a/src/NzbDrone.Core/HealthCheck/Checks/MonoVersionCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/MonoVersionCheck.cs index b1bae1b47..3afeb6ae0 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/MonoVersionCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/MonoVersionCheck.cs @@ -24,19 +24,47 @@ namespace NzbDrone.Core.HealthCheck.Checks var monoVersion = _platformInfo.Version; + // Known buggy Mono versions if (monoVersion == new Version("4.4.0") || monoVersion == new Version("4.4.1")) { _logger.Debug("Mono version {0}", monoVersion); - return new HealthCheck(GetType(), HealthCheckResult.Error, $"Your Mono version {monoVersion} has a bug that causes issues connecting to indexers/download clients. You should upgrade to a higher version"); + return new HealthCheck(GetType(), HealthCheckResult.Error, + $"Currently installed Mono version {monoVersion} has a bug that causes issues connecting to indexers/download clients. You should upgrade to a higher version", + "#currently-installed-mono-version-is-old-and-unsupported"); } - if (monoVersion >= new Version("4.4.2")) + // Currently best stable Mono version (5.18 gets us .net 4.7.2 support) + var bestVersion = new Version("5.20"); + var targetVersion = new Version("5.18"); + if (monoVersion >= targetVersion) { - _logger.Debug("Mono version is 4.4.2 or better: {0}", monoVersion); + _logger.Debug("Mono version is {0} or better: {1}", targetVersion, monoVersion); return new HealthCheck(GetType()); } - return new HealthCheck(GetType(), HealthCheckResult.Warning, "You are running an old and unsupported version of Mono. Please upgrade Mono for improved stability."); + // Stable Mono versions + var stableVersion = new Version("5.16"); + if (monoVersion >= stableVersion) + { + _logger.Debug("Mono version is {0} or better: {1}", stableVersion, monoVersion); + return new HealthCheck(GetType(), HealthCheckResult.Notice, + $"Currently installed Mono version {monoVersion} is supported but upgrading to {bestVersion} is recommended.", + "#currently-installed-mono-version-is-supported-but-upgrading-is-recommended"); + } + + // Old but supported Mono versions, there are known bugs + var supportedVersion = new Version("5.4"); + if (monoVersion >= supportedVersion) + { + _logger.Debug("Mono version is {0} or better: {1}", supportedVersion, monoVersion); + return new HealthCheck(GetType(), HealthCheckResult.Warning, + $"Currently installed Mono version {monoVersion} is supported but has some known issues. Please upgrade Mono to version {bestVersion}.", + "#currently-installed-mono-version-is-supported-but-upgrading-is-recommended"); + } + + return new HealthCheck(GetType(), HealthCheckResult.Error, + $"Currently installed Mono version {monoVersion} is old and unsupported. Please upgrade Mono to version {bestVersion}.", + "#currently-installed-mono-version-is-old-and-unsupported"); } public override bool CheckOnSchedule => false; diff --git a/src/NzbDrone.Core/HealthCheck/HealthCheck.cs b/src/NzbDrone.Core/HealthCheck/HealthCheck.cs index 81a0672ba..89e0feb80 100644 --- a/src/NzbDrone.Core/HealthCheck/HealthCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/HealthCheck.cs @@ -46,7 +46,8 @@ namespace NzbDrone.Core.HealthCheck public enum HealthCheckResult { Ok = 0, - Warning = 1, - Error = 2 + Notice = 1, + Warning = 2, + Error = 3 } } diff --git a/src/NzbDrone.Host/MainAppContainerBuilder.cs b/src/NzbDrone.Host/MainAppContainerBuilder.cs index c60ab2764..9537aa929 100644 --- a/src/NzbDrone.Host/MainAppContainerBuilder.cs +++ b/src/NzbDrone.Host/MainAppContainerBuilder.cs @@ -30,7 +30,6 @@ namespace NzbDrone.Host AutoRegisterImplementations(); Container.Register(); - Container.Register(); } } } diff --git a/src/NzbDrone.Update/UpdateContainerBuilder.cs b/src/NzbDrone.Update/UpdateContainerBuilder.cs index 1a9aeb096..8a0f2fa4d 100644 --- a/src/NzbDrone.Update/UpdateContainerBuilder.cs +++ b/src/NzbDrone.Update/UpdateContainerBuilder.cs @@ -10,7 +10,7 @@ namespace NzbDrone.Update private UpdateContainerBuilder(IStartupContext startupContext, List assemblies) : base(startupContext, assemblies) { - Container.Register(); + } public static IContainer Build(IStartupContext startupContext)