Extract symlinking behavior to SymlinkDmbProvider

This commit is contained in:
Jordan Dominion
2023-10-21 18:38:19 -04:00
parent afceee3a96
commit 8129773d69
4 changed files with 70 additions and 36 deletions
@@ -111,15 +111,17 @@ namespace Tgstation.Server.Host.Components.Deployment
goAheadTcs.SetResult();
await IOManager.DeleteDirectory(disposePath, CancellationToken.None); // DCT: We're detached at this point
}
catch (DirectoryNotFoundException ex)
{
logger.LogDebug(ex, "Live directory appears to not exist");
if (!directoryMoved)
goAheadTcs.SetResult();
}
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();
}
logger.LogWarning(ex, "Failed to delete hard linked directory: {disposePath}", disposePath);
if (!directoryMoved)
goAheadTcs.SetException(ex);
}
}
@@ -8,29 +8,34 @@ using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Components.Deployment
{
/// <summary>
/// A <see cref="IDmbProvider"/> that uses symlinks.
/// A <see cref="IDmbProvider"/> that uses filesystem links to change directory structure underneath the server process.
/// </summary>
class SwappableDmbProvider : IDmbProvider
abstract class SwappableDmbProvider : IDmbProvider
{
/// <summary>
/// The directory where the <see cref="baseProvider"/> is symlinked to.
/// The directory where the <see cref="BaseProvider"/> is symlinked to.
/// </summary>
public const string LiveGameDirectory = "Live";
/// <inheritdoc />
public string DmbName => baseProvider.DmbName;
public string DmbName => BaseProvider.DmbName;
/// <inheritdoc />
public string Directory => IOManager.ResolvePath(LiveGameDirectory);
/// <inheritdoc />
public CompileJob CompileJob => baseProvider.CompileJob;
public CompileJob CompileJob => BaseProvider.CompileJob;
/// <summary>
/// If <see cref="MakeActive(CancellationToken)"/> has been run.
/// </summary>
public bool Swapped => swapped != 0;
/// <summary>
/// The <see cref="IDmbProvider"/> we are swapping for.
/// </summary>
protected IDmbProvider BaseProvider { get; }
/// <summary>
/// The <see cref="IIOManager"/> to use.
/// </summary>
@@ -41,11 +46,6 @@ namespace Tgstation.Server.Host.Components.Deployment
/// </summary>
protected ISymlinkFactory SymlinkFactory { get; }
/// <summary>
/// The <see cref="IDmbProvider"/> we are swapping for.
/// </summary>
readonly IDmbProvider baseProvider;
/// <summary>
/// Backing field for <see cref="Swapped"/>.
/// </summary>
@@ -54,21 +54,21 @@ namespace Tgstation.Server.Host.Components.Deployment
/// <summary>
/// Initializes a new instance of the <see cref="SwappableDmbProvider"/> class.
/// </summary>
/// <param name="baseProvider">The value of <see cref="baseProvider"/>.</param>
/// <param name="baseProvider">The value of <see cref="BaseProvider"/>.</param>
/// <param name="ioManager">The value of <see cref="IOManager"/>.</param>
/// <param name="symlinkFactory">The value of <see cref="SymlinkFactory"/>.</param>
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));
}
/// <inheritdoc />
public virtual ValueTask DisposeAsync() => baseProvider.DisposeAsync();
public virtual ValueTask DisposeAsync() => BaseProvider.DisposeAsync();
/// <inheritdoc />
public void KeepAlive() => baseProvider.KeepAlive();
public void KeepAlive() => BaseProvider.KeepAlive();
/// <summary>
/// Make the <see cref="SwappableDmbProvider"/> active by replacing the live link with our <see cref="CompileJob"/>.
@@ -88,25 +88,13 @@ namespace Tgstation.Server.Host.Components.Deployment
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the preparation process.</returns>
public virtual Task FinishActivationPreparation(CancellationToken cancellationToken)
=> Task.CompletedTask;
public abstract Task FinishActivationPreparation(CancellationToken cancellationToken);
/// <summary>
/// Perform the swapping action.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
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);
}
}
@@ -0,0 +1,44 @@
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components.Deployment
{
/// <summary>
/// A <see cref="IDmbProvider"/> that uses symlinks.
/// </summary>
sealed class SymlinkDmbProvider : SwappableDmbProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="SymlinkDmbProvider"/> class.
/// </summary>
/// <param name="baseProvider">The <see cref="IDmbProvider"/> for the <see cref="SwappableDmbProvider"/>.</param>
/// <param name="ioManager">The <see cref="IIOManager"/> for the <see cref="SwappableDmbProvider"/>.</param>
/// <param name="symlinkFactory">The <see cref="ISymlinkFactory"/> for the <see cref="SwappableDmbProvider"/>.</param>
public SymlinkDmbProvider(
IDmbProvider baseProvider,
IIOManager ioManager,
ISymlinkFactory symlinkFactory)
: base(baseProvider, ioManager, symlinkFactory)
{
}
/// <inheritdoc />
public override Task FinishActivationPreparation(CancellationToken cancellationToken) => Task.CompletedTask;
/// <inheritdoc />
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);
}
}
}
@@ -85,6 +85,6 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <inheritdoc />
protected override SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider)
=> new SwappableDmbProvider(dmbProvider, GameIOManager, SymlinkFactory);
=> new SymlinkDmbProvider(dmbProvider, GameIOManager, SymlinkFactory);
}
}