Skip to content

[release/13.4] Fix DistributedApplicationTestingBuilder dashboard with dynamic ports#17731

Merged
joperezr merged 1 commit into
release/13.4from
backport/pr-17729-to-release/13.4
May 30, 2026
Merged

[release/13.4] Fix DistributedApplicationTestingBuilder dashboard with dynamic ports#17731
joperezr merged 1 commit into
release/13.4from
backport/pr-17729-to-release/13.4

Conversation

@aspire-repo-bot
Copy link
Copy Markdown
Contributor

@aspire-repo-bot aspire-repo-bot Bot commented May 30, 2026

Backport of #17729 to release/13.4

/cc @JamesNK

Customer Impact

Users of DistributedApplicationTestingBuilder who enable the dashboard (explicitly via DisableDashboard = false or in future when it becomes the default) hit two failures introduced by #16046:

  1. The resource service gRPC host fails to start when the endpoint uses a loopback IP with port 0 (dynamic port) because ListenLocalhost(0) throws "Dynamic port binding is not supported when binding to localhost."
  2. Removing the explicit endpoint configuration to rely on the new defaults produces an OptionsValidationException because the dashboard URLs default to HTTP but TransportOptionsValidator rejects HTTP without ASPIRE_ALLOW_UNSECURED_TRANSPORT set to true.

This blocks any testing scenario that uses the dashboard with dynamic ports, which is the standard configuration path.

Reported in #17622.

Testing

  • New test DashboardEnabledInTestingBuilderShouldWorkWithDynamicPorts verifies dashboard starts with default dynamic port configuration.
  • New theory LoopbackWithDynamicPorts verifies explicit 127.0.0.1:0 and [::1]:0 endpoints work.
  • New test NonLocalResourceServiceEndpointThrows verifies non-local addresses are rejected.
  • Existing test IsLocalResourceServiceEndpoint_ReturnsExpectedResult removed (method no longer exists).

Risk

Low. The changes are scoped to the testing builder and dashboard service host startup path:

  • Defaulting AllowUnsecuredTransport = true only applies in testing scenarios (matches the HTTP default already set for dashboard URLs).
  • The Kestrel binding change uses Listen(IPAddress, port) instead of ListenLocalhost(port) only for parseable loopback IPs, preserving existing behavior for localhost hostnames.

Regression?

Yes — this is a regression from 13.3 introduced by #16046 in 13.4. The fix restores the ability to use DistributedApplicationTestingBuilder with the dashboard enabled on dynamic ports.

…ed with dynamic ports

