Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show aggregate health status in the dashboard #5770

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ protected override void OnInitialized()
new KnownProperty(KnownProperties.Resource.DisplayName, Loc[Resources.Resources.ResourcesDetailsDisplayNameProperty]),
new KnownProperty(KnownProperties.Resource.State, Loc[Resources.Resources.ResourcesDetailsStateProperty]),
new KnownProperty(KnownProperties.Resource.CreateTime, Loc[Resources.Resources.ResourcesDetailsStartTimeProperty]),
new KnownProperty(KnownProperties.Resource.ExitCode, Loc[Resources.Resources.ResourcesDetailsExitCodeProperty])
new KnownProperty(KnownProperties.Resource.ExitCode, Loc[Resources.Resources.ResourcesDetailsExitCodeProperty]),
new KnownProperty(KnownProperties.Resource.HealthState, Loc[Resources.Resources.ResourcesDetailsHealthStateProperty])
];
_projectProperties =
[
Expand Down
2 changes: 1 addition & 1 deletion src/Aspire.Dashboard/Components/Pages/Resources.razor
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
<AspireTemplateColumn ColumnId="@NameColumn" ColumnManager="@_manager" Title="@ControlsStringsLoc[nameof(ControlsStrings.NameColumnHeader)]" Sortable="true" SortBy="@_nameSort" Tooltip="true" TooltipText="@(c => GetResourceName(c))">
<ResourceNameDisplay Resource="context" FilterText="@_filter" FormatName="GetResourceName" />
</AspireTemplateColumn>
<AspireTemplateColumn ColumnId="@StateColumn" ColumnManager="@_manager" Title="@Loc[nameof(Dashboard.Resources.Resources.ResourcesStateColumnHeader)]" Sortable="true" SortBy="@_stateSort" Tooltip="true" TooltipText="@(c => c.State.Humanize())">
<AspireTemplateColumn ColumnId="@StateColumn" ColumnManager="@_manager" Title="@Loc[nameof(Dashboard.Resources.Resources.ResourcesStateColumnHeader)]" Sortable="true" SortBy="@_stateSort" Tooltip="true" TooltipText="@(c => GetResourceStateTooltip(c))">
<StateColumnDisplay Resource="@context" UnviewedErrorCounts="@_applicationUnviewedErrorCounts" />
</AspireTemplateColumn>
<AspireTemplateColumn ColumnId="@StartTimeColumn" ColumnManager="@_manager" Title="@Loc[nameof(Dashboard.Resources.Resources.ResourcesStartTimeColumnHeader)]" Sortable="true" SortBy="@_startTimeSort" TooltipText="@(context => context.CreationTimeStamp != null ? FormatHelpers.FormatDateTime(TimeProvider, context.CreationTimeStamp.Value, MillisecondsDisplay.None, CultureInfo.CurrentCulture) : null)" Tooltip="true">
Expand Down
6 changes: 6 additions & 0 deletions src/Aspire.Dashboard/Components/Pages/Resources.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Aspire.Dashboard.Otlp.Storage;
using Aspire.Dashboard.Resources;
using Aspire.Dashboard.Utils;
using Humanizer;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using Microsoft.FluentUI.AspNetCore.Components;
Expand Down Expand Up @@ -331,6 +332,11 @@ private async Task ExecuteResourceCommandAsync(ResourceViewModel resource, Comma
}
}

private static string GetResourceStateTooltip(ResourceViewModel resource) =>
resource.ShowReadinessState() ?
$"{resource.State.Humanize()} ({resource.ReadinessState.Humanize()})"
: resource.State.Humanize();

private static (string Value, string? ContentAfterValue, string ValueToCopy, string Tooltip)? GetSourceColumnValueAndTooltip(ResourceViewModel resource)
{
// NOTE projects are also executables, so we have to check for projects first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@using Aspire.Dashboard.Otlp.Model
@using Aspire.Dashboard.Otlp.Storage
@using Aspire.Dashboard.Resources
@using Microsoft.Extensions.Diagnostics.HealthChecks
@using Humanizer

@inject IStringLocalizer<Columns> Loc
Expand Down Expand Up @@ -78,9 +79,20 @@ else if (!string.IsNullOrEmpty(Resource.StateStyle))
}
else
{
<FluentIcon Icon="Icons.Filled.Size16.CheckmarkCircle"
Color="Color.Success"
Class="severity-icon" />
switch (Resource.ReadinessState)
{
// Unknown state is treated as ready state (we don't know if it's ready or not)
case ReadinessState.Unknown or ReadinessState.Ready:
<FluentIcon Icon="Icons.Filled.Size16.CheckmarkCircle"
Color="Color.Success"
Class="severity-icon" />
break;
default:
<FluentIcon Icon="Icons.Regular.Size16.CheckmarkCircleWarning"
Color="Color.Neutral"
Class="severity-icon" />
break;
}
}

@if (Resource.HasNoState())
Expand All @@ -89,7 +101,14 @@ else
}
else
{
<span>@Resource.State.Humanize()</span>
if (Resource.ShowReadinessState())
{
<span>@Resource.State.Humanize() (@Resource.ReadinessState.Humanize())</span>
davidfowl marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
<span>@Resource.State.Humanize()</span>
}
}

<UnreadLogErrorsBadge UnviewedErrorCounts="UnviewedErrorCounts" Resource="@Resource" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ public static bool IsStartingOrBuildingOrWaiting(this ResourceViewModel resource
}

public static bool HasNoState(this ResourceViewModel resource) => string.IsNullOrEmpty(resource.State);

