Merge branch 'feature/health-check-scheduler' of https://github.com/AndyButland/Umbraco-CMS into temp-U4-9984
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
{
|
||||
public class DisabledHealthCheckElement : ConfigurationElement
|
||||
{
|
||||
private const string ID_KEY = "id";
|
||||
private const string DISABLED_ON_KEY = "disabledOn";
|
||||
private const string DISABLED_BY_KEY = "disabledBy";
|
||||
|
||||
[ConfigurationProperty(ID_KEY, IsKey = true, IsRequired = true)]
|
||||
public Guid Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((Guid)(base[ID_KEY]));
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(DISABLED_ON_KEY, IsKey = false, IsRequired = false)]
|
||||
public DateTime DisabledOn
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((DateTime)(base[DISABLED_ON_KEY]));
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(DISABLED_BY_KEY, IsKey = false, IsRequired = false)]
|
||||
public int DisabledBy
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)(base[DISABLED_BY_KEY]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
{
|
||||
[ConfigurationCollection(typeof(DisabledHealthCheckElement), AddItemName = "check")]
|
||||
public class DisabledHealthChecksElementCollection : ConfigurationElementCollection, IEnumerable<DisabledHealthCheckElement>
|
||||
{
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new DisabledHealthCheckElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((DisabledHealthCheckElement)(element)).Id;
|
||||
}
|
||||
|
||||
new public DisabledHealthCheckElement this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DisabledHealthCheckElement)BaseGet(key);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator<DisabledHealthCheckElement> IEnumerable<DisabledHealthCheckElement>.GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
yield return BaseGet(i) as DisabledHealthCheckElement;
|
||||
}
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
{
|
||||
public class HealthCheckNotificationSettingsElement : ConfigurationElement
|
||||
{
|
||||
private const string ENABLED_KEY = "enabled";
|
||||
private const string FIRST_RUN_TIME_KEY = "firstRunTime";
|
||||
private const string PERIOD_KEY = "periodInHours";
|
||||
private const string RECIPIENT_EMAIL_KEY = "recipientEmail";
|
||||
private const string WEBHOOK_URL_KEY = "webHookUrl";
|
||||
private const string DISABLED_CHECKS_KEY = "disabledChecks";
|
||||
|
||||
[ConfigurationProperty(ENABLED_KEY, IsRequired = true)]
|
||||
public bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((bool)(base[ENABLED_KEY]));
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(FIRST_RUN_TIME_KEY, IsRequired = true)]
|
||||
public string FirstRunTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((string)(base[FIRST_RUN_TIME_KEY]));
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(PERIOD_KEY, IsRequired = true)]
|
||||
public int PeriodInHours
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)(base[PERIOD_KEY]));
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(RECIPIENT_EMAIL_KEY, IsRequired = true)]
|
||||
public string RecipientEmail
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((string)(base[RECIPIENT_EMAIL_KEY]));
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(WEBHOOK_URL_KEY, IsRequired = true)]
|
||||
public string WebhookUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((string)(base[WEBHOOK_URL_KEY]));
|
||||
}
|
||||
}
|
||||
|
||||
[ConfigurationProperty(DISABLED_CHECKS_KEY, IsDefaultCollection = true, IsRequired = false)]
|
||||
public DisabledHealthChecksElementCollection DisabledChecks
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((DisabledHealthChecksElementCollection)(base[DISABLED_CHECKS_KEY]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
{
|
||||
public class HealthChecksSection : ConfigurationSection
|
||||
{
|
||||
private const string DISABLED_CHECKS_KEY = "disabledChecks";
|
||||
private const string NOTIFICATION_SETTINGS_KEY = "notificationSettings";
|
||||
|
||||
[ConfigurationProperty(DISABLED_CHECKS_KEY)]
|
||||
public DisabledHealthChecksElementCollection DisabledChecks
|
||||
{
|
||||
get { return ((DisabledHealthChecksElementCollection)(base[DISABLED_CHECKS_KEY])); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(NOTIFICATION_SETTINGS_KEY, IsRequired = true)]
|
||||
public HealthCheckNotificationSettingsElement NotificationSettings
|
||||
{
|
||||
get { return ((HealthCheckNotificationSettingsElement)(base[NOTIFICATION_SETTINGS_KEY])); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,43 @@ namespace Umbraco.Core
|
||||
Hour,
|
||||
Minute,
|
||||
Second
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
|
||||
/// date time is before the time, calculate onto next day)
|
||||
/// </summary>
|
||||
/// <param name="fromDate">Date to start from</param>
|
||||
/// <param name="scheduledTime">Time to compare against (in Hmm form, e.g. 330, 2200)</param>
|
||||
/// <returns></returns>
|
||||
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
|
||||
{
|
||||
// Ensure time provided is 4 digits long
|
||||
if (scheduledTime.Length == 3)
|
||||
{
|
||||
scheduledTime = "0" + scheduledTime;
|
||||
}
|
||||
|
||||
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
|
||||
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
|
||||
|
||||
DateTime scheduledDateTime;
|
||||
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
|
||||
{
|
||||
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nextDay = fromDateTime.AddDays(1);
|
||||
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
|
||||
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
|
||||
}
|
||||
|
||||
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
|
||||
{
|
||||
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,8 +218,12 @@
|
||||
<Compile Include="Configuration\Dashboard\SectionElement.cs" />
|
||||
<Compile Include="Configuration\Dashboard\TabCollection.cs" />
|
||||
<Compile Include="Configuration\Dashboard\TabElement.cs" />
|
||||
<Compile Include="Configuration\HealthChecks\HealthCheckNotificationSettingsElement.cs" />
|
||||
<Compile Include="Configuration\HealthChecks\DisabledHealthCheckElement.cs" />
|
||||
<Compile Include="Configuration\FileSystemProviderElement.cs" />
|
||||
<Compile Include="Configuration\HealthChecks\DisabledHealthChecksElementCollection.cs" />
|
||||
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
|
||||
<Compile Include="Configuration\HealthChecks\HealthChecksSection.cs" />
|
||||
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
|
||||
<Compile Include="Configuration\Grid\GridConfig.cs" />
|
||||
<Compile Include="Configuration\Grid\GridEditorsConfig.cs" />
|
||||
|
||||
46
src/Umbraco.Tests/DateTimeExtensionsTests.cs
Normal file
46
src/Umbraco.Tests/DateTimeExtensionsTests.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class DateTimeExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PostTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "1145";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(75, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PriorTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "900";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(1350, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PriorTime_WithLeadingZero_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "0900";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(1350, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_SameTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "1030";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(0, minutesBetween);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,6 +161,7 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DateTimeExtensionsTests.cs" />
|
||||
<Compile Include="Cache\CacheRefresherEventHandlerTests.cs" />
|
||||
<Compile Include="Dependencies\NuGet.cs" />
|
||||
<Compile Include="CallContextTests.cs" />
|
||||
|
||||
@@ -40,8 +40,7 @@
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile />
|
||||
<UseIISExpress>true</UseIISExpress>
|
||||
<IISExpressSSLPort>
|
||||
</IISExpressSSLPort>
|
||||
<IISExpressSSLPort>44319</IISExpressSSLPort>
|
||||
<IISExpressAnonymousAuthentication>enabled</IISExpressAnonymousAuthentication>
|
||||
<IISExpressWindowsAuthentication>disabled</IISExpressWindowsAuthentication>
|
||||
<IISExpressUseClassicPipelineMode>false</IISExpressUseClassicPipelineMode>
|
||||
@@ -633,6 +632,13 @@
|
||||
<None Include="Config\EmbeddedMedia.Release.config">
|
||||
<DependentUpon>EmbeddedMedia.config</DependentUpon>
|
||||
</None>
|
||||
<Content Include="Config\HealthChecks.config">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="Config\HealthChecks.Release.config">
|
||||
<DependentUpon>HealthChecks.config</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<None Include="Config\umbracoSettings.Release.config">
|
||||
<DependentUpon>umbracoSettings.config</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
|
||||
5
src/Umbraco.Web.UI/config/HealthChecks.Release.config
Normal file
5
src/Umbraco.Web.UI/config/HealthChecks.Release.config
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<HealthChecks>
|
||||
</HealthChecks>
|
||||
|
||||
|
||||
21
src/Umbraco.Web.UI/config/HealthChecks.config
Normal file
21
src/Umbraco.Web.UI/config/HealthChecks.config
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
|
||||
Please note: to use healthcheck notifications you must ensure to set the umbracoApplicationUrl value
|
||||
in umbracoSettings.config. For more information on this setting, please see:
|
||||
https://our.umbraco.org/documentation/reference/config/umbracosettings/#web-routing
|
||||
-->
|
||||
<HealthChecks>
|
||||
<disabledChecks>
|
||||
<check id="1B5D221B-CE99-4193-97CB-5F3261EC73DF" disabledOn="" disabledBy="0" /> <!-- Smtp-->
|
||||
</disabledChecks>
|
||||
<notificationSettings enabled="true" firstRunTime="2300" periodInHours="24" recipientEmail="" webHookUrl="">
|
||||
<disabledChecks>
|
||||
<check id="EB66BB3B-1BCD-4314-9531-9DA2C1D6D9A7" disabledOn="" disabledBy="0" /> <!-- Https -->
|
||||
<check id="ED0D7E40-971E-4BE8-AB6D-8CC5D0A6A5B0" disabledOn="" disabledBy="0" /> <!-- Click jack -->
|
||||
<check id="92ABBAA2-0586-4089-8AE2-9A843439D577" disabledOn="" disabledBy="0" /> <!-- Excessive headers -->
|
||||
</disabledChecks>
|
||||
</notificationSettings>
|
||||
</HealthChecks>
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<section name="BaseRestExtensions" type="Umbraco.Core.Configuration.BaseRest.BaseRestSection, Umbraco.Core" requirePermission="false" />
|
||||
<section name="FileSystemProviders" type="Umbraco.Core.Configuration.FileSystemProvidersSection, Umbraco.Core" requirePermission="false" />
|
||||
<section name="dashBoard" type="Umbraco.Core.Configuration.Dashboard.DashboardSection, Umbraco.Core" requirePermission="false" />
|
||||
<section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecksSection, Umbraco.Core" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
|
||||
</configSections>
|
||||
@@ -47,6 +48,7 @@
|
||||
<BaseRestExtensions configSource="config\BaseRestExtensions.config" />
|
||||
<FileSystemProviders configSource="config\FileSystemProviders.config" />
|
||||
<dashBoard configSource="config\Dashboard.config" />
|
||||
<HealthChecks configSource="config\HealthChecks.config" />
|
||||
</umbracoConfiguration>
|
||||
|
||||
<appSettings xdt:Transform="Remove" xdt:Locator="Condition(@configSource != '')" />
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
<section name="BaseRestExtensions" type="Umbraco.Core.Configuration.BaseRest.BaseRestSection, Umbraco.Core" requirePermission="false" />
|
||||
<section name="FileSystemProviders" type="Umbraco.Core.Configuration.FileSystemProvidersSection, Umbraco.Core" requirePermission="false" />
|
||||
<section name="dashBoard" type="Umbraco.Core.Configuration.Dashboard.DashboardSection, Umbraco.Core" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
<section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecksSection, Umbraco.Core" requirePermission="false"/>
|
||||
</sectionGroup>
|
||||
|
||||
<sectionGroup name="imageProcessor">
|
||||
<section name="security" requirePermission="false" type="ImageProcessor.Web.Configuration.ImageSecuritySection, ImageProcessor.Web" />
|
||||
@@ -26,6 +27,7 @@
|
||||
<BaseRestExtensions configSource="config\BaseRestExtensions.config" />
|
||||
<FileSystemProviders configSource="config\FileSystemProviders.config" />
|
||||
<dashBoard configSource="config\Dashboard.config" />
|
||||
<HealthChecks configSource="config\HealthChecks.config" />
|
||||
</umbracoConfiguration>
|
||||
|
||||
<microsoft.scripting configSource="config\scripting.config" />
|
||||
|
||||
@@ -7,7 +7,6 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
{
|
||||
|
||||
public abstract class AbstractConfigCheck : HealthCheck
|
||||
{
|
||||
private readonly ConfigurationService _configurationService;
|
||||
@@ -46,7 +45,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
protected AbstractConfigCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext)
|
||||
{
|
||||
_textService = healthCheckContext.ApplicationContext.Services.TextService;
|
||||
_configurationService = new ConfigurationService(AbsoluteFilePath, XPath);
|
||||
_configurationService = new ConfigurationService(AbsoluteFilePath, XPath, _textService);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -17,11 +17,11 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
/// <param name="configFilePath">The absolute file location of the configuration file</param>
|
||||
/// <param name="xPath">The XPath to select the value</param>
|
||||
/// <returns></returns>
|
||||
public ConfigurationService(string configFilePath, string xPath)
|
||||
public ConfigurationService(string configFilePath, string xPath, ILocalizedTextService textService)
|
||||
{
|
||||
_configFilePath = configFilePath;
|
||||
_xPath = xPath;
|
||||
_textService = UmbracoContext.Current.Application.Services.TextService;
|
||||
_textService = textService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -61,13 +61,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
{
|
||||
var message = string.Empty;
|
||||
var success = false;
|
||||
var url = HealthCheckContext.HttpContext.Request.Url;
|
||||
|
||||
// Access the site home page and check for the click-jack protection header or meta tag
|
||||
var serverVariables = HealthCheckContext.HttpContext.Request.ServerVariables;
|
||||
var useSsl = GlobalSettings.UseSSL || serverVariables["SERVER_PORT"] == "443";
|
||||
var address = string.Format("http{0}://{1}:{2}", useSsl ? "s" : "", url.Host.ToLower(), url.Port);
|
||||
var request = WebRequest.Create(address);
|
||||
var url = HealthCheckContext.SiteUrl;
|
||||
var request = WebRequest.Create(url);
|
||||
request.Method = "GET";
|
||||
try
|
||||
{
|
||||
@@ -88,7 +85,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { address, ex.Message });
|
||||
message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { url, ex.Message });
|
||||
}
|
||||
|
||||
var actions = new List<HealthCheckAction>();
|
||||
|
||||
@@ -46,13 +46,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
{
|
||||
var message = string.Empty;
|
||||
var success = false;
|
||||
var url = HealthCheckContext.HttpContext.Request.Url;
|
||||
var url = HealthCheckContext.SiteUrl;
|
||||
|
||||
// Access the site home page and check for the headers
|
||||
var serverVariables = HealthCheckContext.HttpContext.Request.ServerVariables;
|
||||
var useSsl = GlobalSettings.UseSSL || serverVariables["SERVER_PORT"] == "443";
|
||||
var address = string.Format("http{0}://{1}:{2}", useSsl ? "s" : "", url.Host.ToLower(), url.Port);
|
||||
var request = WebRequest.Create(address);
|
||||
var request = WebRequest.Create(url);
|
||||
request.Method = "HEAD";
|
||||
try
|
||||
{
|
||||
@@ -69,7 +66,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { address, ex.Message });
|
||||
message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { url, ex.Message });
|
||||
}
|
||||
|
||||
var actions = new List<HealthCheckAction>();
|
||||
|
||||
@@ -54,12 +54,11 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
{
|
||||
var message = string.Empty;
|
||||
var success = false;
|
||||
var url = HealthCheckContext.HttpContext.Request.Url;
|
||||
|
||||
// Attempt to access the site over HTTPS to see if it HTTPS is supported
|
||||
// and a valid certificate has been configured
|
||||
var address = string.Format("https://{0}:{1}", url.Host.ToLower(), url.Port);
|
||||
var request = (HttpWebRequest)WebRequest.Create(address);
|
||||
var url = HealthCheckContext.SiteUrl.Replace("http:", "https:");
|
||||
var request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Method = "HEAD";
|
||||
|
||||
try
|
||||
@@ -74,11 +73,11 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
{
|
||||
message = exception.Status == WebExceptionStatus.TrustFailure
|
||||
? _textService.Localize("healthcheck/httpsCheckInvalidCertificate", new [] { exception.Message })
|
||||
: _textService.Localize("healthcheck/httpsCheckInvalidUrl", new [] { address, exception.Message });
|
||||
: _textService.Localize("healthcheck/httpsCheckInvalidUrl", new [] { url, exception.Message });
|
||||
}
|
||||
else
|
||||
{
|
||||
message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { address, ex.Message });
|
||||
message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { url, ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
{
|
||||
var configFile = IOHelper.MapPath("~/Web.config");
|
||||
const string xPath = "/configuration/appSettings/add[@key='umbracoUseSSL']/@value";
|
||||
var configurationService = new ConfigurationService(configFile, xPath);
|
||||
var configurationService = new ConfigurationService(configFile, xPath, _textService);
|
||||
var updateConfigFile = configurationService.UpdateConfigFile("true");
|
||||
|
||||
if (updateConfigFile.Success)
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Services
|
||||
var message = string.Empty;
|
||||
var success = false;
|
||||
|
||||
var config = WebConfigurationManager.OpenWebConfiguration(HealthCheckContext.HttpContext.Request.ApplicationPath);
|
||||
var config = WebConfigurationManager.OpenWebConfiguration(HealthCheckContext.ApplicationPath);
|
||||
var settings = (MailSettingsSectionGroup)config.GetSectionGroup("system.net/mailSettings");
|
||||
if (settings == null)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Umbraco.Web.HealthCheck
|
||||
[DataContract(Name = "healtCheckAction", Namespace = "")]
|
||||
public class HealthCheckAction
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
/// <summary>
|
||||
/// Empty ctor used for serialization
|
||||
/// </summary>
|
||||
@@ -51,7 +53,7 @@ namespace Umbraco.Web.HealthCheck
|
||||
/// The name of the action - this is used to name the fix button
|
||||
/// </summary>
|
||||
[DataMember(Name = "name")]
|
||||
private string _name = UmbracoContext.Current.Application.Services.TextService.Localize("healthcheck/rectifyButton");
|
||||
private string _name;
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
|
||||
@@ -9,19 +9,44 @@ namespace Umbraco.Web.HealthCheck
|
||||
/// </summary>
|
||||
public class HealthCheckContext
|
||||
{
|
||||
private readonly HttpContextBase _httpContext;
|
||||
|
||||
private readonly UmbracoContext _umbracoContext;
|
||||
|
||||
public HealthCheckContext(HttpContextBase httpContext, UmbracoContext umbracoContext)
|
||||
{
|
||||
if (httpContext == null) throw new ArgumentNullException("httpContext");
|
||||
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
|
||||
HttpContext = httpContext;
|
||||
UmbracoContext = umbracoContext;
|
||||
ApplicationContext = UmbracoContext.Application;
|
||||
_httpContext = httpContext;
|
||||
_umbracoContext = umbracoContext;
|
||||
ApplicationContext = _umbracoContext.Application;
|
||||
}
|
||||
|
||||
public HealthCheckContext(ApplicationContext applicationContext)
|
||||
{
|
||||
ApplicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public HttpContextBase HttpContext { get; private set; }
|
||||
public UmbracoContext UmbracoContext { get; private set; }
|
||||
public ApplicationContext ApplicationContext { get; private set; }
|
||||
|
||||
//TODO: Do we need any more info/service exposed here?
|
||||
public string SiteUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
return _httpContext != null
|
||||
? _httpContext.Request.Url.GetLeftPart(UriPartial.Authority)
|
||||
: ApplicationContext.UmbracoApplicationUrl.Replace("/umbraco", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public string ApplicationPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _httpContext != null
|
||||
? _httpContext.Request.ApplicationPath
|
||||
: "/";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace Umbraco.Web.HealthCheck
|
||||
internal class HealthCheckResolver : LazyManyObjectsResolverBase<HealthCheckResolver, HealthCheck>, IHealthCheckResolver
|
||||
{
|
||||
public HealthCheckResolver(ILogger logger, Func<IEnumerable<Type>> lazyTypeList)
|
||||
: base(new HealthCheckServiceProvider(), logger, lazyTypeList, ObjectLifetimeScope.HttpRequest)
|
||||
: base(new HealthCheckServiceProvider(), logger, lazyTypeList, ObjectLifetimeScope.Transient)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -46,9 +46,13 @@ namespace Umbraco.Web.HealthCheck
|
||||
var found = serviceType.GetConstructor(normalArgs);
|
||||
if (found != null)
|
||||
{
|
||||
var gotUmbracoContext = UmbracoContext.Current != null;
|
||||
var healthCheckContext = gotUmbracoContext
|
||||
? new HealthCheckContext(new HttpContextWrapper(HttpContext.Current), UmbracoContext.Current)
|
||||
: new HealthCheckContext(ApplicationContext.Current);
|
||||
return found.Invoke(new object[]
|
||||
{
|
||||
new HealthCheckContext(new HttpContextWrapper(HttpContext.Current), UmbracoContext.Current)
|
||||
healthCheckContext
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
86
src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs
Normal file
86
src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.HealthCheck;
|
||||
|
||||
namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
internal class HealthCheckNotifier : RecurringTaskBase
|
||||
{
|
||||
private readonly ApplicationContext _appContext;
|
||||
private readonly IHealthCheckResolver _healthCheckResolver;
|
||||
|
||||
public HealthCheckNotifier(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
|
||||
ApplicationContext appContext)
|
||||
: base(runner, delayMilliseconds, periodMilliseconds)
|
||||
{
|
||||
_appContext = appContext;
|
||||
_healthCheckResolver = HealthCheckResolver.Current;
|
||||
}
|
||||
|
||||
public override async Task<bool> PerformRunAsync(CancellationToken token)
|
||||
{
|
||||
if (_appContext == null) return true; // repeat...
|
||||
|
||||
// ensure we do not run if not main domain, but do NOT lock it
|
||||
if (_appContext.MainDom.IsMainDom == false)
|
||||
{
|
||||
LogHelper.Debug<HealthCheckNotifier>("Does not run if not MainDom.");
|
||||
return false; // do NOT repeat, going down
|
||||
}
|
||||
|
||||
using (DisposableTimer.DebugDuration<KeepAlive>(() => "Health checks executing", () => "Health checks complete"))
|
||||
{
|
||||
var healthCheckConfig = (HealthChecksSection)ConfigurationManager.GetSection("umbracoConfiguration/HealthChecks");
|
||||
|
||||
// Don't notify for any checks that are disabled, nor for any disabled
|
||||
// just for notifications
|
||||
var disabledCheckIds = healthCheckConfig.NotificationSettings.DisabledChecks
|
||||
.Select(x => x.Id)
|
||||
.Union(healthCheckConfig.DisabledChecks
|
||||
.Select(x => x.Id))
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
var checks = _healthCheckResolver.HealthChecks
|
||||
.Where(x => disabledCheckIds.Contains(x.Id) == false);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var check in checks)
|
||||
{
|
||||
// TODO: get all sub-checks, not just first
|
||||
var status = check.GetStatus().First();
|
||||
sb.AppendFormat(" - Check {0} returned {1} with message {2}.", check.Name, status.ResultType, status.Message);
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
// TODO: get email address and send
|
||||
if (!string.IsNullOrEmpty(healthCheckConfig.NotificationSettings.RecipientEmail))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// TODO: get web hook and post
|
||||
if (!string.IsNullOrEmpty(healthCheckConfig.NotificationSettings.WebhookUrl))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LogHelper.Info<HealthCheckNotifier>("Health check results:");
|
||||
LogHelper.Info<HealthCheckNotifier>(sb.ToString());
|
||||
}
|
||||
|
||||
return true; // repeat
|
||||
}
|
||||
|
||||
public override bool IsAsync
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
@@ -21,6 +23,7 @@ namespace Umbraco.Web.Scheduling
|
||||
private BackgroundTaskRunner<IBackgroundTask> _publishingRunner;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _tasksRunner;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _scrubberRunner;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _healthCheckRunner;
|
||||
private bool _started = false;
|
||||
private object _locker = new object();
|
||||
private IBackgroundTask[] _tasks;
|
||||
@@ -35,6 +38,7 @@ namespace Umbraco.Web.Scheduling
|
||||
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledPublishing", applicationContext.ProfilingLogger.Logger);
|
||||
_tasksRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledTasks", applicationContext.ProfilingLogger.Logger);
|
||||
_scrubberRunner = new BackgroundTaskRunner<IBackgroundTask>("LogScrubber", applicationContext.ProfilingLogger.Logger);
|
||||
_healthCheckRunner = new BackgroundTaskRunner<IBackgroundTask>("HealthCheckNotifier", applicationContext.ProfilingLogger.Logger);
|
||||
|
||||
//We will start the whole process when a successful request is made
|
||||
UmbracoModule.RouteAttempt += UmbracoModuleRouteAttempt;
|
||||
@@ -61,14 +65,29 @@ namespace Umbraco.Web.Scheduling
|
||||
LogHelper.Debug<Scheduler>(() => "Initializing the scheduler");
|
||||
var settings = UmbracoConfig.For.UmbracoSettings();
|
||||
|
||||
var healthCheckConfig = (HealthChecksSection)ConfigurationManager.GetSection("umbracoConfiguration/HealthChecks");
|
||||
|
||||
const int DelayMilliseconds = 60000;
|
||||
var tasks = new List<IBackgroundTask>
|
||||
{
|
||||
new KeepAlive(_keepAliveRunner, 60000, 300000, e.UmbracoContext.Application),
|
||||
new ScheduledPublishing(_publishingRunner, 60000, 60000, e.UmbracoContext.Application, settings),
|
||||
new ScheduledTasks(_tasksRunner, 60000, 60000, e.UmbracoContext.Application, settings),
|
||||
new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings), e.UmbracoContext.Application, settings)
|
||||
new KeepAlive(_keepAliveRunner, DelayMilliseconds, 300000, e.UmbracoContext.Application),
|
||||
new ScheduledPublishing(_publishingRunner, DelayMilliseconds, 60000, e.UmbracoContext.Application, settings),
|
||||
new ScheduledTasks(_tasksRunner, DelayMilliseconds, 60000, e.UmbracoContext.Application, settings),
|
||||
new LogScrubber(_scrubberRunner, DelayMilliseconds, LogScrubber.GetLogScrubbingInterval(settings), e.UmbracoContext.Application, settings),
|
||||
};
|
||||
|
||||
if (healthCheckConfig.NotificationSettings.Enabled)
|
||||
{
|
||||
var delayInMilliseconds = DateTime.Now.PeriodicMinutesFrom(healthCheckConfig.NotificationSettings.FirstRunTime) * 60 * 1000;
|
||||
if (delayInMilliseconds < DelayMilliseconds)
|
||||
{
|
||||
delayInMilliseconds = DelayMilliseconds;
|
||||
}
|
||||
|
||||
var periodInMilliseconds = healthCheckConfig.NotificationSettings.PeriodInHours * 60 * 60 * 1000;
|
||||
tasks.Add(new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, e.UmbracoContext.Application));
|
||||
}
|
||||
|
||||
// ping/keepalive
|
||||
// on all servers
|
||||
_keepAliveRunner.TryAdd(tasks[0]);
|
||||
@@ -83,6 +102,11 @@ namespace Umbraco.Web.Scheduling
|
||||
// install on all, will only run on non-slaves servers
|
||||
_scrubberRunner.TryAdd(tasks[3]);
|
||||
|
||||
if (healthCheckConfig.NotificationSettings.Enabled)
|
||||
{
|
||||
_healthCheckRunner.TryAdd(tasks[4]);
|
||||
}
|
||||
|
||||
return tasks.ToArray();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -419,6 +419,7 @@
|
||||
<Compile Include="RelatedLinksTypeConverter.cs" />
|
||||
<Compile Include="RouteDataExtensions.cs" />
|
||||
<Compile Include="Routing\ContentFinderByRedirectUrl.cs" />
|
||||
<Compile Include="Scheduling\HealthCheckNotifier.cs" />
|
||||
<Compile Include="Scheduling\LatchedBackgroundTaskBase.cs" />
|
||||
<Compile Include="Security\Identity\ExternalSignInAutoLinkOptions.cs" />
|
||||
<Compile Include="Security\Identity\FixWindowsAuthMiddlware.cs" />
|
||||
|
||||
Reference in New Issue
Block a user