From 45939ff75dbddd72ac75e3d7bb85fa63b23a593f Mon Sep 17 00:00:00 2001 From: cairnsj <51908793+cairnsj@users.noreply.github.com> Date: Wed, 4 Oct 2023 08:28:36 +0100 Subject: [PATCH 1/2] Enable health checks and add health check around the configuration --- .../ConfigurationHealthCheckTests.cs | 152 ++++++++++++++++++ .../ConfigurationHealthCheck.cs | 52 ++++++ Childrens-Social-Care-CPD/Program.cs | 2 + .../WebApplicationBuilderExtensions.cs | 2 + 4 files changed, 208 insertions(+) create mode 100644 Childrens-Social-Care-CPD-Tests/ConfigurationHealthCheckTests.cs create mode 100644 Childrens-Social-Care-CPD/ConfigurationHealthCheck.cs diff --git a/Childrens-Social-Care-CPD-Tests/ConfigurationHealthCheckTests.cs b/Childrens-Social-Care-CPD-Tests/ConfigurationHealthCheckTests.cs new file mode 100644 index 00000000..d7d1c115 --- /dev/null +++ b/Childrens-Social-Care-CPD-Tests/ConfigurationHealthCheckTests.cs @@ -0,0 +1,152 @@ +using Castle.Core.Logging; +using Childrens_Social_Care_CPD; +using Childrens_Social_Care_CPD.Configuration; +using FluentAssertions; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using NSubstitute; +using NSubstitute.Extensions; +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Childrens_Social_Care_CPD_Tests; + +public class ConfigurationHealthCheckTests +{ + private ILogger _logger; + private IApplicationConfiguration _applicationConfiguration; + + [SetUp] + public void Setup() + { + _logger = Substitute.For>(); + _applicationConfiguration = Substitute.For(); + _applicationConfiguration.ReturnsForAll("foo"); + } + + [Test] + public async Task Fails_When_Disable_Cookies_Is_True() + { + // arrange + _applicationConfiguration.DisableSecureCookies.Returns(true); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } + + [Test] + public async Task Fails_When_AppInsightsConnectionString_Is_Not_Set() + { + // arrange + _applicationConfiguration.AppInsightsConnectionString.Returns(string.Empty); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } + + [Test] + public async Task Fails_When_AppVersion_Is_Not_Set() + { + // arrange + _applicationConfiguration.AppVersion.Returns(string.Empty); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } + + [Test] + public async Task Fails_When_AzureEnvironment_Is_Not_Set() + { + // arrange + _applicationConfiguration.AzureEnvironment.Returns(string.Empty); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } + + [Test] + public async Task Fails_When_ClarityProjectId_Is_Not_Set() + { + // arrange + _applicationConfiguration.ClarityProjectId.Returns(string.Empty); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } + + [Test] + public async Task Fails_When_ContentfulDeliveryApiKey_Is_Not_Set() + { + // arrange + _applicationConfiguration.ContentfulDeliveryApiKey.Returns(string.Empty); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } + + [Test] + public async Task Fails_When_ContentfulEnvironment_Is_Not_Set() + { + // arrange + _applicationConfiguration.ContentfulEnvironment.Returns(string.Empty); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } + + [Test] + public async Task Fails_When_ContentfulSpaceId_Is_Not_Set() + { + // arrange + _applicationConfiguration.ContentfulSpaceId.Returns(string.Empty); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } + + [Test] + public async Task Fails_When_GoogleTagManagerKey_Is_Not_Set() + { + // arrange + _applicationConfiguration.GoogleTagManagerKey.Returns(string.Empty); + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Unhealthy); + } +} diff --git a/Childrens-Social-Care-CPD/ConfigurationHealthCheck.cs b/Childrens-Social-Care-CPD/ConfigurationHealthCheck.cs new file mode 100644 index 00000000..a6401d76 --- /dev/null +++ b/Childrens-Social-Care-CPD/ConfigurationHealthCheck.cs @@ -0,0 +1,52 @@ +using Childrens_Social_Care_CPD.Configuration; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Childrens_Social_Care_CPD; + +public class ConfigurationHealthCheck : IHealthCheck +{ + private readonly ILogger _logger; + private readonly IApplicationConfiguration _applicationConfiguration; + + public ConfigurationHealthCheck(ILogger logger, IApplicationConfiguration applicationConfiguration) + { + _logger = logger; + _applicationConfiguration = applicationConfiguration; + } + + private bool CheckSetting(string name, string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + _logger.LogError("Configuration setting {propertyName} does not have a value", name); + return false; + } + + return true; + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + var healthy = CheckSetting("AppInsightsConnectionString", _applicationConfiguration.AppInsightsConnectionString) + && CheckSetting("AppVersion", _applicationConfiguration.AppVersion) + && CheckSetting("AzureEnvironment", _applicationConfiguration.AzureEnvironment) + && CheckSetting("ClarityProjectId", _applicationConfiguration.ClarityProjectId) + && CheckSetting("ContentfulDeliveryApiKey", _applicationConfiguration.ContentfulDeliveryApiKey) + && CheckSetting("ContentfulEnvironment", _applicationConfiguration.ContentfulEnvironment) + && CheckSetting("ContentfulSpaceId", _applicationConfiguration.ContentfulSpaceId) + && CheckSetting("GoogleTagManagerKey", _applicationConfiguration.GoogleTagManagerKey); + + if (_applicationConfiguration.DisableSecureCookies) + { + _logger.LogError("DisableSecureCookies should not be enabled for standard environments"); + healthy = false; + } + + if (!healthy) + { + return Task.FromResult(HealthCheckResult.Unhealthy("One or more application configuration settings is missing or incorrect")); + } + + return Task.FromResult(HealthCheckResult.Healthy("Application configuration is OK")); + } +} \ No newline at end of file diff --git a/Childrens-Social-Care-CPD/Program.cs b/Childrens-Social-Care-CPD/Program.cs index 559b699b..27f0cd3f 100644 --- a/Childrens-Social-Care-CPD/Program.cs +++ b/Childrens-Social-Care-CPD/Program.cs @@ -24,6 +24,8 @@ app.MapControllerRoute( name: "default", pattern: "{controller=Content}/{action=Index}"); +app.MapHealthChecks("application/status"); + app.Run(); diff --git a/Childrens-Social-Care-CPD/WebApplicationBuilderExtensions.cs b/Childrens-Social-Care-CPD/WebApplicationBuilderExtensions.cs index 31fef763..6975fb9d 100644 --- a/Childrens-Social-Care-CPD/WebApplicationBuilderExtensions.cs +++ b/Childrens-Social-Care-CPD/WebApplicationBuilderExtensions.cs @@ -74,5 +74,7 @@ public static void AddFeatures(this WebApplicationBuilder builder) options.Rules.Insert(0, new LoggerFilterRule(typeof(ApplicationInsightsLoggerProvider).FullName, null, LogLevel.Information, null)); } }); + + builder.Services.AddHealthChecks().AddCheck("Configuration Health Check", tags: new[] {"configuration"}); } } From 41084001eab16e29ad79db25c018b6fdc5fc47a4 Mon Sep 17 00:00:00 2001 From: cairnsj <51908793+cairnsj@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:08:19 +0100 Subject: [PATCH 2/2] Update ConfigurationHealthCheckTests.cs --- .../ConfigurationHealthCheckTests.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Childrens-Social-Care-CPD-Tests/ConfigurationHealthCheckTests.cs b/Childrens-Social-Care-CPD-Tests/ConfigurationHealthCheckTests.cs index d7d1c115..6caca9c8 100644 --- a/Childrens-Social-Care-CPD-Tests/ConfigurationHealthCheckTests.cs +++ b/Childrens-Social-Care-CPD-Tests/ConfigurationHealthCheckTests.cs @@ -24,6 +24,19 @@ public void Setup() _applicationConfiguration.ReturnsForAll("foo"); } + [Test] + public async Task Passes_When_All_Values_Set_And_Cookies_Are_Secured() + { + // arrange + var sut = new ConfigurationHealthCheck(_logger, _applicationConfiguration); + + // act + var result = await sut.CheckHealthAsync(null, default); + + // assert + result.Status.Should().Be(HealthStatus.Healthy); + } + [Test] public async Task Fails_When_Disable_Cookies_Is_True() {