Skip to content
6 changes: 3 additions & 3 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"sdk": {
"version": "9.0.200",
"rollForward": "major",
"version": "8.0.115",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert global.json

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reverted global.json back to its original state. Commit: e96b811

"rollForward": "latestMajor",
"allowPrerelease": true
},
"tools": {
"dotnet": "9.0.200",
"dotnet": "8.0.115",
"runtimes": {
"dotnet/x64": [
"$(DotNetRuntimePreviousVersionForTesting)"
Expand Down
6 changes: 3 additions & 3 deletions src/Aspire.Cli/Backchannel/AppHostBackchannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal interface IAppHostBackchannel
Task<long> PingAsync(long timestamp, CancellationToken cancellationToken);
Task RequestStopAsync(CancellationToken cancellationToken);
Task<(string BaseUrlWithLoginToken, string? CodespacesUrlWithLoginToken)> GetDashboardUrlsAsync(CancellationToken cancellationToken);
IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints)> GetResourceStatesAsync(CancellationToken cancellationToken);
IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints, string? Health)> GetResourceStatesAsync(CancellationToken cancellationToken);
Task ConnectAsync(string socketPath, CancellationToken cancellationToken);
IAsyncEnumerable<(string Id, string StatusText, bool IsComplete, bool IsError)> GetPublishingActivitiesAsync(CancellationToken cancellationToken);
Task<string[]> GetCapabilitiesAsync(CancellationToken cancellationToken);
Expand Down Expand Up @@ -77,15 +77,15 @@ await rpc.InvokeWithCancellationAsync(
return url;
}

public async IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints)> GetResourceStatesAsync([EnumeratorCancellation]CancellationToken cancellationToken)
public async IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints, string? Health)> GetResourceStatesAsync([EnumeratorCancellation]CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity();

var rpc = await _rpcTaskCompletionSource.Task;

logger.LogDebug("Requesting resource states");

var resourceStates = await rpc.InvokeWithCancellationAsync<IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints)>>(
var resourceStates = await rpc.InvokeWithCancellationAsync<IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints, string? Health)>>(
"GetResourceStatesAsync",
Array.Empty<object>(),
cancellationToken);
Expand Down
14 changes: 12 additions & 2 deletions src/Aspire.Cli/Commands/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,12 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell

await _ansiConsole.Live(rows).StartAsync(async context =>
{
var knownResources = new SortedDictionary<string, (string Resource, string Type, string State, string[] Endpoints)>();
var knownResources = new SortedDictionary<string, (string Resource, string Type, string State, string[] Endpoints, string? Health)>();

table.AddColumn("Resource");
table.AddColumn("Type");
table.AddColumn("State");
table.AddColumn("Health");
table.AddColumn("Endpoint(s)");

var resourceStates = backchannel.GetResourceStatesAsync(cancellationToken);
Expand Down Expand Up @@ -210,6 +211,15 @@ await _ansiConsole.Live(rows).StartAsync(async context =>
_ => new Text(knownResource.Value.State ?? "Unknown", new Style().Foreground(Color.Grey))
};

var healthRenderable = knownResource.Value.Health switch
{
"Healthy" => new Text(knownResource.Value.Health, new Style().Foreground(Color.Green)),
"Degraded" => new Text(knownResource.Value.Health, new Style().Foreground(Color.Yellow)),
"Unhealthy" => new Text(knownResource.Value.Health, new Style().Foreground(Color.Red)),
null => new Text("Unknown", new Style().Foreground(Color.Grey)),
_ => new Text(knownResource.Value.Health, new Style().Foreground(Color.Grey))
};

IRenderable endpointsRenderable = new Text("None");
if (knownResource.Value.Endpoints?.Length > 0)
{
Expand All @@ -218,7 +228,7 @@ await _ansiConsole.Live(rows).StartAsync(async context =>
);
}

table.AddRow(nameRenderable, typeRenderable, stateRenderable, endpointsRenderable);
table.AddRow(nameRenderable, typeRenderable, stateRenderable, healthRenderable, endpointsRenderable);
}

context.Refresh();
Expand Down
9 changes: 7 additions & 2 deletions src/Aspire.Hosting/Backchannel/AppHostRpcTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ DistributedApplicationOptions options
}
}

public async IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints)> GetResourceStatesAsync([EnumeratorCancellation]CancellationToken cancellationToken)
public async IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints, string? Health)> GetResourceStatesAsync([EnumeratorCancellation]CancellationToken cancellationToken)
{
var resourceEvents = resourceNotificationService.WatchAsync(cancellationToken);

Expand All @@ -74,12 +74,17 @@ DistributedApplicationOptions options
.Where(e => e.AllocatedEndpoint != null)
.Select(e => e.AllocatedEndpoint!.UriString)
.ToArray();

// Compute health status
var healthStatus = CustomResourceSnapshot.ComputeHealthStatus(resourceEvent.Snapshot.HealthReports, resourceEvent.Snapshot.State?.Text);

// TODO: Decide on whether we want to define a type and share it between codebases for this.
yield return (
resourceEvent.Resource.Name,
resourceEvent.Snapshot.ResourceType,
resourceEvent.Snapshot.State?.Text ?? "Unknown",
endpointUris
endpointUris,
healthStatus?.ToString()
);
}
}
Expand Down
8 changes: 4 additions & 4 deletions tests/Aspire.Cli.Tests/TestServices/TestAppHostBackchannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal sealed class TestAppHostBackchannel : IAppHostBackchannel
public Func<CancellationToken, Task<(string, string?)>>? GetDashboardUrlsAsyncCallback { get; set; }

public TaskCompletionSource? GetResourceStatesAsyncCalled { get; set; }
public Func<CancellationToken, IAsyncEnumerable<(string, string, string, string[])>>? GetResourceStatesAsyncCallback { get; set; }
public Func<CancellationToken, IAsyncEnumerable<(string, string, string, string[], string?)>>? GetResourceStatesAsyncCallback { get; set; }

public TaskCompletionSource? ConnectAsyncCalled { get; set; }
public Func<string, CancellationToken, Task>? ConnectAsyncCallback { get; set; }
Expand Down Expand Up @@ -58,7 +58,7 @@ public Task RequestStopAsync(CancellationToken cancellationToken)
: Task.FromResult<(string, string?)>(("http://localhost:5000/login?t=abcd", "https://monalisa-hot-potato-vrpqrxxrx7x2rxx-5000.app.github.dev/login?t=abcd"));
}

public async IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints)> GetResourceStatesAsync([EnumeratorCancellation]CancellationToken cancellationToken)
public async IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints, string? Health)> GetResourceStatesAsync([EnumeratorCancellation]CancellationToken cancellationToken)
{
GetResourceStatesAsyncCalled?.SetResult();

Expand All @@ -75,8 +75,8 @@ public Task RequestStopAsync(CancellationToken cancellationToken)
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
while (await timer.WaitForNextTickAsync(cancellationToken))
{
yield return ("frontend", "Project", "Starting", new[] { "http://localhost:5000" });
yield return ("backend", "Project", "Running", new[] { "http://localhost:5001" });
yield return ("frontend", "Project", "Starting", new[] { "http://localhost:5000" }, "Healthy");
yield return ("backend", "Project", "Running", new[] { "http://localhost:5001" }, "Healthy");
}
}
}
Expand Down