From 7455a7a40344414bddc7adc0e1d4ba71c8ededbf Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sat, 27 Jan 2024 18:15:26 +1100 Subject: [PATCH 1/2] Fix dashboard not working with two applicationUrls specified. --- .../eShopLite/AppHost/Properties/launchSettings.json | 2 +- src/Aspire.Hosting/Dcp/ApplicationExecutor.cs | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/playground/eShopLite/AppHost/Properties/launchSettings.json b/playground/eShopLite/AppHost/Properties/launchSettings.json index f752c9e26b6..10744f258fa 100644 --- a/playground/eShopLite/AppHost/Properties/launchSettings.json +++ b/playground/eShopLite/AppHost/Properties/launchSettings.json @@ -4,7 +4,7 @@ "commandName": "Project", "launchBrowser": true, "dotnetRunMessages": true, - "applicationUrl": "http://localhost:15888", + "applicationUrl": "https://localhost:15887;http://localhost:15888", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", diff --git a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs index dfed6188a9e..a7ea682f458 100644 --- a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs +++ b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs @@ -198,7 +198,7 @@ private async Task StartDashboardAsDcpExecutableAsync(CancellationToken cancella } var otlpEndpointUrl = environmentVariables.GetString("DOTNET_DASHBOARD_OTLP_ENDPOINT_URL"); - var dashboardUrl = environmentVariables.GetString("ASPNETCORE_URLS") ?? throw new DistributedApplicationException("ASPNETCORE_URLS environment variable not set."); + var dashboardUrls = environmentVariables.GetString("ASPNETCORE_URLS") ?? throw new DistributedApplicationException("ASPNETCORE_URLS environment variable not set."); var aspnetcoreEnvironment = environmentVariables.GetString("ASPNETCORE_ENVIRONMENT"); dashboardExecutableSpec.Env = @@ -211,7 +211,7 @@ private async Task StartDashboardAsDcpExecutableAsync(CancellationToken cancella new() { Name = "ASPNETCORE_URLS", - Value = dashboardUrl + Value = dashboardUrls }, new() { @@ -230,8 +230,9 @@ private async Task StartDashboardAsDcpExecutableAsync(CancellationToken cancella Metadata = { Name = KnownResourceNames.AspireDashboard } }; + var firstDashboardUrl = dashboardUrls.Split(";")[0]; await kubernetesService.CreateAsync(dashboardExecutable, cancellationToken).ConfigureAwait(false); - await WaitForHttpSuccessOrThrow(dashboardUrl, DashboardAvailabilityTimeoutDuration, cancellationToken).ConfigureAwait(false); + await WaitForHttpSuccessOrThrow(firstDashboardUrl, DashboardAvailabilityTimeoutDuration, cancellationToken).ConfigureAwait(false); } private static TimeSpan DashboardAvailabilityTimeoutDuration @@ -639,12 +640,13 @@ private async Task CreateExecutablesAsync(IEnumerable executableRes { // We just check the HTTP endpoint because this will prove that the // dashboard is listening and is ready to process requests. - if (Environment.GetEnvironmentVariable("ASPNETCORE_URLS") is not { } dashboardUrl) + if (Environment.GetEnvironmentVariable("ASPNETCORE_URLS") is not { } dashboardUrls) { throw new DistributedApplicationException("Cannot check dashboard availability since ASPNETCORE_URLS environment variable not set."); } - await WaitForHttpSuccessOrThrow(dashboardUrl, DashboardAvailabilityTimeoutDuration, cancellationToken).ConfigureAwait(false); + var firstDashboardUrl = dashboardUrls.Split(";")[0]; + await WaitForHttpSuccessOrThrow(firstDashboardUrl, DashboardAvailabilityTimeoutDuration, cancellationToken).ConfigureAwait(false); } } From 3baeb41509208d6e0604fd945d80fd92219de814 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sat, 27 Jan 2024 18:49:10 +1100 Subject: [PATCH 2/2] PR feedback. Defensive code. --- src/Aspire.Hosting/Dcp/ApplicationExecutor.cs | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs index a7ea682f458..c55a261698b 100644 --- a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs +++ b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Net.Sockets; using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Dashboard; @@ -230,9 +231,8 @@ private async Task StartDashboardAsDcpExecutableAsync(CancellationToken cancella Metadata = { Name = KnownResourceNames.AspireDashboard } }; - var firstDashboardUrl = dashboardUrls.Split(";")[0]; await kubernetesService.CreateAsync(dashboardExecutable, cancellationToken).ConfigureAwait(false); - await WaitForHttpSuccessOrThrow(firstDashboardUrl, DashboardAvailabilityTimeoutDuration, cancellationToken).ConfigureAwait(false); + await CheckDashboardAvailabilityAsync(dashboardUrls, cancellationToken).ConfigureAwait(false); } private static TimeSpan DashboardAvailabilityTimeoutDuration @@ -252,7 +252,19 @@ private static TimeSpan DashboardAvailabilityTimeoutDuration private const int DefaultDashboardAvailabilityTimeoutDurationInSeconds = 60; - private async Task WaitForHttpSuccessOrThrow(string url, TimeSpan timeout, CancellationToken cancellationToken = default) + private async Task CheckDashboardAvailabilityAsync(string delimitedUrlList, CancellationToken cancellationToken) + { + if (TryGetUriFromDelimitedString(delimitedUrlList, ";", out var firstDashboardUrl)) + { + await WaitForHttpSuccessOrThrow(firstDashboardUrl, DashboardAvailabilityTimeoutDuration, cancellationToken).ConfigureAwait(false); + } + else + { + _logger.LogWarning("Skipping dashboard availability check because ASPNETCORE_URLS environment variable could not be parsed."); + } + } + + private async Task WaitForHttpSuccessOrThrow(Uri url, TimeSpan timeout, CancellationToken cancellationToken = default) { using var timeoutCts = new CancellationTokenSource(timeout); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); @@ -645,8 +657,7 @@ private async Task CreateExecutablesAsync(IEnumerable executableRes throw new DistributedApplicationException("Cannot check dashboard availability since ASPNETCORE_URLS environment variable not set."); } - var firstDashboardUrl = dashboardUrls.Split(";")[0]; - await WaitForHttpSuccessOrThrow(firstDashboardUrl, DashboardAvailabilityTimeoutDuration, cancellationToken).ConfigureAwait(false); + await CheckDashboardAvailabilityAsync(dashboardUrls, cancellationToken).ConfigureAwait(false); } } @@ -658,6 +669,21 @@ private async Task CreateExecutablesAsync(IEnumerable executableRes } } + private static bool TryGetUriFromDelimitedString(string input, string delimiter, [NotNullWhen(true)]out Uri? uri) + { + if (!string.IsNullOrEmpty(input) + && input.Split(delimiter) is { Length: > 0} splitInput + && Uri.TryCreate(splitInput[0], UriKind.Absolute, out uri)) + { + return true; + } + else + { + uri = null; + return false; + } + } + private static void ApplyLaunchProfile(AppResource executableResource, Dictionary config, string launchProfileName, LaunchSettings launchSettings) { // Populate DOTNET_LAUNCH_PROFILE environment variable for consistency with "dotnet run" and "dotnet watch".