diff --git a/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor.cs b/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor.cs index 8eae92ec6e..d38ae21e20 100644 --- a/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor.cs +++ b/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor.cs @@ -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 = [ diff --git a/src/Aspire.Dashboard/Components/Pages/Resources.razor b/src/Aspire.Dashboard/Components/Pages/Resources.razor index 3254250af3..fb18f71843 100644 --- a/src/Aspire.Dashboard/Components/Pages/Resources.razor +++ b/src/Aspire.Dashboard/Components/Pages/Resources.razor @@ -94,7 +94,7 @@ - + diff --git a/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs b/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs index d5fb9db3ab..9f717dcc6e 100644 --- a/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs @@ -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; @@ -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 diff --git a/src/Aspire.Dashboard/Components/ResourcesGridColumns/StateColumnDisplay.razor b/src/Aspire.Dashboard/Components/ResourcesGridColumns/StateColumnDisplay.razor index acccf9e208..3374c26b3f 100644 --- a/src/Aspire.Dashboard/Components/ResourcesGridColumns/StateColumnDisplay.razor +++ b/src/Aspire.Dashboard/Components/ResourcesGridColumns/StateColumnDisplay.razor @@ -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 Loc @@ -78,9 +79,20 @@ else if (!string.IsNullOrEmpty(Resource.StateStyle)) } else { - + 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: + + break; + default: + + break; + } } @if (Resource.HasNoState()) @@ -89,7 +101,14 @@ else } else { - @Resource.State.Humanize() + if (Resource.ShowReadinessState()) + { + @Resource.State.Humanize() (@Resource.ReadinessState.Humanize()) + } + else + { + @Resource.State.Humanize() + } } diff --git a/src/Aspire.Dashboard/Extensions/ResourceViewModelExtensions.cs b/src/Aspire.Dashboard/Extensions/ResourceViewModelExtensions.cs index 7932a6216f..a09b1a1735 100644 --- a/src/Aspire.Dashboard/Extensions/ResourceViewModelExtensions.cs +++ b/src/Aspire.Dashboard/Extensions/ResourceViewModelExtensions.cs @@ -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; } diff --git a/src/Aspire.Dashboard/Model/ResourceViewModel.cs b/src/Aspire.Dashboard/Model/ResourceViewModel.cs index c2d9c45f5c..0de2f04d59 100644 --- a/src/Aspire.Dashboard/Model/ResourceViewModel.cs +++ b/src/Aspire.Dashboard/Model/ResourceViewModel.cs @@ -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 Environment { get; init; } public required ImmutableArray Urls { get; init; } @@ -60,6 +61,13 @@ public static string GetResourceName(ResourceViewModel resource, IDictionary ReadinessState.Ready, + _ => ReadinessState.NotReady, + } : ReadinessState.Unknown, Commands = GetCommands() }; } diff --git a/src/Aspire.Dashboard/Resources/Resources.Designer.cs b/src/Aspire.Dashboard/Resources/Resources.Designer.cs index ade01090fc..4e82deeebf 100644 --- a/src/Aspire.Dashboard/Resources/Resources.Designer.cs +++ b/src/Aspire.Dashboard/Resources/Resources.Designer.cs @@ -240,6 +240,15 @@ public static string ResourcesDetailsExitCodeProperty { } } + /// + /// Looks up a localized string similar to Health State. + /// + public static string ResourcesDetailsHealthStateProperty { + get { + return ResourceManager.GetString("ResourcesDetailsHealthStateProperty", resourceCulture); + } + } + /// /// Looks up a localized string similar to Project path. /// diff --git a/src/Aspire.Dashboard/Resources/Resources.resx b/src/Aspire.Dashboard/Resources/Resources.resx index 29b1a4789b..431c2269f2 100644 --- a/src/Aspire.Dashboard/Resources/Resources.resx +++ b/src/Aspire.Dashboard/Resources/Resources.resx @@ -232,4 +232,7 @@ View console logs + + Health State + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf index 2f47e1ad30..8443f71a37 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf @@ -102,6 +102,11 @@ Ukončovací kód + + Health State + Health State + + Project path Cesta k projektu diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf index 7b1c36a5ac..087f52c7c8 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf @@ -102,6 +102,11 @@ Exitcode + + Health State + Health State + + Project path Projektpfad diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf index 48cca31b1e..351a734e4f 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf @@ -102,6 +102,11 @@ Código de salida + + Health State + Health State + + Project path Ruta de acceso del proyecto diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf index 265cde3252..7931f273ad 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf @@ -102,6 +102,11 @@ Code de sortie + + Health State + Health State + + Project path Chemin de projet diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf index be434f752a..44a2f1b39e 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf @@ -102,6 +102,11 @@ Codice di uscita + + Health State + Health State + + Project path Percorso del progetto diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf index 587ef7feb6..f3b21a7d23 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf @@ -102,6 +102,11 @@ 終了コード + + Health State + Health State + + Project path プロジェクト パス diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf index fd7309ebb7..eb52ba41a8 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf @@ -102,6 +102,11 @@ 종료 코드 + + Health State + Health State + + Project path 프로젝트 경로 diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf index 6c03742335..4f11f7ab0a 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf @@ -102,6 +102,11 @@ Kod zakończenia + + Health State + Health State + + Project path Ścieżka projektu diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf index bcf3a79a9e..9524712f71 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf @@ -102,6 +102,11 @@ Código de saída + + Health State + Health State + + Project path Caminho do projeto diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf index b7d808fcec..cc41bc60c1 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf @@ -102,6 +102,11 @@ Код завершения + + Health State + Health State + + Project path Путь к проекту diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf index 325c7817f9..ad13c11d4e 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf @@ -102,6 +102,11 @@ Çıkış kodu + + Health State + Health State + + Project path Proje yolu diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf index 5385d454f1..68a545e384 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf @@ -102,6 +102,11 @@ 退出代码 + + Health State + Health State + + Project path 项目路径 diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf index 81cb162ee9..695f989c0e 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf @@ -102,6 +102,11 @@ 結束代碼 + + Health State + Health State + + Project path 專案路徑 diff --git a/src/Aspire.Hosting/ApplicationModel/ReplicaInstancesAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/ReplicaInstancesAnnotation.cs new file mode 100644 index 0000000000..fb83af0312 --- /dev/null +++ b/src/Aspire.Hosting/ApplicationModel/ReplicaInstancesAnnotation.cs @@ -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; + +/// +/// Keep track of active replicas ids from DCP. +/// +internal class ReplicaInstancesAnnotation : IResourceAnnotation +{ + public ConcurrentDictionary Instances { get; } = []; +} diff --git a/src/Aspire.Hosting/Dashboard/DashboardServiceData.cs b/src/Aspire.Hosting/Dashboard/DashboardServiceData.cs index 18b179875f..413cdb8457 100644 --- a/src/Aspire.Hosting/Dashboard/DashboardServiceData.cs +++ b/src/Aspire.Hosting/Dashboard/DashboardServiceData.cs @@ -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; @@ -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(out _) ? snapshot.HealthStatus switch + { + HealthStatus.Healthy => HealthStateKind.Healthy, + HealthStatus.Unhealthy => HealthStateKind.Unhealthy, + HealthStatus.Degraded => HealthStateKind.Degraded, + _ => HealthStateKind.Unknown, + } : null }; } diff --git a/src/Aspire.Hosting/Dashboard/ResourceSnapshot.cs b/src/Aspire.Hosting/Dashboard/ResourceSnapshot.cs index 14fb75f623..f899c0177f 100644 --- a/src/Aspire.Hosting/Dashboard/ResourceSnapshot.cs +++ b/src/Aspire.Hosting/Dashboard/ResourceSnapshot.cs @@ -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; @@ -22,8 +23,8 @@ internal abstract class ResourceSnapshot public required DateTime? CreationTimeStamp { get; init; } public required ImmutableArray Environment { get; init; } public required ImmutableArray Volumes { get; init; } - public required ImmutableArray Urls { get; init; } + public required HealthStateKind? HealthState { get; set; } protected abstract IEnumerable<(string Key, Value Value)> GetProperties(); @@ -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()) { diff --git a/src/Aspire.Hosting/Dashboard/proto/Partials.cs b/src/Aspire.Hosting/Dashboard/proto/Partials.cs index 9b0665872b..ffe7ee8c9a 100644 --- a/src/Aspire.Hosting/Dashboard/proto/Partials.cs +++ b/src/Aspire.Hosting/Dashboard/proto/Partials.cs @@ -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()); diff --git a/src/Aspire.Hosting/Dashboard/proto/resource_service.proto b/src/Aspire.Hosting/Dashboard/proto/resource_service.proto index fe78a23852..e876fbb478 100644 --- a/src/Aspire.Hosting/Dashboard/proto/resource_service.proto +++ b/src/Aspire.Hosting/Dashboard/proto/resource_service.proto @@ -170,6 +170,16 @@ message Resource { // The set of volumes mounted to the resource. Only applies to containers. repeated Volume volumes = 15; + + // The aggregate health state of the resource + optional HealthStateKind HealthState = 16; +} + +enum HealthStateKind { + UNKNOWN = 0; + HEALTHY = 1; + UNHEALTHY = 2; + DEGRADED = 3; } //////////////////////////////////////////// diff --git a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs index 26c7fdbe13..2d2cb3394e 100644 --- a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs +++ b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs @@ -394,6 +394,11 @@ private async Task ProcessResourceChange(WatchEventType watchEventType, T res { _logger.LogTrace("Deleting application model resource {ResourceName} with {ResourceKind} resource {ResourceName}", appModelResource.Name, resourceKind, resource.Metadata.Name); } + + if (appModelResource.TryGetLastAnnotation(out var replicaAnnotation)) + { + replicaAnnotation.Instances.TryRemove(resource.Metadata.Name, out _); + } } else { @@ -586,6 +591,15 @@ await notificationService.PublishUpdateAsync(appModelResource, cr.Metadata.Name, private CustomResourceSnapshot ToSnapshot(Container container, CustomResourceSnapshot previous) { + if (container.AppModelResourceName is not null && + _applicationModel.TryGetValue(container.AppModelResourceName, out var appModelResource)) + { + if (appModelResource.TryGetLastAnnotation(out var replicaAnnotation)) + { + replicaAnnotation.Instances.TryAdd(container.Metadata.Name, container.Metadata.Name); + } + } + var containerId = container.Status?.ContainerId; var urls = GetUrls(container); var volumes = GetVolumes(container); @@ -639,6 +653,11 @@ private CustomResourceSnapshot ToSnapshot(Executable executable, CustomResourceS _applicationModel.TryGetValue(executable.AppModelResourceName, out var appModelResource)) { projectPath = appModelResource is ProjectResource p ? p.GetProjectMetadata().ProjectPath : null; + + if (appModelResource.TryGetLastAnnotation(out var replicaAnnotation)) + { + replicaAnnotation.Instances.TryAdd(executable.Metadata.Name, executable.Metadata.Name); + } } var state = executable.AppModelInitialState is "Hidden" ? "Hidden" : executable.Status?.State; @@ -1021,6 +1040,8 @@ private void PreparePlainExecutables() foreach (var executable in modelExecutableResources) { + EnsureReplicaInstancesAnnotation(executable); + var nameSuffix = GetRandomNameSuffix(); var exeName = GetObjectNameForResource(executable, nameSuffix); var exePath = executable.Command; @@ -1051,6 +1072,8 @@ private void PrepareProjectExecutables() throw new InvalidOperationException("A project resource is missing required metadata"); // Should never happen. } + EnsureReplicaInstancesAnnotation(project); + int replicas = project.GetReplicaCount(); var ers = ExecutableReplicaSet.Create(GetObjectNameForResource(project), replicas, "dotnet"); @@ -1102,7 +1125,7 @@ private void PrepareProjectExecutables() if (!string.IsNullOrEmpty(_distributedApplicationOptions.Configuration)) { - exeSpec.Args.AddRange(new [] {"-c", _distributedApplicationOptions.Configuration}); + exeSpec.Args.AddRange(new[] { "-c", _distributedApplicationOptions.Configuration }); } // We pretty much always want to suppress the normal launch profile handling @@ -1134,6 +1157,16 @@ private void PrepareProjectExecutables() } } + private static void EnsureReplicaInstancesAnnotation(IResource resource) + { + // Make sure we have a replica annotation on the resource. + // this is so that we can populate the running instance ids + if (!resource.TryGetLastAnnotation(out _)) + { + resource.Annotations.Add(new ReplicaInstancesAnnotation()); + } + } + private static void SetInitialResourceState(IResource resource, IAnnotationHolder annotationHolder) { // Store the initial state of the resource @@ -1370,6 +1403,8 @@ private void PrepareContainers() throw new InvalidOperationException(); } + EnsureReplicaInstancesAnnotation(container); + var nameSuffix = container.GetContainerLifetimeType() switch { ContainerLifetime.Default => GetRandomNameSuffix(), @@ -1718,9 +1753,9 @@ private static async Task ApplyBuildArgumentsAsync(Container dcpContainerResourc { var dcpBuildSecret = new BuildContextSecret { - Id = buildSecret.Key, - Type = "env", - Value = valueString + Id = buildSecret.Key, + Type = "env", + Value = valueString }; dcpBuildSecrets.Add(dcpBuildSecret); } diff --git a/src/Aspire.Hosting/Health/ResourceNotificationHealthCheckPublisher.cs b/src/Aspire.Hosting/Health/ResourceNotificationHealthCheckPublisher.cs index 7721e171e6..953510ec11 100644 --- a/src/Aspire.Hosting/Health/ResourceNotificationHealthCheckPublisher.cs +++ b/src/Aspire.Hosting/Health/ResourceNotificationHealthCheckPublisher.cs @@ -21,6 +21,17 @@ await resourceNotificationService.PublishUpdateAsync(resource, s => s with { HealthStatus = status }).ConfigureAwait(false); + + if (resource.TryGetLastAnnotation(out var replicaAnnotation)) + { + foreach (var (id, _) in replicaAnnotation.Instances) + { + await resourceNotificationService.PublishUpdateAsync(resource, id, s => s with + { + HealthStatus = status + }).ConfigureAwait(false); + } + } } } } diff --git a/src/Shared/Model/KnownProperties.cs b/src/Shared/Model/KnownProperties.cs index 9ea5b5ec85..47deaa3ede 100644 --- a/src/Shared/Model/KnownProperties.cs +++ b/src/Shared/Model/KnownProperties.cs @@ -22,6 +22,7 @@ public static class Resource public const string ExitCode = "resource.exitCode"; public const string CreateTime = "resource.createTime"; public const string Source = "resource.source"; + public const string HealthState = "resource.healthState"; public const string ConnectionString = "resource.connectionString"; } diff --git a/tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs b/tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs index 1abc8fe171..7ffac8f04d 100644 --- a/tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs +++ b/tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs @@ -148,6 +148,7 @@ private static ResourceViewModel CreateResourceViewModel(string appName, KnownRe State = state?.ToString(), KnownState = state, StateStyle = null, + ReadinessState = ReadinessState.Ready, Commands = [] }; } diff --git a/tests/Aspire.Dashboard.Tests/ConsoleLogsTests/CreateResourceSelectModelsTests.cs b/tests/Aspire.Dashboard.Tests/ConsoleLogsTests/CreateResourceSelectModelsTests.cs index 472091f935..8ac67a0faa 100644 --- a/tests/Aspire.Dashboard.Tests/ConsoleLogsTests/CreateResourceSelectModelsTests.cs +++ b/tests/Aspire.Dashboard.Tests/ConsoleLogsTests/CreateResourceSelectModelsTests.cs @@ -118,6 +118,7 @@ private static ResourceViewModel CreateResourceViewModel(string appName, KnownRe State = state?.ToString(), KnownState = state, StateStyle = null, + ReadinessState = ReadinessState.Ready, Commands = [] }; } diff --git a/tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/MockDashboardClient.cs b/tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/MockDashboardClient.cs index ae0d6f6de4..bf85ca18fa 100644 --- a/tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/MockDashboardClient.cs +++ b/tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/MockDashboardClient.cs @@ -27,6 +27,7 @@ public sealed class MockDashboardClient : IDashboardClient State = "Running", Uid = Guid.NewGuid().ToString(), StateStyle = null, + ReadinessState = ReadinessState.Ready, Urls = [], Volumes = [] }; diff --git a/tests/Aspire.Dashboard.Tests/Model/ResourceEndpointHelpersTests.cs b/tests/Aspire.Dashboard.Tests/Model/ResourceEndpointHelpersTests.cs index 9c701c65ba..e1eccada84 100644 --- a/tests/Aspire.Dashboard.Tests/Model/ResourceEndpointHelpersTests.cs +++ b/tests/Aspire.Dashboard.Tests/Model/ResourceEndpointHelpersTests.cs @@ -203,6 +203,7 @@ private static ResourceViewModel CreateResource(ImmutableArray url State = null, KnownState = null, StateStyle = null, + ReadinessState = ReadinessState.Ready, Commands = [] }; } diff --git a/tests/Aspire.Dashboard.Tests/ResourceOutgoingPeerResolverTests.cs b/tests/Aspire.Dashboard.Tests/ResourceOutgoingPeerResolverTests.cs index f2c1e74302..d6cf0e830d 100644 --- a/tests/Aspire.Dashboard.Tests/ResourceOutgoingPeerResolverTests.cs +++ b/tests/Aspire.Dashboard.Tests/ResourceOutgoingPeerResolverTests.cs @@ -26,6 +26,7 @@ private static ResourceViewModel CreateResource(string name, string? serviceAddr Urls = servicePort is null || servicePort is null ? [] : [new UrlViewModel(name, new($"http://{serviceAddress}:{servicePort}"), isInternal: false)], Volumes = [], State = null, + ReadinessState = ReadinessState.Ready, KnownState = null, StateStyle = null, Commands = [] diff --git a/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs b/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs index 69214eebcf..51071e1426 100644 --- a/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs +++ b/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs @@ -212,6 +212,7 @@ await pipeline.ExecuteAsync(async token => [Fact] [RequiresDocker] + [ActiveIssue("https://github.com/dotnet/aspire/issues/5785")] public async Task VerifyWithPgWeb() { using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(testOutputHelper); @@ -237,6 +238,7 @@ public async Task VerifyWithPgWeb() client.DefaultRequestHeaders.Add("x-session-id", Guid.NewGuid().ToString()); var response = await client.PostAsync("/api/connect", httpContent); + var d = await response.Content.ReadAsStringAsync(); response.EnsureSuccessStatusCode(); } diff --git a/tests/Aspire.Hosting.Tests/Dashboard/ResourcePublisherTests.cs b/tests/Aspire.Hosting.Tests/Dashboard/ResourcePublisherTests.cs index 274f06bfed..666c646ad8 100644 --- a/tests/Aspire.Hosting.Tests/Dashboard/ResourcePublisherTests.cs +++ b/tests/Aspire.Hosting.Tests/Dashboard/ResourcePublisherTests.cs @@ -193,7 +193,8 @@ private static GenericResourceSnapshot CreateResourceSnapshot(string name) DisplayName = "", Urls = [], Volumes = [], - Environment = [] + Environment = [], + HealthState = null }; } }