-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
psp-7417 add health checks for Major pims services. #4025
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using Pims.Api.Repositories.Cdogs; | ||
|
||
namespace Pims.Api.Helpers.Healthchecks | ||
{ | ||
public class PimsCdogsHealthcheck : IHealthCheck | ||
{ | ||
private readonly IDocumentGenerationRepository _generationRepository; | ||
|
||
public PimsCdogsHealthcheck(IDocumentGenerationRepository generationRepository) | ||
{ | ||
_generationRepository = generationRepository; | ||
} | ||
|
||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
try | ||
{ | ||
|
||
var fileTypes = await _generationRepository.TryGetFileTypesAsync(); | ||
if (fileTypes.HttpStatusCode != System.Net.HttpStatusCode.OK || fileTypes.Payload == null || fileTypes.Payload.Dictionary.Count == 0) | ||
{ | ||
return new HealthCheckResult(HealthStatus.Unhealthy, $"received invalid file types response from CDOGS"); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
return new HealthCheckResult(HealthStatus.Degraded, $"Cdogs error response: {e.Message} {e.StackTrace}"); | ||
} | ||
return HealthCheckResult.Healthy(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using System; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using Pims.Core.Exceptions; | ||
|
||
namespace Pims.Api.Helpers.Healthchecks | ||
{ | ||
public class PimsExternalApiHealthcheck : IHealthCheck | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea here is that this is a generic health check that we can use for any external, unauthenticated api service. |
||
{ | ||
private readonly string url; | ||
private readonly int statusCode; | ||
|
||
public PimsExternalApiHealthcheck(string url) | ||
: this(url, statusCode: (int)HttpStatusCode.OK) | ||
{ | ||
} | ||
|
||
public PimsExternalApiHealthcheck(IConfigurationSection section) | ||
{ | ||
this.url = section["Url"]; | ||
this.statusCode = int.Parse(section["StatusCode"]); | ||
} | ||
|
||
public PimsExternalApiHealthcheck(string url, int statusCode) | ||
{ | ||
this.url = url; | ||
this.statusCode = statusCode; | ||
} | ||
|
||
public string ConnectionString { get; } | ||
|
||
public string ProbeExpectedResult { get; } | ||
|
||
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
return CheckExternalApi(context, cancellationToken); | ||
} | ||
|
||
protected async Task<HealthCheckResult> CheckExternalApi(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
using (var client = new HttpClient()) | ||
{ | ||
try | ||
{ | ||
var result = await client.GetAsync(url, cancellationToken); | ||
|
||
if ((int)result.StatusCode != statusCode) | ||
{ | ||
string errorContent = await result.Content.ReadAsStringAsync(cancellationToken); | ||
return new HealthCheckResult(HealthStatus.Degraded, $"http response: {result.StatusCode} does not match: {statusCode}. message: {errorContent}"); | ||
} | ||
} | ||
catch (HttpClientRequestException ex) | ||
{ | ||
return new HealthCheckResult(status: context.Registration.FailureStatus, exception: ex); | ||
} | ||
} | ||
|
||
return HealthCheckResult.Healthy(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using Pims.Geocoder; | ||
|
||
namespace Pims.Api.Helpers.Healthchecks | ||
{ | ||
public class PimsGeocoderHealthcheck : IHealthCheck | ||
{ | ||
private readonly IGeocoderService _geocoderService; | ||
private readonly string _address; | ||
|
||
public PimsGeocoderHealthcheck(IConfiguration configuration, IGeocoderService geocoderService) | ||
{ | ||
_geocoderService = geocoderService; | ||
_address = configuration.GetSection("HealthChecks:Geocoder:Address").Value; | ||
} | ||
|
||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
try | ||
{ | ||
var sites = await _geocoderService.GetSiteAddressesAsync(_address); | ||
if (sites == null || !sites.Features.Any()) | ||
{ | ||
return new HealthCheckResult(HealthStatus.Unhealthy, $"received invalid file types response from Geocoder"); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
return new HealthCheckResult(HealthStatus.Degraded, $"Mayan error response: {e.Message}"); | ||
} | ||
return HealthCheckResult.Healthy(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using System; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Net.Http.Headers; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using Pims.Core.Exceptions; | ||
|
||
namespace Pims.Api.Helpers.Healthchecks | ||
{ | ||
public class PimsGeoserverHealthCheck : IHealthCheck | ||
{ | ||
private readonly IConfiguration _proxyOptions; | ||
private readonly string _url; | ||
private readonly int _statusCode; | ||
|
||
public PimsGeoserverHealthCheck(IConfiguration section) | ||
{ | ||
_proxyOptions = section.GetSection("Geoserver"); | ||
_url = section.GetValue<string>("HealthChecks:Geoserver:Url"); | ||
_statusCode = section.GetValue<int>("HealthChecks:Geoserver:StatusCode"); | ||
} | ||
|
||
public PimsGeoserverHealthCheck(IConfiguration section, string url, int statusCode) | ||
{ | ||
_proxyOptions = section; | ||
_url = url; | ||
_statusCode = statusCode; | ||
} | ||
|
||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
using (var client = new HttpClient()) | ||
{ | ||
try | ||
{ | ||
var basicAuth = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes($"{_proxyOptions.GetValue<string>("ServiceUser")}:{_proxyOptions.GetValue<string>("ServicePassword")}")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will log some tech debt for this - currently all of the proxy logic is directly in the controller for geoserver - as a result the auth logic needs to be reproduced here. Would be nice to have that in a dependency injectable service so that the healthcheck can reuse the exact logic used by the endpoint (this puts this implementation at risk of drifting from the functional implementation). |
||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", basicAuth); | ||
var result = await client.GetAsync($"{_proxyOptions.GetValue<string>("ProxyUrl")}{_url}", cancellationToken); | ||
|
||
if (result.StatusCode != HttpStatusCode.OK) | ||
{ | ||
string errorContent = await result.Content.ReadAsStringAsync(cancellationToken); | ||
return new HealthCheckResult(HealthStatus.Degraded, $"http response: {result.StatusCode} does not match: {_statusCode}. message: {errorContent}"); | ||
} | ||
} | ||
catch (HttpClientRequestException ex) | ||
{ | ||
return new HealthCheckResult(status: context.Registration.FailureStatus, exception: ex); | ||
} | ||
} | ||
|
||
return HealthCheckResult.Healthy(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using Pims.Core.Exceptions; | ||
using Pims.Ltsa; | ||
|
||
namespace Pims.Api.Helpers.Healthchecks | ||
{ | ||
public class PimsLtsaHealthcheck : IHealthCheck | ||
{ | ||
private readonly int _pid; | ||
private readonly ILtsaService _ltsaService; | ||
|
||
public PimsLtsaHealthcheck(IConfiguration configuration, ILtsaService ltsaService) | ||
{ | ||
_pid = int.Parse(configuration.GetSection("HealthChecks:Ltsa:Pid").Value); | ||
_ltsaService = ltsaService; | ||
} | ||
|
||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
try | ||
{ | ||
var titleSummary = await _ltsaService.GetTitleSummariesAsync(_pid); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the lowest impact rest endpoint ltsa has - the only one that doesn't involve generating a document as a result of the request. |
||
if (titleSummary.TitleSummaries.Count == 0) | ||
{ | ||
return new HealthCheckResult(HealthStatus.Unhealthy, $"received invalid title summary response for pid: {_pid}"); | ||
} | ||
} | ||
catch(LtsaException e) | ||
{ | ||
return new HealthCheckResult(HealthStatus.Degraded, $"LTSA error response: {e.Message}"); | ||
} | ||
return HealthCheckResult.Healthy(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using Pims.Api.Repositories.Mayan; | ||
|
||
namespace Pims.Api.Helpers.Healthchecks | ||
{ | ||
public class PimsMayanHealthcheck : IHealthCheck | ||
{ | ||
private readonly IEdmsDocumentRepository _documentRepository; | ||
|
||
public PimsMayanHealthcheck(IEdmsDocumentRepository documentRepository) | ||
{ | ||
_documentRepository = documentRepository; | ||
} | ||
|
||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | ||
{ | ||
try | ||
{ | ||
var documentTypes = await _documentRepository.TryGetDocumentTypesAsync(string.Empty, 1, 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seemed like a realistic choice from the rest spec, if there is a better choice let me know. |
||
if (documentTypes.HttpStatusCode != System.Net.HttpStatusCode.OK || documentTypes.Payload == null || documentTypes.Payload.Count == 0) | ||
{ | ||
return new HealthCheckResult(HealthStatus.Unhealthy, $"received invalid mayan response for document types"); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
return new HealthCheckResult(HealthStatus.Degraded, $"Mayan error response: {e.Message}"); | ||
} | ||
return HealthCheckResult.Healthy(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,11 +7,11 @@ | |
|
||
namespace Pims.Api.Helpers.HealthChecks | ||
{ | ||
public class PimsMetricsHealthCheck : IHealthCheck | ||
public class PimsMetricsHealthcheck : IHealthCheck | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just standardizing naming. |
||
{ | ||
private static readonly Gauge AppDeploymentInfo = Metrics.CreateGauge("api_deployment_info", "Deployment information of the running PSP application", labelNames: new[] { "app_version", "db_version", "runtime_version" }); | ||
|
||
public PimsMetricsHealthCheck(string connectionString) | ||
public PimsMetricsHealthcheck(string connectionString) | ||
{ | ||
ConnectionString = connectionString; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm focusing on real, but low overhead methods for these health checks.