diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs
index 88a2286d47..b9b558cba2 100644
--- a/src/Tgstation.Server.Host/Components/InstanceManager.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs
@@ -12,7 +12,7 @@ using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components
{
///
- sealed class InstanceManager : IInstanceManager, IHostedService, IDisposable
+ sealed class InstanceManager : IInstanceManager, IRestartHandler, IHostedService, IDisposable
{
///
/// The for the
@@ -49,6 +49,11 @@ namespace Tgstation.Server.Host.Components
///
readonly Dictionary instances;
+ ///
+ /// Used in to determine if database downgrades must be made
+ ///
+ Version downgradeVersion;
+
///
/// If the has been d
///
@@ -62,16 +67,21 @@ namespace Tgstation.Server.Host.Components
/// The value of
/// The value of
/// The value of
+ /// The used to register the as a
/// The value of
- public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, ILogger logger)
+ public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger 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();
}
@@ -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);
+ }
+
+ ///
+ public Task HandleRestart(Version updateVersion, CancellationToken cancellationToken)
+ {
+ downgradeVersion = updateVersion != null && updateVersion < application.Version ? updateVersion : null;
+ return Task.CompletedTask;
}
}
}
diff --git a/src/Tgstation.Server.Host/Models/DatabaseContext.cs b/src/Tgstation.Server.Host/Models/DatabaseContext.cs
index 7578c21c8a..e113f1d999 100644
--- a/src/Tgstation.Server.Host/Models/DatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Models/DatabaseContext.cs
@@ -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
///
public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
+
+ ///
+ /// If the MY_ class of migrations should be used instead of the MS_ class
+ ///
+ /// if the MY_ class of migrations should be used instead of the MS_ class, otherwise
+ protected abstract bool UseMySQLMigrations();
+
+ ///
+ 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)Database).Instance;
+ var migrator = dbServiceProvider.GetRequiredService();
+
+ 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);
+ }
+ }
}
}
diff --git a/src/Tgstation.Server.Host/Models/IDatabaseContext.cs b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs
index 8fdb8aad74..e7c8e9dcd2 100644
--- a/src/Tgstation.Server.Host/Models/IDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs
@@ -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
/// The for the operation
/// A representing the running operation
Task Initialize(CancellationToken cancellationToken);
+
+ ///
+ /// Attempt to downgrade the schema to the migration used for a given server
+ ///
+ /// The tgstation-server that the schema should downgrade for
+ /// The for the operation
+ /// A representing the running operation
+ Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs b/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs
index 8c03b31ee0..718fc04d98 100644
--- a/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs
@@ -38,5 +38,8 @@ namespace Tgstation.Server.Host.Models
else
options.UseMySql(DatabaseConfiguration.ConnectionString);
}
+
+ ///
+ protected override bool UseMySQLMigrations() => true;
}
}
diff --git a/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs b/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs
index d3d5aa8260..94240566d3 100644
--- a/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs
@@ -26,5 +26,8 @@ namespace Tgstation.Server.Host.Models
base.OnConfiguring(options);
options.UseSqlServer(DatabaseConfiguration.ConnectionString);
}
+
+ ///
+ protected override bool UseMySQLMigrations() => false;
}
}