-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #607 from microsoft/maxdac/feature/add-endpoint-he…
…alth-check-extensions [Health Check] Added default Endpoint HTTP Health Check
- Loading branch information
Showing
7 changed files
with
307 additions
and
5 deletions.
There are no files selected for viewing
52 changes: 52 additions & 0 deletions
52
src/Diagnostics.HealthChecks.AspNetCore/EndpointLivenessHealthCheck.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT license. | ||
|
||
using System; | ||
using System.Diagnostics; | ||
using System.Net.Http; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Omex.Extensions.Abstractions; | ||
using Microsoft.Omex.Extensions.Diagnostics.HealthChecks.Composables; | ||
|
||
namespace Microsoft.Omex.Extensions.Diagnostics.HealthChecks.AspNetCore; | ||
|
||
/// <summary> | ||
/// This health check will perform a liveness call to the endpoint identified by the given parameters. | ||
/// </summary> | ||
internal class EndpointLivenessHealthCheck : IHealthCheck | ||
{ | ||
private readonly EndpointLivenessHealthCheckParameters m_parameters; | ||
private readonly IHealthCheck m_healthCheck; | ||
|
||
/// <summary> | ||
/// Constructor. | ||
/// </summary> | ||
public EndpointLivenessHealthCheck( | ||
IHttpClientFactory httpClientFactory, | ||
ActivitySource activitySource, | ||
ILogger<EndpointLivenessHealthCheck> logger, | ||
EndpointLivenessHealthCheckParameters parameters) | ||
{ | ||
m_parameters = parameters; | ||
m_healthCheck = HealthCheckComposablesExtensions.CreateLivenessHttpHealthCheck( | ||
CreateHttpRequestMessage, | ||
activitySource, | ||
httpClientFactory, | ||
httpClientName: parameters.HttpClientLogicalName) | ||
.AsObservableHealthCheck(activitySource, logger, parameters: parameters); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) => | ||
m_healthCheck.CheckHealthAsync(context, cancellationToken); | ||
|
||
private HttpRequestMessage CreateHttpRequestMessage() | ||
{ | ||
int port = SfConfigurationProvider.GetEndpointPort(m_parameters.EndpointName); | ||
UriBuilder uriBuilder = new(Uri.UriSchemeHttp, m_parameters.Host, port, m_parameters.EndpointRelativeUri); | ||
return new HttpRequestMessage(HttpMethod.Get, uriBuilder.Uri); | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/Diagnostics.HealthChecks.AspNetCore/EndpointLivenessHealthCheckParameters.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.Net.Http; | ||
|
||
namespace Microsoft.Omex.Extensions.Diagnostics.HealthChecks.AspNetCore; | ||
|
||
/// <summary> | ||
/// Expands the <see cref="HealthCheckParameters"/> class wih additional parameters required by the | ||
/// <see cref="EndpointLivenessHealthCheck"/> class. | ||
/// </summary> | ||
public class EndpointLivenessHealthCheckParameters : HealthCheckParameters | ||
{ | ||
/// <summary> | ||
/// The Service Fabric endpoint name, it will be used to fetch the service port. | ||
/// </summary> | ||
public string EndpointName { get; } | ||
|
||
/// <summary> | ||
/// The endpoint relative url at which the Service Fabric health check will be reachable. | ||
/// </summary> | ||
public string EndpointRelativeUri { get; } | ||
|
||
/// <summary> | ||
/// The name of the <seealso cref="HttpClient"/> that will be used to create the instance used | ||
/// by the health check from <seealso cref="IHttpClientFactory"/>. | ||
/// </summary> | ||
public string HttpClientLogicalName { get; } | ||
|
||
/// <summary> | ||
/// The name of the service host that will be used to perform the health check HTTP call, | ||
/// usually equal to `localhost`. | ||
/// </summary> | ||
public string Host { get; } | ||
|
||
/// <summary> | ||
/// Constructor. | ||
/// </summary> | ||
/// <param name="endpointName">The Service Fabric endpoint name.</param> | ||
/// <param name="httpClientLogicalName">The name of the <seealso cref="HttpClient"/> defined in the consumer service DI.</param> | ||
/// <param name="endpointRelativeUrl"> | ||
/// The endpoint relative url at which the Service Fabric health check will be reachable. | ||
/// </param> | ||
/// <param name="host">The host used to perform the health check HTTP call to the service.</param> | ||
/// <param name="reportData">The report data.</param> | ||
public EndpointLivenessHealthCheckParameters( | ||
string endpointName, | ||
string httpClientLogicalName, | ||
string endpointRelativeUrl, | ||
string host = "localhost", | ||
params KeyValuePair<string, object>[] reportData) | ||
: base(reportData) | ||
{ | ||
EndpointName = endpointName; | ||
HttpClientLogicalName = httpClientLogicalName; | ||
EndpointRelativeUri = endpointRelativeUrl; | ||
Host = host; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
tests/Diagnostics.HealthChecks.AspNetCore.UnitTests/HealthzEndpointHealthCheck.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT license. | ||
|
||
using System; | ||
using System.Diagnostics; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Logging.Abstractions; | ||
using Microsoft.Omex.Extensions.Diagnostics.HealthChecks.AspNetCore; | ||
using Microsoft.Omex.Extensions.Diagnostics.HealthChecks.UnitTests.Composables; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using Moq; | ||
|
||
namespace Microsoft.OMEX.ClusterDiagnostics.Service.Tests.HealthChecks; | ||
|
||
[TestClass] | ||
public class HealthzEndpointHealthCheckTests | ||
{ | ||
private static ILogger<T> GetLogger<T>() => new NullLogger<T>(); | ||
|
||
private const string RelativeUri = "/healthz"; | ||
private const string HttpClientLogicalName = "HealthzEndpointHttpHealthCheckHttpClient"; | ||
|
||
[TestInitialize] | ||
public void Setup() => Environment.SetEnvironmentVariable($"Fabric_Endpoint_{nameof(EndpointLivenessHealthCheck)}", "1234"); | ||
|
||
[TestMethod] | ||
[DataRow(HttpStatusCode.OK, HealthStatus.Healthy)] | ||
[DataRow(HttpStatusCode.InternalServerError, HealthStatus.Unhealthy)] | ||
[DataRow(HttpStatusCode.NotFound, HealthStatus.Unhealthy)] | ||
[DataRow(HttpStatusCode.Unauthorized, HealthStatus.Unhealthy)] | ||
[DataRow(HttpStatusCode.Forbidden, HealthStatus.Unhealthy)] | ||
public async Task HealthzEndpointHttpHealthCheck_ReturnsExpectedStatus(HttpStatusCode returnedStatusCode, HealthStatus expectedHealthStatus) | ||
{ | ||
EndpointLivenessHealthCheckParameters healthCheckParameters = new( | ||
nameof(EndpointLivenessHealthCheck), | ||
$"{nameof(EndpointLivenessHealthCheck)}_HttpClient", | ||
"healthz"); | ||
|
||
HealthCheckTestHelpers.SetLocalServiceInfo(); | ||
Mock<IHttpClientFactory> clientFactory = HealthCheckTestHelpers.GetHttpClientFactoryMock( | ||
HealthCheckTestHelpers.GetHttpResponseMessageMock(returnedStatusCode, message: string.Empty)); | ||
|
||
ActivitySource activitySourceMock = new(nameof(EndpointLivenessHealthCheck)); | ||
|
||
IHealthCheck healthCheck = new EndpointLivenessHealthCheck( | ||
clientFactory.Object, | ||
activitySourceMock, | ||
GetLogger<EndpointLivenessHealthCheck>(), | ||
healthCheckParameters); | ||
|
||
CancellationTokenSource source = new(); | ||
|
||
HealthCheckResult healthCheckResult = await healthCheck.CheckHealthAsync( | ||
HealthCheckTestHelpers.GetHealthCheckContext(healthCheck), | ||
source.Token); | ||
|
||
Assert.AreEqual(expectedHealthStatus, healthCheckResult.Status); | ||
} | ||
|
||
[TestMethod] | ||
[DataRow(HttpStatusCode.OK, HealthStatus.Healthy)] | ||
[DataRow(HttpStatusCode.InternalServerError, HealthStatus.Unhealthy)] | ||
[DataRow(HttpStatusCode.NotFound, HealthStatus.Unhealthy)] | ||
[DataRow(HttpStatusCode.Unauthorized, HealthStatus.Unhealthy)] | ||
[DataRow(HttpStatusCode.Forbidden, HealthStatus.Unhealthy)] | ||
public async Task HealthzEndpointHttpHealthCheck_ReturnsCorrectErrorMessageAndException(HttpStatusCode returnedStatusCode, HealthStatus expectedHealthStatus) | ||
{ | ||
EndpointLivenessHealthCheckParameters healthCheckParameters = new( | ||
nameof(EndpointLivenessHealthCheck), | ||
$"{nameof(EndpointLivenessHealthCheck)}_HttpClient", | ||
"healthz"); | ||
|
||
HealthCheckTestHelpers.SetLocalServiceInfo(); | ||
Mock<IHttpClientFactory> clientFactory = HealthCheckTestHelpers.GetHttpClientFactoryMock( | ||
HealthCheckTestHelpers.GetHttpResponseMessageMock(returnedStatusCode, message: string.Empty), | ||
shouldThrowException: true); | ||
|
||
ActivitySource activitySourceMock = new(nameof(EndpointLivenessHealthCheck)); | ||
|
||
IHealthCheck healthCheck = new EndpointLivenessHealthCheck( | ||
clientFactory.Object, | ||
activitySourceMock, | ||
GetLogger<EndpointLivenessHealthCheck>(), | ||
healthCheckParameters); | ||
|
||
CancellationTokenSource source = new(); | ||
|
||
HealthCheckResult healthCheckResult = await healthCheck.CheckHealthAsync( | ||
HealthCheckTestHelpers.GetHealthCheckContext(healthCheck), | ||
source.Token); | ||
|
||
Assert.AreEqual(expectedHealthStatus, healthCheckResult.Status); | ||
|
||
if (expectedHealthStatus != HealthStatus.Healthy) | ||
{ | ||
Assert.IsTrue(healthCheckResult.Description?.Contains(returnedStatusCode.ToString(), StringComparison.InvariantCulture)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters