Refactor refactor refactor some more

This commit is contained in:
Cyberboss
2018-05-03 10:44:06 -04:00
parent 4ac4f3f987
commit 39d084473b
22 changed files with 229 additions and 48 deletions
+3 -3
View File
@@ -18,7 +18,7 @@
var/access_token
var/instance_id
var/instance_name
var/host_port
var/host_path
var/port_1
var/port_2
var/cached_json
@@ -50,7 +50,7 @@
access_token = cached_json["access_token"]
instance_id = text2num(cached_json["instance_id"])
host_port = text2num(cached_json["host_port"])
host_path = cached_json["host_path"]
if(cached_json["api_validate_only"])
Export(TGS4_COMM_VALIDATE)
del(world)
@@ -110,7 +110,7 @@
world.OpenPort(new_port)
/datum/tgs_api/v4/proc/Export(command)
return world.Export("http://127.0.0.1:[host_port]/Interop/[instance_id]?command=[url_encode(command)]&access_token=[access_token]")
return world.Export("[host_path]/Interop/[instance_id]?command=[url_encode(command)]&access_token=[access_token]")
/datum/tgs_api/v4/OnReboot()
var/json = Export(TGS4_COMM_SERVER_REBOOT)
+1 -1
View File
@@ -15,7 +15,7 @@
results[command_name] = list("help_text" = stc.help_text, "admin_only" = stc.admin_only)
custom_commands[command_name] = stc
var/commands_file = cached_json["chat_commands_file"]
var/commands_file = cached_json["chat_commands_json"]
if(!commands_file)
return
text2file(json_encode(results), commands_file)
@@ -40,5 +40,15 @@ namespace Tgstation.Server.Api.Models
/// </summary>
[Permissions(WriteRight = InstanceManagerRights.SetConfiguration)]
public bool ConfigurationAllowed { get; set; }
/// <inheritdoc />
public Instance CloneMetadata() => new Instance
{
Id = Id,
Name = Name,
Path = Path,
Online = Online,
ConfigurationAllowed = ConfigurationAllowed
};
}
}
@@ -12,7 +12,7 @@ namespace Tgstation.Server.Api.Models.Internal
/// </summary>
[Permissions(DenyWrite = true)]
[Required, StringLength(40)]
public string Revision { get; set; }
public string Commit { get; set; }
/// <summary>
/// The sha of the most recent remote commit
@@ -31,6 +31,12 @@ namespace Tgstation.Server.Api.Models.Internal
[Required]
public string BodyAtMerge { get; set; }
/// <summary>
/// The URL of the pull request
/// </summary>
[Required]
public Uri Url { get; set; }
/// <summary>
/// The author of the pull request
/// </summary>
@@ -1,10 +1,13 @@
using System;
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Models;
using Tgstation.Server.Host.Core;
namespace Tgstation.Server.Host.Components
@@ -53,18 +56,41 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public async Task<int> RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource<object> onSuccessfulStartup, string dreamDaemonPath, string dmbPath, string accessToken, bool usePrimaryPort, bool alwaysKill, CancellationToken cancellationToken)
public async Task<int> RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource<object> onSuccessfulStartup, string dreamDaemonPath, IDmbProvider dmbProvider, InteropInfo interopInfo, bool alwaysKill, CancellationToken cancellationToken)
{
if (launchParameters == null)
throw new ArgumentNullException(nameof(launchParameters));
if (dreamDaemonPath == null)
throw new ArgumentNullException(nameof(dreamDaemonPath));
if (dmbProvider == null)
throw new ArgumentNullException(nameof(dmbProvider));
if (interopInfo == null)
throw new ArgumentNullException(nameof(interopInfo));
if (interopInfo.NextPort != launchParameters.PrimaryPort && interopInfo.NextPort != launchParameters.SecondaryPort)
throw new ArgumentOutOfRangeException(nameof(interopInfo), interopInfo, "interopInfo.NextPort must match primary or secondary ports!");
var isPrimary = interopInfo.NextPort == launchParameters.SecondaryPort;
//serialize the interop info to the json
var jsonPath = String.Concat(Guid.NewGuid(), ".tgs.json");
var json = JsonConvert.SerializeObject(interopInfo);
using (var proc = new Process())
{
proc.StartInfo.FileName = dreamDaemonPath;
proc.StartInfo.WorkingDirectory = ioManager.GetDirectoryName(dmbPath);
proc.StartInfo.Arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} {2}-close -{3} -verbose -public -params \"{4}={5}&{6}={7}&{8}={9}&{10}={11}&{12}={13}\"", dmbPath, usePrimaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort, launchParameters.AllowWebClient ? "-webclient " : String.Empty, SecurityWord(launchParameters.SecurityLevel),
DreamDaemonParameters.AccessToken, accessToken,
proc.StartInfo.WorkingDirectory = isPrimary ? dmbProvider.PrimaryDirectory : dmbProvider.SecondaryDirectory;
await ioManager.WriteAllBytes(ioManager.ConcatPath(proc.StartInfo.WorkingDirectory, jsonPath), Encoding.UTF8.GetBytes(json), cancellationToken).ConfigureAwait(false);
proc.StartInfo.Arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} {2}-close -{3} -verbose -public -params \"{4}={5}&{6}={7}\"",
dmbProvider.DmbName,
isPrimary ? launchParameters.PrimaryPort : launchParameters.SecondaryPort,
launchParameters.AllowWebClient ? "-webclient " : String.Empty,
SecurityWord(launchParameters.SecurityLevel),
DreamDaemonParameters.HostVersion, Application.Version,
DreamDaemonParameters.IsSecondaryServer, !usePrimaryPort,
DreamDaemonParameters.PrimaryPort, launchParameters.PrimaryPort,
DreamDaemonParameters.SecondaryPort, launchParameters.SecondaryPort);
DreamDaemonParameters.InfoJsonPath, jsonPath);
proc.EnableRaisingEvents = true;
var tcs = new TaskCompletionSource<object>();
@@ -85,7 +111,7 @@ namespace Tgstation.Server.Host.Components
}
finally
{
if (!alwaysKill && !await instanceShutdownMethod.PreserveActiveExecutablesIfNecessary(launchParameters, accessToken, proc.Id, usePrimaryPort).ConfigureAwait(false))
if (!alwaysKill && !await instanceShutdownMethod.PreserveActiveExecutablesIfNecessary(launchParameters, interopInfo.AccessToken, proc.Id, isPrimary).ConfigureAwait(false))
{
proc.Kill();
proc.WaitForExit();
@@ -3,12 +3,12 @@
static class DreamDaemonParameters
{
/// <summary>
/// Do not change to keep forwards/backwards api compatibility
/// Host version. Do not change to keep forwards/backwards api compatibility
/// </summary>
public const string HostVersion = "server_service_versions";
public const string AccessToken = "tgs_key";
public const string PrimaryPort = "tgs_port1";
public const string SecondaryPort = "tgs_port2";
public const string IsSecondaryServer = "tgs_second";
public const string HostVersion = "server_service_version";
/// <summary>
/// Path to <see cref="Models.InteropInfo"/> json
/// </summary>
public const string InfoJsonPath = "tgs_json";
}
}
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -41,9 +42,11 @@ namespace Tgstation.Server.Host.Components
/// Send a chat <paramref name="message"/> to a given set of <paramref name="channelIds"/>
/// </summary>
/// <param name="message">The message being sent</param>
/// <param name="channelIds">The <see cref="Models.ChatChannel.Id"/>s of the <see cref="Models.ChatChannel"/>s to send to</param>
/// <param name="channelIds">The <see cref="Host.Models.ChatChannel.Id"/>s of the <see cref="Host.Models.ChatChannel"/>s to send to</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task SendMessage(string message, IEnumerable<long> channelIds, CancellationToken cancellationToken);
Task<IDisposable> TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken);
}
}
@@ -1,5 +1,5 @@
using System;
using System.Threading.Tasks;
using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Components
{
@@ -22,5 +22,10 @@ namespace Tgstation.Server.Host.Components
/// The secondary game directory with a trailing directory separator
/// </summary>
string SecondaryDirectory { get; }
/// <summary>
/// The <see cref="Host.Models.RevisionInformation"/> of the .dmb
/// </summary>
RevisionInformation RevisionInformation { get; }
}
}
@@ -1,6 +1,7 @@
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Models;
namespace Tgstation.Server.Host.Components
{
@@ -15,12 +16,11 @@ namespace Tgstation.Server.Host.Components
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/></param>
/// <param name="onSuccessfulStartup">The <see cref="TaskCompletionSource{TResult}"/> that is completed when dream daemon starts without crashing</param>
/// <param name="dreamDaemonPath">The path to the dreamdaemon executable</param>
/// <param name="dmbPath">The path to the .dmb to run, the working directory will be derived from this</param>
/// <param name="accessToken">The access token to be used for communication</param>
/// <param name="usePrimaryPort">If the server should open on <see cref="DreamDaemonLaunchParameters.PrimaryPort"/> or <see cref="DreamDaemonLaunchParameters.SecondaryPort"/> of <paramref name="launchParameters"/></param>
/// <param name="dmbProvider">The <see cref="IDmbProvider"/> for the .dmb to run</param>
/// <param name="interopInfo">The <see cref="InteropInfo"/> for the run</param>
/// <param name="alwaysKill">If the resulting process should never be left alive</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task{TResult}"/> representing the lifetime of the process and resulting in the exit code</returns>
Task<int> RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource<object> onSuccessfulStartup, string dreamDaemonPath, string dmbPath, string accessToken, bool usePrimaryPort, bool alwaysKill, CancellationToken cancellationToken);
Task<int> RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource<object> onSuccessfulStartup, string dreamDaemonPath, IDmbProvider dmbProvider, InteropInfo interopInfo, bool alwaysKill, CancellationToken cancellationToken);
}
}
@@ -16,6 +16,8 @@ namespace Tgstation.Server.Host.Components
IConfiguration Configuration { get; }
Api.Models.Instance GetMetadata();
void Rename(string newName);
}
}
@@ -2,8 +2,16 @@
namespace Tgstation.Server.Host.Components
{
/// <summary>
/// Factory for creating <see cref="IInstance"/>s
/// </summary>
interface IInstanceFactory
{
IInstance CreateInstance(Models.Instance metadata);
/// <summary>
/// Create an <see cref="IInstance"/>
/// </summary>
/// <param name="metadata">The <see cref="Host.Models.Instance"/></param>
/// <returns>A new <see cref="IInstance"/></returns>
IInstance CreateInstance(Host.Models.Instance metadata);
}
}
@@ -11,33 +11,33 @@ namespace Tgstation.Server.Host.Components
/// <summary>
/// Get the <see cref="IInstance"/> associated with given <paramref name="metadata"/>
/// </summary>
/// <param name="metadata">The <see cref="Models.Instance"/> of the desired <see cref="IInstance"/></param>
/// <param name="metadata">The <see cref="Host.Models.Instance"/> of the desired <see cref="IInstance"/></param>
/// <returns>The <see cref="IInstance"/> associated with the given <paramref name="metadata"/></returns>
IInstance GetInstance(Models.Instance metadata);
IInstance GetInstance(Host.Models.Instance metadata);
/// <summary>
/// Online an <see cref="IInstance"/>
/// </summary>
/// <param name="metadata">The <see cref="Models.Instance"/> of the desired <see cref="IInstance"/></param>
/// <param name="metadata">The <see cref="Host.Models.Instance"/> of the desired <see cref="IInstance"/></param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken);
Task OnlineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken);
/// <summary>
/// Offline an <see cref="IInstance"/>
/// </summary>
/// <param name="metadata">The <see cref="Models.Instance"/> of the desired <see cref="IInstance"/></param>
/// <param name="metadata">The <see cref="Host.Models.Instance"/> of the desired <see cref="IInstance"/></param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken);
Task OfflineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken);
/// <summary>
/// Move an <see cref="IInstance"/>
/// </summary>
/// <param name="metadata">The <see cref="Models.Instance"/> of the desired <see cref="IInstance"/></param>
/// <param name="metadata">The <see cref="Host.Models.Instance"/> of the desired <see cref="IInstance"/></param>
/// <param name="newPath">The new path of the <see cref="IInstance"/>. <paramref name="metadata"/> will have this set on <see cref="Api.Models.Instance.Path"/> if the operation completes successfully</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task MoveInstance(Models.Instance metadata, string newPath, CancellationToken cancellationToken);
Task MoveInstance(Host.Models.Instance metadata, string newPath, CancellationToken cancellationToken);
}
}
@@ -31,6 +31,8 @@ namespace Tgstation.Server.Host.Components
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
public Api.Models.Instance GetMetadata() => metadata.CloneMetadata();
public void Rename(string newName)
{
if (String.IsNullOrWhiteSpace(newName))
@@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.Components
public InstanceFactory(IIOManager ioManager) => this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
/// <inheritdoc />
public IInstance CreateInstance(Models.Instance metadata)
public IInstance CreateInstance(Host.Models.Instance metadata)
{
//Create the ioManager for the instance
@@ -47,7 +47,7 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public IInstance GetInstance(Models.Instance metadata)
public IInstance GetInstance(Host.Models.Instance metadata)
{
lock (this)
{
@@ -58,7 +58,7 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public async Task MoveInstance(Models.Instance instance, string newPath, CancellationToken cancellationToken)
public async Task MoveInstance(Host.Models.Instance instance, string newPath, CancellationToken cancellationToken)
{
if (newPath == null)
throw new ArgumentNullException(nameof(newPath));
@@ -84,7 +84,7 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public async Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken)
public async Task OfflineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken)
{
IInstance instance;
lock (this)
@@ -97,7 +97,7 @@ namespace Tgstation.Server.Host.Components
}
/// <inheritdoc />
public async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
public async Task OnlineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken)
{
var instance = instanceFactory.CreateInstance(metadata);
lock (this)
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using Tgstation.Server.Api.Models.Internal;
namespace Tgstation.Server.Host.Components.Models
{
sealed class InteropInfo
{
public string AccessToken { get; set; }
public long InstanceId { get; set; }
public string HostPath { get; set; }
public bool ApiValidateOnly { get; set; }
public string InstanceName { get; set; }
public ushort NextPort { get; set; }
public string ChatChannelsJson { get; set; }
public string ChatCommandsJson { get; set; }
public RevisionInformation Revision { get; set; }
public List<TestMerge> TestMerges { get; } = new List<TestMerge>();
}
}
@@ -0,0 +1,16 @@
using Tgstation.Server.Api.Models.Internal;
namespace Tgstation.Server.Host.Components.Models
{
sealed class TestMerge : RevisionInformation
{
public int Number { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string Author { get; set; }
public string Url { get; set; }
public string PullRequestCommit { get; set; }
public long TimeMerged { get; set; }
public string Comment { get; set; }
}
}
@@ -2,6 +2,8 @@
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Models;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.Security;
namespace Tgstation.Server.Host.Components
@@ -14,6 +16,10 @@ namespace Tgstation.Server.Host.Components
/// </summary>
readonly IByond byond;
/// <summary>
/// The <see cref="IChat"/> for the <see cref="Watchdog"/>
/// </summary>
readonly IChat chat;
/// <summary>
/// The <see cref="IDreamDaemonExecutor"/> for the <see cref="Watchdog"/>
/// </summary>
readonly IDreamDaemonExecutor dreamDaemonExecutor;
@@ -33,6 +39,14 @@ namespace Tgstation.Server.Host.Components
/// The <see cref="IEventConsumer"/> for the <see cref="Watchdog"/>
/// </summary>
readonly IEventConsumer eventConsumer;
/// <summary>
/// The <see cref="IInstanceManager"/> for the <see cref="Watchdog"/>
/// </summary>
readonly IInstanceManager instanceManager;
/// <summary>
/// The <see cref="Api.Models.Instance.Id"/> of the <see cref="Watchdog"/>
/// </summary>
readonly long instanceId;
/// <summary>
/// Represents the currently running <see cref="Watchdog"/>
@@ -47,19 +61,26 @@ namespace Tgstation.Server.Host.Components
/// Construct a <see cref="IWatchdog"/>
/// </summary>
/// <param name="byond">The value of <see cref="byond"/></param>
/// <param name="chat">The value of <see cref="chat"/></param>
/// <param name="dreamDaemonExecutor">The value of <see cref="dreamDaemonExecutor"/></param>
/// <param name="interop">The value of <see cref="interop"/></param>
/// <param name="dmbFactory">The value of <see cref="dmbFactory"/></param>
/// <param name="cryptographySuite">The value of <see cref="cryptographySuite"/></param>
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
public Watchdog(IByond byond, IDreamDaemonExecutor dreamDaemonExecutor, IInterop interop, IDmbFactory dmbFactory, ICryptographySuite cryptographySuite, IEventConsumer eventConsumer)
/// <param name="instanceManager">The value of <see cref="instanceManager"/></param>
/// <param name="instanceId">The value of <see cref="instanceId"/></param>
public Watchdog(IByond byond, IChat chat, IDreamDaemonExecutor dreamDaemonExecutor, IInterop interop, IDmbFactory dmbFactory, ICryptographySuite cryptographySuite, IEventConsumer eventConsumer, IInstanceManager instanceManager, long instanceId)
{
this.byond = byond ?? throw new ArgumentNullException(nameof(byond));
this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
this.dreamDaemonExecutor = dreamDaemonExecutor ?? throw new ArgumentNullException(nameof(dreamDaemonExecutor));
this.interop = interop ?? throw new ArgumentNullException(nameof(interop));
this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
this.instanceId = instanceId;
}
/// <inheritdoc />
@@ -70,19 +91,47 @@ namespace Tgstation.Server.Host.Components
/// </summary>
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the run</param>
/// <param name="onSuccessfulStartup">The <see cref="TaskCompletionSource{TResult}"/> to be completed once the server starts if any</param>
/// <param name="accessToken">The access token for the server</param>
/// <param name="interopInfo">The <see cref="InteropInfo"/> the server</param>
/// <param name="dreamDaemonPath">The path to the DreamDaemon executable</param>
/// <param name="isPrimary">If a primary server is being launched</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the exit code of DreamDaemon</returns>
async Task<int> RunServer(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource<object> onSuccessfulStartup, string accessToken, string dreamDaemonPath, bool isPrimary, CancellationToken cancellationToken)
async Task<int> RunServer(DreamDaemonLaunchParameters launchParameters, TaskCompletionSource<object> onSuccessfulStartup, InteropInfo interopInfo, string dreamDaemonPath, CancellationToken cancellationToken)
{
using (var dmb = await dmbFactory.LockNextDmb(cancellationToken).ConfigureAwait(false))
return await dreamDaemonExecutor.RunDreamDaemon(launchParameters, onSuccessfulStartup, dreamDaemonPath, String.Concat(dmb.PrimaryDirectory, dmb.DmbName), accessToken, isPrimary, false, cancellationToken).ConfigureAwait(false);
{
var chatJsonGuid = Guid.NewGuid();
var isPrimary = interopInfo.NextPort == launchParameters.SecondaryPort;
//set up chat stuff
interopInfo.ChatChannelsJson = String.Concat(chatJsonGuid, ".channels.json");
//only track chat commands from primary server
if(isPrimary)
interopInfo.ChatCommandsJson = String.Concat(chatJsonGuid, ".commands.json");
//set up revision
interopInfo.Revision = dmb.RevisionInformation;
foreach (var I in dmb.RevisionInformation.TestMerges)
interopInfo.TestMerges.Add(new Models.TestMerge
{
Author = I.Author,
Body = I.BodyAtMerge,
Comment = I.Comment,
Commit = I.RevisionInformation.Commit,
OriginRevision = I.RevisionInformation.OriginRevision,
Number = I.Number,
PullRequestCommit = I.PullRequestRevision,
TimeMerged = I.MergedAt.ToUnixTimeSeconds(),
Title = I.TitleAtMerge,
Url = I.Url.ToString()
});
using (await chat.TrackJsons(isPrimary ? dmb.PrimaryDirectory : dmb.SecondaryDirectory, interopInfo.ChatChannelsJson, isPrimary ? interopInfo.ChatCommandsJson : null, cancellationToken).ConfigureAwait(false))
return await dreamDaemonExecutor.RunDreamDaemon(launchParameters, onSuccessfulStartup, dreamDaemonPath, dmb, interopInfo, false, cancellationToken).ConfigureAwait(false);
}
}
/// <summary>
/// Locks in a <see cref="IByond"/> version and runs a server through <see cref="RunServer(DreamDaemonLaunchParameters, TaskCompletionSource{object}, string, string, bool, CancellationToken)"/>
/// Locks in a <see cref="IByond"/> version and runs a server through <see cref="RunServer(DreamDaemonLaunchParameters, TaskCompletionSource{object}, InteropInfo, string, CancellationToken)"/>
/// </summary>
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the run</param>
/// <param name="onSuccessfulStartup">The <see cref="TaskCompletionSource{TResult}"/> to be completed once the server starts if any</param>
@@ -97,7 +146,18 @@ namespace Tgstation.Server.Host.Components
try
{
var ddToken = cancellationTokenSource.Token;
var ddTask = byond.UseExecutable(dreamDaemonPath => RunServer(launchParameters, onSuccessfulStartup, accessToken, dreamDaemonPath, isPrimary, ddToken), false, true);
var interopInfo = new InteropInfo
{
AccessToken = accessToken,
ApiValidateOnly = false,
HostPath = Application.HostingPath,
InstanceId = instanceId,
NextPort = isPrimary ? launchParameters.SecondaryPort : launchParameters.PrimaryPort,
InstanceName = instanceManager.GetInstance(new Host.Models.Instance { Id = instanceId }).GetMetadata().Name,
};
var ddTask = byond.UseExecutable(dreamDaemonPath => RunServer(launchParameters, onSuccessfulStartup, interopInfo, dreamDaemonPath, ddToken), false, true);
interop.SetRun(isPrimary ? launchParameters.PrimaryPort : launchParameters.SecondaryPort, accessToken, isPrimary);
return ddTask;
}
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
@@ -10,6 +11,7 @@ using Microsoft.IdentityModel.Tokens;
using System;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Reflection;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Configuration;
@@ -24,6 +26,11 @@ namespace Tgstation.Server.Host.Core
/// </summary>
sealed class Application
{
/// <summary>
/// The url the server can be reached at locally
/// </summary>
public static string HostingPath { get; private set; }
/// <summary>
/// The version of the <see cref="Application"/>
/// </summary>
@@ -151,6 +158,9 @@ namespace Tgstation.Server.Host.Core
if (applicationBuilder == null)
throw new ArgumentNullException(nameof(applicationBuilder));
var addresses = applicationBuilder.ServerFeatures.Get<IServerAddressesFeature>();
HostingPath = addresses.Addresses.First();
if (hostingEnvironment.IsDevelopment())
applicationBuilder.UseDeveloperExceptionPage();
@@ -109,7 +109,7 @@ namespace Tgstation.Server.Host.Models
LogModelBuilderHelper.Build(modelBuilder.Entity<Log>());
modelBuilder.Entity<Log>().ToTable(nameof(Logs));
modelBuilder.Entity<RevisionInformation>().HasIndex(x => x.Revision).IsUnique();
modelBuilder.Entity<RevisionInformation>().HasIndex(x => x.Commit).IsUnique();
var user = modelBuilder.Entity<User>();
user.HasIndex(x => new { x.Name, x.SystemIdentifier }).IsUnique();
user.HasOne(x => x.CreatedBy).WithMany(x => x.CreatedUsers).OnDelete(DeleteBehavior.Restrict);
@@ -10,5 +10,10 @@ namespace Tgstation.Server.Host.Models
/// </summary>
[Required]
public User MergedBy { get; set; }
/// <summary>
/// The <see cref="Models.RevisionInformation"/> for the <see cref="TestMerge"/>
/// </summary>
public RevisionInformation RevisionInformation { get; set; }
}
}