mirror of
https://github.com/Radarr/Radarr.git
synced 2024-08-17 07:49:45 +02:00
Update improvements
Include NzbDrone.Update in mono/osx package Do not ignore certificate warnings for services Check hash before extracting update New: Update support for Linux/OS X - see the wiki for more information
This commit is contained in:
parent
5c2f77339d
commit
ef3777fccf
@ -6,6 +6,7 @@ $testPackageFolder = '.\_tests\'
|
||||
$testSearchPattern = '*.Test\bin\x86\Release'
|
||||
$sourceFolder = '.\src'
|
||||
$updateFolder = $outputFolder + '\NzbDrone.Update'
|
||||
$updateFolderMono = $outputFolderMono + '\NzbDrone.Update'
|
||||
|
||||
Function Build()
|
||||
{
|
||||
@ -73,9 +74,6 @@ Function PackageMono()
|
||||
|
||||
Copy-Item $outputFolder $outputFolderMono -recurse
|
||||
|
||||
Write-Host Removing Update Client
|
||||
Remove-Item -Recurse -Force "$outputFolderMono\NzbDrone.Update"
|
||||
|
||||
Write-Host Creating MDBs
|
||||
get-childitem $outputFolderMono -File -Include @("*.exe", "*.dll") -Exclude @("MediaInfo.dll", "sqlite3.dll") -Recurse | foreach ($_) {
|
||||
Write-Host "Creating .mdb for $_"
|
||||
@ -110,6 +108,9 @@ Function PackageMono()
|
||||
|
||||
Remove-Item "$outputFolderMono\NzbDrone.Console.vshost.exe"
|
||||
|
||||
Write-Host Adding NzbDrone.Mono to UpdatePackage
|
||||
Copy-Item $outputFolderMono\* $updateFolderMono -Filter NzbDrone.Mono.*
|
||||
|
||||
Write-Host "##teamcity[progressFinish 'Creating Mono Package']"
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,9 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Update;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
using Omu.ValueInjecter;
|
||||
|
||||
namespace NzbDrone.Api.Config
|
||||
@ -12,7 +14,7 @@ public class HostConfigModule : NzbDroneRestModule<HostConfigResource>
|
||||
{
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public HostConfigModule(ConfigFileProvider configFileProvider)
|
||||
public HostConfigModule(IConfigFileProvider configFileProvider)
|
||||
: base("/config/host")
|
||||
{
|
||||
_configFileProvider = configFileProvider;
|
||||
@ -29,6 +31,8 @@ public HostConfigModule(ConfigFileProvider configFileProvider)
|
||||
|
||||
SharedValidator.RuleFor(c => c.SslPort).ValidPort().When(c => c.EnableSsl);
|
||||
SharedValidator.RuleFor(c => c.SslCertHash).NotEmpty().When(c => c.EnableSsl && OsInfo.IsWindows);
|
||||
|
||||
SharedValidator.RuleFor(c => c.UpdateScriptPath).IsValidPath().When(c => c.UpdateMechanism == UpdateMechanism.Script);
|
||||
}
|
||||
|
||||
private HostConfigResource GetHostConfig()
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
namespace NzbDrone.Api.Config
|
||||
{
|
||||
@ -14,10 +15,12 @@ public class HostConfigResource : RestResource
|
||||
public String Password { get; set; }
|
||||
public String LogLevel { get; set; }
|
||||
public String Branch { get; set; }
|
||||
public Boolean AutoUpdate { get; set; }
|
||||
public String ApiKey { get; set; }
|
||||
public Boolean Torrent { get; set; }
|
||||
public String SslCertHash { get; set; }
|
||||
public String UrlBase { get; set; }
|
||||
public Boolean UpdateAutomatically { get; set; }
|
||||
public UpdateMechanism UpdateMechanism { get; set; }
|
||||
public String UpdateScriptPath { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +162,7 @@
|
||||
<Compile Include="Mapping\MappingValidation.cs" />
|
||||
<Compile Include="Mapping\ResourceMappingException.cs" />
|
||||
<Compile Include="Mapping\ValueInjectorExtensions.cs" />
|
||||
<Compile Include="Update\UpdateResource.cs" />
|
||||
<Compile Include="Wanted\CutoffModule.cs" />
|
||||
<Compile Include="Wanted\LegacyMissingModule.cs" />
|
||||
<Compile Include="Wanted\MissingModule.cs" />
|
||||
|
@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Update;
|
||||
using NzbDrone.Api.Mapping;
|
||||
@ -44,18 +41,4 @@ private List<UpdateResource> GetRecentUpdates()
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateResource : RestResource
|
||||
{
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
|
||||
public Version Version { get; set; }
|
||||
|
||||
public String Branch { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public String FileName { get; set; }
|
||||
public String Url { get; set; }
|
||||
public Boolean IsUpgrade { get; set; }
|
||||
public Boolean Installed { get; set; }
|
||||
public UpdateChanges Changes { get; set; }
|
||||
}
|
||||
}
|
22
src/NzbDrone.Api/Update/UpdateResource.cs
Normal file
22
src/NzbDrone.Api/Update/UpdateResource.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
namespace NzbDrone.Api.Update
|
||||
{
|
||||
public class UpdateResource : RestResource
|
||||
{
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
|
||||
public Version Version { get; set; }
|
||||
|
||||
public String Branch { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public String FileName { get; set; }
|
||||
public String Url { get; set; }
|
||||
public Boolean IsUpgrade { get; set; }
|
||||
public Boolean Installed { get; set; }
|
||||
public UpdateChanges Changes { get; set; }
|
||||
public String Hash { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Model;
|
||||
@ -18,17 +19,25 @@ public void Setup()
|
||||
{
|
||||
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess())
|
||||
.Returns(new ProcessInfo() { Id = CURRENT_PROCESS_ID });
|
||||
|
||||
Mocker.GetMock<IProcessProvider>()
|
||||
.Setup(s => s.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME))
|
||||
.Returns(new List<ProcessInfo>());
|
||||
|
||||
Mocker.GetMock<IProcessProvider>()
|
||||
.Setup(s => s.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME))
|
||||
.Returns(new List<ProcessInfo>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_continue_if_only_instance()
|
||||
{
|
||||
Mocker.GetMock<INzbDroneProcessProvider>().Setup(c => c.FindNzbDroneProcesses())
|
||||
.Returns(new List<ProcessInfo>
|
||||
{
|
||||
new ProcessInfo{Id = CURRENT_PROCESS_ID}
|
||||
});
|
||||
|
||||
Mocker.GetMock<IProcessProvider>()
|
||||
.Setup(c => c.FindProcessByName(It.Is<String>(f => f.Contains("NzbDrone"))))
|
||||
.Returns(new List<ProcessInfo>
|
||||
{
|
||||
new ProcessInfo {Id = CURRENT_PROCESS_ID}
|
||||
});
|
||||
|
||||
Subject.PreventStartIfAlreadyRunning();
|
||||
|
||||
@ -38,13 +47,13 @@ public void should_continue_if_only_instance()
|
||||
[Test]
|
||||
public void should_enforce_if_another_console_is_running()
|
||||
{
|
||||
Mocker.GetMock<INzbDroneProcessProvider>()
|
||||
.Setup(c => c.FindNzbDroneProcesses())
|
||||
.Returns(new List<ProcessInfo>
|
||||
{
|
||||
new ProcessInfo{Id = 10},
|
||||
new ProcessInfo{Id = CURRENT_PROCESS_ID}
|
||||
});
|
||||
Mocker.GetMock<IProcessProvider>()
|
||||
.Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME))
|
||||
.Returns(new List<ProcessInfo>
|
||||
{
|
||||
new ProcessInfo {Id = 10},
|
||||
new ProcessInfo {Id = CURRENT_PROCESS_ID}
|
||||
});
|
||||
|
||||
Assert.Throws<TerminateApplicationException>(() => Subject.PreventStartIfAlreadyRunning());
|
||||
Mocker.GetMock<IBrowserService>().Verify(c => c.LaunchWebUI(), Times.Once());
|
||||
@ -54,14 +63,14 @@ public void should_enforce_if_another_console_is_running()
|
||||
[Test]
|
||||
public void should_return_false_if_another_gui_is_running()
|
||||
{
|
||||
Mocker.GetMock<INzbDroneProcessProvider>()
|
||||
.Setup(c => c.FindNzbDroneProcesses())
|
||||
.Returns(new List<ProcessInfo>
|
||||
{
|
||||
new ProcessInfo{Id = CURRENT_PROCESS_ID},
|
||||
new ProcessInfo{Id = 10}
|
||||
|
||||
});
|
||||
Mocker.GetMock<IProcessProvider>()
|
||||
.Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME))
|
||||
.Returns(new List<ProcessInfo>
|
||||
{
|
||||
new ProcessInfo {Id = CURRENT_PROCESS_ID},
|
||||
new ProcessInfo {Id = 10}
|
||||
|
||||
});
|
||||
|
||||
Assert.Throws<TerminateApplicationException>(() => Subject.PreventStartIfAlreadyRunning());
|
||||
Mocker.GetMock<IBrowserService>().Verify(c => c.LaunchWebUI(), Times.Once());
|
||||
|
@ -168,25 +168,25 @@ public void Config_path_test()
|
||||
[Test]
|
||||
public void Sanbox()
|
||||
{
|
||||
GetIAppDirectoryInfo().GetUpdateSandboxFolder().Should().BeEquivalentTo(@"C:\Temp\Nzbdrone_update\".AsOsAgnostic());
|
||||
GetIAppDirectoryInfo().GetUpdateSandboxFolder().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\".AsOsAgnostic());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUpdatePackageFolder()
|
||||
{
|
||||
GetIAppDirectoryInfo().GetUpdatePackageFolder().Should().BeEquivalentTo(@"C:\Temp\Nzbdrone_update\NzbDrone\".AsOsAgnostic());
|
||||
GetIAppDirectoryInfo().GetUpdatePackageFolder().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\NzbDrone\".AsOsAgnostic());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUpdateClientFolder()
|
||||
{
|
||||
GetIAppDirectoryInfo().GetUpdateClientFolder().Should().BeEquivalentTo(@"C:\Temp\Nzbdrone_update\NzbDrone\NzbDrone.Update\".AsOsAgnostic());
|
||||
GetIAppDirectoryInfo().GetUpdateClientFolder().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\NzbDrone\NzbDrone.Update\".AsOsAgnostic());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUpdateClientExePath()
|
||||
{
|
||||
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\Nzbdrone_update\NzbDrone.Update.exe".AsOsAgnostic());
|
||||
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\NzbDrone.Update.exe".AsOsAgnostic());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -447,5 +447,15 @@ public string GetVolumeLabel(string path)
|
||||
|
||||
return driveInfo.VolumeLabel;
|
||||
}
|
||||
|
||||
public FileStream StreamFile(string path)
|
||||
{
|
||||
if (!FileExists(path))
|
||||
{
|
||||
throw new FileNotFoundException("Unable to find file: " + path, path);
|
||||
}
|
||||
|
||||
return new FileStream(path, FileMode.Open);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ public interface IDiskProvider
|
||||
void InheritFolderPermissions(string filename);
|
||||
void SetPermissions(string path, string mask, string user, string group);
|
||||
long? GetTotalSize(string path);
|
||||
|
||||
DateTime FolderGetLastWrite(string path);
|
||||
DateTime FileGetLastWrite(string path);
|
||||
DateTime FileGetLastWriteUtc(string path);
|
||||
@ -44,5 +43,6 @@ public interface IDiskProvider
|
||||
void EmptyFolder(string path);
|
||||
string[] GetFixedDrives();
|
||||
string GetVolumeLabel(string path);
|
||||
FileStream StreamFile(string path);
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Common.EnvironmentInfo
|
||||
{
|
||||
|
@ -105,7 +105,6 @@
|
||||
<Compile Include="Messaging\IEvent.cs" />
|
||||
<Compile Include="Messaging\IMessage.cs" />
|
||||
<Compile Include="PathEqualityComparer.cs" />
|
||||
<Compile Include="Processes\INzbDroneProcessProvider.cs" />
|
||||
<Compile Include="Processes\PidFileProvider.cs" />
|
||||
<Compile Include="Processes\ProcessOutput.cs" />
|
||||
<Compile Include="RateGate.cs" />
|
||||
|
@ -13,10 +13,10 @@ public static class PathExtensions
|
||||
private const string NZBDRONE_LOG_DB = "logs.db";
|
||||
private const string BACKUP_ZIP_FILE = "NzbDrone_Backup.zip";
|
||||
private const string NLOG_CONFIG_FILE = "nlog.config";
|
||||
private const string UPDATE_CLIENT_EXE = "nzbdrone.update.exe";
|
||||
private const string UPDATE_CLIENT_EXE = "NzbDrone.Update.exe";
|
||||
|
||||
private static readonly string UPDATE_SANDBOX_FOLDER_NAME = "nzbdrone_update" + Path.DirectorySeparatorChar;
|
||||
private static readonly string UPDATE_PACKAGE_FOLDER_NAME = "nzbdrone" + Path.DirectorySeparatorChar;
|
||||
private static readonly string UPDATE_PACKAGE_FOLDER_NAME = "NzbDrone" + Path.DirectorySeparatorChar;
|
||||
private static readonly string UPDATE_BACKUP_FOLDER_NAME = "nzbdrone_backup" + Path.DirectorySeparatorChar;
|
||||
private static readonly string UPDATE_BACKUP_APPDATA_FOLDER_NAME = "nzbdrone_appdata_backup" + Path.DirectorySeparatorChar;
|
||||
private static readonly string UPDATE_CLIENT_FOLDER_NAME = "NzbDrone.Update" + Path.DirectorySeparatorChar;
|
||||
|
@ -1,10 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Model;
|
||||
|
||||
namespace NzbDrone.Common.Processes
|
||||
{
|
||||
public interface INzbDroneProcessProvider
|
||||
{
|
||||
List<ProcessInfo> FindNzbDroneProcesses();
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ public interface IProcessProvider
|
||||
void SetPriority(int processId, ProcessPriorityClass priority);
|
||||
void KillAll(string processName);
|
||||
void Kill(int processId);
|
||||
bool Exists(string processName);
|
||||
Boolean Exists(string processName);
|
||||
ProcessPriorityClass GetCurrentProcessPriority();
|
||||
Process Start(string path, string args = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null);
|
||||
Process SpawnNewProcess(string path, string args = null);
|
||||
@ -35,20 +35,12 @@ public class ProcessProvider : IProcessProvider
|
||||
public const string NZB_DRONE_PROCESS_NAME = "NzbDrone";
|
||||
public const string NZB_DRONE_CONSOLE_PROCESS_NAME = "NzbDrone.Console";
|
||||
|
||||
private static List<Process> GetProcessesByName(string name)
|
||||
{
|
||||
var monoProcesses = Process.GetProcessesByName("mono")
|
||||
.Where(process => process.Modules.Cast<ProcessModule>().Any(module => module.ModuleName.ToLower() == name.ToLower() + ".exe"));
|
||||
return Process.GetProcessesByName(name)
|
||||
.Union(monoProcesses).ToList();
|
||||
}
|
||||
|
||||
public ProcessInfo GetCurrentProcess()
|
||||
{
|
||||
return ConvertToProcessInfo(Process.GetCurrentProcess());
|
||||
}
|
||||
|
||||
public bool Exists(string processName)
|
||||
public Boolean Exists(string processName)
|
||||
{
|
||||
return GetProcessesByName(processName).Any();
|
||||
}
|
||||
@ -78,7 +70,7 @@ public ProcessInfo GetProcessById(int id)
|
||||
|
||||
public List<ProcessInfo> FindProcessByName(string name)
|
||||
{
|
||||
return Process.GetProcessesByName(name).Select(ConvertToProcessInfo).Where(c => c != null).ToList();
|
||||
return GetProcessesByName(name).Select(ConvertToProcessInfo).Where(c => c != null).ToList();
|
||||
}
|
||||
|
||||
public void OpenDefaultBrowser(string url)
|
||||
@ -203,12 +195,40 @@ public void SetPriority(int processId, ProcessPriorityClass priority)
|
||||
process.PriorityClass = priority;
|
||||
}
|
||||
|
||||
public void Kill(int processId)
|
||||
{
|
||||
var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId);
|
||||
|
||||
if (process == null)
|
||||
{
|
||||
Logger.Warn("Cannot find process with id: {0}", processId);
|
||||
return;
|
||||
}
|
||||
|
||||
process.Refresh();
|
||||
|
||||
if (process.HasExited)
|
||||
{
|
||||
Logger.Debug("Process has already exited");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info("[{0}]: Killing process", process.Id);
|
||||
process.Kill();
|
||||
Logger.Info("[{0}]: Waiting for exit", process.Id);
|
||||
process.WaitForExit();
|
||||
Logger.Info("[{0}]: Process terminated successfully", process.Id);
|
||||
}
|
||||
|
||||
public void KillAll(string processName)
|
||||
{
|
||||
var processToKill = GetProcessesByName(processName);
|
||||
var processes = GetProcessesByName(processName);
|
||||
|
||||
foreach (var processInfo in processToKill)
|
||||
Logger.Debug("Found {0} processes to kill", processes.Count);
|
||||
|
||||
foreach (var processInfo in processes)
|
||||
{
|
||||
Logger.Debug("Killing process: {0} [{1}]", processInfo.Id, processInfo.ProcessName);
|
||||
Kill(processInfo.Id);
|
||||
}
|
||||
}
|
||||
@ -254,29 +274,23 @@ private static string GetExeFileName(Process process)
|
||||
return process.Modules.Cast<ProcessModule>().FirstOrDefault(module => module.ModuleName.ToLower().EndsWith(".exe")).FileName;
|
||||
}
|
||||
|
||||
public void Kill(int processId)
|
||||
private static List<Process> GetProcessesByName(string name)
|
||||
{
|
||||
var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId);
|
||||
//TODO: move this to an OS specific class
|
||||
|
||||
if (process == null)
|
||||
{
|
||||
Logger.Warn("Cannot find process with id: {0}", processId);
|
||||
return;
|
||||
}
|
||||
var monoProcesses = Process.GetProcessesByName("mono")
|
||||
.Union(Process.GetProcessesByName("mono-sgen"))
|
||||
.Where(process =>
|
||||
process.Modules.Cast<ProcessModule>()
|
||||
.Any(module =>
|
||||
module.ModuleName.ToLower() == name.ToLower() + ".exe"));
|
||||
|
||||
process.Refresh();
|
||||
var processes = Process.GetProcessesByName(name)
|
||||
.Union(monoProcesses).ToList();
|
||||
|
||||
if (process.HasExited)
|
||||
{
|
||||
Logger.Debug("Process has already exited");
|
||||
return;
|
||||
}
|
||||
Logger.Debug("Found {0} processes with the name: {1}", processes.Count, name);
|
||||
|
||||
Logger.Info("[{0}]: Killing process", process.Id);
|
||||
process.Kill();
|
||||
Logger.Info("[{0}]: Waiting for exit", process.Id);
|
||||
process.WaitForExit();
|
||||
Logger.Info("[{0}]: Process terminated successfully", process.Id);
|
||||
return processes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,5 +12,5 @@
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
|
||||
[assembly: AssemblyVersion("10.0.0.*")]
|
||||
[assembly: AssemblyVersion("2.0.0.1")]
|
||||
[assembly: AssemblyFileVersion("10.0.0.*")]
|
||||
|
@ -13,6 +13,15 @@ public static void Register()
|
||||
|
||||
private static bool ValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
|
||||
{
|
||||
var request = sender as HttpWebRequest;
|
||||
|
||||
if (request != null &&
|
||||
request.Address.OriginalString.ContainsIgnoreCase("nzbdrone.com") &&
|
||||
sslpolicyerrors != SslPolicyErrors.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
namespace NzbDrone.Common
|
||||
{
|
||||
@ -65,5 +66,20 @@ public static bool IsNullOrWhiteSpace(this string text)
|
||||
{
|
||||
return String.IsNullOrWhiteSpace(text);
|
||||
}
|
||||
|
||||
public static bool ContainsIgnoreCase(this string text, string contains)
|
||||
{
|
||||
return text.IndexOf(contains, StringComparison.InvariantCultureIgnoreCase) > -1;
|
||||
}
|
||||
|
||||
public static string WrapInQuotes(this string text)
|
||||
{
|
||||
if (!text.Contains(" "))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
return "\"" + text + "\"";
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Model;
|
||||
using NzbDrone.Common.Processes;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Update;
|
||||
using NzbDrone.Core.Update.Commands;
|
||||
@ -49,12 +50,28 @@ public void Setup()
|
||||
|
||||
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.TempFolder).Returns(TempFolder);
|
||||
Mocker.GetMock<ICheckUpdateService>().Setup(c => c.AvailableUpdate()).Returns(_updatePackage);
|
||||
Mocker.GetMock<IVerifyUpdates>().Setup(c => c.Verify(It.IsAny<UpdatePackage>(), It.IsAny<String>())).Returns(true);
|
||||
|
||||
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 });
|
||||
Mocker.GetMock<IRuntimeInfo>().Setup(c => c.ExecutingApplication).Returns(@"C:\Test\NzbDrone.exe");
|
||||
|
||||
_sandboxFolder = Mocker.GetMock<IAppFolderInfo>().Object.GetUpdateSandboxFolder();
|
||||
}
|
||||
|
||||
private void GivenInstallScript(string path)
|
||||
{
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.SetupGet(s => s.UpdateMechanism)
|
||||
.Returns(UpdateMechanism.Script);
|
||||
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.SetupGet(s => s.UpdateScriptPath)
|
||||
.Returns(path);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.FileExists(path, true))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_sandbox_before_update_if_folder_exists()
|
||||
@ -77,7 +94,6 @@ public void should_not_delete_sandbox_before_update_if_folder_doesnt_exists()
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.DeleteFolder(_sandboxFolder, true), Times.Never());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Should_download_update_package()
|
||||
{
|
||||
@ -118,11 +134,11 @@ public void should_start_update_client()
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
Mocker.GetMock<IProcessProvider>()
|
||||
.Verify(c => c.Start(It.IsAny<string>(), "12", null, null), Times.Once());
|
||||
.Verify(c => c.Start(It.IsAny<string>(), It.Is<String>(s => s.StartsWith("12")), null, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void when_no_updates_are_available_should_return_without_error_or_warnings()
|
||||
public void should_return_without_error_or_warnings_when_no_updates_are_available()
|
||||
{
|
||||
Mocker.GetMock<ICheckUpdateService>().Setup(c => c.AvailableUpdate()).Returns<UpdatePackage>(null);
|
||||
|
||||
@ -132,6 +148,75 @@ public void when_no_updates_are_available_should_return_without_error_or_warning
|
||||
ExceptionVerification.AssertNoUnexpectedLogs();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_extract_if_verification_fails()
|
||||
{
|
||||
Mocker.GetMock<IVerifyUpdates>().Setup(c => c.Verify(It.IsAny<UpdatePackage>(), It.IsAny<String>())).Returns(false);
|
||||
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
Mocker.GetMock<IArchiveService>().Verify(v => v.Extract(It.IsAny<String>(), It.IsAny<String>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Platform("Mono")]
|
||||
public void should_run_script_if_configured()
|
||||
{
|
||||
const string scriptPath = "/tmp/nzbdrone/update.sh";
|
||||
|
||||
GivenInstallScript(scriptPath);
|
||||
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Platform("Mono")]
|
||||
public void should_throw_if_script_is_not_set()
|
||||
{
|
||||
const string scriptPath = "/tmp/nzbdrone/update.sh";
|
||||
|
||||
GivenInstallScript("");
|
||||
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Platform("Mono")]
|
||||
public void should_throw_if_script_is_null()
|
||||
{
|
||||
const string scriptPath = "/tmp/nzbdrone/update.sh";
|
||||
|
||||
GivenInstallScript(null);
|
||||
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Platform("Mono")]
|
||||
public void should_throw_if_script_path_does_not_exist()
|
||||
{
|
||||
const string scriptPath = "/tmp/nzbdrone/update.sh";
|
||||
|
||||
GivenInstallScript(scriptPath);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.FileExists(scriptPath, true))
|
||||
.Returns(false);
|
||||
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
[IntegrationTest]
|
||||
public void Should_download_and_extract_to_temp_folder()
|
||||
|
@ -11,6 +11,7 @@
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
|
||||
namespace NzbDrone.Core.Configuration
|
||||
@ -30,11 +31,13 @@ public interface IConfigFileProvider : IHandleAsync<ApplicationStartedEvent>,
|
||||
string Password { get; }
|
||||
string LogLevel { get; }
|
||||
string Branch { get; }
|
||||
bool AutoUpdate { get; }
|
||||
string ApiKey { get; }
|
||||
bool Torrent { get; }
|
||||
string SslCertHash { get; }
|
||||
string UrlBase { get; }
|
||||
Boolean UpdateAutomatically { get; }
|
||||
UpdateMechanism UpdateMechanism { get; }
|
||||
String UpdateScriptPath { get; }
|
||||
}
|
||||
|
||||
public class ConfigFileProvider : IConfigFileProvider
|
||||
@ -141,11 +144,6 @@ public string Branch
|
||||
get { return GetValue("Branch", "master").ToLowerInvariant(); }
|
||||
}
|
||||
|
||||
public bool AutoUpdate
|
||||
{
|
||||
get { return GetValueBoolean("AutoUpdate", false, persist: false); }
|
||||
}
|
||||
|
||||
public string Username
|
||||
{
|
||||
get { return GetValue("Username", ""); }
|
||||
@ -181,6 +179,21 @@ public string UrlBase
|
||||
}
|
||||
}
|
||||
|
||||
public bool UpdateAutomatically
|
||||
{
|
||||
get { return GetValueBoolean("UpdateAutomatically", false, false); }
|
||||
}
|
||||
|
||||
public UpdateMechanism UpdateMechanism
|
||||
{
|
||||
get { return GetValueEnum("UpdateMechanism", UpdateMechanism.BuiltIn, false); }
|
||||
}
|
||||
|
||||
public string UpdateScriptPath
|
||||
{
|
||||
get { return GetValue("UpdateScriptPath", "", false ); }
|
||||
}
|
||||
|
||||
public int GetValueInt(string key, int defaultValue)
|
||||
{
|
||||
return Convert.ToInt32(GetValue(key, defaultValue));
|
||||
@ -191,9 +204,9 @@ public bool GetValueBoolean(string key, bool defaultValue, bool persist = true)
|
||||
return Convert.ToBoolean(GetValue(key, defaultValue, persist));
|
||||
}
|
||||
|
||||
public T GetValueEnum<T>(string key, T defaultValue)
|
||||
public T GetValueEnum<T>(string key, T defaultValue, bool persist = true)
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), true);
|
||||
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), persist);
|
||||
}
|
||||
|
||||
public string GetValue(string key, object defaultValue, bool persist = true)
|
||||
@ -210,7 +223,9 @@ public string GetValue(string key, object defaultValue, bool persist = true)
|
||||
var valueHolder = parentContainer.Descendants(key).ToList();
|
||||
|
||||
if (valueHolder.Count() == 1)
|
||||
{
|
||||
return valueHolder.First().Value.Trim();
|
||||
}
|
||||
|
||||
//Save the value
|
||||
if (persist)
|
||||
|
@ -6,6 +6,7 @@
|
||||
using NzbDrone.Core.Configuration.Events;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
|
||||
namespace NzbDrone.Core.Configuration
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Update;
|
||||
|
||||
namespace NzbDrone.Core.Configuration
|
||||
{
|
||||
@ -23,7 +24,6 @@ public interface IConfigService
|
||||
Int32 BlacklistRetryInterval { get; set; }
|
||||
Int32 BlacklistRetryLimit { get; set; }
|
||||
|
||||
|
||||
//Media Management
|
||||
Boolean AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; }
|
||||
String RecycleBin { get; set; }
|
||||
|
@ -693,10 +693,13 @@
|
||||
<Compile Include="Update\InstallUpdateService.cs" />
|
||||
<Compile Include="Update\RecentUpdateProvider.cs" />
|
||||
<Compile Include="Update\UpdateChanges.cs" />
|
||||
<Compile Include="Update\UpdateMechanism.cs" />
|
||||
<Compile Include="Update\UpdatePackageAvailable.cs" />
|
||||
<Compile Include="Update\UpdatePackageProvider.cs" />
|
||||
<Compile Include="Update\UpdatePackage.cs" />
|
||||
<Compile Include="Update\UpdateCheckService.cs" />
|
||||
<Compile Include="Update\UpdateVerification.cs" />
|
||||
<Compile Include="Update\UpdateVerificationFailedException.cs" />
|
||||
<Compile Include="Validation\Paths\SeriesExistsValidator.cs" />
|
||||
<Compile Include="Validation\Paths\RootFolderValidator.cs" />
|
||||
<Compile Include="Validation\Paths\DroneFactoryValidator.cs" />
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core
|
||||
@ -7,17 +8,28 @@ public static class Security
|
||||
{
|
||||
public static string SHA256Hash(this string input)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
using (var hash = SHA256Managed.Create())
|
||||
{
|
||||
var enc = Encoding.UTF8;
|
||||
var result = hash.ComputeHash(enc.GetBytes(input));
|
||||
return GetHash(hash.ComputeHash(enc.GetBytes(input)));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var b in result)
|
||||
{
|
||||
stringBuilder.Append(b.ToString("x2"));
|
||||
}
|
||||
public static string SHA256Hash(this Stream input)
|
||||
{
|
||||
using (var hash = SHA256Managed.Create())
|
||||
{
|
||||
return GetHash(hash.ComputeHash(input));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetHash(byte[] bytes)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
stringBuilder.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
|
@ -6,6 +6,7 @@
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Processes;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Update.Commands;
|
||||
@ -27,18 +28,31 @@ public class InstallUpdateService : IInstallUpdates, IExecute<ApplicationUpdateC
|
||||
private readonly IHttpProvider _httpProvider;
|
||||
private readonly IArchiveService _archiveService;
|
||||
private readonly IProcessProvider _processProvider;
|
||||
private readonly IVerifyUpdates _updateVerifier;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly IRuntimeInfo _runtimeInfo;
|
||||
|
||||
|
||||
public InstallUpdateService(ICheckUpdateService checkUpdateService, IAppFolderInfo appFolderInfo,
|
||||
IDiskProvider diskProvider, IHttpProvider httpProvider,
|
||||
IArchiveService archiveService, IProcessProvider processProvider, Logger logger)
|
||||
IArchiveService archiveService, IProcessProvider processProvider,
|
||||
IVerifyUpdates updateVerifier,
|
||||
IConfigFileProvider configFileProvider,
|
||||
IRuntimeInfo runtimeInfo, Logger logger)
|
||||
{
|
||||
if (configFileProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException("configFileProvider");
|
||||
}
|
||||
_checkUpdateService = checkUpdateService;
|
||||
_appFolderInfo = appFolderInfo;
|
||||
_diskProvider = diskProvider;
|
||||
_httpProvider = httpProvider;
|
||||
_archiveService = archiveService;
|
||||
_processProvider = processProvider;
|
||||
_updateVerifier = updateVerifier;
|
||||
_configFileProvider = configFileProvider;
|
||||
_runtimeInfo = runtimeInfo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -60,19 +74,34 @@ public void InstallUpdate(UpdatePackage updatePackage)
|
||||
_logger.Debug("Downloading update package from [{0}] to [{1}]", updatePackage.Url, packageDestination);
|
||||
_httpProvider.DownloadFile(updatePackage.Url, packageDestination);
|
||||
|
||||
_logger.ProgressInfo("Verifying update package");
|
||||
|
||||
if (!_updateVerifier.Verify(updatePackage, packageDestination))
|
||||
{
|
||||
_logger.Error("Update package is invalid");
|
||||
throw new UpdateVerificationFailedException("Update file '{0}' is invalid", packageDestination);
|
||||
}
|
||||
|
||||
_logger.Info("Update package verified successfully");
|
||||
|
||||
_logger.ProgressInfo("Extracting Update package");
|
||||
_archiveService.Extract(packageDestination, updateSandboxFolder);
|
||||
_logger.Info("Update package extracted successfully");
|
||||
|
||||
if (OsInfo.IsMono && _configFileProvider.UpdateMechanism == UpdateMechanism.Script)
|
||||
{
|
||||
InstallUpdateWithScript(updateSandboxFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Info("Preparing client");
|
||||
_diskProvider.MoveFolder(_appFolderInfo.GetUpdateClientFolder(),
|
||||
updateSandboxFolder);
|
||||
|
||||
_logger.Info("Starting update client {0}", _appFolderInfo.GetUpdateClientExePath());
|
||||
|
||||
_logger.ProgressInfo("NzbDrone will restart shortly.");
|
||||
|
||||
_processProvider.Start(_appFolderInfo.GetUpdateClientExePath(), _processProvider.GetCurrentProcess().Id.ToString());
|
||||
_processProvider.Start(_appFolderInfo.GetUpdateClientExePath(), GetUpdaterArgs(updateSandboxFolder));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -80,6 +109,36 @@ public void InstallUpdate(UpdatePackage updatePackage)
|
||||
}
|
||||
}
|
||||
|
||||
private void InstallUpdateWithScript(String updateSandboxFolder)
|
||||
{
|
||||
var scriptPath = _configFileProvider.UpdateScriptPath;
|
||||
|
||||
if (scriptPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ArgumentException("Update Script has not been defined");
|
||||
}
|
||||
|
||||
if (!_diskProvider.FileExists(scriptPath, true))
|
||||
{
|
||||
var message = String.Format("Update Script: '{0}' does not exist", scriptPath);
|
||||
throw new FileNotFoundException(message, scriptPath);
|
||||
}
|
||||
|
||||
_logger.Info("Removing NzbDrone.Update");
|
||||
_diskProvider.DeleteFolder(_appFolderInfo.GetUpdateClientFolder(), true);
|
||||
|
||||
_logger.ProgressInfo("Starting update script: {0}", _configFileProvider.UpdateScriptPath);
|
||||
_processProvider.Start(scriptPath, GetUpdaterArgs(updateSandboxFolder.WrapInQuotes()));
|
||||
}
|
||||
|
||||
private string GetUpdaterArgs(string updateSandboxFolder)
|
||||
{
|
||||
var processId = _processProvider.GetCurrentProcess().Id.ToString();
|
||||
var executingApplication = _runtimeInfo.ExecutingApplication;
|
||||
|
||||
return String.Join(" ", processId, updateSandboxFolder.WrapInQuotes(), executingApplication.WrapInQuotes());
|
||||
}
|
||||
|
||||
public void Execute(ApplicationUpdateCommand message)
|
||||
{
|
||||
_logger.ProgressDebug("Checking for updates");
|
||||
|
@ -18,7 +18,9 @@ public class CheckUpdateService : ICheckUpdateService
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
||||
public CheckUpdateService(IUpdatePackageProvider updatePackageProvider, IConfigFileProvider configFileProvider, Logger logger)
|
||||
public CheckUpdateService(IUpdatePackageProvider updatePackageProvider,
|
||||
IConfigFileProvider configFileProvider,
|
||||
Logger logger)
|
||||
{
|
||||
_updatePackageProvider = updatePackageProvider;
|
||||
_configFileProvider = configFileProvider;
|
||||
@ -27,7 +29,10 @@ public CheckUpdateService(IUpdatePackageProvider updatePackageProvider, IConfigF
|
||||
|
||||
public UpdatePackage AvailableUpdate()
|
||||
{
|
||||
if (OsInfo.IsMono) return null;
|
||||
if (OsInfo.IsMono && !_configFileProvider.UpdateAutomatically)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var latestAvailable = _updatePackageProvider.GetLatestUpdate(_configFileProvider.Branch, BuildInfo.Version);
|
||||
|
||||
|
8
src/NzbDrone.Core/Update/UpdateMechanism.cs
Normal file
8
src/NzbDrone.Core/Update/UpdateMechanism.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace NzbDrone.Core.Update
|
||||
{
|
||||
public enum UpdateMechanism
|
||||
{
|
||||
BuiltIn = 0,
|
||||
Script = 1
|
||||
}
|
||||
}
|
@ -11,5 +11,6 @@ public class UpdatePackage
|
||||
public String FileName { get; set; }
|
||||
public String Url { get; set; }
|
||||
public UpdateChanges Changes { get; set; }
|
||||
public String Hash { get; set; }
|
||||
}
|
||||
}
|
||||
|
30
src/NzbDrone.Core/Update/UpdateVerification.cs
Normal file
30
src/NzbDrone.Core/Update/UpdateVerification.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Update
|
||||
{
|
||||
public interface IVerifyUpdates
|
||||
{
|
||||
Boolean Verify(UpdatePackage updatePackage, String packagePath);
|
||||
}
|
||||
|
||||
public class UpdateVerification : IVerifyUpdates
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
|
||||
public UpdateVerification(IDiskProvider diskProvider)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
}
|
||||
|
||||
public Boolean Verify(UpdatePackage updatePackage, String packagePath)
|
||||
{
|
||||
using (var fileStream = _diskProvider.StreamFile(packagePath))
|
||||
{
|
||||
var hash = fileStream.SHA256Hash();
|
||||
|
||||
return hash.Equals(updatePackage.Hash, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Update
|
||||
{
|
||||
public class UpdateVerificationFailedException : NzbDroneException
|
||||
{
|
||||
public UpdateVerificationFailedException(string message, params object[] args) : base(message, args)
|
||||
{
|
||||
}
|
||||
|
||||
public UpdateVerificationFailedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -16,17 +16,14 @@ public class SingleInstancePolicy : ISingleInstancePolicy
|
||||
{
|
||||
private readonly IProcessProvider _processProvider;
|
||||
private readonly IBrowserService _browserService;
|
||||
private readonly INzbDroneProcessProvider _nzbDroneProcessProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SingleInstancePolicy(IProcessProvider processProvider,
|
||||
IBrowserService browserService,
|
||||
INzbDroneProcessProvider nzbDroneProcessProvider,
|
||||
Logger logger)
|
||||
{
|
||||
_processProvider = processProvider;
|
||||
_browserService = browserService;
|
||||
_nzbDroneProcessProvider = nzbDroneProcessProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -56,10 +53,11 @@ private bool IsAlreadyRunning()
|
||||
private List<int> GetOtherNzbDroneProcessIds()
|
||||
{
|
||||
var currentId = _processProvider.GetCurrentProcess().Id;
|
||||
var otherProcesses = _nzbDroneProcessProvider.FindNzbDroneProcesses()
|
||||
.Select(c => c.Id)
|
||||
.Except(new[] {currentId})
|
||||
.ToList();
|
||||
var otherProcesses = _processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME)
|
||||
.Union(_processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME))
|
||||
.Select(c => c.Id)
|
||||
.Except(new[] {currentId})
|
||||
.ToList();
|
||||
|
||||
if (otherProcesses.Any())
|
||||
{
|
||||
|
@ -70,7 +70,6 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="DiskProvider.cs" />
|
||||
<Compile Include="LinuxPermissionsException.cs" />
|
||||
<Compile Include="NzbDroneProcessProvider.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -1,43 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Model;
|
||||
using NzbDrone.Common.Processes;
|
||||
|
||||
namespace NzbDrone.Mono
|
||||
{
|
||||
public class NzbDroneProcessProvider : INzbDroneProcessProvider
|
||||
{
|
||||
private readonly IProcessProvider _processProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NzbDroneProcessProvider(IProcessProvider processProvider, Logger logger)
|
||||
{
|
||||
_processProvider = processProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ProcessInfo> FindNzbDroneProcesses()
|
||||
{
|
||||
var monoProcesses = _processProvider.FindProcessByName("mono");
|
||||
|
||||
return monoProcesses.Where(c =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var processArgs = _processProvider.StartAndCapture("ps", String.Format("-p {0} -o args=", c.Id));
|
||||
|
||||
return processArgs.Standard.Any(p => p.Contains(ProcessProvider.NZB_DRONE_PROCESS_NAME + ".exe") ||
|
||||
p.Contains(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME + ".exe"));
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.WarnException("Error getting process arguments", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ protected static void InitLogging()
|
||||
LogManager.Configuration = new LoggingConfiguration();
|
||||
var consoleTarget = new ConsoleTarget { Layout = "${level}: ${message} ${exception}" };
|
||||
LogManager.Configuration.AddTarget(consoleTarget.GetType().Name, consoleTarget);
|
||||
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, consoleTarget));
|
||||
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, consoleTarget));
|
||||
|
||||
RegisterExceptionVerification();
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ public class DummyApp
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Dummy process. ID:{0} Path:{1}", Process.GetCurrentProcess().Id, Process.GetCurrentProcess().MainModule.FileName);
|
||||
var process = Process.GetCurrentProcess();
|
||||
|
||||
Console.WriteLine("Dummy process. ID:{0} Name:{1} Path:{2}", process.Id, process.ProcessName, process.MainModule.FileName);
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@
|
||||
<Link>Properties\SharedAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AppType.cs" />
|
||||
<Compile Include="UpdateStartupContext.cs" />
|
||||
<Compile Include="UpdateApp.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="UpdateContainerBuilder.cs" />
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
@ -50,24 +52,60 @@ public static void Main(string[] args)
|
||||
|
||||
public void Start(string[] args)
|
||||
{
|
||||
var processId = ParseProcessId(args);
|
||||
var startupContext = ParseArgs(args);
|
||||
string targetFolder;
|
||||
|
||||
var exeFileInfo = new FileInfo(_processProvider.GetProcessById(processId).StartPath);
|
||||
var targetFolder = exeFileInfo.Directory.FullName;
|
||||
if (startupContext.ExecutingApplication.IsNullOrWhiteSpace())
|
||||
{
|
||||
var exeFileInfo = new FileInfo(_processProvider.GetProcessById(startupContext.ProcessId).StartPath);
|
||||
targetFolder = exeFileInfo.Directory.FullName;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var exeFileInfo = new FileInfo(startupContext.ExecutingApplication);
|
||||
targetFolder = exeFileInfo.Directory.FullName;
|
||||
}
|
||||
|
||||
logger.Info("Starting update process. Target Path:{0}", targetFolder);
|
||||
_installUpdateService.Start(targetFolder);
|
||||
}
|
||||
|
||||
private int ParseProcessId(string[] args)
|
||||
private UpdateStartupContext ParseArgs(string[] args)
|
||||
{
|
||||
int id;
|
||||
if (args == null || !Int32.TryParse(args[0], out id) || id <= 0)
|
||||
if (args == null || !args.Any())
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("args", "Invalid process ID");
|
||||
throw new ArgumentOutOfRangeException("args", "args must be specified");
|
||||
}
|
||||
|
||||
logger.Debug("NzbDrone processId:{0}", id);
|
||||
var startupContext = new UpdateStartupContext
|
||||
{
|
||||
ProcessId = ParseProcessId(args[0])
|
||||
};
|
||||
|
||||
if (args.Count() == 1)
|
||||
{
|
||||
return startupContext;
|
||||
}
|
||||
|
||||
if (args.Count() >= 3)
|
||||
{
|
||||
startupContext.UpdateLocation = args[1];
|
||||
startupContext.ExecutingApplication = args[2];
|
||||
}
|
||||
|
||||
return startupContext;
|
||||
}
|
||||
|
||||
private int ParseProcessId(string arg)
|
||||
{
|
||||
int id;
|
||||
if (!Int32.TryParse(arg, out id) || id <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("arg", "Invalid process ID");
|
||||
}
|
||||
|
||||
logger.Debug("NzbDrone process ID: {0}", id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Processes;
|
||||
|
||||
namespace NzbDrone.Update.UpdateEngine
|
||||
@ -21,6 +22,12 @@ public DetectApplicationType(IServiceProvider serviceProvider, IProcessProvider
|
||||
|
||||
public AppType GetAppType()
|
||||
{
|
||||
if (OsInfo.IsMono)
|
||||
{
|
||||
//Tehcnically its the console, but its been renamed for mono (Linux/OS X)
|
||||
return AppType.Normal;
|
||||
}
|
||||
|
||||
if (_serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME)
|
||||
&& _serviceProvider.IsServiceRunning(ServiceProvider.NZBDRONE_SERVICE_NAME))
|
||||
{
|
||||
|
@ -82,7 +82,6 @@ public void Start(string installationFolder)
|
||||
_backupAndRestore.Restore(installationFolder);
|
||||
_logger.FatalException("Failed to copy upgrade package to target folder.", e);
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Processes;
|
||||
using IServiceProvider = NzbDrone.Common.IServiceProvider;
|
||||
|
||||
@ -26,6 +27,15 @@ public TerminateNzbDrone(IServiceProvider serviceProvider, IProcessProvider proc
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
if (OsInfo.IsMono)
|
||||
{
|
||||
_logger.Info("Stopping all instances");
|
||||
_processProvider.KillAll(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME);
|
||||
_processProvider.KillAll(ProcessProvider.NZB_DRONE_PROCESS_NAME);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Info("Stopping all running services");
|
||||
|
||||
if (_serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME)
|
||||
@ -35,7 +45,6 @@ public void Terminate()
|
||||
{
|
||||
_logger.Info("NzbDrone Service is installed and running");
|
||||
_serviceProvider.Stop(ServiceProvider.NZBDRONE_SERVICE_NAME);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
11
src/NzbDrone.Update/UpdateStartupContext.cs
Normal file
11
src/NzbDrone.Update/UpdateStartupContext.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Update
|
||||
{
|
||||
public class UpdateStartupContext
|
||||
{
|
||||
public Int32 ProcessId { get; set; }
|
||||
public String ExecutingApplication { get; set; }
|
||||
public String UpdateLocation { get; set; }
|
||||
}
|
||||
}
|
@ -63,7 +63,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DiskProvider.cs" />
|
||||
<Compile Include="NzbDroneProcessProvider.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -1,25 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Model;
|
||||
using NzbDrone.Common.Processes;
|
||||
|
||||
namespace NzbDrone.Windows
|
||||
{
|
||||
public class NzbDroneProcessProvider : INzbDroneProcessProvider
|
||||
{
|
||||
private readonly IProcessProvider _processProvider;
|
||||
|
||||
public NzbDroneProcessProvider(IProcessProvider processProvider)
|
||||
{
|
||||
_processProvider = processProvider;
|
||||
}
|
||||
|
||||
public List<ProcessInfo> FindNzbDroneProcesses()
|
||||
{
|
||||
var consoleProcesses = _processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME);
|
||||
var winformProcesses = _processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME);
|
||||
|
||||
return consoleProcesses.Concat(winformProcesses).ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -12,19 +12,22 @@ define(
|
||||
template: 'Settings/General/GeneralViewTemplate',
|
||||
|
||||
events: {
|
||||
'change .x-auth' : '_setAuthOptionsVisibility',
|
||||
'change .x-ssl' : '_setSslOptionsVisibility',
|
||||
'click .x-reset-api-key' : '_resetApiKey'
|
||||
'change .x-auth' : '_setAuthOptionsVisibility',
|
||||
'change .x-ssl' : '_setSslOptionsVisibility',
|
||||
'click .x-reset-api-key' : '_resetApiKey',
|
||||
'change .x-update-mechanism' : '_setScriptGroupVisibility'
|
||||
},
|
||||
|
||||
ui: {
|
||||
authToggle : '.x-auth',
|
||||
authOptions : '.x-auth-options',
|
||||
sslToggle : '.x-ssl',
|
||||
sslOptions : '.x-ssl-options',
|
||||
resetApiKey : '.x-reset-api-key',
|
||||
copyApiKey : '.x-copy-api-key',
|
||||
apiKeyInput : '.x-api-key'
|
||||
authToggle : '.x-auth',
|
||||
authOptions : '.x-auth-options',
|
||||
sslToggle : '.x-ssl',
|
||||
sslOptions : '.x-ssl-options',
|
||||
resetApiKey : '.x-reset-api-key',
|
||||
copyApiKey : '.x-copy-api-key',
|
||||
apiKeyInput : '.x-api-key',
|
||||
updateMechanism : '.x-update-mechanism',
|
||||
scriptGroup : '.x-script-group'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
@ -40,6 +43,10 @@ define(
|
||||
this.ui.sslOptions.hide();
|
||||
}
|
||||
|
||||
if (!this._showScriptGroup()) {
|
||||
this.ui.scriptGroup.hide();
|
||||
}
|
||||
|
||||
CommandController.bindToCommand({
|
||||
element: this.ui.resetApiKey,
|
||||
command: {
|
||||
@ -79,7 +86,7 @@ define(
|
||||
},
|
||||
|
||||
_resetApiKey: function () {
|
||||
if (window.confirm("Reset API Key?")) {
|
||||
if (window.confirm('Reset API Key?')) {
|
||||
CommandController.Execute('resetApiKey', {
|
||||
name : 'resetApiKey'
|
||||
});
|
||||
@ -90,6 +97,21 @@ define(
|
||||
if (options.command.get('name') === 'resetapikey') {
|
||||
this.model.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
_setScriptGroupVisibility: function () {
|
||||
|
||||
if (this._showScriptGroup()) {
|
||||
this.ui.scriptGroup.slideDown();
|
||||
}
|
||||
|
||||
else {
|
||||
this.ui.scriptGroup.slideUp();
|
||||
}
|
||||
},
|
||||
|
||||
_showScriptGroup: function () {
|
||||
return this.ui.updateMechanism.val() === 'script';
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -102,7 +102,7 @@
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" class='x-auth' name="authenticationEnabled"/>
|
||||
<input type="checkbox" class="x-auth" name="authenticationEnabled"/>
|
||||
<p>
|
||||
<span>On</span>
|
||||
<span>Off</span>
|
||||
@ -117,7 +117,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='x-auth-options'>
|
||||
<div class="x-auth-options">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Username</label>
|
||||
|
||||
@ -174,9 +174,8 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{{#if_windows}}
|
||||
<fieldset class="advanced-setting">
|
||||
<legend>Development</legend>
|
||||
<legend>Updating</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Branch</label>
|
||||
@ -186,28 +185,56 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--{{#if_mono}}-->
|
||||
<!--<div class="form-group">-->
|
||||
<!--<label class="control-label">Auto Update</label>-->
|
||||
{{#if_mono}}
|
||||
<div class="alert alert-warning">Please see: <a href="https://github.com/NzbDrone/NzbDrone/wiki/Updating">the wiki</a> for more information</div>
|
||||
|
||||
<!--<div class="controls">-->
|
||||
<!--<label class="checkbox toggle well">-->
|
||||
<!--<input type="checkbox" name="autoUpdate"/>-->
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Automatic</label>
|
||||
|
||||
<!--<p>-->
|
||||
<!--<span>Yes</span>-->
|
||||
<!--<span>No</span>-->
|
||||
<!--</p>-->
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="updateAutomatically"/>
|
||||
<p>
|
||||
<span>On</span>
|
||||
<span>Off</span>
|
||||
</p>
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<!--<div class="btn btn-primary slide-button"/>-->
|
||||
<!--</label>-->
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-nd-form-info" title="Automatically download and install updates. You will still be able to install from System: Updates"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<span class="help-inline-checkbox">-->
|
||||
<!--<i class="icon-nd-form-info" title="Use drone's built in auto update instead of package manager/manual updating"/>-->
|
||||
<!--</span>-->
|
||||
<!--</div>-->
|
||||
<!--</div>-->
|
||||
<!--{{/if_mono}}-->
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Mechanism</label>
|
||||
|
||||
<div class="col-sm-1 col-sm-push-4 help-inline">
|
||||
<i class="icon-nd-form-info" title="Use built-in updater or external script"/>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 col-sm-pull-1">
|
||||
<select name="updateMechanism" class="form-control x-update-mechanism">
|
||||
<option value="builtIn">Built-in</option>
|
||||
<option value="script">Script</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group x-script-group">
|
||||
<label class="col-sm-3 control-label">Script Path</label>
|
||||
|
||||
<div class="col-sm-1 col-sm-push-4 help-inline">
|
||||
<i class="icon-nd-form-info" title="Path to a custom script that take an extracted update package and handle the remainder of the update process"/>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 col-sm-pull-1">
|
||||
<input type="text" name="updateScriptPath" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
{{/if_mono}}
|
||||
</fieldset>
|
||||
{{/if_windows}}
|
||||
</div>
|
||||
|
@ -5,17 +5,9 @@
|
||||
- {{ShortDate releaseDate}}
|
||||
{{#if installed}}<i class="icon-ok" title="Installed"></i>{{/if}}
|
||||
|
||||
{{#if_windows}}
|
||||
{{#if isUpgrade}}
|
||||
<span class="label label-default install-update x-install-update">Install</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if isUpgrade}}
|
||||
<span class="label label-default install-update">
|
||||
<a href="https://github.com/NzbDrone/NzbDrone/wiki/Installation#linux">Install</a>
|
||||
</span>
|
||||
{{/if}}
|
||||
{{/if_windows}}
|
||||
{{#if isUpgrade}}
|
||||
<span class="label label-default install-update x-install-update">Install</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
</legend>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user