From 0e1af05ef96e625b6d704b9bd3456d5585faf702 Mon Sep 17 00:00:00 2001 From: Tom Kerkhove Date: Thu, 25 Jul 2019 21:39:18 +0200 Subject: [PATCH] Provide more information in health check (#635) * Provide more information in health check * Code styling --- .../Controllers/v1/HealthController.cs | 17 ++++- src/Promitor.Scraper.Host/Docs/Open-Api.xml | 19 +++-- .../IServiceCollectionExtensions.cs | 70 ++++++++++++------- src/Promitor.Scraper.Host/Startup.cs | 20 +++--- 4 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/Promitor.Scraper.Host/Controllers/v1/HealthController.cs b/src/Promitor.Scraper.Host/Controllers/v1/HealthController.cs index 655b1ce74..4e4f959c4 100644 --- a/src/Promitor.Scraper.Host/Controllers/v1/HealthController.cs +++ b/src/Promitor.Scraper.Host/Controllers/v1/HealthController.cs @@ -1,5 +1,7 @@ using System.Net; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Swashbuckle.AspNetCore.Annotations; namespace Promitor.Scraper.Host.Controllers.v1 @@ -7,17 +9,26 @@ namespace Promitor.Scraper.Host.Controllers.v1 [Route("api/v1/health")] public class HealthController : Controller { + private readonly HealthCheckService _healthCheckService; + + public HealthController(HealthCheckService healthCheckService) + { + _healthCheckService = healthCheckService; + } + /// /// Get Health /// /// Provides an indication about the health of the scraper [HttpGet] [SwaggerOperation(OperationId = "Health_Get")] - [SwaggerResponse((int)HttpStatusCode.OK, Description = "Scraper is healthy")] + [SwaggerResponse((int)HttpStatusCode.OK, Description = "Scraper is healthy", Type = typeof(HealthReport))] [SwaggerResponse((int)HttpStatusCode.ServiceUnavailable, Description = "Scraper is not healthy")] - public IActionResult Get() + public async Task Get() { - return Ok(); + var report = await _healthCheckService.CheckHealthAsync(); + + return report.Status == HealthStatus.Healthy ? Ok(report) : StatusCode((int)HttpStatusCode.ServiceUnavailable, report); } } } \ No newline at end of file diff --git a/src/Promitor.Scraper.Host/Docs/Open-Api.xml b/src/Promitor.Scraper.Host/Docs/Open-Api.xml index 0fc672183..c45b25466 100644 --- a/src/Promitor.Scraper.Host/Docs/Open-Api.xml +++ b/src/Promitor.Scraper.Host/Docs/Open-Api.xml @@ -35,19 +35,26 @@ Collections of services in application - + - Expose services as Web API + Defines the dependencies that Promitor requires + Collections of services in application - + - Inject configuration + Use health checks + Collections of services in application - + - Inject dependencies + Expose services as Web API + + + + + Inject configuration diff --git a/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs b/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs index 7af5865b0..e6d8b9a9e 100644 --- a/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs +++ b/src/Promitor.Scraper.Host/Extensions/IServiceCollectionExtensions.cs @@ -4,7 +4,10 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Logging; +using Promitor.Core.Scraping.Configuration.Providers; +using Promitor.Core.Scraping.Configuration.Providers.Interfaces; using Promitor.Core.Configuration.FeatureFlags; using Promitor.Core.Configuration.Model.FeatureFlags; using Promitor.Core.Configuration.Model.Metrics; @@ -12,8 +15,6 @@ using Promitor.Core.Configuration.Model.Server; using Promitor.Core.Configuration.Model.Telemetry; using Promitor.Core.Configuration.Model.Telemetry.Sinks; -using Promitor.Core.Scraping.Configuration.Providers; -using Promitor.Core.Scraping.Configuration.Providers.Interfaces; using Promitor.Core.Scraping.Factories; using Promitor.Core.Telemetry; using Promitor.Core.Telemetry.Interfaces; @@ -34,14 +35,13 @@ public static class IServiceCollectionExtensions /// Defines to use the cron scheduler /// /// Collections of services in application - public static void ScheduleMetricScraping(this IServiceCollection services) + public static IServiceCollection ScheduleMetricScraping(this IServiceCollection services) { var spToCreateJobsWith = services.BuildServiceProvider(); var metricsProvider = spToCreateJobsWith.GetService(); - var metrics = metricsProvider.Get(applyDefaults: true); + var metrics = metricsProvider.Get(true); foreach (var metric in metrics.Metrics) - { services.AddScheduler(builder => { builder.AddJob(serviceProvider => new MetricScrapingJob(metric, @@ -52,13 +52,44 @@ public static void ScheduleMetricScraping(this IServiceCollection services) serviceProvider.GetService())); builder.UnobservedTaskExceptionHandler = (sender, exceptionEventArgs) => UnobservedJobHandlerHandler(sender, exceptionEventArgs, services); }); - } + + return services; + } + + /// + /// Defines the dependencies that Promitor requires + /// + /// Collections of services in application + public static IServiceCollection DefineDependencies(this IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + return services; + } + + /// + /// Use health checks + /// + /// Collections of services in application + public static IServiceCollection UseHealthChecks(this IServiceCollection services) + { + services.AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy()); + + return services; } /// /// Expose services as Web API /// - public static void UseWebApi(this IServiceCollection services) + public static IServiceCollection UseWebApi(this IServiceCollection services) { services.AddMvc() .AddJsonOptions(jsonOptions => @@ -66,12 +97,14 @@ public static void UseWebApi(this IServiceCollection services) jsonOptions.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); jsonOptions.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; }); + + return services; } /// /// Inject configuration /// - public static void InjectConfiguration(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection ConfigureYamlConfiguration(this IServiceCollection services, IConfiguration configuration) { services.Configure(configuration.GetSection("featureFlags")); services.Configure(configuration.GetSection("metricsConfiguration")); @@ -81,21 +114,8 @@ public static void InjectConfiguration(this IServiceCollection services, IConfig services.Configure(configuration.GetSection("server")); services.Configure(configuration.GetSection("prometheus")); services.Configure(configuration.GetSection("prometheus:scrapeEndpoint")); - } - /// - /// Inject dependencies - /// - public static void InjectDependencies(this IServiceCollection services) - { - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + return services; } /// @@ -104,7 +124,7 @@ public static void InjectDependencies(this IServiceCollection services) /// Collections of services in application /// Endpoint where the prometheus scraping is exposed /// Version of the API - public static void UseOpenApiSpecifications(this IServiceCollection services, string prometheusScrapeEndpointPath, int apiVersion) + public static IServiceCollection UseOpenApiSpecifications(this IServiceCollection services, string prometheusScrapeEndpointPath, int apiVersion) { var openApiInformation = new Info { @@ -136,6 +156,8 @@ public static void UseOpenApiSpecifications(this IServiceCollection services, st swaggerGenerationOptions.IncludeXmlComments(xmlDocumentationPath); } }); + + return services; } private static string GetXmlDocumentationPath(IServiceCollection services) @@ -162,4 +184,4 @@ private static void UnobservedJobHandlerHandler(object sender, UnobservedTaskExc e.SetObserved(); } } -} +} \ No newline at end of file diff --git a/src/Promitor.Scraper.Host/Startup.cs b/src/Promitor.Scraper.Host/Startup.cs index 234b4cd9d..1b11350bc 100644 --- a/src/Promitor.Scraper.Host/Startup.cs +++ b/src/Promitor.Scraper.Host/Startup.cs @@ -10,6 +10,9 @@ namespace Promitor.Scraper.Host { public class Startup { + private readonly IConfiguration _configuration; + private readonly string _prometheusBaseUriPath; + public Startup(IConfiguration configuration) { _configuration = configuration; @@ -18,16 +21,11 @@ public Startup(IConfiguration configuration) _prometheusBaseUriPath = scrapeEndpointConfiguration.BaseUriPath; } - private readonly IConfiguration _configuration; - private readonly string _prometheusBaseUriPath; - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) - { app.UseDeveloperExceptionPage(); - } ValidateRuntimeConfiguration(app); @@ -39,12 +37,12 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.InjectConfiguration(_configuration); - services.InjectDependencies(); - - services.UseWebApi(); - services.UseOpenApiSpecifications(_prometheusBaseUriPath, apiVersion: 1); - services.ScheduleMetricScraping(); + services.DefineDependencies() + .ConfigureYamlConfiguration(_configuration) + .UseWebApi() + .UseOpenApiSpecifications(_prometheusBaseUriPath, 1) + .UseHealthChecks() + .ScheduleMetricScraping(); } private void ValidateRuntimeConfiguration(IApplicationBuilder app)