File: src\Shared\WslcContainerRuntimeProbe.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 System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
 
namespace Aspire.Shared;
 
/// <summary>
/// Handles Windows-only WSLc support and its session-manager metadata protocol.
/// </summary>
internal sealed class WslcContainerRuntimeProbe() : ContainerRuntimeProbe(KnownContainerRuntimes.Wslc, "wslc", "WSLc", 2)
{
    public override bool IsSupportedHost(bool isWindows)
    {
        return isWindows;
    }
 
    protected override async Task<RuntimeVersionInfo> ReadVersionInfoAsync(bool isRunning, ILogger? logger, CancellationToken cancellationToken)
    {
        if (!isRunning)
        {
            // Doctor does not consume version metadata for a stopped WSLc session.
            return default;
        }
 
        var result = await ReadVersionOutputAsync("info --format json", async (stream, token) =>
        {
            var info = await JsonSerializer.DeserializeAsync(stream, WslcRuntimeJsonContext.Default.WslcRuntimeInfoJson, token).ConfigureAwait(false);
            return NormalizeInfo(info);
        }, logger, cancellationToken).ConfigureAwait(false);
        return result.ClientVersion is null || result.ServerVersion is null
            ? result with { Error = result.Error ?? "WSLc did not return valid client and session manager versions." }
            : result;
    }
 
    /// <summary>
    /// Normalizes WSLc info metadata into the common client/server version result.
    /// </summary>
    internal static RuntimeVersionInfo ParseInfoOutput(string? output)
    {
        if (string.IsNullOrWhiteSpace(output))
        {
            return new RuntimeVersionInfo { Error = "WSLc returned empty version metadata." };
        }
 
        try
        {
            // `wslc info --format json` reports:
            // {"Client":{"Version":"3.0.3.0"},"Server":{"SessionManagerVersion":"3.0.3"}}
            var info = JsonSerializer.Deserialize(output, WslcRuntimeJsonContext.Default.WslcRuntimeInfoJson);
            return NormalizeInfo(info);
        }
        catch (JsonException ex)
        {
            return new RuntimeVersionInfo { Error = $"WSLc returned invalid version metadata: {ex.Message}" };
        }
    }
 
    private static RuntimeVersionInfo NormalizeInfo(WslcRuntimeInfoJson? info)
    {
        var clientVersion = ParseRuntimeVersion(info?.Client?.Version, out var clientPrerelease);
        var serverVersion = ParseRuntimeVersion(info?.Server?.SessionManagerVersion, out var serverPrerelease);
 
        return new RuntimeVersionInfo(clientVersion, serverVersion, false, null)
        {
            IsClientPrerelease = clientPrerelease,
            IsServerPrerelease = serverPrerelease,
            ClientVersionText = info?.Client?.Version,
            ServerVersionText = info?.Server?.SessionManagerVersion,
            Error = clientVersion is null || serverVersion is null
                ? "WSLc did not return valid client and session manager versions."
                : null
        };
    }
}
 
internal sealed class WslcRuntimeInfoJson
{
    public WslcClientVersionJson? Client { get; set; }
    public WslcServerVersionJson? Server { get; set; }
}
 
internal sealed class WslcClientVersionJson
{
    public string? Version { get; set; }
}
 
internal sealed class WslcServerVersionJson
{
    public string? SessionManagerVersion { get; set; }
}
 
[JsonSerializable(typeof(WslcRuntimeInfoJson))]
internal sealed partial class WslcRuntimeJsonContext : JsonSerializerContext
{
}