Merge branch 'feature/health-check-scheduler' of https://github.com/AndyButland/Umbraco-CMS into temp-U4-9984

This commit is contained in:
Jeavon Leopold
2017-06-05 09:26:39 +02:00
25 changed files with 476 additions and 42 deletions

View File

@@ -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]));
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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]));
}
}
}
}

View File

@@ -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])); }
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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" />

View 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);
}
}
}

View File

@@ -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" />

View File

@@ -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>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<HealthChecks>
</HealthChecks>

View 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>

View File

@@ -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 != '')" />

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>();

View File

@@ -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>();

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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; }

View File

@@ -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
: "/";
}
}
}
}

View File

@@ -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
});
}

View 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; }
}
}
}

View File

@@ -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();
});
}

View File

@@ -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" />