diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs
index e0045ccbc9..d76ab47826 100644
--- a/src/Tgstation.Server.Host/Components/InstanceManager.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs
@@ -67,6 +67,11 @@ namespace Tgstation.Server.Host.Components
///
readonly IAsyncDelayer asyncDelayer;
+ ///
+ /// The for the
+ ///
+ readonly IDatabaseSeeder databaseSeeder;
+
///
/// The for the
///
@@ -113,6 +118,7 @@ namespace Tgstation.Server.Host.Components
/// The value of
/// The value of .
/// The value of .
+ /// The value of .
/// The containing the value of .
/// The value of
public InstanceManager(
@@ -124,6 +130,7 @@ namespace Tgstation.Server.Host.Components
IServerControl serverControl,
ISystemIdentityFactory systemIdentityFactory,
IAsyncDelayer asyncDelayer,
+ IDatabaseSeeder databaseSeeder,
IOptions generalConfigurationOptions,
ILogger logger)
{
@@ -135,6 +142,7 @@ namespace Tgstation.Server.Host.Components
this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl));
this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
+ this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -274,7 +282,7 @@ namespace Tgstation.Server.Host.Components
{
CheckSystemCompatibility();
var factoryStartup = instanceFactory.StartAsync(cancellationToken);
- await databaseContext.Initialize(cancellationToken).ConfigureAwait(false);
+ await databaseSeeder.Initialize(databaseContext, cancellationToken).ConfigureAwait(false);
await jobManager.StartAsync(cancellationToken).ConfigureAwait(false);
var dbInstances = databaseContext
.Instances
@@ -323,7 +331,7 @@ namespace Tgstation.Server.Host.Components
// downgrade the db if necessary
if (downgradeVersion != null)
- await databaseContextFactory.UseContext(db => db.SchemaDowngradeForServerVersion(downgradeVersion, cancellationToken)).ConfigureAwait(false);
+ await databaseContextFactory.UseContext(db => databaseSeeder.Downgrade(db, downgradeVersion, cancellationToken)).ConfigureAwait(false);
}
///
diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs
index 8bc5133bc8..1883a4bf71 100644
--- a/src/Tgstation.Server.Host/Core/Application.cs
+++ b/src/Tgstation.Server.Host/Core/Application.cs
@@ -18,6 +18,7 @@ using Serilog.Formatting.Display;
using System;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
+using System.Reflection;
using System.Threading.Tasks;
using Tgstation.Server.Api;
using Tgstation.Server.Api.Models;
@@ -207,10 +208,23 @@ namespace Tgstation.Server.Host.Core
void AddTypedContext() where TContext : DatabaseContext
{
- services.AddDbContext(builder =>
+ // HACK HACK HACK HACK HACK
+ const string ConfigureMethodName = nameof(SqlServerDatabaseContext.ConfigureWith);
+ var configureFunction = typeof(TContext).GetMethod(
+ nameof(SqlServerDatabaseContext.ConfigureWith),
+ BindingFlags.Public | BindingFlags.Static);
+
+ if (configureFunction == null)
+ throw new InvalidOperationException($"Context type {typeof(TContext).FullName} missing static {ConfigureMethodName} function!");
+
+ services.AddDbContextPool((serviceProvider, builder) =>
{
if (hostingEnvironment.IsDevelopment())
builder.EnableSensitiveDataLogging();
+
+ var databaseConfigOptions = serviceProvider.GetRequiredService>();
+ var databaseConfig = databaseConfigOptions.Value ?? throw new InvalidOperationException("DatabaseConfiguration missing!");
+ configureFunction.Invoke(null, new object[] { builder, databaseConfig });
});
services.AddScoped(x => x.GetRequiredService());
}
diff --git a/src/Tgstation.Server.Host/Database/DatabaseContext.cs b/src/Tgstation.Server.Host/Database/DatabaseContext.cs
index b75baf3e09..25243ea7e4 100644
--- a/src/Tgstation.Server.Host/Database/DatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Database/DatabaseContext.cs
@@ -3,7 +3,6 @@ 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;
@@ -19,11 +18,8 @@ namespace Tgstation.Server.Host.Database
/// Backend abstract implementation of
///
#pragma warning disable CA1506 // TODO: Decomplexify
- abstract class DatabaseContext : DbContext, IDatabaseContext
+ public abstract class DatabaseContext : DbContext, IDatabaseContext
{
- ///
- public DatabaseType DatabaseType => DatabaseConfiguration.DatabaseType;
-
///
/// The s in the .
///
@@ -99,16 +95,6 @@ namespace Tgstation.Server.Host.Database
///
public DbSet RevInfoTestMerges { get; set; }
- ///
- /// The for the
- ///
- protected ILogger Logger { get; }
-
- ///
- /// The for the
- ///
- protected DatabaseConfiguration DatabaseConfiguration { get; }
-
///
/// The for the / foreign key.
///
@@ -153,11 +139,6 @@ namespace Tgstation.Server.Host.Database
///
IDatabaseCollection IDatabaseContext.WatchdogReattachInformations => watchdogReattachInformationsCollection;
- ///
- /// The for the
- ///
- readonly IDatabaseSeeder databaseSeeder;
-
///
/// Backing field for .
///
@@ -227,15 +208,8 @@ namespace Tgstation.Server.Host.Database
/// Construct a
///
/// The for the .
- /// The containing the value of
- /// The value of
- /// The value of
- public DatabaseContext(DbContextOptions dbContextOptions, IOptions databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions)
+ public DatabaseContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
{
- DatabaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
- this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
- Logger = logger ?? throw new ArgumentNullException(nameof(logger));
-
usersCollection = new DatabaseCollection(Users);
instancesCollection = new DatabaseCollection(Instances);
instanceUsersCollection = new DatabaseCollection(InstanceUsers);
@@ -254,8 +228,9 @@ namespace Tgstation.Server.Host.Database
///
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- // Setup our more complex database relations
- Logger.LogTrace("Building entity framework context...");
+ if (modelBuilder == null)
+ throw new ArgumentNullException(nameof(modelBuilder));
+
base.OnModelCreating(modelBuilder);
var userModel = modelBuilder.Entity();
@@ -306,52 +281,41 @@ namespace Tgstation.Server.Host.Database
}
///
- public async Task Initialize(CancellationToken cancellationToken)
+ public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
+
+ ///
+ public Task Drop(CancellationToken cancellationToken) => Database.EnsureCreatedAsync(cancellationToken);
+
+ ///
+ public async Task Migrate(ILogger logger, CancellationToken cancellationToken)
{
- ValidateDatabaseType();
-
- if (DatabaseConfiguration.DropDatabase)
- {
- Logger.LogCritical("DropDatabase configuration option set! Dropping any existing database...");
- await Database.EnsureDeletedAsync(cancellationToken).ConfigureAwait(false);
- }
-
+ if (logger == null)
+ throw new ArgumentNullException(nameof(logger));
var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false);
var wasEmpty = !migrations.Any();
if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false)).Any())
{
- Logger.LogInformation("Migrating database...");
+ logger.LogInformation("Migrating database...");
await Database.MigrateAsync(cancellationToken).ConfigureAwait(false);
}
else
- Logger.LogDebug("No migrations to apply.");
+ logger.LogDebug("No migrations to apply");
wasEmpty |= (await Users.AsQueryable().CountAsync(cancellationToken).ConfigureAwait(false)) == 0;
- if (wasEmpty)
- {
- Logger.LogInformation("Seeding database...");
- await databaseSeeder.SeedDatabase(this, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- if (DatabaseConfiguration.ResetAdminPassword)
- {
- Logger.LogWarning("Enabling and resetting admin password due to configuration!");
- await databaseSeeder.ResetAdminPassword(this, cancellationToken).ConfigureAwait(false);
- }
-
- await databaseSeeder.SanitizeDatabase(this, cancellationToken).ConfigureAwait(false);
- }
+ return wasEmpty;
}
///
- public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
-
- ///
- public async Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)
+ public async Task SchemaDowngradeForServerVersion(
+ ILogger logger,
+ Version version,
+ DatabaseType currentDatabaseType,
+ CancellationToken cancellationToken)
{
+ if(logger == null)
+ throw new ArgumentNullException(nameof(logger));
if (version == null)
throw new ArgumentNullException(nameof(version));
if (version < new Version(4, 0))
@@ -360,23 +324,23 @@ namespace Tgstation.Server.Host.Database
// Update this with new migrations as they are made
string targetMigration = null;
- if (DatabaseType == DatabaseType.PostgresSql && version < new Version(4, 3, 0))
+ if (currentDatabaseType == DatabaseType.PostgresSql && version < new Version(4, 3, 0))
throw new NotSupportedException("Cannot migrate below version 4.3.0 with PostgresSql!");
if (version < new Version(4, 1, 0))
throw new NotSupportedException("Cannot migrate below version 4.1.0!");
if (version < new Version(4, 2, 0))
- targetMigration = DatabaseType == DatabaseType.Sqlite ? nameof(SLRebuild) : nameof(MSFixCascadingDelete);
+ targetMigration = currentDatabaseType == DatabaseType.Sqlite ? nameof(SLRebuild) : nameof(MSFixCascadingDelete);
if (targetMigration == null)
{
- Logger.LogDebug("No down migration required.");
+ logger.LogDebug("No down migration required.");
return;
}
string migrationSubstitution;
- switch (DatabaseType)
+ switch (currentDatabaseType)
{
case DatabaseType.SqlServer:
// already setup
@@ -393,7 +357,7 @@ namespace Tgstation.Server.Host.Database
migrationSubstitution = "PG{0}";
break;
default:
- throw new InvalidOperationException($"Invalid DatabaseType: {DatabaseType}");
+ throw new InvalidOperationException($"Invalid DatabaseType: {currentDatabaseType}");
}
if (migrationSubstitution != null)
@@ -403,20 +367,15 @@ namespace Tgstation.Server.Host.Database
var dbServiceProvider = ((IInfrastructure)Database).Instance;
var migrator = dbServiceProvider.GetRequiredService();
- Logger.LogInformation("Migrating down to version {0}. Target: {1}", version, targetMigration);
+ 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);
+ logger.LogCritical("Failed to migrate! Exception: {0}", e);
}
}
-
- ///
- /// Ensure the is correct for the .
- ///
- protected abstract void ValidateDatabaseType();
}
}
diff --git a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
index e7173b0067..4bbc995197 100644
--- a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
+++ b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
@@ -1,9 +1,12 @@
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Rights;
+using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
using Tgstation.Server.Host.System;
@@ -23,15 +26,41 @@ namespace Tgstation.Server.Host.Database
///
readonly IPlatformIdentifier platformIdentifier;
+ ///
+ /// The used for s.
+ ///
+ readonly ILogger databaseLogger;
+
+ ///
+ /// The for the .
+ ///
+ readonly ILogger logger;
+
+ ///
+ /// The for the .
+ ///
+ readonly DatabaseConfiguration databaseConfiguration;
+
///
/// Construct a
///
/// The value of
/// The value of .
- public DatabaseSeeder(ICryptographySuite cryptographySuite, IPlatformIdentifier platformIdentifier)
+ /// The containing the value of .
+ /// The value of
+ /// The value of .
+ public DatabaseSeeder(
+ ICryptographySuite cryptographySuite,
+ IPlatformIdentifier platformIdentifier,
+ IOptions databaseConfigurationOptions,
+ ILogger databaseLogger,
+ ILogger logger)
{
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
+ databaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
+ this.databaseLogger = databaseLogger ?? throw new ArgumentNullException(nameof(databaseLogger));
+ this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
///
@@ -53,15 +82,25 @@ namespace Tgstation.Server.Host.Database
databaseContext.Users.Add(admin);
}
- ///
- public async Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
+ ///
+ /// Initially seed a given
+ ///
+ /// The to seed
+ /// The for the operation
+ /// A representing the running operation
+ async Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
{
SeedAdminUser(databaseContext);
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
}
- ///
- public async Task SanitizeDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
+ ///
+ /// Correct invalid database data caused by previous versions.
+ ///
+ /// The to sanitize.
+ /// The for the operation.
+ /// A representing the running operation.
+ async Task SanitizeDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
{
var admin = await GetAdminUser(databaseContext, cancellationToken).ConfigureAwait(false);
if (admin != null)
@@ -88,8 +127,13 @@ namespace Tgstation.Server.Host.Database
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
}
- ///
- public async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken)
+ ///
+ /// Changes the admin password in back to it's default and enables the account
+ ///
+ /// The to reset the admin password for
+ /// The for the operation
+ /// A representing the running operation
+ async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken)
{
var admin = await GetAdminUser(databaseContext, cancellationToken).ConfigureAwait(false);
if (admin != null)
@@ -120,5 +164,46 @@ namespace Tgstation.Server.Host.Database
return admin;
}
+
+ ///
+ public async Task Initialize(IDatabaseContext databaseContext, CancellationToken cancellationToken)
+ {
+ if (databaseContext == null)
+ throw new ArgumentNullException(nameof(databaseContext));
+
+ if (databaseConfiguration.DropDatabase)
+ {
+ logger.LogCritical("DropDatabase configuration option set! Dropping any existing database...");
+ await databaseContext.Drop(cancellationToken).ConfigureAwait(false);
+ }
+
+ var wasEmpty = await databaseContext.Migrate(databaseLogger, cancellationToken).ConfigureAwait(false);
+ if (wasEmpty)
+ {
+ logger.LogInformation("Seeding database...");
+ await SeedDatabase(databaseContext, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ if (databaseConfiguration.ResetAdminPassword)
+ {
+ logger.LogWarning("Enabling and resetting admin password due to configuration!");
+ await ResetAdminPassword(databaseContext, cancellationToken).ConfigureAwait(false);
+ }
+
+ await SanitizeDatabase(databaseContext, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ public Task Downgrade(IDatabaseContext databaseContext, Version downgradeVersion, CancellationToken cancellationToken)
+ {
+ if (databaseContext == null)
+ throw new ArgumentNullException(nameof(databaseContext));
+ if (downgradeVersion == null)
+ throw new ArgumentNullException(nameof(downgradeVersion));
+
+ return databaseContext.SchemaDowngradeForServerVersion(databaseLogger, downgradeVersion, databaseConfiguration.DatabaseType, cancellationToken);
+ }
}
}
diff --git a/src/Tgstation.Server.Host/Database/Design/MySqlDesignTimeDbContextFactory.cs b/src/Tgstation.Server.Host/Database/Design/MySqlDesignTimeDbContextFactory.cs
deleted file mode 100644
index 5b39d9e63d..0000000000
--- a/src/Tgstation.Server.Host/Database/Design/MySqlDesignTimeDbContextFactory.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Design;
-using Microsoft.Extensions.Logging;
-using Tgstation.Server.Host.Configuration;
-using Tgstation.Server.Host.Models;
-using Tgstation.Server.Host.Security;
-using Tgstation.Server.Host.System;
-
-namespace Tgstation.Server.Host.Database.Design
-{
- ///
- /// for creating s.
- ///
- sealed class MySqlDesignTimeDbContextFactory : IDesignTimeDbContextFactory
- {
- ///
- public MySqlDatabaseContext CreateDbContext(string[] args)
- {
- using var loggerFactory = new LoggerFactory();
- return new MySqlDatabaseContext(
- new DbContextOptions(),
- DesignTimeDbContextFactoryHelpers.GetDatabaseConfiguration(
- DatabaseType.MariaDB,
- "Server=127.0.0.1;User Id=root;Password=fake;Database=TGS_Design"),
- new DatabaseSeeder(
- new CryptographySuite(
- new PasswordHasher()),
- new PlatformIdentifier()),
- loggerFactory.CreateLogger());
- }
- }
-}
diff --git a/src/Tgstation.Server.Host/Database/Design/PostgresSqlDesignTimeDbContextFactory.cs b/src/Tgstation.Server.Host/Database/Design/PostgresSqlDesignTimeDbContextFactory.cs
deleted file mode 100644
index 2af7d7c939..0000000000
--- a/src/Tgstation.Server.Host/Database/Design/PostgresSqlDesignTimeDbContextFactory.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Design;
-using Microsoft.Extensions.Logging;
-using Tgstation.Server.Host.Configuration;
-using Tgstation.Server.Host.Models;
-using Tgstation.Server.Host.Security;
-using Tgstation.Server.Host.System;
-
-namespace Tgstation.Server.Host.Database.Design
-{
- ///
- sealed class PostgresSqlDesignTimeDbContextFactory : IDesignTimeDbContextFactory
- {
- ///
- public PostgresSqlDatabaseContext CreateDbContext(string[] args)
- {
- using var loggerFactory = new LoggerFactory();
- return new PostgresSqlDatabaseContext(
- new DbContextOptions(),
- DesignTimeDbContextFactoryHelpers.GetDatabaseConfiguration(
- DatabaseType.PostgresSql,
- "Application Name=tgstation-server;Host=127.0.0.1;Password=qCkWimNgLfWwpr7TnUHs;Username=postgres;Database=TGS_Design"),
- new DatabaseSeeder(
- new CryptographySuite(
- new PasswordHasher()),
- new PlatformIdentifier()),
- loggerFactory.CreateLogger());
- }
- }
-}
diff --git a/src/Tgstation.Server.Host/Database/Design/SqlServerDesignTimeDbContextFactory.cs b/src/Tgstation.Server.Host/Database/Design/SqlServerDesignTimeDbContextFactory.cs
deleted file mode 100644
index 427d84ba8a..0000000000
--- a/src/Tgstation.Server.Host/Database/Design/SqlServerDesignTimeDbContextFactory.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Design;
-using Microsoft.Extensions.Logging;
-using Tgstation.Server.Host.Configuration;
-using Tgstation.Server.Host.Models;
-using Tgstation.Server.Host.Security;
-using Tgstation.Server.Host.System;
-
-namespace Tgstation.Server.Host.Database.Design
-{
- ///
- /// for creating s.
- ///
- sealed class SqlServerDesignTimeDbContextFactory : IDesignTimeDbContextFactory
- {
- ///
- public SqlServerDatabaseContext CreateDbContext(string[] args)
- {
- using var loggerFactory = new LoggerFactory();
- return new SqlServerDatabaseContext(
- new DbContextOptions(),
- DesignTimeDbContextFactoryHelpers.GetDatabaseConfiguration(
- DatabaseType.SqlServer,
- "Data Source=fake;Initial Catalog=TGS_Design;Integrated Security=True;Application Name=tgstation-server"),
- new DatabaseSeeder(
- new CryptographySuite(
- new PasswordHasher()),
- new PlatformIdentifier()),
- loggerFactory.CreateLogger());
- }
- }
-}
diff --git a/src/Tgstation.Server.Host/Database/Design/SqliteDesignTimeDbContextFactory.cs b/src/Tgstation.Server.Host/Database/Design/SqliteDesignTimeDbContextFactory.cs
index 91de1284f2..330cbc39f3 100644
--- a/src/Tgstation.Server.Host/Database/Design/SqliteDesignTimeDbContextFactory.cs
+++ b/src/Tgstation.Server.Host/Database/Design/SqliteDesignTimeDbContextFactory.cs
@@ -1,11 +1,7 @@
-using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Logging;
using Tgstation.Server.Host.Configuration;
-using Tgstation.Server.Host.Models;
-using Tgstation.Server.Host.Security;
-using Tgstation.Server.Host.System;
namespace Tgstation.Server.Host.Database.Design
{
@@ -18,16 +14,13 @@ namespace Tgstation.Server.Host.Database.Design
public SqliteDatabaseContext CreateDbContext(string[] args)
{
using var loggerFactory = new LoggerFactory();
- return new SqliteDatabaseContext(
- new DbContextOptions(),
+ var config =
DesignTimeDbContextFactoryHelpers.GetDatabaseConfiguration(
DatabaseType.Sqlite,
- "Data Source=tgs_design.sqlite3;Mode=ReadWriteCreate"),
- new DatabaseSeeder(
- new CryptographySuite(
- new PasswordHasher()),
- new PlatformIdentifier()),
- loggerFactory.CreateLogger());
+ "Data Source=tgs_design.sqlite3;Mode=ReadWriteCreate");
+ SqliteDatabaseContext.DesignTime = config.Value.DesignTime;
+ return new SqliteDatabaseContext(
+ new DbContextOptions());
}
}
}
diff --git a/src/Tgstation.Server.Host/Database/IDatabaseContext.cs b/src/Tgstation.Server.Host/Database/IDatabaseContext.cs
index 90c472d5b7..36ce44583b 100644
--- a/src/Tgstation.Server.Host/Database/IDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Database/IDatabaseContext.cs
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -12,11 +13,6 @@ namespace Tgstation.Server.Host.Database
///
public interface IDatabaseContext
{
- ///
- /// The .
- ///
- DatabaseType DatabaseType { get; }
-
///
/// The s in the
///
@@ -89,19 +85,33 @@ namespace Tgstation.Server.Host.Database
/// A representing the running operation
Task Save(CancellationToken cancellationToken);
+ ///
+ /// Attempts to delete all tables and drop the database in use.
+ ///
+ /// The for the operation.
+ /// A representing the running operation.
+ Task Drop(CancellationToken cancellationToken);
+
///
/// Creates and migrates the
///
+ /// The to use.
/// The for the operation
- /// A representing the running operation
- Task Initialize(CancellationToken cancellationToken);
+ /// A resulting in if the database should be seeded, otherwise.
+ Task Migrate(ILogger logger, CancellationToken cancellationToken);
///
/// Attempt to downgrade the schema to the migration used for a given server
///
+ /// The to use.
/// The tgstation-server that the schema should downgrade for
+ /// The in use.
/// The for the operation
/// A representing the running operation
- Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken);
+ Task SchemaDowngradeForServerVersion(
+ ILogger logger,
+ Version version,
+ DatabaseType currentDatabaseType,
+ CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Database/IDatabaseSeeder.cs b/src/Tgstation.Server.Host/Database/IDatabaseSeeder.cs
index 7ff210979a..8e218c5e61 100644
--- a/src/Tgstation.Server.Host/Database/IDatabaseSeeder.cs
+++ b/src/Tgstation.Server.Host/Database/IDatabaseSeeder.cs
@@ -1,35 +1,29 @@
-using System.Threading;
+using System;
+using System.Threading;
using System.Threading.Tasks;
namespace Tgstation.Server.Host.Database
{
///
- /// For initially seeding a database
+ /// For initially setting up a database.
///
interface IDatabaseSeeder
{
///
- /// Initially seed a given
+ /// Setup up a given .
///
- /// The to seed
- /// The for the operation
- /// A representing the running operation
- Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken);
-
- ///
- /// Correct invalid database data caused by previous versions.
- ///
- /// The to sanitize.
+ /// The to setup.
/// The for the operation.
/// A representing the running operation.
- Task SanitizeDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken);
+ Task Initialize(IDatabaseContext databaseContext, CancellationToken cancellationToken);
///
- /// Changes the admin password in back to it's default and enables the account
+ /// Migrate a given down.
///
- /// The to reset the admin password for
- /// The for the operation
- /// A representing the running operation
- Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken);
+ /// The to downgrade.
+ /// The migration to downgrade the to.
+ /// The for the operation.
+ /// A representing the running operation.
+ Task Downgrade(IDatabaseContext databaseContext, Version downgradeVersion, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Database/MySqlDatabaseContext.cs b/src/Tgstation.Server.Host/Database/MySqlDatabaseContext.cs
index 78e936352c..46bd93309c 100644
--- a/src/Tgstation.Server.Host/Database/MySqlDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Database/MySqlDatabaseContext.cs
@@ -1,7 +1,4 @@
using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using MySql.Data.MySqlClient;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using System;
using Tgstation.Server.Host.Configuration;
@@ -20,42 +17,37 @@ namespace Tgstation.Server.Host.Database
/// Construct a
///
/// The for the
- /// The of for the
- /// The for the
- /// The for the
- public MySqlDatabaseContext(DbContextOptions dbContextOptions, IOptions databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
+ public MySqlDatabaseContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
{ }
- ///
- protected override void OnConfiguring(DbContextOptionsBuilder options)
+ ///
+ /// Configure the .
+ ///
+ /// The to configure.
+ /// The .
+ public static void ConfigureWith(DbContextOptionsBuilder options, DatabaseConfiguration databaseConfiguration)
{
- base.OnConfiguring(options);
- var stringDeconstructor = new MySqlConnectionStringBuilder
- {
- ConnectionString = DatabaseConfiguration.ConnectionString
- };
- if (stringDeconstructor.Server == "localhost")
- Logger.LogWarning("MariaDB/MySQL server address is set to 'localhost'! If there are connection issues, try setting it to '127.0.0.1'!");
+ if (options == null)
+ throw new ArgumentNullException(nameof(options));
+ if (databaseConfiguration == null)
+ throw new ArgumentNullException(nameof(databaseConfiguration));
+
+ if (databaseConfiguration.DatabaseType != DatabaseType.MariaDB && databaseConfiguration.DatabaseType != DatabaseType.MySql)
+ throw new InvalidOperationException($"Invalid DatabaseType for {nameof(MySqlDatabaseContext)}!");
+
options.UseMySql(
- DatabaseConfiguration.ConnectionString,
+ databaseConfiguration.ConnectionString,
mySqlOptions =>
{
mySqlOptions.EnableRetryOnFailure();
- if (!String.IsNullOrEmpty(DatabaseConfiguration.ServerVersion))
+ if (!String.IsNullOrEmpty(databaseConfiguration.ServerVersion))
mySqlOptions.ServerVersion(
- Version.Parse(DatabaseConfiguration.ServerVersion),
- DatabaseConfiguration.DatabaseType == DatabaseType.MariaDB
+ Version.Parse(databaseConfiguration.ServerVersion),
+ databaseConfiguration.DatabaseType == DatabaseType.MariaDB
? ServerType.MariaDb
: ServerType.MySql);
});
}
-
- ///
- protected override void ValidateDatabaseType()
- {
- if (DatabaseType != DatabaseType.MariaDB && DatabaseType != DatabaseType.MySql)
- throw new InvalidOperationException("Invalid DatabaseType for MySqlDatabaseContext!");
- }
}
}
diff --git a/src/Tgstation.Server.Host/Database/PostgresSqlDatabaseContext.cs b/src/Tgstation.Server.Host/Database/PostgresSqlDatabaseContext.cs
index 6514bc5333..090ec770ca 100644
--- a/src/Tgstation.Server.Host/Database/PostgresSqlDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Database/PostgresSqlDatabaseContext.cs
@@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
using System;
using Tgstation.Server.Host.Configuration;
@@ -18,36 +16,34 @@ namespace Tgstation.Server.Host.Database
/// Construct a
///
/// The for the
- /// The of for the
- /// The for the
- /// The for the
public PostgresSqlDatabaseContext(
- DbContextOptions dbContextOptions,
- IOptions databaseConfiguration,
- IDatabaseSeeder databaseSeeder,
- ILogger logger)
- : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
+ DbContextOptions dbContextOptions)
+ : base(dbContextOptions)
{ }
- ///
- protected override void OnConfiguring(DbContextOptionsBuilder options)
+ ///
+ /// Configure the .
+ ///
+ /// The to configure.
+ /// The .
+ public static void ConfigureWith(DbContextOptionsBuilder options, DatabaseConfiguration databaseConfiguration)
{
- base.OnConfiguring(options);
- options.UseNpgsql(DatabaseConfiguration.ConnectionString, options =>
+ if (options == null)
+ throw new ArgumentNullException(nameof(options));
+ if (databaseConfiguration == null)
+ throw new ArgumentNullException(nameof(databaseConfiguration));
+
+ if (databaseConfiguration.DatabaseType != DatabaseType.PostgresSql)
+ throw new InvalidOperationException($"Invalid DatabaseType for {nameof(PostgresSqlDatabaseContext)}!");
+
+ options.UseNpgsql(databaseConfiguration.ConnectionString, options =>
{
options.EnableRetryOnFailure();
- if (!String.IsNullOrEmpty(DatabaseConfiguration.ServerVersion))
+ if (!String.IsNullOrEmpty(databaseConfiguration.ServerVersion))
options.SetPostgresVersion(
- Version.Parse(DatabaseConfiguration.ServerVersion));
+ Version.Parse(databaseConfiguration.ServerVersion));
});
}
-
- ///
- protected override void ValidateDatabaseType()
- {
- if (DatabaseType != DatabaseType.PostgresSql)
- throw new InvalidOperationException("Invalid DatabaseType for PostgresSqlDatabaseContext!");
- }
}
}
diff --git a/src/Tgstation.Server.Host/Database/SqlServerDatabaseContext.cs b/src/Tgstation.Server.Host/Database/SqlServerDatabaseContext.cs
index 42c6aedc4e..3b5eb77b83 100644
--- a/src/Tgstation.Server.Host/Database/SqlServerDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Database/SqlServerDatabaseContext.cs
@@ -1,6 +1,4 @@
using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
using System;
using Tgstation.Server.Host.Configuration;
@@ -15,24 +13,25 @@ namespace Tgstation.Server.Host.Database
/// Construct a
///
/// The for the
- /// The of for the
- /// The for the
- /// The for the
- public SqlServerDatabaseContext(DbContextOptions dbContextOptions, IOptions databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
+ public SqlServerDatabaseContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
{ }
- ///
- protected override void OnConfiguring(DbContextOptionsBuilder options)
+ ///
+ /// Configure the .
+ ///
+ /// The to configure.
+ /// The .
+ public static void ConfigureWith(DbContextOptionsBuilder options, DatabaseConfiguration databaseConfiguration)
{
- base.OnConfiguring(options);
- options.UseSqlServer(DatabaseConfiguration.ConnectionString, x => x.EnableRetryOnFailure());
- }
+ if (options == null)
+ throw new ArgumentNullException(nameof(options));
+ if (databaseConfiguration == null)
+ throw new ArgumentNullException(nameof(databaseConfiguration));
- ///
- protected override void ValidateDatabaseType()
- {
- if (DatabaseType != DatabaseType.SqlServer)
- throw new InvalidOperationException("Invalid DatabaseType for SqlServerDatabaseContext!");
+ if (databaseConfiguration.DatabaseType != DatabaseType.SqlServer)
+ throw new InvalidOperationException($"Invalid DatabaseType for {nameof(SqlServerDatabaseContext)}!");
+
+ options.UseSqlServer(databaseConfiguration.ConnectionString, x => x.EnableRetryOnFailure());
}
}
}
diff --git a/src/Tgstation.Server.Host/Database/SqliteDatabaseContext.cs b/src/Tgstation.Server.Host/Database/SqliteDatabaseContext.cs
index 4d9fecf844..db2f500e28 100644
--- a/src/Tgstation.Server.Host/Database/SqliteDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Database/SqliteDatabaseContext.cs
@@ -1,7 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
using System;
using System.Linq;
using Tgstation.Server.Host.Configuration;
@@ -9,25 +7,38 @@ using Tgstation.Server.Host.Configuration;
namespace Tgstation.Server.Host.Database
{
///
- /// for MySQL
+ /// for SQLite.
///
sealed class SqliteDatabaseContext : DatabaseContext
{
+ ///
+ /// Static property to receive the configured value of .
+ ///
+ public static bool DesignTime { get; set; }
+
///
/// Construct a
///
/// The for the
- /// The of for the
- /// The for the
- /// The for the
- public SqliteDatabaseContext(DbContextOptions dbContextOptions, IOptions databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
+ public SqliteDatabaseContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
{ }
- ///
- protected override void OnConfiguring(DbContextOptionsBuilder options)
+ ///
+ /// Configure the .
+ ///
+ /// The to configure.
+ /// The .
+ public static void ConfigureWith(DbContextOptionsBuilder options, DatabaseConfiguration databaseConfiguration)
{
- base.OnConfiguring(options);
- options.UseSqlite(DatabaseConfiguration.ConnectionString);
+ if (options == null)
+ throw new ArgumentNullException(nameof(options));
+ if (databaseConfiguration == null)
+ throw new ArgumentNullException(nameof(databaseConfiguration));
+
+ if (databaseConfiguration.DatabaseType != DatabaseType.Sqlite)
+ throw new InvalidOperationException($"Invalid DatabaseType for {nameof(SqliteDatabaseContext)}!");
+
+ options.UseSqlite(databaseConfiguration.ConnectionString);
}
///
@@ -43,7 +54,7 @@ namespace Tgstation.Server.Host.Database
// use the DateTimeOffsetToBinaryConverter
// Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754
// This only supports millisecond precision, but should be sufficient for most use cases.
- if (!DatabaseConfiguration.DesignTime)
+ if (DesignTime)
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var properties = entityType
@@ -57,12 +68,5 @@ namespace Tgstation.Server.Host.Database
.HasConversion(new DateTimeOffsetToBinaryConverter());
}
}
-
- ///
- protected override void ValidateDatabaseType()
- {
- if (DatabaseType != DatabaseType.Sqlite)
- throw new InvalidOperationException("Invalid DatabaseType for SqliteDatabaseContext!");
- }
}
}