From d1dcfe2ed57815e0f920cd656f7ee96635f1a5ba Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 21 Sep 2018 22:14:40 -0400 Subject: [PATCH 1/8] Throw in some sanity --- src/Tgstation.Server.Host/Controllers/ConfigurationController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index acaf5206dd..40906558fb 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -171,6 +171,7 @@ namespace Tgstation.Server.Host.Controllers try { + model.IsDirectory = true; return await instanceManager.GetInstance(Instance).Configuration.CreateDirectory(model.Path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false) ? (IActionResult)Json(model) : StatusCode((int)HttpStatusCode.Created, model); } catch (NotImplementedException) From e490a7f7b146af1277b2a0408c9cf52b441d7d51 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 21 Sep 2018 22:45:43 -0400 Subject: [PATCH 2/8] Fix some config shit --- .../Components/StaticFiles/IConfiguration.cs | 2 +- .../Controllers/ConfigurationController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs index 29b8b66da0..7c6ec4298b 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs @@ -74,7 +74,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles /// The data to write. If , the file is deleted /// The hash any existing file must match in order for the write to succeed /// The for the operation. Usage may result in partial writes - /// A resulting in the updated + /// A resulting in the updated or if the write failed due to conflicts Task Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index 40906558fb..327f289072 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -68,7 +68,7 @@ namespace Tgstation.Server.Host.Controllers if (newFile == null) return Conflict(new ErrorMessage { - Message = "" + Message = "This file has been updated since you last viewed it!" }); newFile.Content = null; From efdad3b6108966721e007a319a6c31eecd502a9a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 21 Sep 2018 23:14:02 -0400 Subject: [PATCH 3/8] Add missing call to SetAutoUpdateInterval in InstanceController --- src/Tgstation.Server.Host/Controllers/InstanceController.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 0c6801cbf2..28cc4d34e7 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -295,6 +295,7 @@ namespace Tgstation.Server.Host.Controllers } } + var oldAutoUpdateInterval = originalModel.AutoUpdateInterval.Value; var originalOnline = originalModel.Online.Value; var renamed = model.Name != null && originalModel.Name != model.Name; @@ -356,6 +357,9 @@ namespace Tgstation.Server.Host.Controllers api.MoveJob = job.ToApi(); } + if (originalModel.Online.Value && model.AutoUpdateInterval.HasValue && oldAutoUpdateInterval != model.AutoUpdateInterval) + await instanceManager.GetInstance(originalModel).SetAutoUpdateInterval(model.AutoUpdateInterval.Value).ConfigureAwait(false); + return Json(api); } From 91543d73ff6948921ba84df52004567768537e4d Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 21 Sep 2018 23:19:27 -0400 Subject: [PATCH 4/8] Cleanup TimeSpan usage --- src/Tgstation.Server.Host/Components/Instance.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 91302f8fdc..e4b4d6079e 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -184,7 +184,7 @@ namespace Tgstation.Server.Host.Components while (true) try { - await Task.Delay(new TimeSpan(0, minutes > Int32.MaxValue ? Int32.MaxValue : (int)minutes, 0), cancellationToken).ConfigureAwait(false); + await Task.Delay(TimeSpan.FromMinutes(minutes > Int32.MaxValue ? Int32.MaxValue : (int)minutes), cancellationToken).ConfigureAwait(false); try { From 3a049117e36e8df416f1c8090143d9ba351fc5dc Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 21 Sep 2018 23:20:16 -0400 Subject: [PATCH 5/8] Add missing user to auto update repository job --- src/Tgstation.Server.Host/Components/Instance.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index e4b4d6079e..2db114c54f 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -198,7 +198,8 @@ namespace Tgstation.Server.Host.Components }, Description = "Scheduled repository update", CancelRightsType = RightsType.Repository, - CancelRight = (ulong)RepositoryRights.CancelPendingChanges + CancelRight = (ulong)RepositoryRights.CancelPendingChanges, + StartedBy = user }; var noRepo = false; From 7b5cf1821ea6d4fbe5c29cacd7d3ddc671aff9bc Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 21 Sep 2018 23:37:40 -0400 Subject: [PATCH 6/8] Adds a bit of auto update logging and exception cases --- .../Components/Instance.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 2db114c54f..b676c9f94f 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -185,7 +185,7 @@ namespace Tgstation.Server.Host.Components try { await Task.Delay(TimeSpan.FromMinutes(minutes > Int32.MaxValue ? Int32.MaxValue : (int)minutes), cancellationToken).ConfigureAwait(false); - + logger.LogDebug("Beginning auto update..."); try { Models.User user = null; @@ -202,7 +202,7 @@ namespace Tgstation.Server.Host.Components StartedBy = user }; - var noRepo = false; + var repositoryUpdateSuccess = false; await jobManager.RegisterOperation(repositoryUpdateJob, async (paramJob, databaseContext, progressReporter, jobCancellationToken) => { var repositorySettingsTask = databaseContext.RepositorySettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(jobCancellationToken); @@ -226,8 +226,7 @@ namespace Tgstation.Server.Host.Components { if (repo == null) { - //no repo, no auto updates - noRepo = true; + logger.LogTrace("Aborting repo update, no repository!"); return; } @@ -242,13 +241,15 @@ namespace Tgstation.Server.Host.Components bool shouldSyncTracked; if (repositorySettings.AutoUpdatesKeepTestMerges.Value) { + logger.LogTrace("Preserving test merges..."); var result = await repo.MergeOrigin(repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), jobCancellationToken).ConfigureAwait(false); if (!result.HasValue) - return; + throw new JobException("Merge conflict while preserving test merges!"); shouldSyncTracked = result.Value; } else { + logger.LogTrace("Not preserving test merges..."); await repo.ResetToOrigin(NextProgressReporter(), jobCancellationToken).ConfigureAwait(false); shouldSyncTracked = true; } @@ -259,12 +260,16 @@ namespace Tgstation.Server.Host.Components progressReporter(5 * ProgressStep); } + repositoryUpdateSuccess = true; }, cancellationToken).ConfigureAwait(false); await jobManager.WaitForJobCompletion(repositoryUpdateJob, user, cancellationToken, default).ConfigureAwait(false); - if (noRepo) + if (!repositoryUpdateSuccess) + { + logger.LogTrace("Aborting auto update, repository error!"); continue; + } //finally set up the job var compileProcessJob = new Job From 3ed59d28a6a5bdd926ccb220785aaee428143560 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Fri, 21 Sep 2018 23:40:15 -0400 Subject: [PATCH 7/8] Fix MergeOrigin aborting --- .../Components/Repository/Repository.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index 4113a66945..0c7f7687ee 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -442,6 +442,7 @@ namespace Tgstation.Server.Host.Components.Repository Branch trackedBranch = null; var oldHead = repository.Head; + var oldTip = oldHead.Tip; await Task.Factory.StartNew(() => { @@ -468,7 +469,10 @@ namespace Tgstation.Server.Host.Components.Repository if (result.Status == MergeStatus.Conflicts) { logger.LogDebug("Merge conflict, aborting and reverting to {0}", oldHead.FriendlyName); - RawCheckout(oldHead.CanonicalName, progressReporter, cancellationToken); + repository.Reset(ResetMode.Hard, oldTip, new CheckoutOptions + { + OnCheckoutProgress = CheckoutProgressHandler(progressReporter) + }); cancellationToken.ThrowIfCancellationRequested(); } @@ -477,7 +481,7 @@ namespace Tgstation.Server.Host.Components.Repository if (result.Status == MergeStatus.Conflicts) { - await eventConsumer.HandleEvent(EventType.RepoMergeConflict, new List { oldHead.Tip.Sha, trackedBranch.Tip.Sha, oldHead.FriendlyName ?? UnknownReference, trackedBranch.FriendlyName }, cancellationToken).ConfigureAwait(false); + await eventConsumer.HandleEvent(EventType.RepoMergeConflict, new List { oldTip.Sha, trackedBranch.Tip.Sha, oldHead.FriendlyName ?? UnknownReference, trackedBranch.FriendlyName }, cancellationToken).ConfigureAwait(false); return null; } From ac84657b06fa2a911f8d2ec98cf2049a129ec983 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 22 Sep 2018 00:43:46 -0400 Subject: [PATCH 8/8] Fix watchdog not working in paths with spaces --- .../Watchdog.cs | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs index d69ff7ee72..0227e83be7 100644 --- a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs @@ -35,77 +35,77 @@ namespace Tgstation.Server.Host.Watchdog { logger.LogInformation("Host watchdog starting..."); - var enviromentPath = Environment.GetEnvironmentVariable("PATH"); - var paths = enviromentPath.Split(';'); - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - - var exeName = "dotnet"; - IEnumerable enumerator; - if (isWindows) - { - exeName += ".exe"; - enumerator = paths; - } - else - enumerator = paths.Select(x => x.Split(':')).SelectMany(x => x); - - enumerator = enumerator.Select(x => Path.Combine(x, exeName)); - - var dotnetPath = enumerator - .Where(x => - { - logger.LogTrace("Checking for dotnet at {0}", x); - return File.Exists(x); - }) - .FirstOrDefault(); - - if (dotnetPath == default) - { - logger.LogCritical("Unable to locate dotnet executable in PATH! Please ensure the .NET Core runtime is installed and is in your PATH!"); - return; - } - logger.LogInformation("Detected dotnet executable at {0}", dotnetPath); - - var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - - var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog - var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default")); -#if DEBUG - //just copy the shit where it belongs - Directory.Delete(assemblyStoragePath, true); - Directory.CreateDirectory(defaultAssemblyPath); - - var sourcePath = "../../../../Tgstation.Server.Host/bin/Debug/netcoreapp2.1"; - foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) - Directory.CreateDirectory(dirPath.Replace(sourcePath, defaultAssemblyPath)); - - foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories)) - File.Copy(newPath, newPath.Replace(sourcePath, defaultAssemblyPath), true); - - const string AppSettingsJson = "appsettings.json"; - var rootJson = Path.Combine(rootLocation, AppSettingsJson); - File.Delete(rootJson); - File.Move(Path.Combine(defaultAssemblyPath, AppSettingsJson), rootJson); -#endif - - var assemblyName = String.Join(".", nameof(Tgstation), nameof(Server), nameof(Host), "dll"); - var assemblyPath = Path.Combine(defaultAssemblyPath, assemblyName); - - if (assemblyPath.Contains("\"")) - { - logger.LogCritical("Running from paths with \"'s in the name is not supported!"); - return; - } - - if (!File.Exists(assemblyPath)) - { - logger.LogCritical("Unable to locate host assembly!"); - return; - } - string updateDirectory = null; try { + var enviromentPath = Environment.GetEnvironmentVariable("PATH"); + var paths = enviromentPath.Split(';'); + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + var exeName = "dotnet"; + IEnumerable enumerator; + if (isWindows) + { + exeName += ".exe"; + enumerator = paths; + } + else + enumerator = paths.Select(x => x.Split(':')).SelectMany(x => x); + + enumerator = enumerator.Select(x => Path.Combine(x, exeName)); + + var dotnetPath = enumerator + .Where(x => + { + logger.LogTrace("Checking for dotnet at {0}", x); + return File.Exists(x); + }) + .FirstOrDefault(); + + if (dotnetPath == default) + { + logger.LogCritical("Unable to locate dotnet executable in PATH! Please ensure the .NET Core runtime is installed and is in your PATH!"); + return; + } + logger.LogInformation("Detected dotnet executable at {0}", dotnetPath); + + var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog + var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default")); +#if DEBUG + //just copy the shit where it belongs + Directory.Delete(assemblyStoragePath, true); + Directory.CreateDirectory(defaultAssemblyPath); + + var sourcePath = "../../../../Tgstation.Server.Host/bin/Debug/netcoreapp2.1"; + foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) + Directory.CreateDirectory(dirPath.Replace(sourcePath, defaultAssemblyPath)); + + foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories)) + File.Copy(newPath, newPath.Replace(sourcePath, defaultAssemblyPath), true); + + const string AppSettingsJson = "appsettings.json"; + var rootJson = Path.Combine(rootLocation, AppSettingsJson); + File.Delete(rootJson); + File.Move(Path.Combine(defaultAssemblyPath, AppSettingsJson), rootJson); +#endif + + var assemblyName = String.Join(".", nameof(Tgstation), nameof(Server), nameof(Host), "dll"); + var assemblyPath = Path.Combine(defaultAssemblyPath, assemblyName); + + if (assemblyPath.Contains("\"")) + { + logger.LogCritical("Running from paths with \"'s in the name is not supported!"); + return; + } + + if (!File.Exists(assemblyPath)) + { + logger.LogCritical("Unable to locate host assembly!"); + return; + } + while (!cancellationToken.IsCancellationRequested) using (logger.BeginScope("Host invocation")) { @@ -119,7 +119,7 @@ namespace Tgstation.Server.Host.Watchdog var arguments = new List { '"' + assemblyPath + '"', - updateDirectory + '"' + updateDirectory + '"' }; if (Debugger.IsAttached) arguments.Add("--attach-debugger");