Merge pull request #824 from tgstation/4.0.1.4 [TGSDeploy]

Version 4.0.1.4
This commit is contained in:
Jordan Brown
2018-11-25 10:03:38 -05:00
committed by GitHub
19 changed files with 117 additions and 35 deletions
@@ -4,7 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<DebugType>Full</DebugType>
<Version>4.0.1.3</Version>
<Version>4.0.1.4</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -7,5 +7,5 @@ using System.Runtime.InteropServices;
[assembly: Guid("29927416-3b78-49a7-a560-5ccaa638b6b4")]
[assembly: InternalsVisibleTo("Tgstation.Server.Host.Service.Tests")]
[assembly: AssemblyVersion("4.0.1.3")]
[assembly: AssemblyFileVersion("4.0.1.3")]
[assembly: AssemblyVersion("4.0.1.4")]
[assembly: AssemblyFileVersion("4.0.1.4")]
@@ -4,7 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<DebugType>Full</DebugType>
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<Version>4.0.1.3</Version>
<Version>4.0.1.4</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -38,12 +38,12 @@ namespace Tgstation.Server.Host.Components.Chat
/// <summary>
/// If this is considered a channel for admin commands
/// </summary>
public bool IsAdmin { get; set; }
public bool IsAdminChannel { get; set; }
/// <summary>
/// If this is a 1-to-1 chat channel
/// </summary>
public bool IsPrivate { get; set; }
public bool IsPrivateChannel { get; set; }
/// <summary>
/// For user use
@@ -195,7 +195,7 @@ namespace Tgstation.Server.Host.Components.Chat
{
var providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First();
var enumerable = mappedChannels.Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == message.User.Channel.RealId);
if (message.User.Channel.IsPrivate)
if (message.User.Channel.IsPrivateChannel)
lock (mappedChannels)
{
if (!provider.Connected)
@@ -223,7 +223,7 @@ namespace Tgstation.Server.Host.Components.Chat
var mapping = enumerable.First().Value;
message.User.Channel.Id = mapping.Channel.Id;
message.User.Channel.Tag = mapping.Channel.Tag;
message.User.Channel.IsAdmin = mapping.Channel.IsAdmin;
message.User.Channel.IsAdminChannel = mapping.Channel.IsAdminChannel;
}
}
@@ -236,7 +236,7 @@ namespace Tgstation.Server.Host.Components.Chat
var addressed = address == CommonMention.ToUpperInvariant() || address == provider.BotMention.ToUpperInvariant();
if (!addressed && !message.User.Channel.IsPrivate)
if (!addressed && !message.User.Channel.IsPrivateChannel)
//no mention
return;
@@ -302,7 +302,7 @@ namespace Tgstation.Server.Host.Components.Chat
return;
}
if (commandHandler.AdminOnly && !message.User.Channel.IsAdmin)
if (commandHandler.AdminOnly && !message.User.Channel.IsAdminChannel)
{
await SendMessage("Use this command in an admin channel!", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
return;
@@ -19,7 +19,7 @@ namespace Tgstation.Server.Host.Components.Chat.Commands
string HelpText { get; }
/// <summary>
/// If the command should only be available to <see cref="User"/>s who's <see cref="User.Channel"/> has <see cref="Channel.IsAdmin"/> set
/// If the command should only be available to <see cref="User"/>s who's <see cref="User.Channel"/> has <see cref="Channel.IsAdminChannel"/> set
/// </summary>
bool AdminOnly { get; }
@@ -97,7 +97,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
Channel = new Channel
{
RealId = e.Channel.Id,
IsPrivate = pm,
IsPrivateChannel = pm,
ConnectionName = pm ? e.Author.Username : (e.Channel as ITextChannel)?.Guild.Name ?? "UNKNOWN",
FriendlyName = e.Channel.Name
//isAdmin and Tag populated by manager
@@ -188,10 +188,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
return new Channel
{
RealId = discordChannel.Id,
IsAdmin = channel.IsAdminChannel == true,
IsAdminChannel = channel.IsAdminChannel == true,
ConnectionName = discordChannel.Guild.Name,
FriendlyName = discordChannel.Name,
IsPrivate = false,
IsPrivateChannel = false,
Tag = channel.Tag
};
};
@@ -204,7 +204,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
ConnectionName = address,
FriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName,
RealId = channelId,
IsPrivate = isPrivate
IsPrivateChannel = isPrivate
//isAdmin and Tag populated by manager
},
FriendlyName = username,
@@ -393,10 +393,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
return new Channel
{
RealId = id.Value,
IsAdmin = x.IsAdminChannel == true,
IsAdminChannel = x.IsAdminChannel == true,
ConnectionName = address,
FriendlyName = channelIdMap[id.Value],
IsPrivate = false,
IsPrivateChannel = false,
Tag = x.Tag
};
}).ToList();
@@ -12,7 +12,7 @@ using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components
{
/// <inheritdoc />
sealed class InstanceManager : IInstanceManager, IHostedService, IDisposable
sealed class InstanceManager : IInstanceManager, IRestartHandler, IHostedService, IDisposable
{
/// <summary>
/// The <see cref="IInstanceFactory"/> for the <see cref="InstanceManager"/>
@@ -49,6 +49,11 @@ namespace Tgstation.Server.Host.Components
/// </summary>
readonly Dictionary<long, IInstance> instances;
/// <summary>
/// Used in <see cref="StopAsync(CancellationToken)"/> to determine if database downgrades must be made
/// </summary>
Version downgradeVersion;
/// <summary>
/// If the <see cref="InstanceManager"/> has been <see cref="Dispose"/>d
/// </summary>
@@ -62,16 +67,21 @@ namespace Tgstation.Server.Host.Components
/// <param name="databaseContextFactory">The value of <paramref name="databaseContextFactory"/></param>
/// <param name="application">The value of <see cref="application"/></param>
/// <param name="jobManager">The value of <see cref="jobManager"/></param>
/// <param name="serverControl">The <see cref="IServerControl"/> used to register the <see cref="InstanceManager"/> as a <see cref="IRestartHandler"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, ILogger<InstanceManager> logger)
public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger<InstanceManager> logger)
{
this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
this.application = application ?? throw new ArgumentNullException(nameof(application));
this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
if (serverControl == null)
throw new ArgumentNullException(nameof(serverControl));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
serverControl.RegisterForRestart(this);
instances = new Dictionary<long, IInstance>();
}
@@ -225,6 +235,17 @@ namespace Tgstation.Server.Host.Components
await jobManager.StopAsync(cancellationToken).ConfigureAwait(false);
await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false);
await instanceFactory.StopAsync(cancellationToken).ConfigureAwait(false);
//downgrade the db if necessary
if (downgradeVersion != null)
await databaseContextFactory.UseContext(db => db.SchemaDowngradeForServerVersion(downgradeVersion, cancellationToken)).ConfigureAwait(false);
}
/// <inheritdoc />
public Task HandleRestart(Version updateVersion, CancellationToken cancellationToken)
{
downgradeVersion = updateVersion != null && updateVersion < application.Version ? updateVersion : null;
return Task.CompletedTask;
}
}
}
@@ -5,7 +5,7 @@ namespace Tgstation.Server.Host.Components.Interop
sealed class ChatCommand
{
public string Command { get; set; }
public string Parameters { get; set; }
public string Params { get; set; }
public User User { get; set; }
}
}
@@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.Components.Interop
/// <summary>
/// The dictionary of the <see cref="CommCommand"/>
/// </summary>
public IReadOnlyDictionary<string, string> Parameters { get; set; }
public IReadOnlyDictionary<string, object> Parameters { get; set; }
/// <summary>
/// The raw JSON of the <see cref="CommCommand"/>
@@ -109,7 +109,7 @@ namespace Tgstation.Server.Host.Components.Interop
{
command = new CommCommand
{
Parameters = JsonConvert.DeserializeObject<IReadOnlyDictionary<string, string>>(file),
Parameters = JsonConvert.DeserializeObject<IReadOnlyDictionary<string, object>>(file),
RawJson = file
};
}
@@ -221,6 +221,8 @@ namespace Tgstation.Server.Host.Components.Watchdog
return result;
};
LaunchResult = GetLaunchResult();
logger.LogDebug("Created session controller. Primary: {0}, CommsKey: {1}, Port: {2}", IsPrimary, reattachInformation.AccessIdentifier, Port);
}
/// <summary>
@@ -316,7 +318,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
case Constants.DMCommandNewPort:
lock (this)
{
if (!query.TryGetValue(Constants.DMParameterData, out var stringPort) || !UInt16.TryParse(stringPort, out var currentPort))
if (!query.TryGetValue(Constants.DMParameterData, out var stringPortObject) || !UInt16.TryParse(stringPortObject as string, out var currentPort))
{
/////UHHHH
logger.LogWarning("DreamDaemon sent new port command without providing it's own!");
@@ -354,7 +356,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
content = new ErrorMessage { Message = "Invalid API validation request!" };
break;
}
if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevel) || !Enum.TryParse<DreamDaemonSecurity>(stringMinimumSecurityLevel, out var minimumSecurityLevel))
if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevelObject) || !Enum.TryParse<DreamDaemonSecurity>(stringMinimumSecurityLevelObject as string, out var minimumSecurityLevel))
apiValidationStatus = ApiValidationStatus.BadValidationRequest;
else
switch (minimumSecurityLevel)
@@ -479,6 +481,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
return false;
}
reattachInformation.Port = port;
return true;
}
@@ -453,20 +453,13 @@ namespace Tgstation.Server.Host.Components.Watchdog
//servers now swapped
//enable this now
monitorState.ActiveServer.ClosePortOnReboot = true;
//enable this now if inactive server is not still valid
monitorState.ActiveServer.ClosePortOnReboot = restartOnceSwapped;
if (!restartOnceSwapped)
{
//inactive server still valid
//disable this
monitorState.InactiveServer.ClosePortOnReboot = false;
//now try to reopen it on the private port
//failing that, just reboot it
restartOnceSwapped = !await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.SecondaryPort.Value, cancellationToken).ConfigureAwait(false);
}
//break either way because any issues past this point would be solved by the reboot
if (restartOnceSwapped)
@@ -1059,7 +1052,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
var commandObject = new ChatCommand
{
Command = commandName,
Parameters = arguments,
Params = arguments,
User = sender
};
@@ -1,7 +1,11 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -169,5 +173,51 @@ namespace Tgstation.Server.Host.Models
/// <inheritdoc />
public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
/// <summary>
/// If the MY_ class of migrations should be used instead of the MS_ class
/// </summary>
/// <returns><see langword="true"/> if the MY_ class of migrations should be used instead of the MS_ class, <see langword="false"/> otherwise</returns>
protected abstract bool UseMySQLMigrations();
/// <inheritdoc />
public async Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)
{
if (version == null)
throw new ArgumentNullException(nameof(version));
if (version < new Version(4, 0))
throw new ArgumentOutOfRangeException(nameof(version), version, "Not a valid V4 version!");
string targetMigration = null;
//Update this with new migrations as they are made
//Always use the MS class
//TODO: Uncomment once #816 is merged
/*
if (version < new Version(4, 0, 2))
targetMigration = nameof(MSReattachCompileJobRequired);
*/
if (targetMigration == null)
return;
if (UseMySQLMigrations())
targetMigration = String.Format(CultureInfo.InvariantCulture, "MY" + targetMigration.Substring(2));
//even though it clearly implements it in the DatabaseFacade definition this won't work without casting (╯ಠ益ಠ)╯︵ ┻━┻
var dbServiceProvider = ((IInfrastructure<IServiceProvider>)Database).Instance;
var migrator = dbServiceProvider.GetRequiredService<IMigrator>();
Logger.LogInformation("Migrating down to version {0}. Target: {1}", version, targetMigration);
try
{
await migrator.MigrateAsync(targetMigration, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
Logger.LogCritical("Failed to migrate! Exception: {0}", e);
}
}
}
}
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -87,5 +88,13 @@ namespace Tgstation.Server.Host.Models
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task Initialize(CancellationToken cancellationToken);
/// <summary>
/// Attempt to downgrade the schema to the migration used for a given server <paramref name="version"/>
/// </summary>
/// <param name="version">The tgstation-server <see cref="Version"/> that the schema should downgrade for</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken);
}
}
@@ -38,5 +38,8 @@ namespace Tgstation.Server.Host.Models
else
options.UseMySql(DatabaseConfiguration.ConnectionString);
}
/// <inheritdoc />
protected override bool UseMySQLMigrations() => true;
}
}
@@ -26,5 +26,8 @@ namespace Tgstation.Server.Host.Models
base.OnConfiguring(options);
options.UseSqlServer(DatabaseConfiguration.ConnectionString);
}
/// <inheritdoc />
protected override bool UseMySQLMigrations() => false;
}
}
@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<DebugType>Full</DebugType>
<Version>4.0.1.3</Version>
<Version>4.0.1.4</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">