// We only care about the readiness state if the resource is running
public static bool ShowReadinessState(this ResourceViewModel resource) =>
resource.IsRunningState() && resource.ReadinessState is ReadinessState.NotReady;
}
8 changes: 8 additions & 0 deletions src/Aspire.Dashboard/Model/ResourceViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public sealed class ResourceViewModel
public required string Uid { get; init; }
public required string? State { get; init; }
public required string? StateStyle { get; init; }
public required ReadinessState ReadinessState { get; init; }
public required DateTime? CreationTimeStamp { get; init; }
public required ImmutableArray<EnvironmentVariableViewModel> Environment { get; init; }
public required ImmutableArray<UrlViewModel> Urls { get; init; }
Expand Down Expand Up @@ -60,6 +61,13 @@ public static string GetResourceName(ResourceViewModel resource, IDictionary<str
}
}

public enum ReadinessState
{
Unknown,
NotReady,
Ready
}

[DebuggerDisplay("CommandType = {CommandType}, DisplayName = {DisplayName}")]
public sealed class CommandViewModel
{
Expand Down
5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/ResourceService/Partials.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public ResourceViewModel ToViewModel()
State = HasState ? State : null,
KnownState = HasState ? Enum.TryParse(State, out KnownResourceState knownState) ? knownState : null : null,
StateStyle = HasStateStyle ? StateStyle : null,
ReadinessState = HasHealthState ? HealthState switch
{
HealthStateKind.Healthy => ReadinessState.Ready,
_ => ReadinessState.NotReady,
} : ReadinessState.Unknown,
Commands = GetCommands()
};
}
Expand Down
9 changes: 9 additions & 0 deletions src/Aspire.Dashboard/Resources/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Aspire.Dashboard/Resources/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,7 @@
<data name="ResourceDetailsViewConsoleLogs" xml:space="preserve">
<value>View console logs</value>
</data>
<data name="ResourcesDetailsHealthStateProperty" xml:space="preserve">
<value>Health State</value>
</data>
</root>
5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions src/Aspire.Hosting/ApplicationModel/ReplicaInstancesAnnotation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Concurrent;

namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// Keep track of active replicas ids from DCP.
/// </summary>
internal class ReplicaInstancesAnnotation : IResourceAnnotation
{
public ConcurrentDictionary<string, string> Instances { get; } = [];
}
11 changes: 10 additions & 1 deletion src/Aspire.Hosting/Dashboard/DashboardServiceData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using System.Runtime.CompilerServices;
using Aspire.Hosting.ApplicationModel;
using Aspire.ResourceService.Proto.V1;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;

namespace Aspire.Hosting.Dashboard;
Expand Down Expand Up @@ -42,7 +44,14 @@ static GenericResourceSnapshot CreateResourceSnapshot(IResource resource, string
Environment = snapshot.EnvironmentVariables,
ExitCode = snapshot.ExitCode,
State = snapshot.State?.Text,
StateStyle = snapshot.State?.Style
StateStyle = snapshot.State?.Style,
HealthState = resource.TryGetLastAnnotation<HealthCheckAnnotation>(out _) ? snapshot.HealthStatus switch
{
HealthStatus.Healthy => HealthStateKind.Healthy,
HealthStatus.Unhealthy => HealthStateKind.Unhealthy,
HealthStatus.Degraded => HealthStateKind.Degraded,
_ => HealthStateKind.Unknown,
} : null
};
}

Expand Down
4 changes: 3 additions & 1 deletion src/Aspire.Hosting/Dashboard/ResourceSnapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Globalization;
using Aspire.Dashboard.Model;
using Aspire.Hosting.ApplicationModel;
using Aspire.ResourceService.Proto.V1;
using Google.Protobuf.WellKnownTypes;

namespace Aspire.Hosting.Dashboard;
Expand All @@ -22,8 +23,8 @@ internal abstract class ResourceSnapshot
public required DateTime? CreationTimeStamp { get; init; }
public required ImmutableArray<EnvironmentVariableSnapshot> Environment { get; init; }
public required ImmutableArray<VolumeSnapshot> Volumes { get; init; }

public required ImmutableArray<UrlSnapshot> Urls { get; init; }
public required HealthStateKind? HealthState { get; set; }

protected abstract IEnumerable<(string Key, Value Value)> GetProperties();

Expand All @@ -38,6 +39,7 @@ internal abstract class ResourceSnapshot
yield return (KnownProperties.Resource.State, State is null ? Value.ForNull() : Value.ForString(State));
yield return (KnownProperties.Resource.ExitCode, ExitCode is null ? Value.ForNull() : Value.ForString(ExitCode.Value.ToString("D", CultureInfo.InvariantCulture)));
yield return (KnownProperties.Resource.CreateTime, CreationTimeStamp is null ? Value.ForNull() : Value.ForString(CreationTimeStamp.Value.ToString("O")));
yield return (KnownProperties.Resource.HealthState, HealthState is null ? Value.ForNull() : Value.ForString(HealthState.ToString()));

foreach (var pair in GetProperties())
{
Expand Down
5 changes: 5 additions & 0 deletions src/Aspire.Hosting/Dashboard/proto/Partials.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public static Resource FromSnapshot(ResourceSnapshot snapshot)
StateStyle = snapshot.StateStyle ?? "",
};

if (snapshot.HealthState is HealthStateKind healthState)
{
resource.HealthState = healthState;
}

if (snapshot.CreationTimeStamp.HasValue)
{
resource.CreatedAt = Timestamp.FromDateTime(snapshot.CreationTimeStamp.Value.ToUniversalTime());
Expand Down
Loading