When users set DisableDashboard = false on DistributedApplicationTestingBuilder,
the TransportOptionsValidator rejected the HTTP URLs that the testing builder
defaults (http://localhost:8080, http://localhost:4317) because
ASPIRE_ALLOW_UNSECURED_TRANSPORT was not set.

Fix: Set AllowUnsecuredTransport = true by default in PreConfigureBuilderOptions,
since all URLs are already defaulted to HTTP.

Add a regression test that enables the dashboard and verifies it becomes healthy.

Fixes #17622
Copilot AI review requested due to automatic review settings May 30, 2026 15:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@github-actions
Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17731

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17731"

@github-actions
Copy link
Copy Markdown
Contributor

CLI E2E Tests unknown — 110 passed, 0 failed, 2 unknown (commit 722f71f)

View all recordings
Status Test Recording Job Artifacts
AddPackageInteractiveWhileAppHostRunningDetached Recording #78657686034 Logs
AddPackageWhileAppHostRunningDetached Recording #78657686034 Logs
AgentCommands_AllHelpOutputs_AreCorrect Recording #78657686068 Logs
AgentInitCommand_DefaultSelection_InstallsDefaultSkills Recording #78657686068 Logs
AgentInitCommand_MigratesDeprecatedConfig Recording #78657686068 Logs
AgentInitCommand_NonInteractive_BundleOnlySkillsBeyondCliCatalog_AreInstallable Recording #78657686068 Logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp Recording #78657685965 Logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_DevLocalhost Recording #78657685965 Logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_Isolated Recording #78657685965 Logs
AllPublishMethodsBuildDockerImages Recording #78657685723 Logs
AspireAddAndStartWorkAgainstLegacyAppHostTs Recording #78657686117 Logs
AspireAddPackageVersionToDirectoryPackagesProps Recording #78657686059 Logs
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost Recording #78657685807 Logs
AspireInitWithExistingAppHostDirRecreatesMissingNuGetConfigAndPreservesFiles Recording #78657685838 Logs
AspireInitWithSolutionFileGeneratesAppHostThatBuildsAgainstChannelHive Recording #78657685838 Logs
AspireStartUpdatesStaleTypeScriptAppHostPath Recording #78657686104 Logs
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps Recording #78657686059 Logs
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent Recording #78657686059 Logs
Banner_DisplayedOnFirstRun Recording #78657686063 Logs
Banner_DisplayedWithExplicitFlag Recording #78657686063 Logs
Banner_NotDisplayedWithNoLogoFlag Recording #78657686063 Logs
CertificatesClean_RemovesCertificates Recording #78657685894 Logs
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate Recording #78657685894 Logs
CertificatesTrust_WithUntrustedCert_TrustsCertificate Recording #78657685894 Logs
ConfigSetGet_CreatesNestedJsonFormat Recording #78657686103 Logs
CreateAndRunAspireStarterProject Recording #78657685876 Logs
CreateAndRunAspireStarterProjectWithBundle Recording #78657686038 Logs
CreateAndRunEmptyAppHostProject Recording #78657686045 Logs
CreateAndRunJavaEmptyAppHostProject Recording #78657685757 Logs
CreateAndRunJsReactProject Recording #78657685916 Logs
CreateAndRunPolyglotAppHostWithDevLocalhostUrls Recording #78657685876 Logs
CreateAndRunPythonReactProject Recording #78657686056 Logs
CreateAndRunTypeScriptEmptyAppHostProject Recording #78657685779 Logs
CreateAndRunTypeScriptStarterProject Recording #78657685944 Logs
CreateJavaAppHostWithViteApp Recording #78657685764 Logs
CreateTypeScriptAppHostWithViteApp_AllowsGuestAppPackageManagerToDiffer Recording #78657685856 Logs
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain Recording #78657685856 Logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces Recording #78657685806 Logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces_DevLocalhost Recording #78657685806 Logs
DashboardRunWithOtelTracesReturnsNoTraces Recording #78657685806 Logs
DashboardRunWithOtelTracesReturnsNoTraces_DevLocalhost Recording #78657685806 Logs
DeployK8sBasicApiService Recording #78657685698 Logs
DeployK8sWithExternalHelmChart Recording #78657685737 Logs
DeployK8sWithGarnet Recording #78657685837 Logs
DeployK8sWithMongoDB Recording #78657686044 Logs
DeployK8sWithMySql Recording #78657686088 Logs
DeployK8sWithPostgres Recording #78657686084 Logs
DeployK8sWithRabbitMQ Recording #78657686054 Logs
DeployK8sWithRedis Recording #78657686053 Logs
DeployK8sWithSqlServer Recording #78657685729 Logs
DeployK8sWithValkey Recording #78657685843 Logs
DeployTypeScriptAppToKubernetes Recording #78657685919 Logs
DescribeCommandResolvesReplicaNames Recording #78657685768 Logs
DescribeCommandShowsRunningResources Recording #78657685768 Logs
DetachFormatJsonProducesValidJson Recording #78657685883 Logs
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance Recording #78657685883 Logs
DoPublishAndDeployListStepsWork Recording #78657685722 Logs
DocsCommand_RendersInteractiveMarkdownFromLocalSource Recording #78657685744 Logs
DoctorCommand_DetectsDeprecatedAgentConfig Recording #78657686068 Logs
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain Recording #78657685815 Logs
DoctorCommand_WithSslCertDir_ShowsTrusted Recording #78657685815 Logs
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted Recording #78657685815 Logs
GatewayWithoutExternalEndpoint_FailsPublishWithGuidance Recording #78657686005 Logs
GeneratedAspireDevScript_StartsWatchMode_WithConfiguredToolchain Recording #78657685856 Logs
GlobalMigration_HandlesCommentsAndTrailingCommas Recording #78657686103 Logs
GlobalMigration_HandlesMalformedLegacyJson Recording #78657686103 Logs
GlobalMigration_PreservesAllValueTypes Recording #78657686103 Logs
GlobalMigration_SkipsWhenNewConfigExists Recording #78657686103 Logs
GlobalSettings_MigratedFromLegacyFormat Recording #78657686103 Logs
IngressWithoutExternalEndpoint_FailsPublishWithGuidance Recording #78657686005 Logs
InitTypeScriptAppHost_AugmentsExistingViteRepoInWorkspaceSubdirectory Recording #78657685856 Logs
InteractiveCSharpInitCreatesExpectedFiles Recording #78657685819 Logs
InvalidAppHostPathWithComments_IsHealedOnRun Recording #78657685984 Logs
JavaScriptHostingApisRunFromTypeScriptAppHost Recording #78657685723 Logs
LatestCliCanStartStableChannelAppHost Recording #78657685876 Logs
LatestCliCanStartStableChannelTypeScriptAppHost Recording #78657685876 Logs
LegacySettingsMigration_AdjustsRelativeAppHostPath Recording #78657686104 Logs
LogsCommandShowsResourceLogs Recording #78657685865 Logs
OtelLogsReturnsStructuredLogsFromStarterApp Recording #78657685870 Logs
OtelLogsReturnsStructuredLogsFromStarterAppIsolated Recording #78657685870 Logs
PsCommandListsRunningAppHost Recording #78657685882 Logs
PsFormatJsonOutputsOnlyJsonToStdout Recording #78657685882 Logs
PublishJavaScriptPatternsGeneratesExpectedDockerComposeArtifacts Recording #78657685839 Logs
PublishWithConfigureEnvFileUpdatesEnvOutput Recording #78657685839 Logs
PublishWithDockerComposeServiceCallbackSucceeds Recording #78657685839 Logs
PublishWithoutOutputPathUsesAppHostDirectoryDefault Recording #78657685839 Logs
ResourceCommand_FailedExecution_DisplaysAppHostLogPathAndLogContainsEntries Recording #78657685836 Logs
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput Recording #78657685836 Logs
RestoreGeneratesSdkFiles Recording #78657685858 Logs
RestoreGeneratesSdkFiles_WithConfiguredToolchain Recording #78657685829 Logs
RestoreRefreshesGeneratedSdkAfterAddingIntegration Recording #78657685829 Logs
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes Recording #78657686113 Logs
RunFromParentDirectory_UsesExistingConfigNearAppHost Recording #78657685688 Logs
RunReportsSyntaxErrorsForDotNetAppHost Recording #78657685706 Logs
RunReportsSyntaxErrorsForTypeScriptAppHost Recording #78657685706 Logs
SecretCrudOnDotNetAppHost Recording #78657685933 Logs
SecretCrudOnTypeScriptAppHost Recording #78657686041 Logs
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels Recording #78657685812 Logs
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets Recording #78657685929 Logs
StartReportsSyntaxErrorsForDotNetAppHost Recording #78657685706 Logs
StartReportsSyntaxErrorsForTypeScriptAppHost Recording #78657685706 Logs
StopAllAppHostsFromAppHostDirectory Recording #78657686033 Logs
StopJavaPolyglotAppHostUsingApphostDirectory Recording #78657685756 Logs
StopNonInteractiveSingleAppHost Recording #78657686033 Logs
StopTypeScriptPolyglotAppHostUsingApphostDirectory Recording #78657686057 Logs
StopWithNoRunningAppHostExitsSuccessfully Recording #78657686034 Logs
TypeScriptAppHostRunDoesNotDeadlockWhenLazyOptionsInvokeAsyncCallback Recording #78657685779 Logs
UnAwaitedChainsCompileWithAutoResolvePromises Recording #78657685829 Logs
UpdateProjectChannelToStable_CSharpEmptyAppHost_PreservesAspireConfigChannel Recording #78657685973 Logs
UpdateProjectChannelToStable_CSharpSingleFileInit_PreservesAspireConfigChannel Recording #78657685973 Logs
UpdateProjectChannelToStable_TypeScriptSingleFileInit_PreservesAspireConfigChannel Recording #78657685973 Logs
UpdateProjectChannelToStable_TypeScript_PreviewsStablePackagesAndPreservesChannel Recording #78657685973 Logs

📹 Recordings uploaded automatically from CI run #26687044927

@joperezr
Copy link
Copy Markdown
Member

Failures are unrelated. Merging.

@joperezr joperezr merged commit 2574ef5 into release/13.4 May 30, 2026
614 of 618 checks passed
@joperezr joperezr deleted the backport/pr-17729-to-release/13.4 branch May 30, 2026 21:55
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.4 milestone May 30, 2026
@aspire-repo-bot
Copy link
Copy Markdown
Contributor Author

✅ No documentation update needed.

docs_optional → bug_fix_restores_documented_behavior

No signals triggered (signal_count = 0). This PR is a backport of #17729 to release/13.4 that fixes a regression introduced by #16046. It restores the ability to use DistributedApplicationTestingBuilder with DisableDashboard = false and dynamic ports — behavior that existed in 13.3 and is already documented.

Changed files:

  • src/Aspire.Hosting.Testing/DistributedApplicationFactory.cs — internal: defaults ASPIRE_ALLOW_UNSECURED_TRANSPORT=true in the testing builder
  • src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs — internal: fixes Kestrel loopback binding for 127.0.0.1:0 / [::1]:0
  • Three test files only

The PR body explicitly states: "this is a regression from 13.3 introduced by #16046 in 13.4. The fix restores the ability to use DistributedApplicationTestingBuilder with the dashboard enabled on dynamic ports." No new public APIs, options, or user-facing behavior was introduced.

@davidfowl
Copy link
Copy Markdown
Contributor

@joperezr @JamesNK There were some test failures. I think they were real. They are also in the main PR build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Servicing-approved Approved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants