From 571c555360aa3b9fb06d9d0b1592fbd976121408 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Wed, 18 Oct 2023 00:19:16 -0400 Subject: [PATCH 01/14] Finally find a way to test if Linux is being shitty to us about links --- tests/DMAPI/LongRunning/Test.dm | 9 ++++ tests/DMAPI/LongRunning/long_running_test.dme | 3 ++ .../LongRunning/long_running_test_copy.dme | 1 + tests/DMAPI/LongRunning/resource.txt | 8 ++++ .../Live/Instance/WatchdogTest.cs | 48 +++++++++++++++++++ tgstation-server.sln | 1 + 6 files changed, 70 insertions(+) create mode 100644 tests/DMAPI/LongRunning/resource.txt diff --git a/tests/DMAPI/LongRunning/Test.dm b/tests/DMAPI/LongRunning/Test.dm index a77c3377b5d..657fadbfd07 100644 --- a/tests/DMAPI/LongRunning/Test.dm +++ b/tests/DMAPI/LongRunning/Test.dm @@ -22,6 +22,15 @@ if(!length(channels)) FailTest("Expected some chat channels!") + var/test_str = "aljsdhfjahsfkjnsalkjdfhskljdackmcnvxkljhvkjsdanv,jdshlkufhklasjeFDhfjkalhdkjlfhalksfdjh" + var/res_contents = file2text('resource.txt') // we need a .rsc to be generated + + if(!findtext(res_contents, test_str)) + FailTest("Failed to resource? Did not find magic: [res_contents]") + + if(!fexists("[DME_NAME].rsc")) + FailTest("Failed to create .rsc!") + StartAsync() /proc/dab() diff --git a/tests/DMAPI/LongRunning/long_running_test.dme b/tests/DMAPI/LongRunning/long_running_test.dme index b9270144556..322587647dc 100644 --- a/tests/DMAPI/LongRunning/long_running_test.dme +++ b/tests/DMAPI/LongRunning/long_running_test.dme @@ -13,5 +13,8 @@ // BEGIN_INCLUDE #include "Config.dm" #include "../test_prelude.dm" +#ifndef DME_NAME +#define DME_NAME "long_running_test" +#endif #include "Test.dm" // END_INCLUDE diff --git a/tests/DMAPI/LongRunning/long_running_test_copy.dme b/tests/DMAPI/LongRunning/long_running_test_copy.dme index b4da8e0d964..e443c4b3312 100644 --- a/tests/DMAPI/LongRunning/long_running_test_copy.dme +++ b/tests/DMAPI/LongRunning/long_running_test_copy.dme @@ -11,5 +11,6 @@ // END_PREFERENCES // BEGIN_INCLUDE +#define DME_NAME "long_running_test_copy" #include "long_running_test.dme" // END_INCLUDE diff --git a/tests/DMAPI/LongRunning/resource.txt b/tests/DMAPI/LongRunning/resource.txt new file mode 100644 index 00000000000..94b848fe2ac --- /dev/null +++ b/tests/DMAPI/LongRunning/resource.txt @@ -0,0 +1,8 @@ + + if(!length(channels)) + FailTest("Expected some chat channels!") + + var/test_str = "aljsdhfjahsfkjnsalkjdfhskljdackmcnvxkljhvkjsdanv,jdshlkufhklasjeFDhfjkalhdkjlfhalksfdjh" + var/self_contents = file2text(file('Test.dm')) // we need a .rsc to be generated + + if(!(test_str in self_contents)) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 329fab6aa19..30b2ef36563 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -3,17 +3,22 @@ using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Mono.Unix; +using Mono.Unix.Native; + using Moq; using Newtonsoft.Json; using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -471,6 +476,47 @@ async Task RunBasicTest(CancellationToken cancellationToken) Assert.AreEqual(string.Empty, daemonStatus.AdditionalParameters); } + void TestLinuxIsntBeingFuckingCheekyAboutFilePaths(DreamDaemonResponse currentStatus, CompileJobResponse previousStatus, CancellationToken cancellationToken) + { + if (new PlatformIdentifier().IsWindows) + return; + + Assert.IsNotNull(currentStatus.ActiveCompileJob); + Assert.IsTrue(currentStatus.ActiveCompileJob.DmeName.Contains("long_running_test")); + Assert.AreEqual(WatchdogStatus.Online, currentStatus.Status); + + var procs = TestLiveServer.GetDDProcessesOnPort(currentStatus.Port.Value); + Assert.AreEqual(1, procs.Count); + var failingLinks = new List(); + using var proc = procs[0]; + var pid = proc.Id; + var foundLivePath = false; + var allPaths = new List(); + foreach (var fd in Directory.EnumerateFiles($"/proc/{pid}/fd")) + { + var sb = new StringBuilder(); + if (Syscall.readlink(fd, sb) == -1) + throw new UnixIOException(Stdlib.GetLastError()); + + var path = sb.ToString(); + + allPaths.Add(path); + if (path.Contains($"Game/{previousStatus.DirectoryName}")) + failingLinks.Add($"Found fd {fd} resolving to previous absolute path game dir path: {path}"); + + if (path.Contains($"Game/{currentStatus.ActiveCompileJob.DirectoryName}")) + failingLinks.Add($"Found fd {fd} resolving to current absolute path game dir path: {path}"); + + if (path.Contains($"Game/Live")) + foundLivePath = true; + } + + if (!foundLivePath) + failingLinks.Add($"Failed to find a path containing the 'Live' directory! Found {allPaths.Count}: \"{String.Join("\", \"", allPaths)}\""); + + Assert.IsTrue(failingLinks.Count == 0, String.Join(Environment.NewLine, failingLinks)); + } + async Task RunHealthCheckTest(bool checkDump, CancellationToken cancellationToken) { System.Console.WriteLine("TEST: WATCHDOG HEALTH CHECK TEST"); @@ -901,6 +947,8 @@ async Task RunLongRunningTestThenUpdate(CancellationToken cancellationToken) Assert.AreNotEqual(initialCompileJob.Id, daemonStatus.ActiveCompileJob.Id); Assert.IsNull(daemonStatus.StagedCompileJob); + TestLinuxIsntBeingFuckingCheekyAboutFilePaths(daemonStatus, initialCompileJob, cancellationToken); + await instanceClient.DreamDaemon.Shutdown(cancellationToken); daemonStatus = await instanceClient.DreamDaemon.Read(cancellationToken); diff --git a/tgstation-server.sln b/tgstation-server.sln index 20eef245e10..c1cde46821b 100644 --- a/tgstation-server.sln +++ b/tgstation-server.sln @@ -158,6 +158,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LongRunning", "LongRunning" tests\DMAPI\LongRunning\long_running_test.dme = tests\DMAPI\LongRunning\long_running_test.dme tests\DMAPI\LongRunning\long_running_test_copy.dme = tests\DMAPI\LongRunning\long_running_test_copy.dme tests\DMAPI\LongRunning\long_running_test_rooted.dme = tests\DMAPI\LongRunning\long_running_test_rooted.dme + tests\DMAPI\LongRunning\resource.txt = tests\DMAPI\LongRunning\resource.txt tests\DMAPI\LongRunning\Test.dm = tests\DMAPI\LongRunning\Test.dm EndProjectSection EndProject From 44729820eddd656f55c2090eb31c83e203eaa324 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 12:27:53 -0400 Subject: [PATCH 02/14] Remove unused variable and fix test buffer size --- tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs | 4 ++-- tests/Tgstation.Server.Tests/Live/TestLiveServer.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 30b2ef36563..96ef4d3c201 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -494,7 +494,7 @@ void TestLinuxIsntBeingFuckingCheekyAboutFilePaths(DreamDaemonResponse currentSt var allPaths = new List(); foreach (var fd in Directory.EnumerateFiles($"/proc/{pid}/fd")) { - var sb = new StringBuilder(); + var sb = new StringBuilder(UInt16.MaxValue); if (Syscall.readlink(fd, sb) == -1) throw new UnixIOException(Stdlib.GetLastError()); @@ -512,7 +512,7 @@ void TestLinuxIsntBeingFuckingCheekyAboutFilePaths(DreamDaemonResponse currentSt } if (!foundLivePath) - failingLinks.Add($"Failed to find a path containing the 'Live' directory! Found {allPaths.Count}: \"{String.Join("\", \"", allPaths)}\""); + failingLinks.Add($"Failed to find a path containing the 'Live' directory!"); Assert.IsTrue(failingLinks.Count == 0, String.Join(Environment.NewLine, failingLinks)); } diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index f058d9795fe..a78b984fc2e 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -1326,8 +1326,6 @@ async Task FailFast(Task task) async Task RunInstanceTests() { - // Some earlier linux BYOND versions have a critical bug where replacing the directory in non-basic watchdogs causes the DreamDaemon cwd to change - var canRunCompatTests = new PlatformIdentifier().IsWindows; var compatTests = FailFast( instanceTest .RunCompatTests( From a0d583b5961d353d24024e7e200283cdae799a8a Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 18:01:41 -0400 Subject: [PATCH 03/14] New hard link system for PosixWatchdog - IDmbProvider is now `AsyncDisposable` as opposed to `IDisposable`. - Add hard link support to `ISymlinkFactory`. - `PosixWatchdog` now has to mirror deployment structure as hard links before swapping. Because of this deployments may not immediately be applied if the reboots immediately after they were completed. --- .../Components/Deployment/DmbFactory.cs | 7 +- .../Components/Deployment/DmbProvider.cs | 9 +- .../Components/Deployment/DreamMaker.cs | 2 +- .../Deployment/HardLinkDmbProvider.cs | 222 ++++++++++++++++++ .../Components/Deployment/IDmbProvider.cs | 2 +- .../Deployment/SwappableDmbProvider.cs | 58 +++-- .../Deployment/TemporaryDmbProvider.cs | 5 +- .../Components/Session/ISessionController.cs | 4 +- .../Components/Session/SessionController.cs | 11 +- .../Components/Watchdog/BasicWatchdog.cs | 2 +- .../Components/Watchdog/PosixWatchdog.cs | 21 +- .../Watchdog/PosixWatchdogFactory.cs | 1 + .../Components/Watchdog/WindowsWatchdog.cs | 67 ++++-- .../IO/ISymlinkFactory.cs | 9 + .../IO/PosixSymlinkFactory.cs | 16 ++ .../IO/WindowsSymlinkFactory.cs | 4 + 16 files changed, 378 insertions(+), 62 deletions(-) create mode 100644 src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs diff --git a/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs index 09567ba79cd..df587cab111 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs @@ -148,15 +148,18 @@ await remoteDeploymentManager.StageDeployment( cancellationToken); } + ValueTask dmbDisposeTask; lock (jobLockCounts) { - nextDmbProvider?.Dispose(); + dmbDisposeTask = nextDmbProvider?.DisposeAsync() ?? ValueTask.CompletedTask; nextDmbProvider = newProvider; // Oh god dammit var temp = Interlocked.Exchange(ref newerDmbTcs, new TaskCompletionSource()); temp.SetResult(); } + + await dmbDisposeTask; } /// @@ -320,7 +323,7 @@ void CleanupAction() finally { if (!providerSubmitted) - newProvider.Dispose(); + await newProvider.DisposeAsync(); } } #pragma warning restore CA1506 diff --git a/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs index f3711ede4f8..770409f7902 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Models; @@ -30,7 +31,7 @@ sealed class DmbProvider : IDmbProvider readonly string directoryAppend; /// - /// The to run when is called. + /// The to run when is called. /// Action onDispose; @@ -50,7 +51,11 @@ public DmbProvider(CompileJob compileJob, IIOManager ioManager, Action onDispose } /// - public void Dispose() => onDispose?.Invoke(); + public ValueTask DisposeAsync() + { + onDispose?.Invoke(); + return ValueTask.CompletedTask; + } /// public void KeepAlive() => onDispose = null; diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs index 665efba795e..307a1bb1070 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs @@ -802,7 +802,7 @@ async Task VerifyApi( job.MinimumSecurityLevel = securityLevel; // needed for the TempDmbProvider ApiValidationStatus validationStatus; - using (var provider = new TemporaryDmbProvider(ioManager.ResolvePath(job.DirectoryName.ToString()), String.Concat(job.DmeName, DmbExtension), job)) + await using (var provider = new TemporaryDmbProvider(ioManager.ResolvePath(job.DirectoryName.ToString()), String.Concat(job.DmeName, DmbExtension), job)) await using (var controller = await sessionControllerFactory.LaunchNew(provider, byondLock, launchParameters, true, cancellationToken)) { var launchResult = await controller.LaunchResult.WaitAsync(cancellationToken); diff --git a/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs new file mode 100644 index 00000000000..e5ddede026e --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Extensions.Logging; + +using Tgstation.Server.Host.Configuration; +using Tgstation.Server.Host.Extensions; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Utils; + +namespace Tgstation.Server.Host.Components.Deployment +{ + /// + /// A that uses hard links. + /// + sealed class HardLinkDmbProvider : SwappableDmbProvider + { + /// + /// The for . + /// + readonly CancellationTokenSource cancellationTokenSource; + + /// + /// The representing the base provider mirroring operation. + /// + readonly Task mirroringTask; + + /// + /// The for the . + /// + readonly ILogger logger; + + /// + /// Initializes a new instance of the class. + /// + /// The for the . + /// The for the . + /// The for the . + /// The value of . + /// The for the . + public HardLinkDmbProvider( + IDmbProvider baseProvider, + IIOManager ioManager, + ISymlinkFactory symlinkFactory, + ILogger logger, + GeneralConfiguration generalConfiguration) + : base( + baseProvider, + ioManager, + symlinkFactory) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + cancellationTokenSource = new CancellationTokenSource(); + try + { + mirroringTask = MirrorSourceDirectory(generalConfiguration.GetCopyDirectoryTaskThrottle(), cancellationTokenSource.Token); + } + catch + { + cancellationTokenSource.Dispose(); + throw; + } + } + + /// + public override async ValueTask DisposeAsync() + { + cancellationTokenSource.Cancel(); + cancellationTokenSource.Dispose(); + try + { + await mirroringTask; + } + catch (OperationCanceledException ex) + { + logger.LogDebug(ex, "Mirroring task cancelled!"); + } + + await base.DisposeAsync(); + } + + /// + public override Task FinishActivationPreparation(CancellationToken cancellationToken) + { + if (!mirroringTask.IsCompleted) + logger.LogTrace("Waiting for mirroring to complete..."); + + return mirroringTask.WaitAsync(cancellationToken); + } + + /// + protected override async Task DoSwap(CancellationToken cancellationToken) + { + var mirroredDir = await mirroringTask.WaitAsync(cancellationToken); + var goAheadTcs = new TaskCompletionSource(); + + // I feel dirty... + async void DisposeOfOldDirectory() + { + var directoryMoved = false; + var disposePath = Guid.NewGuid().ToString(); + try + { + await IOManager.MoveDirectory(LiveGameDirectory, disposePath, cancellationToken); + directoryMoved = true; + goAheadTcs.SetResult(); + await IOManager.DeleteDirectory(disposePath, CancellationToken.None); // DCT: We're detached at this point + } + catch (Exception ex) + { + if (directoryMoved) + logger.LogWarning(ex, "Failed to delete hard linked directory: {disposePath}", disposePath); + else + { + logger.LogDebug(ex, "Live directory appears to not exist"); + goAheadTcs.SetResult(); + } + } + } + + DisposeOfOldDirectory(); + await goAheadTcs.Task; + await IOManager.MoveDirectory(mirroredDir, LiveGameDirectory, cancellationToken); + } + + /// + /// Mirror the . + /// + /// The optional maximum number of simultaneous tasks allowed to execute. + /// The for the operation. + /// A resulting in the full path to the mirrored directory. + async Task MirrorSourceDirectory(int? taskThrottle, CancellationToken cancellationToken) + { + var stopwatch = Stopwatch.StartNew(); + var mirrorGuid = Guid.NewGuid(); + logger.LogDebug("Starting to mirror {sourceDir} as hard links to {mirrorGuid}...", CompileJob.DirectoryName, mirrorGuid); + if (taskThrottle.HasValue && taskThrottle < 1) + throw new ArgumentOutOfRangeException(nameof(taskThrottle), taskThrottle, "taskThrottle must be at least 1!"); + + var src = IOManager.ResolvePath(CompileJob.DirectoryName.ToString()); + var dest = IOManager.ResolvePath(mirrorGuid.ToString()); + + using var semaphore = taskThrottle.HasValue ? new SemaphoreSlim(taskThrottle.Value) : null; + await Task.WhenAll(MirrorDirectoryImpl(src, dest, semaphore, cancellationToken)); + stopwatch.Stop(); + + logger.LogDebug( + "Finished mirror of {sourceDir} to {mirrorGuid} in {seconds}s...", + CompileJob.DirectoryName, + mirrorGuid, + stopwatch.Elapsed.TotalSeconds.ToString("0.##", CultureInfo.InvariantCulture)); + + return dest; + } + + /// + /// Recursively create tasks to create a hard link directory mirror of to . + /// + /// The source directory path. + /// The destination directory path. + /// Optional used to limit degree of parallelism. + /// The for the operation. + /// A of s representing the running operations. The first returned is always the necessary call to . + /// I genuinely don't know how this will work with symlinked files. Waiting for the issue report I guess. + IEnumerable MirrorDirectoryImpl(string src, string dest, SemaphoreSlim semaphore, CancellationToken cancellationToken) + { + var dir = new DirectoryInfo(src); + Task subdirCreationTask = null; + foreach (var subDirectory in dir.EnumerateDirectories()) + { + // check if we are a symbolic link + if (!subDirectory.Attributes.HasFlag(FileAttributes.Directory) || subDirectory.Attributes.HasFlag(FileAttributes.ReparsePoint)) + { + logger.LogTrace("Skipping symlink to {subdir}", subDirectory.Name); + continue; + } + + var checkingSubdirCreationTask = true; + foreach (var copyTask in MirrorDirectoryImpl(subDirectory.FullName, Path.Combine(dest, subDirectory.Name), semaphore, cancellationToken)) + { + if (subdirCreationTask == null) + { + subdirCreationTask = copyTask; + yield return subdirCreationTask; + } + else if (!checkingSubdirCreationTask) + yield return copyTask; + + checkingSubdirCreationTask = false; + } + } + + foreach (var fileInfo in dir.EnumerateFiles()) + { + if (subdirCreationTask == null) + { + subdirCreationTask = IOManager.CreateDirectory(dest, cancellationToken); + yield return subdirCreationTask; + } + + var sourceFile = fileInfo.FullName; + var destFile = IOManager.ConcatPath(dest, fileInfo.Name); + + async Task LinkThisFile() + { + await subdirCreationTask.WaitAsync(cancellationToken); + using var lockContext = semaphore != null + ? await SemaphoreSlimContext.Lock(semaphore, cancellationToken) + : null; + await SymlinkFactory.CreateHardLink(sourceFile, destFile, cancellationToken); + } + + yield return LinkThisFile(); + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs index efc773a9193..c1c477ffff7 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs @@ -7,7 +7,7 @@ namespace Tgstation.Server.Host.Components.Deployment /// /// Provides absolute paths to the latest compiled .dmbs. /// - public interface IDmbProvider : IDisposable + public interface IDmbProvider : IAsyncDisposable { /// /// The file name of the .dmb. diff --git a/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs index e4964532229..14cdc268e29 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs @@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.Components.Deployment /// /// A that uses symlinks. /// - sealed class SwappableDmbProvider : IDmbProvider + class SwappableDmbProvider : IDmbProvider { /// /// The directory where the is symlinked to. @@ -21,7 +21,7 @@ sealed class SwappableDmbProvider : IDmbProvider public string DmbName => baseProvider.DmbName; /// - public string Directory => ioManager.ResolvePath(LiveGameDirectory); + public string Directory => IOManager.ResolvePath(LiveGameDirectory); /// public CompileJob CompileJob => baseProvider.CompileJob; @@ -32,19 +32,19 @@ sealed class SwappableDmbProvider : IDmbProvider public bool Swapped => swapped != 0; /// - /// The we are swapping for. + /// The to use. /// - readonly IDmbProvider baseProvider; + protected IIOManager IOManager { get; } /// - /// The to use. + /// The to use. /// - readonly IIOManager ioManager; + protected ISymlinkFactory SymlinkFactory { get; } /// - /// The to use. + /// The we are swapping for. /// - readonly ISymlinkFactory symlinkFactory; + readonly IDmbProvider baseProvider; /// /// Backing field for . @@ -55,17 +55,17 @@ sealed class SwappableDmbProvider : IDmbProvider /// Initializes a new instance of the class. /// /// The value of . - /// The value of . - /// The value of . + /// The value of . + /// The value of . public SwappableDmbProvider(IDmbProvider baseProvider, IIOManager ioManager, ISymlinkFactory symlinkFactory) { this.baseProvider = baseProvider ?? throw new ArgumentNullException(nameof(baseProvider)); - this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); - this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + IOManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + SymlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); } /// - public void Dispose() => baseProvider.Dispose(); + public virtual ValueTask DisposeAsync() => baseProvider.DisposeAsync(); /// public void KeepAlive() => baseProvider.KeepAlive(); @@ -75,19 +75,37 @@ public SwappableDmbProvider(IDmbProvider baseProvider, IIOManager ioManager, ISy /// /// The for the operation. /// A representing the running operation. - public async Task MakeActive(CancellationToken cancellationToken) + public Task MakeActive(CancellationToken cancellationToken) { if (Interlocked.Exchange(ref swapped, 1) != 0) throw new InvalidOperationException("Already swapped!"); - if (symlinkFactory.SymlinkedDirectoriesAreDeletedAsFiles) - await ioManager.DeleteFile(LiveGameDirectory, cancellationToken); + return DoSwap(cancellationToken); + } + + /// + /// Should be . before calling to ensure the is ready to instantly swap. Can be called multiple times. + /// + /// The for the operation. + /// A representing the preparation process. + public virtual Task FinishActivationPreparation(CancellationToken cancellationToken) + => Task.CompletedTask; + + /// + /// Perform the swapping action. + /// + /// The for the operation. + /// A representing the running operation. + protected virtual async Task DoSwap(CancellationToken cancellationToken) + { + if (SymlinkFactory.SymlinkedDirectoriesAreDeletedAsFiles) + await IOManager.DeleteFile(LiveGameDirectory, cancellationToken); else - await ioManager.DeleteDirectory(LiveGameDirectory, cancellationToken); + await IOManager.DeleteDirectory(LiveGameDirectory, cancellationToken); - await symlinkFactory.CreateSymbolicLink( - ioManager.ResolvePath(baseProvider.Directory), - ioManager.ResolvePath(LiveGameDirectory), + await SymlinkFactory.CreateSymbolicLink( + IOManager.ResolvePath(baseProvider.Directory), + IOManager.ResolvePath(LiveGameDirectory), cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs index 0bf982cfa38..4476d9d2c3d 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Tgstation.Server.Host.Models; @@ -32,9 +33,7 @@ public TemporaryDmbProvider(string directory, string dmb, CompileJob compileJob) } /// - public void Dispose() - { - } + public ValueTask DisposeAsync() => ValueTask.CompletedTask; /// public void KeepAlive() => throw new NotSupportedException(); diff --git a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs index 3ded84429ac..93f10eb17cc 100644 --- a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs @@ -128,7 +128,7 @@ interface ISessionController : IProcessBase, IRenameNotifyee, IAsyncDisposable /// Replace the in use with a given , disposing the old one. /// /// The new . - /// An to be disposed once certain that the original is no longer in use. - IDisposable ReplaceDmbProvider(IDmbProvider newProvider); + /// An to be disposed once certain that the original is no longer in use. + IAsyncDisposable ReplaceDmbProvider(IDmbProvider newProvider); } } diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs index 2da8c03b58f..eab32bdce18 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs @@ -350,8 +350,13 @@ public async ValueTask DisposeAsync() await process.DisposeAsync(); byondLock.Dispose(); bridgeRegistration?.Dispose(); - ReattachInformation.Dmb.Dispose(); - ReattachInformation.InitialDmb?.Dispose(); + var regularDmbDisposeTask = ReattachInformation.Dmb.DisposeAsync(); + var initialDmb = ReattachInformation.InitialDmb; + if (initialDmb != null) + await initialDmb.DisposeAsync(); + + await regularDmbDisposeTask; + chatTrackingContext.Dispose(); reattachTopicCts.Dispose(); @@ -552,7 +557,7 @@ public void ResetRebootState() public void Resume() => process.Resume(); /// - public IDisposable ReplaceDmbProvider(IDmbProvider dmbProvider) + public IAsyncDisposable ReplaceDmbProvider(IDmbProvider dmbProvider) { var oldDmb = ReattachInformation.Dmb; ReattachInformation.Dmb = dmbProvider ?? throw new ArgumentNullException(nameof(dmbProvider)); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs index 8396ce7c992..fc5c3461b0c 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs @@ -268,7 +268,7 @@ await ReattachFailure( // server didn't get control of this dmb if (dmbToUse != null && !serverWasActive) - dmbToUse.Dispose(); + await dmbToUse.DisposeAsync(); throw; } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs index 044ef3f1ef8..3bf2927e578 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -9,6 +10,7 @@ using Tgstation.Server.Host.Components.Deployment.Remote; using Tgstation.Server.Host.Components.Events; using Tgstation.Server.Host.Components.Session; +using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Jobs; @@ -21,6 +23,11 @@ namespace Tgstation.Server.Host.Components.Watchdog /// sealed class PosixWatchdog : WindowsWatchdog { + /// + /// The for the . + /// + readonly GeneralConfiguration generalConfiguration; + /// /// Initializes a new instance of the class. /// @@ -39,6 +46,7 @@ sealed class PosixWatchdog : WindowsWatchdog /// The for the . /// The for the . /// The for the . + /// The value of . /// The autostart value for the . public PosixWatchdog( IChatManager chat, @@ -56,6 +64,7 @@ public PosixWatchdog( ILogger logger, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, + GeneralConfiguration generalConfiguration, bool autoStart) : base( chat, @@ -75,13 +84,15 @@ public PosixWatchdog( instance, autoStart) { + this.generalConfiguration = generalConfiguration ?? throw new ArgumentNullException(nameof(generalConfiguration)); } /// protected override Task ApplyInitialDmb(CancellationToken cancellationToken) - { - // not necessary to hold initial .dmb on Linux because of based inode deletes - return Task.CompletedTask; - } + => Task.CompletedTask; // not necessary to hold initial .dmb on Linux because of based inode deletes + + /// + protected override SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider) + => new HardLinkDmbProvider(dmbProvider, GameIOManager, SymlinkFactory, Logger, generalConfiguration); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdogFactory.cs index 6774c73cb55..787127a05c9 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdogFactory.cs @@ -76,6 +76,7 @@ public override IWatchdog CreateWatchdog( LoggerFactory.CreateLogger(), settings, instance, + GeneralConfiguration, settings.AutoStart ?? throw new ArgumentNullException(nameof(settings))); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs index c8d6c7eb442..0e42c3b660e 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs @@ -37,7 +37,7 @@ class WindowsWatchdog : BasicWatchdog /// /// The for the . /// - readonly ISymlinkFactory symlinkFactory; + protected ISymlinkFactory SymlinkFactory { get; } /// /// of s that are waiting to clean up old deployments. @@ -68,7 +68,7 @@ class WindowsWatchdog : BasicWatchdog /// The for the . /// The for the . /// The value of . - /// The value of . + /// The value of . /// The for the . /// The for the . /// The for the . @@ -109,7 +109,7 @@ public WindowsWatchdog( try { GameIOManager = gameIOManager ?? throw new ArgumentNullException(nameof(gameIOManager)); - this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + SymlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); deploymentCleanupTasks = new List(); } @@ -131,7 +131,7 @@ protected override async Task DisposeAndNullControllersImpl() // If we reach this point, we can guarantee PrepServerForLaunch will be called before starting again. ActiveSwappable = null; - pendingSwappable?.Dispose(); + await (pendingSwappable?.DisposeAsync() ?? ValueTask.CompletedTask); pendingSwappable = null; await DrainDeploymentCleanupTasks(true); @@ -142,9 +142,10 @@ protected override async Task HandleNormalReboot(CancellationToke { if (pendingSwappable != null) { - var updateTask = BeforeApplyDmb(pendingSwappable.CompileJob, cancellationToken); + Task RunPrequel() => BeforeApplyDmb(pendingSwappable.CompileJob, cancellationToken); - if (!pendingSwappable.Swapped) + var needToSwap = !pendingSwappable.Swapped; + if (needToSwap) { // IMPORTANT: THE SESSIONCONTROLLER SHOULD STILL BE PROCESSING THE BRIDGE REQUEST SO WE KNOW DD IS SLEEPING // OTHERWISE, IT COULD RETURN TO /world/Reboot() TOO EARLY AND LOAD THE WRONG .DMB @@ -153,18 +154,29 @@ protected override async Task HandleNormalReboot(CancellationToke // integration test logging will catch this Logger.LogError( "The reboot bridge request completed before the watchdog could suspend the server! This can lead to buggy DreamDaemon behaviour and should be reported! To ensure stability, we will need to hard reboot the server"); - await updateTask; + await RunPrequel(); return MonitorAction.Restart; } - await PerformDmbSwap(pendingSwappable, cancellationToken); + // DCT: Not necessary + if (!pendingSwappable.FinishActivationPreparation(CancellationToken.None).IsCompleted) + { + // rare pokemon + Logger.LogInformation("Deployed .dme is not ready to swap, delaying until next reboot!"); + Chat.QueueWatchdogMessage("The pending deployment was not ready to be activated this reboot. It will be applied at the next one."); + return MonitorAction.Continue; + } } + var updateTask = RunPrequel(); + if (needToSwap) + await PerformDmbSwap(pendingSwappable, cancellationToken); + var currentCompileJobId = Server.ReattachInformation.Dmb.CompileJob.Id; await DrainDeploymentCleanupTasks(false); - IDisposable lingeringDeployment; + IAsyncDisposable lingeringDeployment; var localDeploymentCleanupGate = new TaskCompletionSource(); async Task CleanupLingeringDeployment() { @@ -191,7 +203,7 @@ async Task CleanupLingeringDeployment() ? " due to timeout!" : "..."); - lingeringDeployment.Dispose(); + await lingeringDeployment.DisposeAsync(); } var oldDeploymentCleanupGate = Interlocked.Exchange(ref deploymentCleanupGate, localDeploymentCleanupGate); @@ -247,31 +259,31 @@ protected override async Task HandleNewDmbAvailable(CancellationToken cancellati if (!canSeamlesslySwap) { - compileJobProvider.Dispose(); + await compileJobProvider.DisposeAsync(); await base.HandleNewDmbAvailable(cancellationToken); return; } - SwappableDmbProvider windowsProvider = null; + SwappableDmbProvider swappableProvider = null; try { - windowsProvider = new SwappableDmbProvider(compileJobProvider, GameIOManager, symlinkFactory); + swappableProvider = CreateSwappableDmbProvider(compileJobProvider); if (ActiveCompileJob.DMApiVersion == null) { Logger.LogWarning("Active compile job has no DMAPI! Commencing immediate .dmb swap. Note this behavior is known to be buggy in some DM code contexts. See https://github.com/tgstation/tgstation-server/issues/1550"); - await PerformDmbSwap(windowsProvider, cancellationToken); + await PerformDmbSwap(swappableProvider, cancellationToken); } } catch (Exception ex) { Logger.LogError(ex, "Exception while swapping"); - IDmbProvider providerToDispose = windowsProvider ?? compileJobProvider; - providerToDispose.Dispose(); + IDmbProvider providerToDispose = swappableProvider ?? compileJobProvider; + await providerToDispose.DisposeAsync(); throw; } - pendingSwappable?.Dispose(); - pendingSwappable = windowsProvider; + await (pendingSwappable?.DisposeAsync() ?? ValueTask.CompletedTask); + pendingSwappable = swappableProvider; } /// @@ -284,7 +296,7 @@ protected sealed override async Task PrepServerForLaunch(IDmbProvi Logger.LogTrace("Prep for server launch"); - ActiveSwappable = new SwappableDmbProvider(dmbToUse, GameIOManager, symlinkFactory); + ActiveSwappable = CreateSwappableDmbProvider(dmbToUse); try { await InitialLink(cancellationToken); @@ -310,6 +322,14 @@ protected virtual async Task ApplyInitialDmb(CancellationToken cancellationToken Server.ReattachInformation.InitialDmb = await DmbFactory.FromCompileJob(Server.CompileJob, cancellationToken); } + /// + /// Create a for a given . + /// + /// The to create a for. + /// A new . + protected virtual SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider) + => new SwappableDmbProvider(dmbProvider, GameIOManager, SymlinkFactory); + /// protected override async Task SessionStartupPersist(CancellationToken cancellationToken) { @@ -332,10 +352,11 @@ protected override async Task HandleMonitorWakeup(MonitorActivati /// /// The for the operation. /// A representing the running operation. - Task InitialLink(CancellationToken cancellationToken) + async ValueTask InitialLink(CancellationToken cancellationToken) { - Logger.LogTrace("Symlinking compile job..."); - return ActiveSwappable.MakeActive(cancellationToken); + await ActiveSwappable.FinishActivationPreparation(cancellationToken); + Logger.LogTrace("Linking compile job..."); + await ActiveSwappable.MakeActive(cancellationToken); } /// @@ -348,6 +369,8 @@ async ValueTask PerformDmbSwap(SwappableDmbProvider newProvider, CancellationTok { Logger.LogDebug("Swapping to compile job {id}...", newProvider.CompileJob.Id); + await newProvider.FinishActivationPreparation(cancellationToken); + var suspended = false; var server = Server; try diff --git a/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs b/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs index 1adbd99d975..08320a3d150 100644 --- a/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs +++ b/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs @@ -22,5 +22,14 @@ interface ISymlinkFactory /// The for the operation. /// A representing the running operation. Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken); + + /// + /// Creates a hard link. + /// + /// The path to the hard target. + /// The path to the link. + /// The for the operation. + /// A representing the running operation. + Task CreateHardLink(string targetPath, string linkPath, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs b/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs index ddbcbb86fb7..05d64a3bf07 100644 --- a/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs +++ b/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs @@ -15,6 +15,22 @@ sealed class PosixSymlinkFactory : ISymlinkFactory /// public bool SymlinkedDirectoriesAreDeletedAsFiles => true; + /// + public Task CreateHardLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew( + () => + { + ArgumentNullException.ThrowIfNull(targetPath); + ArgumentNullException.ThrowIfNull(linkPath); + + cancellationToken.ThrowIfCancellationRequested(); + var fsInfo = new UnixFileInfo(targetPath); + cancellationToken.ThrowIfCancellationRequested(); + fsInfo.CreateLink(linkPath); + }, + cancellationToken, + DefaultIOManager.BlockingTaskCreationOptions, + TaskScheduler.Current); + /// public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew( () => diff --git a/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs b/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs index 18abde6b8f9..864242d992d 100644 --- a/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs +++ b/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs @@ -16,6 +16,10 @@ sealed class WindowsSymlinkFactory : ISymlinkFactory /// public bool SymlinkedDirectoriesAreDeletedAsFiles => false; + /// + public Task CreateHardLink(string targetPath, string linkPath, CancellationToken cancellationToken) + => throw new NotSupportedException(); + /// public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew( () => From f6a843e651942907221f2ed5a6cf54749f9d0825 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 18:04:14 -0400 Subject: [PATCH 04/14] Rename `WindowsWatchdog` to `AdvancedWatchdog` --- .../{WindowsWatchdog.cs => AdvancedWatchdog.cs} | 14 +++++++------- .../Components/Watchdog/PosixWatchdog.cs | 8 ++++---- .../Components/Watchdog/WindowsWatchdogFactory.cs | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) rename src/Tgstation.Server.Host/Components/Watchdog/{WindowsWatchdog.cs => AdvancedWatchdog.cs} (97%) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs similarity index 97% rename from src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs rename to src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs index 0e42c3b660e..3552149c3cc 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs @@ -20,9 +20,9 @@ namespace Tgstation.Server.Host.Components.Watchdog { /// - /// A that, instead of killing servers for updates, uses the wonders of symlinks to swap out changes without killing DreamDaemon. + /// A that, instead of killing servers for updates, uses the wonders of filesystem links to swap out changes without killing the server process. /// - class WindowsWatchdog : BasicWatchdog + class AdvancedWatchdog : BasicWatchdog { /// /// The for . @@ -30,12 +30,12 @@ class WindowsWatchdog : BasicWatchdog protected SwappableDmbProvider ActiveSwappable { get; private set; } /// - /// The for the pointing to the Game directory. + /// The for the pointing to the Game directory. /// protected IIOManager GameIOManager { get; } /// - /// The for the . + /// The for the . /// protected ISymlinkFactory SymlinkFactory { get; } @@ -55,7 +55,7 @@ class WindowsWatchdog : BasicWatchdog volatile TaskCompletionSource deploymentCleanupGate; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The for the . /// The for the . @@ -73,7 +73,7 @@ class WindowsWatchdog : BasicWatchdog /// The for the . /// The for the . /// The autostart value for the . - public WindowsWatchdog( + public AdvancedWatchdog( IChatManager chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, @@ -86,7 +86,7 @@ public WindowsWatchdog( IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, IIOManager gameIOManager, ISymlinkFactory symlinkFactory, - ILogger logger, + ILogger logger, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs index 3bf2927e578..961fee9988a 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs @@ -19,9 +19,9 @@ namespace Tgstation.Server.Host.Components.Watchdog { /// - /// A variant of the that works on POSIX systems. + /// A variant of the that works on POSIX systems. /// - sealed class PosixWatchdog : WindowsWatchdog + sealed class PosixWatchdog : AdvancedWatchdog { /// /// The for the . @@ -41,8 +41,8 @@ sealed class PosixWatchdog : WindowsWatchdog /// The for the . /// The for the . /// The for the . - /// The pointing to the game directory for the .. - /// The for the . + /// The pointing to the game directory for the .. + /// The for the . /// The for the . /// The for the . /// The for the . diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs index 7909be59aff..fd7bdfe9b29 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs @@ -18,7 +18,7 @@ namespace Tgstation.Server.Host.Components.Watchdog { /// - /// for creating s. + /// for creating s. /// class WindowsWatchdogFactory : WatchdogFactory { @@ -78,7 +78,7 @@ public override IWatchdog CreateWatchdog( remoteDeploymentManagerFactory, gameIOManager, SymlinkFactory, - LoggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), settings, instance, settings.AutoStart ?? throw new ArgumentNullException(nameof(settings))); From 4b02e43c3841c9f1a869a3cc2a8ed74334e0be3c Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 18:10:35 -0400 Subject: [PATCH 05/14] Abstract `AdvancedWatchdog` Move Windows specific functionality out to `WindowsWatchdog`. --- .../Components/Watchdog/AdvancedWatchdog.cs | 16 ++-- .../Components/Watchdog/WindowsWatchdog.cs | 90 +++++++++++++++++++ .../Watchdog/WindowsWatchdogFactory.cs | 2 +- 3 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs diff --git a/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs index 3552149c3cc..07954e5a88f 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs @@ -22,7 +22,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// A that, instead of killing servers for updates, uses the wonders of filesystem links to swap out changes without killing the server process. /// - class AdvancedWatchdog : BasicWatchdog + abstract class AdvancedWatchdog : BasicWatchdog { /// /// The for . @@ -125,7 +125,7 @@ public AdvancedWatchdog( } /// - protected override async Task DisposeAndNullControllersImpl() + protected sealed override async Task DisposeAndNullControllersImpl() { await base.DisposeAndNullControllersImpl(); @@ -138,7 +138,7 @@ protected override async Task DisposeAndNullControllersImpl() } /// - protected override async Task HandleNormalReboot(CancellationToken cancellationToken) + protected sealed override async Task HandleNormalReboot(CancellationToken cancellationToken) { if (pendingSwappable != null) { @@ -231,7 +231,7 @@ async Task CleanupLingeringDeployment() } /// - protected override async Task HandleNewDmbAvailable(CancellationToken cancellationToken) + protected sealed override async Task HandleNewDmbAvailable(CancellationToken cancellationToken) { IDmbProvider compileJobProvider = DmbFactory.LockNextDmb(1); bool canSeamlesslySwap = true; @@ -317,18 +317,14 @@ protected sealed override async Task PrepServerForLaunch(IDmbProvi /// /// The for the operation. /// A representing the running operation. - protected virtual async Task ApplyInitialDmb(CancellationToken cancellationToken) - { - Server.ReattachInformation.InitialDmb = await DmbFactory.FromCompileJob(Server.CompileJob, cancellationToken); - } + protected abstract Task ApplyInitialDmb(CancellationToken cancellationToken); /// /// Create a for a given . /// /// The to create a for. /// A new . - protected virtual SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider) - => new SwappableDmbProvider(dmbProvider, GameIOManager, SymlinkFactory); + protected abstract SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider); /// protected override async Task SessionStartupPersist(CancellationToken cancellationToken) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs new file mode 100644 index 00000000000..917bafa1348 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs @@ -0,0 +1,90 @@ +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Extensions.Logging; + +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Chat; +using Tgstation.Server.Host.Components.Deployment; +using Tgstation.Server.Host.Components.Deployment.Remote; +using Tgstation.Server.Host.Components.Events; +using Tgstation.Server.Host.Components.Session; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Jobs; +using Tgstation.Server.Host.Utils; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// A variant of the that works on Windows systems. + /// + sealed class WindowsWatchdog : AdvancedWatchdog + { + /// + /// Initializes a new instance of the class. + /// + /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The pointing to the game directory for the .. + /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The autostart value for the . + public WindowsWatchdog( + IChatManager chat, + ISessionControllerFactory sessionControllerFactory, + IDmbFactory dmbFactory, + ISessionPersistor sessionPersistor, + IJobManager jobManager, + IServerControl serverControl, + IAsyncDelayer asyncDelayer, + IIOManager diagnosticsIOManager, + IEventConsumer eventConsumer, + IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, + IIOManager gameIOManager, + ISymlinkFactory symlinkFactory, + ILogger logger, + DreamDaemonLaunchParameters initialLaunchParameters, + Api.Models.Instance instance, + bool autoStart) + : base( + chat, + sessionControllerFactory, + dmbFactory, + sessionPersistor, + jobManager, + serverControl, + asyncDelayer, + diagnosticsIOManager, + eventConsumer, + remoteDeploymentManagerFactory, + gameIOManager, + symlinkFactory, + logger, + initialLaunchParameters, + instance, + autoStart) + { + } + + /// + protected override async Task ApplyInitialDmb(CancellationToken cancellationToken) + { + Server.ReattachInformation.InitialDmb = await DmbFactory.FromCompileJob(Server.CompileJob, cancellationToken); + } + + /// + protected override SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider) + => new SwappableDmbProvider(dmbProvider, GameIOManager, SymlinkFactory); + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs index fd7bdfe9b29..4cbe20577e6 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs @@ -78,7 +78,7 @@ public override IWatchdog CreateWatchdog( remoteDeploymentManagerFactory, gameIOManager, SymlinkFactory, - LoggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), settings, instance, settings.AutoStart ?? throw new ArgumentNullException(nameof(settings))); From afceee3a96a4aa979046f4344901f284f647ffcd Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 18:24:54 -0400 Subject: [PATCH 06/14] I was unsure about `DefaultIOManager.DeleteDirectory`, so here's another unit test. --- .../IO/TestIOManager.cs | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs b/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs index f92ffaa04db..5e34e809dc1 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs @@ -1,11 +1,12 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -using Remora.Discord.API.Objects; - -using System; +using System; using System.IO; +using System.Linq; +using System.Text; +using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.IO.Tests @@ -38,6 +39,53 @@ public async Task TestDeleteDirectory() } } + [TestMethod] + public async Task TestDeleteDirectoryWithSymlinkInsideDoesntRecurse() + { + var linkFactory = (ISymlinkFactory)(new PlatformIdentifier().IsWindows + ? new WindowsSymlinkFactory() + : new PosixSymlinkFactory()); + + var tempPath = Path.GetTempFileName(); + File.Delete(tempPath); + Directory.CreateDirectory(tempPath); + try + { + var targetDir = ioManager.ConcatPath(tempPath, "targetdir"); + await ioManager.CreateDirectory(targetDir, CancellationToken.None); + var fileInTargetDir = ioManager.ConcatPath(targetDir, "test1.txt"); + + var expectedBytes = Encoding.UTF8.GetBytes("I want to live"); + await ioManager.WriteAllBytes(fileInTargetDir, expectedBytes, CancellationToken.None); + + var testDir = ioManager.ConcatPath(tempPath, "testdir"); + await ioManager.CreateDirectory(testDir, CancellationToken.None); + var symlinkedFile = ioManager.ConcatPath(testDir, "test1.txt"); + var symlinkedDir = ioManager.ConcatPath(testDir, "linkedDir"); + + await linkFactory.CreateSymbolicLink(targetDir, symlinkedDir, CancellationToken.None); + await linkFactory.CreateSymbolicLink(fileInTargetDir, symlinkedFile, CancellationToken.None); + + Assert.IsTrue(await ioManager.DirectoryExists(symlinkedDir, CancellationToken.None)); + Assert.IsTrue(await ioManager.FileExists(symlinkedFile, CancellationToken.None)); + Assert.IsTrue(await ioManager.FileExists(ioManager.ConcatPath(symlinkedDir, "test1.txt"), CancellationToken.None)); + Assert.IsTrue(await ioManager.FileExists(fileInTargetDir, CancellationToken.None)); + + await ioManager.DeleteDirectory(testDir, CancellationToken.None); + + Assert.IsFalse(await ioManager.DirectoryExists(symlinkedDir, CancellationToken.None)); + Assert.IsFalse(await ioManager.FileExists(symlinkedFile, CancellationToken.None)); + Assert.IsFalse(await ioManager.FileExists(ioManager.ConcatPath(symlinkedDir, "test1.txt"), CancellationToken.None)); + Assert.IsTrue(await ioManager.FileExists(fileInTargetDir, CancellationToken.None)); + Assert.IsTrue(expectedBytes.SequenceEqual(await ioManager.ReadAllBytes(fileInTargetDir, CancellationToken.None))); + } + catch + { + Directory.Delete(tempPath, true); + throw; + } + } + [TestMethod] public async Task TestFileExists() { From 8129773d69d7e8d4c27c3783bf8cf5f962da30e1 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 18:37:01 -0400 Subject: [PATCH 07/14] Extract symlinking behavior to `SymlinkDmbProvider` --- .../Deployment/HardLinkDmbProvider.cs | 16 ++++--- .../Deployment/SwappableDmbProvider.cs | 44 +++++++------------ .../Deployment/SymlinkDmbProvider.cs | 44 +++++++++++++++++++ .../Components/Watchdog/WindowsWatchdog.cs | 2 +- 4 files changed, 70 insertions(+), 36 deletions(-) create mode 100644 src/Tgstation.Server.Host/Components/Deployment/SymlinkDmbProvider.cs diff --git a/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs index e5ddede026e..634af753f42 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs @@ -111,15 +111,17 @@ async void DisposeOfOldDirectory() goAheadTcs.SetResult(); await IOManager.DeleteDirectory(disposePath, CancellationToken.None); // DCT: We're detached at this point } - catch (Exception ex) + catch (DirectoryNotFoundException ex) { - if (directoryMoved) - logger.LogWarning(ex, "Failed to delete hard linked directory: {disposePath}", disposePath); - else - { - logger.LogDebug(ex, "Live directory appears to not exist"); + logger.LogDebug(ex, "Live directory appears to not exist"); + if (!directoryMoved) goAheadTcs.SetResult(); - } + } + catch (Exception ex) + { + logger.LogWarning(ex, "Failed to delete hard linked directory: {disposePath}", disposePath); + if (!directoryMoved) + goAheadTcs.SetException(ex); } } diff --git a/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs index 14cdc268e29..b82f5cc6761 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs @@ -8,29 +8,34 @@ namespace Tgstation.Server.Host.Components.Deployment { /// - /// A that uses symlinks. + /// A that uses filesystem links to change directory structure underneath the server process. /// - class SwappableDmbProvider : IDmbProvider + abstract class SwappableDmbProvider : IDmbProvider { /// - /// The directory where the is symlinked to. + /// The directory where the is symlinked to. /// public const string LiveGameDirectory = "Live"; /// - public string DmbName => baseProvider.DmbName; + public string DmbName => BaseProvider.DmbName; /// public string Directory => IOManager.ResolvePath(LiveGameDirectory); /// - public CompileJob CompileJob => baseProvider.CompileJob; + public CompileJob CompileJob => BaseProvider.CompileJob; /// /// If has been run. /// public bool Swapped => swapped != 0; + /// + /// The we are swapping for. + /// + protected IDmbProvider BaseProvider { get; } + /// /// The to use. /// @@ -41,11 +46,6 @@ class SwappableDmbProvider : IDmbProvider /// protected ISymlinkFactory SymlinkFactory { get; } - /// - /// The we are swapping for. - /// - readonly IDmbProvider baseProvider; - /// /// Backing field for . /// @@ -54,21 +54,21 @@ class SwappableDmbProvider : IDmbProvider /// /// Initializes a new instance of the class. /// - /// The value of . + /// The value of . /// The value of . /// The value of . public SwappableDmbProvider(IDmbProvider baseProvider, IIOManager ioManager, ISymlinkFactory symlinkFactory) { - this.baseProvider = baseProvider ?? throw new ArgumentNullException(nameof(baseProvider)); + BaseProvider = baseProvider ?? throw new ArgumentNullException(nameof(baseProvider)); IOManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); SymlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); } /// - public virtual ValueTask DisposeAsync() => baseProvider.DisposeAsync(); + public virtual ValueTask DisposeAsync() => BaseProvider.DisposeAsync(); /// - public void KeepAlive() => baseProvider.KeepAlive(); + public void KeepAlive() => BaseProvider.KeepAlive(); /// /// Make the active by replacing the live link with our . @@ -88,25 +88,13 @@ public Task MakeActive(CancellationToken cancellationToken) /// /// The for the operation. /// A representing the preparation process. - public virtual Task FinishActivationPreparation(CancellationToken cancellationToken) - => Task.CompletedTask; + public abstract Task FinishActivationPreparation(CancellationToken cancellationToken); /// /// Perform the swapping action. /// /// The for the operation. /// A representing the running operation. - protected virtual async Task DoSwap(CancellationToken cancellationToken) - { - if (SymlinkFactory.SymlinkedDirectoriesAreDeletedAsFiles) - await IOManager.DeleteFile(LiveGameDirectory, cancellationToken); - else - await IOManager.DeleteDirectory(LiveGameDirectory, cancellationToken); - - await SymlinkFactory.CreateSymbolicLink( - IOManager.ResolvePath(baseProvider.Directory), - IOManager.ResolvePath(LiveGameDirectory), - cancellationToken); - } + protected abstract Task DoSwap(CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Components/Deployment/SymlinkDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/SymlinkDmbProvider.cs new file mode 100644 index 00000000000..7e0845b95cf --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Deployment/SymlinkDmbProvider.cs @@ -0,0 +1,44 @@ +using System.Threading; +using System.Threading.Tasks; + +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Deployment +{ + /// + /// A that uses symlinks. + /// + sealed class SymlinkDmbProvider : SwappableDmbProvider + { + /// + /// Initializes a new instance of the class. + /// + /// The for the . + /// The for the . + /// The for the . + public SymlinkDmbProvider( + IDmbProvider baseProvider, + IIOManager ioManager, + ISymlinkFactory symlinkFactory) + : base(baseProvider, ioManager, symlinkFactory) + { + } + + /// + public override Task FinishActivationPreparation(CancellationToken cancellationToken) => Task.CompletedTask; + + /// + protected override async Task DoSwap(CancellationToken cancellationToken) + { + if (SymlinkFactory.SymlinkedDirectoriesAreDeletedAsFiles) + await IOManager.DeleteFile(LiveGameDirectory, cancellationToken); + else + await IOManager.DeleteDirectory(LiveGameDirectory, cancellationToken); + + await SymlinkFactory.CreateSymbolicLink( + IOManager.ResolvePath(BaseProvider.Directory), + IOManager.ResolvePath(LiveGameDirectory), + cancellationToken); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs index 917bafa1348..d882296b068 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs @@ -85,6 +85,6 @@ protected override async Task ApplyInitialDmb(CancellationToken cancellationToke /// protected override SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider) - => new SwappableDmbProvider(dmbProvider, GameIOManager, SymlinkFactory); + => new SymlinkDmbProvider(dmbProvider, GameIOManager, SymlinkFactory); } } From fd6de0fee2d8ab0b2e950de59a94001335952a24 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 18:42:19 -0400 Subject: [PATCH 08/14] Rename *`SymlinkFactory` to *`FilesystemLinkFactory` to better reflect new behavior --- .../Deployment/HardLinkDmbProvider.cs | 8 +++---- .../Deployment/SwappableDmbProvider.cs | 10 ++++----- .../Deployment/SymlinkDmbProvider.cs | 10 ++++----- .../Components/InstanceFactory.cs | 12 +++++----- .../Components/StaticFiles/Configuration.cs | 12 +++++----- .../Components/Watchdog/AdvancedWatchdog.cs | 10 ++++----- .../Components/Watchdog/PosixWatchdog.cs | 8 +++---- .../Watchdog/PosixWatchdogFactory.cs | 8 +++---- .../Components/Watchdog/WindowsWatchdog.cs | 8 +++---- .../Watchdog/WindowsWatchdogFactory.cs | 12 +++++----- src/Tgstation.Server.Host/Core/Application.cs | 4 ++-- ...nkFactory.cs => IFilesystemLinkFactory.cs} | 2 +- ...ctory.cs => PosixFilesystemLinkFactory.cs} | 4 ++-- ...ory.cs => WindowsFilesystemLinkFactory.cs} | 4 ++-- .../StaticFiles/TestConfiguration.cs | 2 +- ...actory.cs => TestFilesystemLinkFactory.cs} | 22 +++++++++---------- .../IO/TestIOManager.cs | 6 ++--- .../System/TestSymlinkFactory.cs | 6 ++--- 18 files changed, 74 insertions(+), 74 deletions(-) rename src/Tgstation.Server.Host/IO/{ISymlinkFactory.cs => IFilesystemLinkFactory.cs} (97%) rename src/Tgstation.Server.Host/IO/{PosixSymlinkFactory.cs => PosixFilesystemLinkFactory.cs} (92%) rename src/Tgstation.Server.Host/IO/{WindowsSymlinkFactory.cs => WindowsFilesystemLinkFactory.cs} (92%) rename tests/Tgstation.Server.Host.Tests/IO/{TestSymlinkFactory.cs => TestFilesystemLinkFactory.cs} (80%) diff --git a/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs index 634af753f42..cd9a3f1185a 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/HardLinkDmbProvider.cs @@ -40,19 +40,19 @@ sealed class HardLinkDmbProvider : SwappableDmbProvider /// /// The for the . /// The for the . - /// The for the . + /// The for the . /// The value of . /// The for the . public HardLinkDmbProvider( IDmbProvider baseProvider, IIOManager ioManager, - ISymlinkFactory symlinkFactory, + IFilesystemLinkFactory linkFactory, ILogger logger, GeneralConfiguration generalConfiguration) : base( baseProvider, ioManager, - symlinkFactory) + linkFactory) { this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); cancellationTokenSource = new CancellationTokenSource(); @@ -214,7 +214,7 @@ async Task LinkThisFile() using var lockContext = semaphore != null ? await SemaphoreSlimContext.Lock(semaphore, cancellationToken) : null; - await SymlinkFactory.CreateHardLink(sourceFile, destFile, cancellationToken); + await LinkFactory.CreateHardLink(sourceFile, destFile, cancellationToken); } yield return LinkThisFile(); diff --git a/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs index b82f5cc6761..b00ba3d518e 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs @@ -42,9 +42,9 @@ abstract class SwappableDmbProvider : IDmbProvider protected IIOManager IOManager { get; } /// - /// The to use. + /// The to use. /// - protected ISymlinkFactory SymlinkFactory { get; } + protected IFilesystemLinkFactory LinkFactory { get; } /// /// Backing field for . @@ -56,12 +56,12 @@ abstract class SwappableDmbProvider : IDmbProvider /// /// The value of . /// The value of . - /// The value of . - public SwappableDmbProvider(IDmbProvider baseProvider, IIOManager ioManager, ISymlinkFactory symlinkFactory) + /// The value of . + public SwappableDmbProvider(IDmbProvider baseProvider, IIOManager ioManager, IFilesystemLinkFactory symlinkFactory) { BaseProvider = baseProvider ?? throw new ArgumentNullException(nameof(baseProvider)); IOManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); - SymlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + LinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); } /// diff --git a/src/Tgstation.Server.Host/Components/Deployment/SymlinkDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/SymlinkDmbProvider.cs index 7e0845b95cf..2edc58d37eb 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/SymlinkDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/SymlinkDmbProvider.cs @@ -15,12 +15,12 @@ sealed class SymlinkDmbProvider : SwappableDmbProvider /// /// The for the . /// The for the . - /// The for the . + /// The for the . public SymlinkDmbProvider( IDmbProvider baseProvider, IIOManager ioManager, - ISymlinkFactory symlinkFactory) - : base(baseProvider, ioManager, symlinkFactory) + IFilesystemLinkFactory linkFactory) + : base(baseProvider, ioManager, linkFactory) { } @@ -30,12 +30,12 @@ public SymlinkDmbProvider( /// protected override async Task DoSwap(CancellationToken cancellationToken) { - if (SymlinkFactory.SymlinkedDirectoriesAreDeletedAsFiles) + if (LinkFactory.SymlinkedDirectoriesAreDeletedAsFiles) await IOManager.DeleteFile(LiveGameDirectory, cancellationToken); else await IOManager.DeleteDirectory(LiveGameDirectory, cancellationToken); - await SymlinkFactory.CreateSymbolicLink( + await LinkFactory.CreateSymbolicLink( IOManager.ResolvePath(BaseProvider.Directory), IOManager.ResolvePath(LiveGameDirectory), cancellationToken); diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index 8c7a8df85e6..a3c7ba21925 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -66,9 +66,9 @@ sealed class InstanceFactory : IInstanceFactory readonly ISynchronousIOManager synchronousIOManager; /// - /// The for the . + /// The for the . /// - readonly ISymlinkFactory symlinkFactory; + readonly IFilesystemLinkFactory linkFactory; /// /// The for the . @@ -173,7 +173,7 @@ sealed class InstanceFactory : IInstanceFactory /// The value of . /// The value of . /// The value of . - /// The value of . + /// The value of . /// The value of . /// The value of . /// The value of . @@ -199,7 +199,7 @@ public InstanceFactory( ITopicClientFactory topicClientFactory, ICryptographySuite cryptographySuite, ISynchronousIOManager synchronousIOManager, - ISymlinkFactory symlinkFactory, + IFilesystemLinkFactory linkFactory, IByondInstaller byondInstaller, IChatManagerFactory chatFactory, IProcessExecutor processExecutor, @@ -225,7 +225,7 @@ public InstanceFactory( this.topicClientFactory = topicClientFactory ?? throw new ArgumentNullException(nameof(topicClientFactory)); this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite)); this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager)); - this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + this.linkFactory = linkFactory ?? throw new ArgumentNullException(nameof(linkFactory)); this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller)); this.chatFactory = chatFactory ?? throw new ArgumentNullException(nameof(chatFactory)); this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor)); @@ -275,7 +275,7 @@ public async Task CreateInstance(IBridgeRegistrar bridgeRegistrar, Mo var configuration = new StaticFiles.Configuration( configurationIoManager, synchronousIOManager, - symlinkFactory, + linkFactory, processExecutor, postWriteHandler, platformIdentifier, diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs index 087503d68c4..eeb5cd71c65 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -94,9 +94,9 @@ sealed class Configuration : IConfiguration readonly ISynchronousIOManager synchronousIOManager; /// - /// The for . + /// The for . /// - readonly ISymlinkFactory symlinkFactory; + readonly IFilesystemLinkFactory linkFactory; /// /// The for . @@ -153,7 +153,7 @@ sealed class Configuration : IConfiguration /// /// The value of . /// The value of . - /// The value of . + /// The value of . /// The value of . /// The value of . /// The value of . @@ -164,7 +164,7 @@ sealed class Configuration : IConfiguration public Configuration( IIOManager ioManager, ISynchronousIOManager synchronousIOManager, - ISymlinkFactory symlinkFactory, + IFilesystemLinkFactory linkFactory, IProcessExecutor processExecutor, IPostWriteHandler postWriteHandler, IPlatformIdentifier platformIdentifier, @@ -175,7 +175,7 @@ public Configuration( { this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager)); - this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + this.linkFactory = linkFactory ?? throw new ArgumentNullException(nameof(linkFactory)); this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor)); this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler)); this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier)); @@ -450,7 +450,7 @@ await Task.WhenAll(entries.Select(async file => var fileExists = await fileExistsTask; if (fileExists) await ioManager.DeleteFile(destPath, cancellationToken); - await symlinkFactory.CreateSymbolicLink(ioManager.ResolvePath(file), ioManager.ResolvePath(destPath), cancellationToken); + await linkFactory.CreateSymbolicLink(ioManager.ResolvePath(file), ioManager.ResolvePath(destPath), cancellationToken); })); } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs index 07954e5a88f..48d2b3e2546 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/AdvancedWatchdog.cs @@ -35,9 +35,9 @@ abstract class AdvancedWatchdog : BasicWatchdog protected IIOManager GameIOManager { get; } /// - /// The for the . + /// The for the . /// - protected ISymlinkFactory SymlinkFactory { get; } + protected IFilesystemLinkFactory LinkFactory { get; } /// /// of s that are waiting to clean up old deployments. @@ -68,7 +68,7 @@ abstract class AdvancedWatchdog : BasicWatchdog /// The for the . /// The for the . /// The value of . - /// The value of . + /// The value of . /// The for the . /// The for the . /// The for the . @@ -85,7 +85,7 @@ public AdvancedWatchdog( IEventConsumer eventConsumer, IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, IIOManager gameIOManager, - ISymlinkFactory symlinkFactory, + IFilesystemLinkFactory linkFactory, ILogger logger, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, @@ -109,7 +109,7 @@ public AdvancedWatchdog( try { GameIOManager = gameIOManager ?? throw new ArgumentNullException(nameof(gameIOManager)); - SymlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + LinkFactory = linkFactory ?? throw new ArgumentNullException(nameof(linkFactory)); deploymentCleanupTasks = new List(); } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs index 961fee9988a..eb563bd1fa0 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs @@ -42,7 +42,7 @@ sealed class PosixWatchdog : AdvancedWatchdog /// The for the . /// The for the . /// The pointing to the game directory for the .. - /// The for the . + /// The for the . /// The for the . /// The for the . /// The for the . @@ -60,7 +60,7 @@ public PosixWatchdog( IEventConsumer eventConsumer, IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, IIOManager gameIOManager, - ISymlinkFactory symlinkFactory, + IFilesystemLinkFactory linkFactory, ILogger logger, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, @@ -78,7 +78,7 @@ public PosixWatchdog( eventConsumer, remoteDeploymentManagerFactory, gameIOManager, - symlinkFactory, + linkFactory, logger, initialLaunchParameters, instance, @@ -93,6 +93,6 @@ protected override Task ApplyInitialDmb(CancellationToken cancellationToken) /// protected override SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider) - => new HardLinkDmbProvider(dmbProvider, GameIOManager, SymlinkFactory, Logger, generalConfiguration); + => new HardLinkDmbProvider(dmbProvider, GameIOManager, LinkFactory, Logger, generalConfiguration); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdogFactory.cs index 787127a05c9..64c86722625 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdogFactory.cs @@ -29,21 +29,21 @@ sealed class PosixWatchdogFactory : WindowsWatchdogFactory /// The for the . /// The for the . /// The for the . - /// The for the . + /// The for the . /// The for for the . public PosixWatchdogFactory( IServerControl serverControl, ILoggerFactory loggerFactory, IJobManager jobManager, IAsyncDelayer asyncDelayer, - ISymlinkFactory symlinkFactory, + IFilesystemLinkFactory linkFactory, IOptions generalConfigurationOptions) : base( serverControl, loggerFactory, jobManager, asyncDelayer, - symlinkFactory, + linkFactory, generalConfigurationOptions) { } @@ -72,7 +72,7 @@ public override IWatchdog CreateWatchdog( eventConsumer, remoteDeploymentManagerFactory, gameIOManager, - SymlinkFactory, + LinkFactory, LoggerFactory.CreateLogger(), settings, instance, diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs index d882296b068..05c1f100888 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs @@ -35,7 +35,7 @@ sealed class WindowsWatchdog : AdvancedWatchdog /// The for the . /// The for the . /// The pointing to the game directory for the .. - /// The for the . + /// The for the . /// The for the . /// The for the . /// The for the . @@ -52,7 +52,7 @@ public WindowsWatchdog( IEventConsumer eventConsumer, IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, IIOManager gameIOManager, - ISymlinkFactory symlinkFactory, + IFilesystemLinkFactory linkFactory, ILogger logger, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, @@ -69,7 +69,7 @@ public WindowsWatchdog( eventConsumer, remoteDeploymentManagerFactory, gameIOManager, - symlinkFactory, + linkFactory, logger, initialLaunchParameters, instance, @@ -85,6 +85,6 @@ protected override async Task ApplyInitialDmb(CancellationToken cancellationToke /// protected override SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider) - => new SymlinkDmbProvider(dmbProvider, GameIOManager, SymlinkFactory); + => new SymlinkDmbProvider(dmbProvider, GameIOManager, LinkFactory); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs index 4cbe20577e6..89ed6b145d7 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs @@ -23,9 +23,9 @@ namespace Tgstation.Server.Host.Components.Watchdog class WindowsWatchdogFactory : WatchdogFactory { /// - /// The for the . + /// The for the . /// - protected ISymlinkFactory SymlinkFactory { get; } + protected IFilesystemLinkFactory LinkFactory { get; } /// /// Initializes a new instance of the class. @@ -34,14 +34,14 @@ class WindowsWatchdogFactory : WatchdogFactory /// The for the . /// The for the . /// The for the . - /// The value of . + /// The value of . /// The for for the . public WindowsWatchdogFactory( IServerControl serverControl, ILoggerFactory loggerFactory, IJobManager jobManager, IAsyncDelayer asyncDelayer, - ISymlinkFactory symlinkFactory, + IFilesystemLinkFactory symlinkFactory, IOptions generalConfigurationOptions) : base( serverControl, @@ -50,7 +50,7 @@ public WindowsWatchdogFactory( asyncDelayer, generalConfigurationOptions) { - SymlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + LinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); } /// @@ -77,7 +77,7 @@ public override IWatchdog CreateWatchdog( eventConsumer, remoteDeploymentManagerFactory, gameIOManager, - SymlinkFactory, + LinkFactory, LoggerFactory.CreateLogger(), settings, instance, diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index ed1cb97cad0..18abdcacf30 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -336,7 +336,7 @@ void AddTypedContext() { AddWatchdog(services, postSetupServices); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -349,7 +349,7 @@ void AddTypedContext() { AddWatchdog(services, postSetupServices); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs b/src/Tgstation.Server.Host/IO/IFilesystemLinkFactory.cs similarity index 97% rename from src/Tgstation.Server.Host/IO/ISymlinkFactory.cs rename to src/Tgstation.Server.Host/IO/IFilesystemLinkFactory.cs index 08320a3d150..3cf0622801a 100644 --- a/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs +++ b/src/Tgstation.Server.Host/IO/IFilesystemLinkFactory.cs @@ -6,7 +6,7 @@ namespace Tgstation.Server.Host.IO /// /// For creating filesystem symbolic links. /// - interface ISymlinkFactory + interface IFilesystemLinkFactory { /// /// If directory symlinks must be deleted as files would in the current environment. diff --git a/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs b/src/Tgstation.Server.Host/IO/PosixFilesystemLinkFactory.cs similarity index 92% rename from src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs rename to src/Tgstation.Server.Host/IO/PosixFilesystemLinkFactory.cs index 05d64a3bf07..b80af9e122a 100644 --- a/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs +++ b/src/Tgstation.Server.Host/IO/PosixFilesystemLinkFactory.cs @@ -8,9 +8,9 @@ namespace Tgstation.Server.Host.IO { /// - /// for posix systems. + /// for POSIX systems. /// - sealed class PosixSymlinkFactory : ISymlinkFactory + sealed class PosixFilesystemLinkFactory : IFilesystemLinkFactory { /// public bool SymlinkedDirectoriesAreDeletedAsFiles => true; diff --git a/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs b/src/Tgstation.Server.Host/IO/WindowsFilesystemLinkFactory.cs similarity index 92% rename from src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs rename to src/Tgstation.Server.Host/IO/WindowsFilesystemLinkFactory.cs index 864242d992d..1513f4e1666 100644 --- a/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs +++ b/src/Tgstation.Server.Host/IO/WindowsFilesystemLinkFactory.cs @@ -9,9 +9,9 @@ namespace Tgstation.Server.Host.IO { /// - /// for windows systems. + /// for windows systems. /// - sealed class WindowsSymlinkFactory : ISymlinkFactory + sealed class WindowsFilesystemLinkFactory : IFilesystemLinkFactory { /// public bool SymlinkedDirectoriesAreDeletedAsFiles => false; diff --git a/tests/Tgstation.Server.Host.Tests/Components/StaticFiles/TestConfiguration.cs b/tests/Tgstation.Server.Host.Tests/Components/StaticFiles/TestConfiguration.cs index 68241988d11..ff3bd0b5b83 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/StaticFiles/TestConfiguration.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/StaticFiles/TestConfiguration.cs @@ -47,7 +47,7 @@ public async Task TestListOrdering() var configuration = new Configuration( ioManager, new SynchronousIOManager(), - Mock.Of(), + Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs b/tests/Tgstation.Server.Host.Tests/IO/TestFilesystemLinkFactory.cs similarity index 80% rename from tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs rename to tests/Tgstation.Server.Host.Tests/IO/TestFilesystemLinkFactory.cs index 27646a9dc76..e735c485414 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestSymlinkFactory.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestFilesystemLinkFactory.cs @@ -8,17 +8,17 @@ namespace Tgstation.Server.Host.IO.Tests { [TestClass] - public sealed class TestSymlinkFactory + public sealed class TestFilesystemLinkFactory { - static ISymlinkFactory symlinkFactory; + static IFilesystemLinkFactory linkFactory; [ClassInitialize] public static void SelectFactory(TestContext _) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - symlinkFactory = new WindowsSymlinkFactory(); + linkFactory = new WindowsFilesystemLinkFactory(); else - symlinkFactory = new PosixSymlinkFactory(); + linkFactory = new PosixFilesystemLinkFactory(); } public static bool HasPermissionToMakeSymlinks() @@ -43,10 +43,10 @@ public async Task TestFileWorks() f2 = f1 + ".linked"; File.WriteAllText(f1, Text); - await Assert.ThrowsExceptionAsync(() => symlinkFactory.CreateSymbolicLink(null, null, default)); - await Assert.ThrowsExceptionAsync(() => symlinkFactory.CreateSymbolicLink(f1, null, default)); + await Assert.ThrowsExceptionAsync(() => linkFactory.CreateSymbolicLink(null, null, default)); + await Assert.ThrowsExceptionAsync(() => linkFactory.CreateSymbolicLink(f1, null, default)); - await symlinkFactory.CreateSymbolicLink(f1, f2, default); + await linkFactory.CreateSymbolicLink(f1, f2, default); Assert.IsTrue(File.Exists(f2)); var f2Contents = File.ReadAllText(f2); @@ -76,10 +76,10 @@ public async Task TestDirectoryWorks() var p1 = Path.Combine(f1, FileName); File.WriteAllText(p1, Text); - await Assert.ThrowsExceptionAsync(() => symlinkFactory.CreateSymbolicLink(null, null, default)); - await Assert.ThrowsExceptionAsync(() => symlinkFactory.CreateSymbolicLink(f1, null, default)); + await Assert.ThrowsExceptionAsync(() => linkFactory.CreateSymbolicLink(null, null, default)); + await Assert.ThrowsExceptionAsync(() => linkFactory.CreateSymbolicLink(f1, null, default)); - await symlinkFactory.CreateSymbolicLink(f1, f2, default); + await linkFactory.CreateSymbolicLink(f1, f2, default); var p2 = Path.Combine(f2, FileName); Assert.IsTrue(File.Exists(p2)); @@ -104,7 +104,7 @@ public async Task TestFailsProperly() try { - await symlinkFactory.CreateSymbolicLink(BadPath, BadPath, default); + await linkFactory.CreateSymbolicLink(BadPath, BadPath, default); Assert.Fail("No exception thrown!"); } catch { } diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs b/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs index 5e34e809dc1..00319f4f30e 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs @@ -42,9 +42,9 @@ public async Task TestDeleteDirectory() [TestMethod] public async Task TestDeleteDirectoryWithSymlinkInsideDoesntRecurse() { - var linkFactory = (ISymlinkFactory)(new PlatformIdentifier().IsWindows - ? new WindowsSymlinkFactory() - : new PosixSymlinkFactory()); + var linkFactory = (IFilesystemLinkFactory)(new PlatformIdentifier().IsWindows + ? new WindowsFilesystemLinkFactory() + : new PosixFilesystemLinkFactory()); var tempPath = Path.GetTempFileName(); File.Delete(tempPath); diff --git a/tests/Tgstation.Server.Host.Tests/System/TestSymlinkFactory.cs b/tests/Tgstation.Server.Host.Tests/System/TestSymlinkFactory.cs index 8e488f11b0d..4d3c5be7492 100644 --- a/tests/Tgstation.Server.Host.Tests/System/TestSymlinkFactory.cs +++ b/tests/Tgstation.Server.Host.Tests/System/TestSymlinkFactory.cs @@ -12,9 +12,9 @@ namespace Tgstation.Server.Host.System.Tests [TestClass] public sealed class TestSymlinkFactory { - readonly ISymlinkFactory factory = new PlatformIdentifier().IsWindows - ? new WindowsSymlinkFactory() - : new PosixSymlinkFactory(); + readonly IFilesystemLinkFactory factory = new PlatformIdentifier().IsWindows + ? new WindowsFilesystemLinkFactory() + : new PosixFilesystemLinkFactory(); [TestMethod] public async Task TestSymlinks() From 5bb655e3f6d3acc6963054f9517c8cd5257c522b Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 18:48:14 -0400 Subject: [PATCH 09/14] Add test for hard links --- .../IO/TestFilesystemLinkFactory.cs | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestFilesystemLinkFactory.cs b/tests/Tgstation.Server.Host.Tests/IO/TestFilesystemLinkFactory.cs index e735c485414..dbf2aaf2a82 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestFilesystemLinkFactory.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestFilesystemLinkFactory.cs @@ -1,10 +1,12 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.IO; using System.Runtime.InteropServices; using System.Security.Principal; +using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + namespace Tgstation.Server.Host.IO.Tests { [TestClass] @@ -30,6 +32,57 @@ public static bool HasPermissionToMakeSymlinks() return principal.IsInRole(WindowsBuiltInRole.Administrator); } + [TestMethod] + public async Task TestHardLinkWorks() + { + string f2 = null; + var f1 = Path.GetTempFileName(); + try + { + f2 = f1 + ".linked"; + const string Text = "Hello world"; + File.WriteAllText(f1, Text); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + await Assert.ThrowsExceptionAsync(() => linkFactory.CreateHardLink(f1, f2, CancellationToken.None)); + Assert.Inconclusive("Windows does not support hardlinks"); + } + + await Assert.ThrowsExceptionAsync(() => linkFactory.CreateHardLink(null, null, CancellationToken.None)); + await Assert.ThrowsExceptionAsync(() => linkFactory.CreateHardLink(f1, null, CancellationToken.None)); + + await linkFactory.CreateHardLink(f1, f2, default); + Assert.IsTrue(File.Exists(f2)); + + var f2Contents = File.ReadAllText(f2); + Assert.AreEqual(Text, f2Contents); + + const string NewText = "asdf"; + File.WriteAllText(f1, NewText); + + f2Contents = File.ReadAllText(f2); + Assert.AreEqual(NewText, f2Contents); + + const string NewText2 = "fdsa"; + File.WriteAllText(f2, NewText2); + + var f1Contents = File.ReadAllText(f1); + Assert.AreEqual(NewText2, f1Contents); + + File.Delete(f1); + Assert.IsFalse(File.Exists(f1)); + Assert.IsTrue(File.Exists(f2)); + f2Contents = File.ReadAllText(f2); + Assert.AreEqual(NewText2, f2Contents); + } + finally + { + File.Delete(f2); + File.Delete(f1); + } + } + [TestMethod] public async Task TestFileWorks() { From cc1c955bc8f2b037f257a3eeec977683533e53c5 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 19:16:47 -0400 Subject: [PATCH 10/14] We call it the Advanced Watchdog now --- .github/workflows/ci-pipeline.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 6ce724ea5d3..766e9660b48 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -365,7 +365,7 @@ jobs: fail-fast: false matrix: database-type: [ 'SqlServer', 'Sqlite', 'PostgresSql', 'MariaDB', 'MySql' ] - watchdog-type: [ 'Basic', 'System' ] + watchdog-type: [ 'Basic', 'Advanced' ] configuration: [ 'Debug', 'Release' ] runs-on: windows-latest steps: @@ -735,25 +735,25 @@ jobs: name: linux-unit-test-coverage-Release path: ./code_coverage/unit_tests/linux_unit_tests_release - - name: Retrieve Linux Integration Test Coverage (Release, System, Sqlite) + - name: Retrieve Linux Integration Test Coverage (Release, Advanced, Sqlite) uses: actions/download-artifact@v3 with: name: linux-integration-test-coverage-Release-System-Sqlite path: ./code_coverage/integration_tests/linux_integration_tests_release_system_sqlite - - name: Retrieve Linux Integration Test Coverage (Release, System, PostgresSql) + - name: Retrieve Linux Integration Test Coverage (Release, Advanced, PostgresSql) uses: actions/download-artifact@v3 with: name: linux-integration-test-coverage-Release-System-PostgresSql path: ./code_coverage/integration_tests/linux_integration_tests_release_system_mariadb - - name: Retrieve Linux Integration Test Coverage (Release, System, MariaDB) + - name: Retrieve Linux Integration Test Coverage (Release, Advanced, MariaDB) uses: actions/download-artifact@v3 with: name: linux-integration-test-coverage-Release-System-MariaDB path: ./code_coverage/integration_tests/linux_integration_tests_release_system_mysql - - name: Retrieve Linux Integration Test Coverage (Release, System, MySql) + - name: Retrieve Linux Integration Test Coverage (Release, Advanced, MySql) uses: actions/download-artifact@v3 with: name: linux-integration-test-coverage-Release-System-MySql @@ -789,13 +789,13 @@ jobs: name: linux-integration-test-coverage-Debug-System-Sqlite path: ./code_coverage/integration_tests/linux_integration_tests_debug_system_sqlite - - name: Retrieve Linux Integration Test Coverage (Debug, System, PostgresSql) + - name: Retrieve Linux Integration Test Coverage (Debug, Advanced, PostgresSql) uses: actions/download-artifact@v3 with: name: linux-integration-test-coverage-Debug-System-PostgresSql path: ./code_coverage/integration_tests/linux_integration_tests_debug_system_mariadb - - name: Retrieve Linux Integration Test Coverage (Debug, System, MariaDB) + - name: Retrieve Linux Integration Test Coverage (Debug, Advanced, MariaDB) uses: actions/download-artifact@v3 with: name: linux-integration-test-coverage-Debug-System-MariaDB From 71f5c739d8ba9d68ef691935d3b2e18b2755761f Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 19:21:11 -0400 Subject: [PATCH 11/14] Fix tests using `BasicWatchdog` failing --- .../Live/Instance/InstanceTest.cs | 6 ++++-- .../Live/Instance/WatchdogTest.cs | 12 +++++++----- .../Tgstation.Server.Tests/Live/LiveTestingServer.cs | 4 ++++ tests/Tgstation.Server.Tests/Live/TestLiveServer.cs | 6 ++++-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs index 4457994d792..2940500e206 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/InstanceTest.cs @@ -43,6 +43,7 @@ public async Task RunTests( ushort ddPort, bool highPrioDD, bool lowPrioDeployment, + bool usingBasicWatchdog, CancellationToken cancellationToken) { var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs, fileDownloader, instanceClient.Metadata); @@ -67,7 +68,7 @@ public async Task RunTests( await byondTask; await new WatchdogTest( - await ByondTest.GetEdgeVersion(fileDownloader, cancellationToken), instanceClient, instanceManager, serverPort, highPrioDD, ddPort).Run(cancellationToken); + await ByondTest.GetEdgeVersion(fileDownloader, cancellationToken), instanceClient, instanceManager, serverPort, highPrioDD, ddPort, usingBasicWatchdog).Run(cancellationToken); } public async Task RunCompatTests( @@ -76,6 +77,7 @@ public async Task RunCompatTests( ushort dmPort, ushort ddPort, bool highPrioDD, + bool usingBasicWatchdog, CancellationToken cancellationToken) { System.Console.WriteLine($"COMPAT TEST START: {compatVersion}"); @@ -189,7 +191,7 @@ await instanceClient.Repository.Update(new RepositoryUpdateRequest await configSetupTask; - await new WatchdogTest(compatVersion, instanceClient, instanceManager, serverPort, highPrioDD, ddPort).Run(cancellationToken); + await new WatchdogTest(compatVersion, instanceClient, instanceManager, serverPort, highPrioDD, ddPort, usingBasicWatchdog).Run(cancellationToken); await instanceManagerClient.Update(new InstanceUpdateRequest { diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 96ef4d3c201..b59e9532fec 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -63,10 +63,11 @@ sealed class WatchdogTest : JobsRequiredTest readonly bool highPrioDD; readonly TopicClient topicClient; readonly Version testVersion; + readonly bool usingBasicWatchdog; bool ranTimeoutTest = false; - public WatchdogTest(Version testVersion, IInstanceClient instanceClient, InstanceManager instanceManager, ushort serverPort, bool highPrioDD, ushort ddPort) + public WatchdogTest(Version testVersion, IInstanceClient instanceClient, InstanceManager instanceManager, ushort serverPort, bool highPrioDD, ushort ddPort, bool usingBasicWatchdog) : base(instanceClient.Jobs) { this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient)); @@ -75,8 +76,9 @@ public WatchdogTest(Version testVersion, IInstanceClient instanceClient, Instanc this.highPrioDD = highPrioDD; this.ddPort = ddPort; this.testVersion = testVersion ?? throw new ArgumentNullException(nameof(testVersion)); + this.usingBasicWatchdog = usingBasicWatchdog; - this.topicClient = new(new SocketParameters + topicClient = new(new SocketParameters { SendTimeout = TimeSpan.FromSeconds(30), ReceiveTimeout = TimeSpan.FromSeconds(30), @@ -476,9 +478,9 @@ async Task RunBasicTest(CancellationToken cancellationToken) Assert.AreEqual(string.Empty, daemonStatus.AdditionalParameters); } - void TestLinuxIsntBeingFuckingCheekyAboutFilePaths(DreamDaemonResponse currentStatus, CompileJobResponse previousStatus, CancellationToken cancellationToken) + void TestLinuxIsntBeingFuckingCheekyAboutFilePaths(DreamDaemonResponse currentStatus, CompileJobResponse previousStatus) { - if (new PlatformIdentifier().IsWindows) + if (new PlatformIdentifier().IsWindows || usingBasicWatchdog) return; Assert.IsNotNull(currentStatus.ActiveCompileJob); @@ -947,7 +949,7 @@ async Task RunLongRunningTestThenUpdate(CancellationToken cancellationToken) Assert.AreNotEqual(initialCompileJob.Id, daemonStatus.ActiveCompileJob.Id); Assert.IsNull(daemonStatus.StagedCompileJob); - TestLinuxIsntBeingFuckingCheekyAboutFilePaths(daemonStatus, initialCompileJob, cancellationToken); + TestLinuxIsntBeingFuckingCheekyAboutFilePaths(daemonStatus, initialCompileJob); await instanceClient.DreamDaemon.Shutdown(cancellationToken); diff --git a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs index 56263c17171..33213f6f7c5 100644 --- a/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs +++ b/tests/Tgstation.Server.Tests/Live/LiveTestingServer.cs @@ -63,6 +63,8 @@ static async Task Cleanup(string directory) public bool HighPriorityDreamDaemon { get; } public bool LowPriorityDeployments { get; } + public bool UsingBasicWatchdog { get; } + public bool RestartRequested => RealServer.RestartRequested; readonly List args; @@ -89,6 +91,8 @@ public LiveTestingServer(SwarmConfiguration swarmConfiguration, bool enableOAuth var gitHubAccessToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"); var dumpOpenAPISpecPathEnvVar = Environment.GetEnvironmentVariable("TGS_TEST_DUMP_API_SPEC"); + UsingBasicWatchdog = Boolean.TryParse(Environment.GetEnvironmentVariable("General__UseBasicWatchdog"), out var result) && result; + if (String.IsNullOrEmpty(DatabaseType)) Assert.Inconclusive("No database type configured in env var TGS_TEST_DATABASE_TYPE!"); diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index a78b984fc2e..21ab8be8442 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -1336,6 +1336,7 @@ async Task RunInstanceTests() compatDMPort, compatDDPort, server.HighPriorityDreamDaemon, + server.UsingBasicWatchdog, cancellationToken)); if (TestingUtils.RunningInGitHubActions) // they only have 2 cores, can't handle intense parallelization @@ -1349,6 +1350,7 @@ await FailFast( mainDDPort, server.HighPriorityDreamDaemon, server.LowPriorityDeployments, + server.UsingBasicWatchdog, cancellationToken)); await compatTests; @@ -1529,7 +1531,7 @@ async Task WaitForInitialJobs(IInstanceClient instanceClient) Assert.AreEqual(WatchdogStatus.Online, dd.Status.Value); var compileJob = await instanceClient.DreamMaker.Compile(cancellationToken); - var wdt = new WatchdogTest(edgeByond, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort); + var wdt = new WatchdogTest(edgeByond, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort, server.UsingBasicWatchdog); await wdt.WaitForJob(compileJob, 30, false, null, cancellationToken); dd = await instanceClient.DreamDaemon.Read(cancellationToken); @@ -1576,7 +1578,7 @@ await instanceClient.DreamDaemon.Update(new DreamDaemonRequest Assert.AreEqual(WatchdogStatus.Online, currentDD.Status); Assert.AreEqual(expectedStaged, currentDD.StagedCompileJob.Job.Id.Value); - var wdt = new WatchdogTest(edgeByond, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort); + var wdt = new WatchdogTest(edgeByond, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort, server.UsingBasicWatchdog); currentDD = await wdt.TellWorldToReboot(cancellationToken); Assert.AreEqual(expectedStaged, currentDD.ActiveCompileJob.Job.Id.Value); Assert.IsNull(currentDD.StagedCompileJob); From b22368086becd526e59da65aa6dc71a65d3f8d6a Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 20:40:04 -0400 Subject: [PATCH 12/14] Additional test logging --- tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index b59e9532fec..784fce5f67c 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -502,7 +502,7 @@ void TestLinuxIsntBeingFuckingCheekyAboutFilePaths(DreamDaemonResponse currentSt var path = sb.ToString(); - allPaths.Add(path); + allPaths.Add($"Path: {path}"); if (path.Contains($"Game/{previousStatus.DirectoryName}")) failingLinks.Add($"Found fd {fd} resolving to previous absolute path game dir path: {path}"); @@ -516,7 +516,7 @@ void TestLinuxIsntBeingFuckingCheekyAboutFilePaths(DreamDaemonResponse currentSt if (!foundLivePath) failingLinks.Add($"Failed to find a path containing the 'Live' directory!"); - Assert.IsTrue(failingLinks.Count == 0, String.Join(Environment.NewLine, failingLinks)); + Assert.IsTrue(failingLinks.Count == 0, String.Join(Environment.NewLine, failingLinks.Concat(allPaths))); } async Task RunHealthCheckTest(bool checkDump, CancellationToken cancellationToken) From b53b29189b86081b714e2557e2bf298285a10fef Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 23:17:00 -0400 Subject: [PATCH 13/14] Fix naming of CI matrix steps --- .github/workflows/ci-pipeline.yml | 90 +++++++++++++++---------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 766e9660b48..8e75c03afb2 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -382,7 +382,7 @@ jobs: dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - name: Set TGS_TEST_DUMP_API_SPEC - if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'SqlServer' }} + if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Advanced' && matrix.database-type == 'SqlServer' }} run: echo "TGS_TEST_DUMP_API_SPEC=yes" >> $Env:GITHUB_ENV - name: Set General__UseBasicWatchdog @@ -473,7 +473,7 @@ jobs: path: ./TestResults/ - name: Store OpenAPI Spec - if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'SqlServer' }} + if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Advanced' && matrix.database-type == 'SqlServer' }} uses: actions/upload-artifact@v3 with: name: openapi-spec @@ -562,7 +562,7 @@ jobs: fail-fast: false matrix: database-type: [ 'Sqlite', 'PostgresSql', 'MariaDB', 'MySql' ] - watchdog-type: [ 'Basic', 'System' ] + watchdog-type: [ 'Basic', 'Advanced' ] configuration: [ 'Debug', 'Release' ] runs-on: ubuntu-latest steps: @@ -645,7 +645,7 @@ jobs: path: ./TestResults/ - name: Package Server Console - if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'MariaDB' }} + if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Advanced' && matrix.database-type == 'MariaDB' }} run: | cd src/Tgstation.Server.Host.Console dotnet publish -c ${{ matrix.configuration }} -o ../../artifacts/Console @@ -657,7 +657,7 @@ jobs: build/RemoveUnsupportedRuntimes.sh artifacts/Console - name: Package Server Update Package - if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'PostgresSql' }} + if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Advanced' && matrix.database-type == 'PostgresSql' }} run: | cd src/Tgstation.Server.Host dotnet publish -c ${{ matrix.configuration }}NoWindows --no-build -o ../../artifacts/ServerUpdate @@ -666,14 +666,14 @@ jobs: build/RemoveUnsupportedRuntimes.sh artifacts/ServerUpdate - name: Store Server Console - if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'MariaDB' }} + if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Advanced' && matrix.database-type == 'MariaDB' }} uses: actions/upload-artifact@v3 with: name: ServerConsole path: artifacts/Console/ - name: Store Server Update Package - if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'PostgresSql' }} + if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Advanced' && matrix.database-type == 'PostgresSql' }} uses: actions/upload-artifact@v3 with: name: ServerUpdatePackage @@ -738,97 +738,97 @@ jobs: - name: Retrieve Linux Integration Test Coverage (Release, Advanced, Sqlite) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Release-System-Sqlite + name: linux-integration-test-coverage-Release-Advanced-Sqlite path: ./code_coverage/integration_tests/linux_integration_tests_release_system_sqlite - name: Retrieve Linux Integration Test Coverage (Release, Advanced, PostgresSql) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Release-System-PostgresSql + name: linux-integration-test-coverage-Release-Advanced-PostgresSql path: ./code_coverage/integration_tests/linux_integration_tests_release_system_mariadb - name: Retrieve Linux Integration Test Coverage (Release, Advanced, MariaDB) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Release-System-MariaDB + name: linux-integration-test-coverage-Release-Advanced-MariaDB path: ./code_coverage/integration_tests/linux_integration_tests_release_system_mysql - name: Retrieve Linux Integration Test Coverage (Release, Advanced, MySql) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Release-System-MySql + name: linux-integration-test-coverage-Release-Advanced-MySql path: ./code_coverage/integration_tests/linux_integration_tests_release_system_mysql - name: Retrieve Linux Integration Test Coverage (Release, Basic, Sqlite) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Release-System-Sqlite + name: linux-integration-test-coverage-Release-Advanced-Sqlite path: ./code_coverage/integration_tests/linux_integration_tests_release_basic_sqlite - name: Retrieve Linux Integration Test Coverage (Release, Basic, PostgresSql) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Release-System-PostgresSql + name: linux-integration-test-coverage-Release-Advanced-PostgresSql path: ./code_coverage/integration_tests/linux_integration_tests_release_basic_mariadb - name: Retrieve Linux Integration Test Coverage (Release, Basic, MariaDB) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Release-System-MariaDB + name: linux-integration-test-coverage-Release-Advanced-MariaDB path: ./code_coverage/integration_tests/linux_integration_tests_release_basic_mysql - name: Retrieve Linux Integration Test Coverage (Release, Basic, MySql) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Release-System-MySql + name: linux-integration-test-coverage-Release-Advanced-MySql path: ./code_coverage/integration_tests/linux_integration_tests_release_basic_mysql - - name: Retrieve Linux Integration Test Coverage (Debug, System, Sqlite) + - name: Retrieve Linux Integration Test Coverage (Debug, Advanced, Sqlite) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Debug-System-Sqlite + name: linux-integration-test-coverage-Debug-Advanced-Sqlite path: ./code_coverage/integration_tests/linux_integration_tests_debug_system_sqlite - name: Retrieve Linux Integration Test Coverage (Debug, Advanced, PostgresSql) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Debug-System-PostgresSql + name: linux-integration-test-coverage-Debug-Advanced-PostgresSql path: ./code_coverage/integration_tests/linux_integration_tests_debug_system_mariadb - name: Retrieve Linux Integration Test Coverage (Debug, Advanced, MariaDB) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Debug-System-MariaDB + name: linux-integration-test-coverage-Debug-Advanced-MariaDB path: ./code_coverage/integration_tests/linux_integration_tests_debug_system_mysql - - name: Retrieve Linux Integration Test Coverage (Debug, System, MySql) + - name: Retrieve Linux Integration Test Coverage (Debug, Advanced, MySql) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Debug-System-MySql + name: linux-integration-test-coverage-Debug-Advanced-MySql path: ./code_coverage/integration_tests/linux_integration_tests_debug_system_mysql - name: Retrieve Linux Integration Test Coverage (Debug, Basic, Sqlite) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Debug-System-Sqlite + name: linux-integration-test-coverage-Debug-Advanced-Sqlite path: ./code_coverage/integration_tests/linux_integration_tests_debug_basic_sqlite - name: Retrieve Linux Integration Test Coverage (Debug, Basic, PostgresSql) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Debug-System-PostgresSql + name: linux-integration-test-coverage-Debug-Advanced-PostgresSql path: ./code_coverage/integration_tests/linux_integration_tests_debug_basic_mariadb - name: Retrieve Linux Integration Test Coverage (Debug, Basic, MariaDB) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Debug-System-MariaDB + name: linux-integration-test-coverage-Debug-Advanced-MariaDB path: ./code_coverage/integration_tests/linux_integration_tests_debug_basic_mysql - name: Retrieve Linux Integration Test Coverage (Debug, Basic, MySql) uses: actions/download-artifact@v3 with: - name: linux-integration-test-coverage-Debug-System-MySql + name: linux-integration-test-coverage-Debug-Advanced-MySql path: ./code_coverage/integration_tests/linux_integration_tests_debug_basic_mysql - name: Retrieve Windows Unit Test Coverage (Release) @@ -849,16 +849,16 @@ jobs: name: windows-integration-test-coverage-Release-Basic-SqlServer path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_sqlserver - - name: Retrieve Windows Integration Test Coverage (Debug, System, SqlServer) + - name: Retrieve Windows Integration Test Coverage (Debug, Advanced, SqlServer) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Debug-System-SqlServer + name: windows-integration-test-coverage-Debug-Advanced-SqlServer path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_sqlserver - - name: Retrieve Windows Integration Test Coverage (Release, System, SqlServer) + - name: Retrieve Windows Integration Test Coverage (Release, Advanced, SqlServer) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Release-System-SqlServer + name: windows-integration-test-coverage-Release-Advanced-SqlServer path: ./code_coverage/integration_tests/windows_integration_tests_release_system_sqlserver - name: Retrieve Windows Integration Test Coverage (Debug, Basic, MariaDB) @@ -873,16 +873,16 @@ jobs: name: windows-integration-test-coverage-Release-Basic-MariaDB path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_mariadb - - name: Retrieve Windows Integration Test Coverage (Debug, System, MariaDB) + - name: Retrieve Windows Integration Test Coverage (Debug, Advanced, MariaDB) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Debug-System-MariaDB + name: windows-integration-test-coverage-Debug-Advanced-MariaDB path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_mariadb - - name: Retrieve Windows Integration Test Coverage (Release, System, MariaDB) + - name: Retrieve Windows Integration Test Coverage (Release, Advanced, MariaDB) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Release-System-MariaDB + name: windows-integration-test-coverage-Release-Advanced-MariaDB path: ./code_coverage/integration_tests/windows_integration_tests_release_system_mariadb - name: Retrieve Windows Integration Test Coverage (Debug, Basic, MySql) @@ -897,16 +897,16 @@ jobs: name: windows-integration-test-coverage-Release-Basic-MySql path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_mysql - - name: Retrieve Windows Integration Test Coverage (Debug, System, MySql) + - name: Retrieve Windows Integration Test Coverage (Debug, Advanced, MySql) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Debug-System-MySql + name: windows-integration-test-coverage-Debug-Advanced-MySql path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_mysql - - name: Retrieve Windows Integration Test Coverage (Release, System, MySql) + - name: Retrieve Windows Integration Test Coverage (Release, Advanced, MySql) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Release-System-MySql + name: windows-integration-test-coverage-Release-Advanced-MySql path: ./code_coverage/integration_tests/windows_integration_tests_release_system_mysql - name: Retrieve Windows Integration Test Coverage (Debug, Basic, PostgresSql) @@ -921,16 +921,16 @@ jobs: name: windows-integration-test-coverage-Release-Basic-PostgresSql path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_postgressql - - name: Retrieve Windows Integration Test Coverage (Debug, System, PostgresSql) + - name: Retrieve Windows Integration Test Coverage (Debug, Advanced, PostgresSql) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Debug-System-PostgresSql + name: windows-integration-test-coverage-Debug-Advanced-PostgresSql path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_postgressql - - name: Retrieve Windows Integration Test Coverage (Release, System, PostgresSql) + - name: Retrieve Windows Integration Test Coverage (Release, Advanced, PostgresSql) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Release-System-PostgresSql + name: windows-integration-test-coverage-Release-Advanced-PostgresSql path: ./code_coverage/integration_tests/windows_integration_tests_release_system_postgressql - name: Retrieve Windows Integration Test Coverage (Debug, Basic, Sqlite) @@ -945,16 +945,16 @@ jobs: name: windows-integration-test-coverage-Release-Basic-Sqlite path: ./code_coverage/integration_tests/windows_integration_tests_release_basic_sqlite - - name: Retrieve Windows Integration Test Coverage (Debug, System, Sqlite) + - name: Retrieve Windows Integration Test Coverage (Debug, Advanced, Sqlite) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Debug-System-Sqlite + name: windows-integration-test-coverage-Debug-Advanced-Sqlite path: ./code_coverage/integration_tests/windows_integration_tests_debug_system_sqlite - - name: Retrieve Windows Integration Test Coverage (Release, System, Sqlite) + - name: Retrieve Windows Integration Test Coverage (Release, Advanced, Sqlite) uses: actions/download-artifact@v3 with: - name: windows-integration-test-coverage-Release-System-Sqlite + name: windows-integration-test-coverage-Release-Advanced-Sqlite path: ./code_coverage/integration_tests/windows_integration_tests_release_system_sqlite - name: Upload Coverage to CodeCov From cfca47d999b1090dbc57fd418197075ae94759d8 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 21 Oct 2023 23:20:08 -0400 Subject: [PATCH 14/14] Version bump to 5.16.3 --- build/Version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Version.props b/build/Version.props index 4a4c556f247..c9c9e39ca0a 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,7 +3,7 @@ - 5.16.2 + 5.16.3 4.7.1 9.12.0 6.0.1