diff --git a/src/Tgstation.Server.Host.Startup/IServer.cs b/src/Tgstation.Server.Host.Startup/IServer.cs
index 1fd97d4930..3ab78291f5 100644
--- a/src/Tgstation.Server.Host.Startup/IServer.cs
+++ b/src/Tgstation.Server.Host.Startup/IServer.cs
@@ -12,13 +12,13 @@ namespace Tgstation.Server.Host.Startup
///
/// The path to the updated assembly to run if any. Populated once returns
///
- Guid UpdateGuid { get; }
+ Guid? UpdateGuid { get; }
///
/// Runs the
///
/// The for the operation
/// A representing the running operation
- Task RunAsync(string updatesPath, CancellationToken cancellationToken);
+ Task RunAsync(CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host.Startup/IServerFactory.cs b/src/Tgstation.Server.Host.Startup/IServerFactory.cs
index ed92399240..a787e083f8 100644
--- a/src/Tgstation.Server.Host.Startup/IServerFactory.cs
+++ b/src/Tgstation.Server.Host.Startup/IServerFactory.cs
@@ -9,7 +9,8 @@
/// Create a
///
/// The arguments for the
+ /// The directory in which to install server updates
/// A new
- IServer CreateServer(string[] args);
+ IServer CreateServer(string[] args, string updatePath);
}
}
diff --git a/src/Tgstation.Server.Host/Components/ByondManager.cs b/src/Tgstation.Server.Host/Components/ByondManager.cs
index e046bcf56c..4b85832126 100644
--- a/src/Tgstation.Server.Host/Components/ByondManager.cs
+++ b/src/Tgstation.Server.Host/Components/ByondManager.cs
@@ -86,12 +86,9 @@ namespace Tgstation.Server.Host.Components
await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);
await ioManager.CreateDirectory(versionKey, cancellationToken).ConfigureAwait(false);
- var resolvedPath = ioManager.ResolvePath(versionKey);
- using (var zipBytes = new MemoryStream(await downloadTask.ConfigureAwait(false)))
- using (var archive = new ZipArchive(zipBytes))
- await Task.Factory.StartNew(() => archive.ExtractToDirectory(resolvedPath), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
+ await ioManager.ZipToDirectory(versionKey, await downloadTask.ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
- await byondInstaller.InstallByond(resolvedPath, version, cancellationToken).ConfigureAwait(false);
+ await byondInstaller.InstallByond(ioManager.ResolvePath(versionKey), version, cancellationToken).ConfigureAwait(false);
//make sure to do this last because this is what tells us we have a valid version
await ioManager.WriteAllBytes(ioManager.ConcatPath(versionKey, VersionFileName), Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
diff --git a/src/Tgstation.Server.Host/Core/IServerUpdater.cs b/src/Tgstation.Server.Host/Core/IServerUpdater.cs
index c4b43eb1b2..ed5a4e76f0 100644
--- a/src/Tgstation.Server.Host/Core/IServerUpdater.cs
+++ b/src/Tgstation.Server.Host/Core/IServerUpdater.cs
@@ -1,4 +1,7 @@
using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Core
{
@@ -10,8 +13,11 @@ namespace Tgstation.Server.Host.Core
///
/// Run a new assembly and stop the current one. This will likely trigger all active s
///
- /// The path to the new assembly
- void ApplyUpdate(string updatePath);
+ /// The s of the .zip file that contains the new assembly
+ /// The for the operation
+ /// The for the operation
+ /// A representing the running operation
+ Task ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken);
///
/// Register a given to run before stopping the server for updates
diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
index 793b5881e2..b24653f8fe 100644
--- a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
@@ -46,6 +47,12 @@ namespace Tgstation.Server.Host.IO
dir.Delete(true);
}
+ ///
+ /// Opens a for async writing at a given
+ ///
+ /// The path to open the at
+ /// A new ready for async writing
+ static FileStream OpenWriteStream(string path) => new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, DefaultBufferSize, true);
///
/// Copies a directory from to
@@ -205,7 +212,7 @@ namespace Tgstation.Server.Host.IO
public async Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
{
path = ResolvePath(path);
- using (var file = File.Open(path, FileMode.Create, FileAccess.Write))
+ using (var file = OpenWriteStream(path))
await file.WriteAsync(contents, 0, contents.Length, cancellationToken).ConfigureAwait(false);
}
@@ -252,5 +259,32 @@ namespace Tgstation.Server.Host.IO
using (cancellationToken.Register(() => wc.CancelAsync()))
return await wc.DownloadDataTaskAsync(url).ConfigureAwait(false);
}
+
+ ///
+ public async Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken)
+ {
+ path = ResolvePath(path);
+ if (zipFileBytes == null)
+ throw new ArgumentNullException(nameof(zipFileBytes));
+
+ using (var ms = new MemoryStream(zipFileBytes))
+ {
+ zipFileBytes = null;
+ using (var archive = new ZipArchive(ms))
+ {
+ string GetEntryName(ZipArchiveEntry entry) => ConcatPath(path, GetDirectoryName(entry.FullName));
+ //create directories first
+ await Task.WhenAll(archive.Entries.Where(x => x.Name.Length == 0).Select(x => CreateDirectory(GetEntryName(x), cancellationToken))).ConfigureAwait(false);
+ //extract files
+ await Task.WhenAll(archive.Entries.Where(x => x.Name.Length > 0).Select(async x =>
+ {
+ var entryPath = GetEntryName(x);
+ using (var stream = x.Open())
+ using (var file = OpenWriteStream(entryPath))
+ await stream.CopyToAsync(file).ConfigureAwait(false);
+ })).ConfigureAwait(false);
+ }
+ }
+ }
}
}
diff --git a/src/Tgstation.Server.Host/IO/IIOManager.cs b/src/Tgstation.Server.Host/IO/IIOManager.cs
index ad320805d3..3654fed7f5 100644
--- a/src/Tgstation.Server.Host/IO/IIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/IIOManager.cs
@@ -162,5 +162,14 @@ namespace Tgstation.Server.Host.IO
/// A for the operation
/// A resulting in the s of the downloaded file
Task DownloadFile(Uri url, CancellationToken cancellationToken);
+
+ ///
+ /// Extract a set of to a given
+ ///
+ /// The path to unzip to
+ /// The s of the
+ /// The for the operation
+ /// A representing the running operation
+ Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Server.cs b/src/Tgstation.Server.Host/Server.cs
index 5516c3287f..42f9cafeae 100644
--- a/src/Tgstation.Server.Host/Server.cs
+++ b/src/Tgstation.Server.Host/Server.cs
@@ -75,9 +75,9 @@ namespace Tgstation.Server.Host
UpdateGuid = Guid.NewGuid();
try
{
- await ioManager.WriteAllBytes(serverUpdatePath, updateData, cancellationToken).ConfigureAwait(false);
+ await ioManager.ZipToDirectory(updatePath, updateZipData, cancellationToken).ConfigureAwait(false);
}
- catch (OperationCanceledException)
+ catch
{
UpdateGuid = null;
throw;