Skip to content
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
14 changes: 4 additions & 10 deletions src/Aspire.Hosting/Backchannel/AppHostRpcTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using Aspire.Hosting.Publishing;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -114,16 +113,11 @@ public Task<long> PingAsync(long timestamp, CancellationToken cancellationToken)
throw new InvalidOperationException("Dashboard URL requested but dashboard is disabled.");
}

// Wait for the dashboard to be healthy before returning the URL. This next statement has several
// layers of hacks. Some to work around devcontainer/codespaces port forwarding behavior, and one to
// temporarily work around the fact that resource events abuse the state to mark the resource as
// hidden instead of having another field. There is a corresponding modification in the ResourceHealthService
// which allows the dashboard resource to trigger health reports even though it never enters
// the Running state. This is a hack. The reason we can't just check HealthStatus is because
// the current implementation of HealthStatus depends on the state of the resource as well.
await resourceNotificationService.WaitForResourceAsync(
// Wait for the dashboard to be healthy before returning the URL. This is to ensure that the
// endpoint for the resource is available and the dashboard is ready to be used. This helps
// avoid some issues with port forwarding in devcontainer/codespaces scenarios.
await resourceNotificationService.WaitForResourceHealthyAsync(
KnownResourceNames.AspireDashboard,
re => re.Snapshot.HealthReports.All(h => h.Status == HealthStatus.Healthy),
cancellationToken).ConfigureAwait(false);

var dashboardOptions = serviceProvider.GetService<IOptions<DashboardOptions>>();
Expand Down
9 changes: 4 additions & 5 deletions src/Aspire.Hosting/Dashboard/DashboardLifecycleHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ private void ConfigureAspireDashboardResource(IResource dashboardResource)
}
}

var showDashboardResources = configuration.GetBool(KnownConfigNames.ShowDashboardResources, KnownConfigNames.Legacy.ShowDashboardResources);
var hideDashboard = !(showDashboardResources ?? false);

var snapshot = new CustomResourceSnapshot
{
Properties = [],
Expand All @@ -170,11 +173,7 @@ private void ConfigureAspireDashboardResource(IResource dashboardResource)
ContainerResource => KnownResourceTypes.Container,
_ => dashboardResource.GetType().Name
},
State = configuration.GetBool(KnownConfigNames.ShowDashboardResources, KnownConfigNames.Legacy.ShowDashboardResources) is true
? null
#pragma warning disable CS0618 // Type or member is obsolete
: KnownResourceStates.Hidden
#pragma warning restore CS0618 // Type or member is obsolete
IsHidden = hideDashboard
};

dashboardResource.Annotations.Add(new ResourceSnapshotAnnotation(snapshot));
Expand Down
32 changes: 19 additions & 13 deletions src/Aspire.Hosting/DistributedApplicationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,19 +333,7 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options)
_innerBuilder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<DashboardOptions>, ConfigureDefaultDashboardOptions>());
_innerBuilder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions<DashboardOptions>, ValidateDashboardOptions>());

// Dashboard health check.
_innerBuilder.Services.AddHealthChecks().AddUrlGroup(sp => {

var dashboardOptions = sp.GetRequiredService<IOptions<DashboardOptions>>().Value;
if (StringUtils.TryGetUriFromDelimitedString(dashboardOptions.DashboardUrl, ";", out var firstDashboardUrl))
{
return firstDashboardUrl;
}
else
{
throw new DistributedApplicationException($"The dashboard resource '{KnownResourceNames.AspireDashboard}' does not have endpoints.");
}
}, KnownHealthCheckNames.DasboardHealthCheck);
ConfigureDashboardHealthCheck();
}

if (options.EnableResourceLogging)
Expand Down Expand Up @@ -401,6 +389,24 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options)
LogBuilderConstructed(this);
}

private void ConfigureDashboardHealthCheck()
{
_innerBuilder.Services.AddHealthChecks().AddUrlGroup(sp => {

var dashboardOptions = sp.GetRequiredService<IOptions<DashboardOptions>>().Value;
if (StringUtils.TryGetUriFromDelimitedString(dashboardOptions.DashboardUrl, ";", out var firstDashboardUrl))
{
return firstDashboardUrl;
}
else
{
throw new DistributedApplicationException($"The dashboard resource '{KnownResourceNames.AspireDashboard}' does not have endpoints.");
}
}, KnownHealthCheckNames.DasboardHealthCheck);

_innerBuilder.Services.SuppressHealthCheckHttpClientLogging(KnownHealthCheckNames.DasboardHealthCheck);
}

private void ConfigureHealthChecks()
{
_innerBuilder.Services.AddSingleton<IValidateOptions<HealthCheckServiceOptions>>(sp =>
Expand Down
5 changes: 1 addition & 4 deletions src/Aspire.Hosting/Health/ResourceHealthCheckService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
}
}

// HACK: We are special casing the Aspire dashboard here until we address the issue of the Hidden state
// making it impossible to determine whether a hidden resource is running or not. When that change
// is made we can remove the special case logic here for the dashboard.
if (resourceEvent.Snapshot.State?.Text == KnownResourceStates.Running || resourceEvent.Resource.Name == KnownResourceNames.AspireDashboard)
if (resourceEvent.Snapshot.State?.Text == KnownResourceStates.Running)
{
if (state == null)
{
Expand Down
9 changes: 3 additions & 6 deletions src/Aspire.Hosting/ResourceBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Aspire.Dashboard.Model;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Publishing;
using Aspire.Hosting.Utils;
using HealthChecks.Uris;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
Expand Down Expand Up @@ -1281,12 +1282,8 @@ internal static IResourceBuilder<T> WithHttpHealthCheckInternal<T>(this IResourc
});

var healthCheckKey = $"{builder.Resource.Name}_{endpointName}_{path}_{statusCode}_check";
builder.ApplicationBuilder.Services.AddLogging(configure =>
{
// The AddUrlGroup health check makes use of http client factory.
configure.AddFilter($"System.Net.Http.HttpClient.{healthCheckKey}.LogicalHandler", LogLevel.None);
configure.AddFilter($"System.Net.Http.HttpClient.{healthCheckKey}.ClientHandler", LogLevel.None);
});

builder.ApplicationBuilder.Services.SuppressHealthCheckHttpClientLogging(healthCheckKey);

builder.ApplicationBuilder.Services.AddHealthChecks().AddUrlGroup((UriHealthCheckOptions options) =>
{
Expand Down
20 changes: 20 additions & 0 deletions src/Aspire.Hosting/Utils/LoggingUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Aspire.Hosting.Utils;

internal static class LoggingUtils
{
public static void SuppressHealthCheckHttpClientLogging(this IServiceCollection services, string healthCheckName)
{
services.AddLogging(configure =>
{
// The AddUrlGroup health check makes use of http client factory.
configure.AddFilter($"System.Net.Http.HttpClient.{healthCheckName}.LogicalHandler", LogLevel.None);
configure.AddFilter($"System.Net.Http.HttpClient.{healthCheckName}.ClientHandler", LogLevel.None);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public async Task DashboardIsAutomaticallyAddedAsHiddenResource(string showDashb
Assert.NotNull(dashboard);
Assert.Equal("aspire-dashboard", dashboard.Name);
Assert.Equal(dashboardPath, dashboard.Command);
Assert.Equal("Hidden", initialSnapshot.InitialSnapshot.State);
Assert.True(initialSnapshot.InitialSnapshot.IsHidden);
}

[Fact]
Expand Down