File: Utils\EnvironmentChecker\DcpConnectionHealthCheck.cs
Web Access
Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Globalization;
using Aspire.Cli.Layout;
using Aspire.Cli.Resources;
using Aspire.Shared;
using Microsoft.Extensions.Logging;
 
namespace Aspire.Cli.Utils.EnvironmentChecker;
 
/// <summary>
/// Checks whether the bundled Developer Control Plane can be reached with its generated kubeconfig.
/// </summary>
internal sealed class DcpConnectionHealthCheck(
    ILayoutDiscovery layoutDiscovery,
    IDcpConnectionChecker connectionTester,
    CliExecutionContext executionContext,
    ILogger<DcpConnectionHealthCheck> logger) : IEnvironmentCheck
{
    internal const string BundleCheckName = "dcp-bundle";
    internal const string ConnectionCheckName = "dcp-connection";
    internal const string EphemeralCertificateCheckName = "dcp-ephemeral-certificate";
    internal const string DeveloperCertificateCheckName = "dcp-developer-certificate";
 
    public int Order => 45; // DCP process checks are more expensive than local prerequisite probes.
 
    public async Task<IReadOnlyList<EnvironmentCheckResult>> CheckAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            var dcpDirectory = layoutDiscovery.GetComponentPath(LayoutComponent.Dcp, executionContext.WorkingDirectory.FullName);
            if (string.IsNullOrWhiteSpace(dcpDirectory))
            {
                logger.LogDebug("Skipping DCP connection health checks because no Aspire bundle layout was discovered.");
                return [new EnvironmentCheckResult
                {
                    Category = EnvironmentCheckCategories.Aspire,
                    Name = BundleCheckName,
                    Status = EnvironmentCheckStatus.Warning,
                    Message = DoctorCommandStrings.DcpBundleNotFoundMessage,
                    Details = DoctorCommandStrings.DcpBundleNotFoundDetails
                }];
            }
 
            var dcpExecutablePath = BundleDiscovery.GetDcpExecutablePath(dcpDirectory);
            if (!File.Exists(dcpExecutablePath))
            {
                return [new EnvironmentCheckResult
                {
                    Category = EnvironmentCheckCategories.Aspire,
                    Name = BundleCheckName,
                    Status = EnvironmentCheckStatus.Fail,
                    Message = DoctorCommandStrings.DcpExecutableNotFoundMessage,
                    Details = string.Format(CultureInfo.CurrentCulture, DoctorCommandStrings.DcpExecutableNotFoundDetailsFormat, dcpExecutablePath)
                }];
            }
 
            var ephemeralCertificateTask = connectionTester.TestConnectionAsync(
                dcpDirectory,
                useDeveloperCertificate: false,
                cancellationToken);
 
            var developerCertificateTask = connectionTester.TestConnectionAsync(
                dcpDirectory,
                useDeveloperCertificate: true,
                cancellationToken);
 
            var results = await Task.WhenAll(ephemeralCertificateTask, developerCertificateTask).ConfigureAwait(false);
            if (results.All(result => result.Status == EnvironmentCheckStatus.Pass))
            {
                return [new EnvironmentCheckResult
                {
                    Category = EnvironmentCheckCategories.Aspire,
                    Name = ConnectionCheckName,
                    Status = EnvironmentCheckStatus.Pass,
                    Message = DoctorCommandStrings.DcpConnectionSucceededMessage
                }];
            }
 
            return results;
        }
        catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
        {
            throw;
        }
        catch (Exception ex)
        {
            logger.LogDebug(ex, "Error checking DCP connection health.");
            return [new EnvironmentCheckResult
            {
                Category = EnvironmentCheckCategories.Aspire,
                Name = ConnectionCheckName,
                Status = EnvironmentCheckStatus.Fail,
                Message = DoctorCommandStrings.DcpConnectionCheckFailedMessage,
                Details = ex.Message
            }];
        }
    }
 
}