Add runtime hooks for Deploy
This commit is contained in:
@@ -283,48 +283,60 @@ namespace Umbraco.Core.Migrations.Install
|
||||
if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullOrEmptyException(nameof(connectionString));
|
||||
if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentNullOrEmptyException(nameof(providerName));
|
||||
|
||||
// set the connection string for the new datalayer
|
||||
var connectionStringSettings = new ConnectionStringSettings(Constants.System.UmbracoConnectionName, connectionString, providerName);
|
||||
var fileSource = "web.config";
|
||||
var fileName = IOHelper.MapPath(Path.Combine(SystemDirectories.Root, fileSource));
|
||||
|
||||
var fileName = IOHelper.MapPath($"{SystemDirectories.Root}/web.config");
|
||||
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
|
||||
if (xml.Root == null) throw new Exception("Invalid web.config file.");
|
||||
var connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault();
|
||||
if (connectionStrings == null) throw new Exception("Invalid web.config file.");
|
||||
if (xml.Root == null) throw new Exception($"Invalid {fileSource} file (no root).");
|
||||
|
||||
// honour configSource, if its set, change the xml file we are saving the configuration
|
||||
// to the one set in the configSource attribute
|
||||
if (connectionStrings.Attribute("configSource") != null)
|
||||
var connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault();
|
||||
if (connectionStrings == null) throw new Exception($"Invalid {fileSource} file (no connection strings).");
|
||||
|
||||
// handle configSource
|
||||
var configSourceAttribute = connectionStrings.Attribute("configSource");
|
||||
if (configSourceAttribute != null)
|
||||
{
|
||||
var source = connectionStrings.Attribute("configSource").Value;
|
||||
var configFile = IOHelper.MapPath($"{SystemDirectories.Root}/{source}");
|
||||
logger.Info<DatabaseBuilder>("Storing ConnectionString in {ConfigFile}", configFile);
|
||||
if (File.Exists(configFile))
|
||||
{
|
||||
xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
|
||||
fileName = configFile;
|
||||
}
|
||||
fileSource = configSourceAttribute.Value;
|
||||
fileName = IOHelper.MapPath(Path.Combine(SystemDirectories.Root, fileSource));
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
throw new Exception($"Invalid configSource \"{fileSource}\" (no such file).");
|
||||
|
||||
xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
|
||||
if (xml.Root == null) throw new Exception($"Invalid {fileSource} file (no root).");
|
||||
|
||||
connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault();
|
||||
if (connectionStrings == null) throw new Exception("Invalid web.config file.");
|
||||
if (connectionStrings == null) throw new Exception($"Invalid {fileSource} file (no connection strings).");
|
||||
}
|
||||
|
||||
// update connectionString if it exists, or else create a new connectionString
|
||||
var setting = connectionStrings.Descendants("add").FirstOrDefault(s => s.Attribute("name").Value == Constants.System.UmbracoConnectionName);
|
||||
// create or update connection string
|
||||
var setting = connectionStrings.Descendants("add").FirstOrDefault(s => s.Attribute("name")?.Value == Constants.System.UmbracoConnectionName);
|
||||
if (setting == null)
|
||||
{
|
||||
connectionStrings.Add(new XElement("add",
|
||||
new XAttribute("name", Constants.System.UmbracoConnectionName),
|
||||
new XAttribute("connectionString", connectionStringSettings),
|
||||
new XAttribute("connectionString", connectionString),
|
||||
new XAttribute("providerName", providerName)));
|
||||
}
|
||||
else
|
||||
{
|
||||
setting.Attribute("connectionString").Value = connectionString;
|
||||
setting.Attribute("providerName").Value = providerName;
|
||||
AddOrUpdateAttribute(setting, "connectionString", connectionString);
|
||||
AddOrUpdateAttribute(setting, "providerName", providerName);
|
||||
}
|
||||
|
||||
// save
|
||||
logger.Info<DatabaseBuilder>("Saving connection string to {ConfigFile}.", fileSource);
|
||||
xml.Save(fileName, SaveOptions.DisableFormatting);
|
||||
logger.Info<DatabaseBuilder>("Configured a new ConnectionString using the '{ProviderName}' provider.", providerName);
|
||||
logger.Info<DatabaseBuilder>("Saved connection string to {ConfigFile}.", fileSource);
|
||||
}
|
||||
|
||||
private static void AddOrUpdateAttribute(XElement element, string name, string value)
|
||||
{
|
||||
var attribute = element.Attribute(name);
|
||||
if (attribute == null)
|
||||
element.Add(new XAttribute(name, value));
|
||||
else
|
||||
attribute.Value = value;
|
||||
}
|
||||
|
||||
internal bool IsConnectionStringConfigured(ConnectionStringSettings databaseSettings)
|
||||
@@ -422,7 +434,7 @@ namespace Umbraco.Core.Migrations.Install
|
||||
_logger.Info<DatabaseBuilder>("Database configuration status: Started");
|
||||
|
||||
var database = scope.Database;
|
||||
|
||||
|
||||
var message = string.Empty;
|
||||
|
||||
var schemaResult = ValidateSchema();
|
||||
@@ -482,7 +494,7 @@ namespace Umbraco.Core.Migrations.Install
|
||||
}
|
||||
|
||||
_logger.Info<DatabaseBuilder>("Database upgrade started");
|
||||
|
||||
|
||||
// upgrade
|
||||
var upgrader = new UmbracoUpgrader();
|
||||
upgrader.Execute(_scopeProvider, _migrationBuilder, _keyValueService, _logger, _postMigrations);
|
||||
|
||||
@@ -59,14 +59,18 @@ namespace Umbraco.Core.Persistence
|
||||
/// <remarks>Used by the other ctor and in tests.</remarks>
|
||||
public UmbracoDatabaseFactory(string connectionStringName, ILogger logger, Lazy<IMapperCollection> mappers)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentNullOrEmptyException(nameof(connectionStringName));
|
||||
if (string.IsNullOrWhiteSpace(connectionStringName))
|
||||
throw new ArgumentNullOrEmptyException(nameof(connectionStringName));
|
||||
|
||||
_mappers = mappers ?? throw new ArgumentNullException(nameof(mappers));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
var settings = ConfigurationManager.ConnectionStrings[connectionStringName];
|
||||
if (settings == null)
|
||||
{
|
||||
logger.Debug<UmbracoDatabaseFactory>("Missing connection string, defer configuration.");
|
||||
return; // not configured
|
||||
}
|
||||
|
||||
// could as well be <add name="umbracoDbDSN" connectionString="" providerName="" />
|
||||
// so need to test the values too
|
||||
@@ -74,7 +78,7 @@ namespace Umbraco.Core.Persistence
|
||||
var providerName = settings.ProviderName;
|
||||
if (string.IsNullOrWhiteSpace(connectionString) || string.IsNullOrWhiteSpace(providerName))
|
||||
{
|
||||
logger.Debug<UmbracoDatabaseFactory>("Missing connection string or provider name, defer configuration.");
|
||||
logger.Debug<UmbracoDatabaseFactory>("Empty connection string or provider name, defer configuration.");
|
||||
return; // not configured
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,9 @@ namespace Umbraco.Core.Runtime
|
||||
// throws if not full-trust
|
||||
new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted).Demand();
|
||||
|
||||
// run handlers
|
||||
RuntimeOptions.DoRuntimeBoot(ProfilingLogger);
|
||||
|
||||
// application caches
|
||||
var appCaches = GetAppCaches();
|
||||
|
||||
@@ -131,12 +134,17 @@ namespace Umbraco.Core.Runtime
|
||||
composition = new Composition(register, typeLoader, ProfilingLogger, _state, configs);
|
||||
composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, mainDom, appCaches, databaseFactory, typeLoader, _state);
|
||||
|
||||
// run handlers
|
||||
RuntimeOptions.DoRuntimeEssentials(composition, appCaches, typeLoader, databaseFactory);
|
||||
|
||||
// register runtime-level services
|
||||
// there should be none, really - this is here "just in case"
|
||||
Compose(composition);
|
||||
|
||||
// acquire the main domain, determine our runtime level
|
||||
// acquire the main domain
|
||||
AcquireMainDom(mainDom);
|
||||
|
||||
// determine our runtime level
|
||||
DetermineRuntimeLevel(databaseFactory, ProfilingLogger);
|
||||
|
||||
// get composers, and compose
|
||||
|
||||
114
src/Umbraco.Core/RuntimeOptions.cs
Normal file
114
src/Umbraco.Core/RuntimeOptions.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Components;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides static options for the runtime.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These options can be configured in PreApplicationStart or via appSettings.
|
||||
/// </remarks>
|
||||
public static class RuntimeOptions
|
||||
{
|
||||
private static List<Action<IProfilingLogger>> _onBoot;
|
||||
private static List<Action<Composition, AppCaches, TypeLoader, IUmbracoDatabaseFactory>> _onEssentials;
|
||||
private static bool? _installMissingDatabase;
|
||||
private static bool? _installEmptyDatabase;
|
||||
|
||||
// reads a boolean appSetting
|
||||
private static bool BoolSetting(string key, bool missing) => ConfigurationManager.AppSettings[key]?.InvariantEquals("true") ?? missing;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the runtime should enter Install level when the database is missing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>By default, when a database connection string is configured but it is not possible to
|
||||
/// connect to the database, the runtime enters the BootFailed level. If this options is set to true,
|
||||
/// it enters the Install level instead.</para>
|
||||
/// <para>It is then up to the implementor, that is setting this value, to take over the installation
|
||||
/// sequence.</para>
|
||||
/// </remarks>
|
||||
public static bool InstallMissingDatabase
|
||||
{
|
||||
get => _installEmptyDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallMissingDatabase", false);
|
||||
set => _installEmptyDatabase = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the runtime should enter Install level when the database is empty.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>By default, when a database connection string is configured and it is possible to connect to
|
||||
/// the database, but the database is empty, the runtime enters the BootFailed level. If this options
|
||||
/// is set to true, it enters the Install level instead.</para>
|
||||
/// <para>It is then up to the implementor, that is setting this value, to take over the installation
|
||||
/// sequence.</para>
|
||||
/// </remarks>
|
||||
public static bool InstallEmptyDatabase
|
||||
{
|
||||
get => _installMissingDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallEmptyDatabase", false);
|
||||
set => _installMissingDatabase = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the RuntimeBoot handlers.
|
||||
/// </summary>
|
||||
internal static void DoRuntimeBoot(IProfilingLogger logger)
|
||||
{
|
||||
if (_onBoot == null)
|
||||
return;
|
||||
|
||||
foreach (var action in _onBoot)
|
||||
action(logger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the RuntimeEssentials handlers.
|
||||
/// </summary>
|
||||
internal static void DoRuntimeEssentials(Composition composition, AppCaches appCaches, TypeLoader typeLoader, IUmbracoDatabaseFactory databaseFactory)
|
||||
{
|
||||
if (_onEssentials== null)
|
||||
return;
|
||||
|
||||
foreach (var action in _onEssentials)
|
||||
action(composition, appCaches, typeLoader, databaseFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a RuntimeBoot handler.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>A RuntimeBoot handler runs when the runtime boots, right after the
|
||||
/// loggers have been created, but before anything else.</para>
|
||||
/// </remarks>
|
||||
public static void OnRuntimeBoot(Action<IProfilingLogger> action)
|
||||
{
|
||||
if (_onBoot == null)
|
||||
_onBoot = new List<Action<IProfilingLogger>>();
|
||||
_onBoot.Add(action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a RuntimeEssentials handler.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>A RuntimeEssentials handler runs after the runtime has created a few
|
||||
/// essential things (AppCaches, a TypeLoader, and a database factory) but
|
||||
/// before anything else.</para>
|
||||
/// </remarks>
|
||||
public static void OnRuntimeEssentials(Action<Composition, AppCaches, TypeLoader, IUmbracoDatabaseFactory> action)
|
||||
{
|
||||
if (_onEssentials == null)
|
||||
_onEssentials = new List<Action<Composition, AppCaches, TypeLoader, IUmbracoDatabaseFactory>>();
|
||||
_onEssentials.Add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ namespace Umbraco.Core
|
||||
else if (databaseFactory.Configured == false)
|
||||
{
|
||||
// local version *does* match code version, but the database is not configured
|
||||
// install (again? this is a weird situation...)
|
||||
// install - may happen with Deploy/Cloud/etc
|
||||
logger.Debug<RuntimeState>("Database is not configured, need to install Umbraco.");
|
||||
Level = RuntimeLevel.Install;
|
||||
Reason = RuntimeLevelReason.InstallNoDatabase;
|
||||
@@ -179,7 +179,7 @@ namespace Umbraco.Core
|
||||
// else, keep going,
|
||||
// anything other than install wants a database - see if we can connect
|
||||
// (since this is an already existing database, assume localdb is ready)
|
||||
var tries = RuntimeStateOptions.InstallMissingDatabase ? 2 : 5;
|
||||
var tries = RuntimeOptions.InstallMissingDatabase ? 2 : 5;
|
||||
for (var i = 0;;)
|
||||
{
|
||||
connect = databaseFactory.CanConnect;
|
||||
@@ -193,7 +193,7 @@ namespace Umbraco.Core
|
||||
// cannot connect to configured database, this is bad, fail
|
||||
logger.Debug<RuntimeState>("Could not connect to database.");
|
||||
|
||||
if (RuntimeStateOptions.InstallMissingDatabase)
|
||||
if (RuntimeOptions.InstallMissingDatabase)
|
||||
{
|
||||
// ok to install on a configured but missing database
|
||||
Level = RuntimeLevel.Install;
|
||||
@@ -222,7 +222,7 @@ namespace Umbraco.Core
|
||||
// can connect to the database but cannot check the upgrade state... oops
|
||||
logger.Warn<RuntimeState>(e, "Could not check the upgrade state.");
|
||||
|
||||
if (RuntimeStateOptions.InstallEmptyDatabase)
|
||||
if (RuntimeOptions.InstallEmptyDatabase)
|
||||
{
|
||||
// ok to install on an empty database
|
||||
Level = RuntimeLevel.Install;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows configuration of the <see cref="RuntimeState"/> in PreApplicationStart or in appSettings
|
||||
/// </summary>
|
||||
public static class RuntimeStateOptions
|
||||
{
|
||||
// configured statically or via app settings
|
||||
private static bool BoolSetting(string key, bool missing) => ConfigurationManager.AppSettings[key]?.InvariantEquals("true") ?? missing;
|
||||
|
||||
/// <summary>
|
||||
/// If true the RuntimeState will continue the installation sequence when a database is missing
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In this case it will be up to the implementor that is setting this value to true to take over the bootup/installation sequence
|
||||
/// </remarks>
|
||||
public static bool InstallMissingDatabase
|
||||
{
|
||||
get => _installEmptyDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallMissingDatabase", false);
|
||||
set => _installEmptyDatabase = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true the RuntimeState will continue the installation sequence when a database is available but is empty
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In this case it will be up to the implementor that is setting this value to true to take over the bootup/installation sequence
|
||||
/// </remarks>
|
||||
public static bool InstallEmptyDatabase
|
||||
{
|
||||
get => _installMissingDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallEmptyDatabase", false);
|
||||
set => _installMissingDatabase = value;
|
||||
}
|
||||
|
||||
private static bool? _installMissingDatabase;
|
||||
private static bool? _installEmptyDatabase;
|
||||
}
|
||||
}
|
||||
@@ -530,7 +530,7 @@
|
||||
<Compile Include="ReadLock.cs" />
|
||||
<Compile Include="ReflectionUtilities-Unused.cs" />
|
||||
<Compile Include="RuntimeLevelReason.cs" />
|
||||
<Compile Include="RuntimeStateOptions.cs" />
|
||||
<Compile Include="RuntimeOptions.cs" />
|
||||
<Compile Include="Runtime\CoreRuntime.cs" />
|
||||
<Compile Include="Runtime\CoreRuntimeComponent.cs" />
|
||||
<Compile Include="CustomBooleanTypeConverter.cs" />
|
||||
|
||||
Reference in New Issue
Block a user