File: Health\ResourceHealthCheckScheduler.cs
Web Access
Project: src\src\Aspire.Hosting\Aspire.Hosting.csproj (Aspire.Hosting)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.Hosting.ApplicationModel;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
 
namespace Aspire.Hosting.Health;
 
internal class ResourceHealthCheckScheduler : BackgroundService
{
    private readonly ResourceNotificationService _resourceNotificationService;
    private readonly DistributedApplicationModel _model;
    private readonly Dictionary<string, bool> _checkEnablement = new();
 
    public ResourceHealthCheckScheduler(ResourceNotificationService resourceNotificationService, DistributedApplicationModel model)
    {
        _resourceNotificationService = resourceNotificationService ?? throw new ArgumentNullException(nameof(resourceNotificationService));
        _model = model ?? throw new ArgumentNullException(nameof(model));
 
        foreach (var resource in model.Resources)
        {
            UpdateCheckEnablement(resource, false);
        }
 
        Predicate = (check) =>
        {
            return _checkEnablement.TryGetValue(check.Name, out var enabled) ? enabled : false;
        };
    }
 
    public Func<HealthCheckRegistration, bool> Predicate { get; init; }
 
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            var resourceEvents = _resourceNotificationService.WatchAsync(stoppingToken);
 
            await foreach (var resourceEvent in resourceEvents.ConfigureAwait(false))
            {
                if (resourceEvent.Snapshot.State?.Text == KnownResourceStates.Running)
                {
                    // Each time we receive an event that tells us that the resource is
                    // running we need to enable the health check annotation.
                    UpdateCheckEnablement(resourceEvent.Resource, true);
                }
            }
        }
        catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
        {
            // This was expected as the token was canceled
        }
    }
 
    private void UpdateCheckEnablement(IResource resource, bool enabled)
    {
        if (resource.TryGetAnnotationsOfType<HealthCheckAnnotation>(out var annotations))
        {
            foreach (var annotation in annotations)
            {
                _checkEnablement[annotation.Key] = enabled;
            }
        }
    }
}