Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Build Test Deploy / build-test (push) Has been cancelled
Build Test Deploy / authority-container (push) Has been cancelled
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
113 lines
3.6 KiB
C#
113 lines
3.6 KiB
C#
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.Extensions.Options;
|
|
using StellaOps.Scanner.WebService.Diagnostics;
|
|
using StellaOps.Scanner.WebService.Options;
|
|
|
|
namespace StellaOps.Scanner.WebService.Endpoints;
|
|
|
|
internal static class HealthEndpoints
|
|
{
|
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
|
|
|
public static void MapHealthEndpoints(this IEndpointRouteBuilder endpoints)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(endpoints);
|
|
|
|
var group = endpoints.MapGroup("/");
|
|
group.MapGet("/healthz", HandleHealth)
|
|
.WithName("scanner.health")
|
|
.Produces<HealthDocument>(StatusCodes.Status200OK)
|
|
.AllowAnonymous();
|
|
|
|
group.MapGet("/readyz", HandleReady)
|
|
.WithName("scanner.ready")
|
|
.Produces<ReadyDocument>(StatusCodes.Status200OK)
|
|
.AllowAnonymous();
|
|
}
|
|
|
|
private static IResult HandleHealth(
|
|
ServiceStatus status,
|
|
IOptions<ScannerWebServiceOptions> options,
|
|
HttpContext context)
|
|
{
|
|
ApplyNoCache(context.Response);
|
|
|
|
var snapshot = status.CreateSnapshot();
|
|
var uptimeSeconds = Math.Max((snapshot.CapturedAt - snapshot.StartedAt).TotalSeconds, 0d);
|
|
|
|
var telemetry = new TelemetrySnapshot(
|
|
Enabled: options.Value.Telemetry.Enabled,
|
|
Logging: options.Value.Telemetry.EnableLogging,
|
|
Metrics: options.Value.Telemetry.EnableMetrics,
|
|
Tracing: options.Value.Telemetry.EnableTracing);
|
|
|
|
var document = new HealthDocument(
|
|
Status: "healthy",
|
|
StartedAt: snapshot.StartedAt,
|
|
CapturedAt: snapshot.CapturedAt,
|
|
UptimeSeconds: uptimeSeconds,
|
|
Telemetry: telemetry);
|
|
|
|
return Json(document, StatusCodes.Status200OK);
|
|
}
|
|
|
|
private static async Task<IResult> HandleReady(
|
|
ServiceStatus status,
|
|
HttpContext context,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
ApplyNoCache(context.Response);
|
|
|
|
await Task.CompletedTask;
|
|
|
|
status.RecordReadyCheck(success: true, latency: TimeSpan.Zero, error: null);
|
|
var snapshot = status.CreateSnapshot();
|
|
var ready = snapshot.Ready;
|
|
|
|
var document = new ReadyDocument(
|
|
Status: ready.IsReady ? "ready" : "unready",
|
|
CheckedAt: ready.CheckedAt,
|
|
LatencyMs: ready.Latency?.TotalMilliseconds,
|
|
Error: ready.Error);
|
|
|
|
return Json(document, StatusCodes.Status200OK);
|
|
}
|
|
|
|
private static void ApplyNoCache(HttpResponse response)
|
|
{
|
|
response.Headers.CacheControl = "no-store, no-cache, max-age=0, must-revalidate";
|
|
response.Headers.Pragma = "no-cache";
|
|
response.Headers["Expires"] = "0";
|
|
}
|
|
|
|
private static IResult Json<T>(T value, int statusCode)
|
|
{
|
|
var payload = JsonSerializer.Serialize(value, JsonOptions);
|
|
return Results.Content(payload, "application/json", Encoding.UTF8, statusCode);
|
|
}
|
|
|
|
internal sealed record TelemetrySnapshot(
|
|
bool Enabled,
|
|
bool Logging,
|
|
bool Metrics,
|
|
bool Tracing);
|
|
|
|
internal sealed record HealthDocument(
|
|
string Status,
|
|
DateTimeOffset StartedAt,
|
|
DateTimeOffset CapturedAt,
|
|
double UptimeSeconds,
|
|
TelemetrySnapshot Telemetry);
|
|
|
|
internal sealed record ReadyDocument(
|
|
string Status,
|
|
DateTimeOffset CheckedAt,
|
|
double? LatencyMs,
|
|
string? Error);
|
|
}
|