Skip to content

Fix resource service port collision in --isolated mode#18332

Merged
karolz-ms merged 7 commits into
mainfrom
cli-e2e-isolated-instances-otel-logs
Jun 19, 2026
Merged

Fix resource service port collision in --isolated mode#18332
karolz-ms merged 7 commits into
mainfrom
cli-e2e-isolated-instances-otel-logs

Conversation

@JamesNK

@JamesNK JamesNK commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

When running multiple AppHost instances with --isolated, the resource service endpoint in DashboardServiceHost uses the fixed port from ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL (e.g. port 22147 from launch profiles/user secrets). This causes an "address already in use" error when the second instance tries to bind to the same port.

Fix

In DashboardServiceHost.ConfigureKestrel, when DcpOptions.RandomizePorts is true (set by --isolated mode), always bind to port 0 on loopback instead of using the configured port. This lets the OS assign a unique port for each instance.

Changes

  • DashboardServiceHost.cs: Inject IOptions<DcpOptions> and check RandomizePorts. When true, bypass the configured URI and listen on IPAddress.Loopback with port 0.
  • CliE2EAutomatorHelpers.cs: Add apphost parameter to AspireStartAsync and AspireStopAsync helpers.
  • IsolatedInstancesOtelLogsTests.cs: New E2E test that creates two copies of a Starter project, starts both with --isolated --apphost, and verifies each instance's Content root path in OTLP structured logs points at the correct instance directory.

Copilot AI review requested due to automatic review settings June 19, 2026 06:44
@github-actions

github-actions Bot commented Jun 19, 2026

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 -- 18332

Or

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

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a single CLI end-to-end test, IsolatedInstancesOtelLogsTests, that validates two isolated Aspire instances produce distinct OpenTelemetry log output. It scaffolds a Starter app (no Redis), clones it into instance1/ and instance2/, starts both AppHosts with aspire start --isolated --apphost …, waits for apiservice to be running in each, captures aspire otel logs --format json from each, and asserts both contain resourceLogs and differ. The test follows the established Hex1b automator patterns already used by OtelLogsTests, StartStopTests, and WaitCommandTests, and the --apphost/--isolated/wait/otel logs/start/stop surface it exercises all exist in the CLI.

Changes:

  • Adds IsolatedInstancesOtelLogsTests.TwoIsolatedInstancesProduceDifferentOtelLogs, a new CLI E2E test for per-instance telemetry isolation.
  • Drives two concurrent isolated AppHosts targeted via --apphost and asserts the captured OTLP JSON differs.

Comment thread tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs Outdated
Comment thread tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs Outdated
When running multiple instances with --isolated, the AppHost's dashboard,
resource service, and OTLP endpoints all tried to bind to the same fixed
ports from the launch profile/user secrets. Fix by clearing port-bearing
env vars (ASPNETCORE_URLS, ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL, and
ASPIRE_DASHBOARD_OTLP_*_ENDPOINT_URL) in ConfigureIsolatedModeAsync so
the AppHost falls back to OS-assigned random ports.

Also suppress the launch profile in the dotnet-run fallback path when
isolated, so the fixed ports from the profile don't leak through.
Copilot AI review requested due to automatic review settings June 19, 2026 06:57
@github-actions

This comment has been minimized.

@JamesNK JamesNK marked this pull request as draft June 19, 2026 07:00
@JamesNK JamesNK added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Jun 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs Outdated
Comment thread tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs Outdated
@github-actions

This comment has been minimized.

Copilot AI review requested due to automatic review settings June 19, 2026 07:13
@JamesNK JamesNK force-pushed the cli-e2e-isolated-instances-otel-logs branch from 8be2133 to cfd6ee3 Compare June 19, 2026 07:13
@JamesNK JamesNK force-pushed the cli-e2e-isolated-instances-otel-logs branch from cfd6ee3 to 9d97225 Compare June 19, 2026 07:14
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs Outdated
Comment thread tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs Outdated
Copilot AI review requested due to automatic review settings June 19, 2026 07:39
@JamesNK JamesNK force-pushed the cli-e2e-isolated-instances-otel-logs branch from 9d97225 to fed34da Compare June 19, 2026 07:39
@JamesNK JamesNK force-pushed the cli-e2e-isolated-instances-otel-logs branch from fed34da to a302808 Compare June 19, 2026 07:44
@JamesNK JamesNK changed the title Add CLI E2E test for two isolated instances with distinct otel logs Fix resource service port collision in --isolated mode Jun 19, 2026
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs:109

  • These assertions don't validate what they claim. WaitUntilTextAsync("instance1") / "instance2" succeed immediately because those literals are already on screen from the directory names (mv IsolatedApp instance1, cp -r instance1 instance2) and the echoed commands themselves (otel_instance1.json, otel_instance2.json) — independent of whether grep actually extracted a "Content root path" line. As written, the test passes even if the captured JSON contains no content-root path at all, so it does not prove the two isolated instances produce distinct telemetry (the stated purpose). Note this also diverges from the PR description, which says it asserts the payloads differ via service.instance.id; neither service.instance.id nor any payload comparison is performed. Follow the sentinel-marker convention used just above for the resourceLogs checks: print a marker only when grep matches the instance-specific path, and wait for that marker.
        // Stop both instances
        await auto.AspireStopAsync(counter, apphost: appHost1);
        await auto.AspireStopAsync(counter, apphost: appHost2);
    }
}

Comment thread src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs Outdated
Comment thread tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs Outdated
@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Tests selector (audit mode)

The full test matrix and all jobs still run in audit mode. The tests and jobs below are what selective CI would run under enforcement.

Test projects (46 / 97)

  • Aspire.Cli.EndToEnd.Teststests/Aspire.Cli.EndToEnd.Tests/Helpers/CliE2EAutomatorHelpers.cs, tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs, via graph from tests/Aspire.Cli.EndToEnd.Tests/Helpers/CliE2EAutomatorHelpers.cs
  • Aspire.EndToEnd.Tests — affected project Aspire.Hosting
  • Aspire.Hosting.Analyzers.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs (2 hops)
  • Aspire.Hosting.Azure.Kubernetes.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Azure.Kusto.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Azure.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
  • Aspire.Hosting.Blazor.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Browsers.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.CodeGeneration.Go.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
  • Aspire.Hosting.CodeGeneration.Java.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
  • Aspire.Hosting.CodeGeneration.Python.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
  • Aspire.Hosting.CodeGeneration.Rust.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
  • Aspire.Hosting.CodeGeneration.TypeScript.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
  • Aspire.Hosting.Containers.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.DevTunnels.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Docker.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.DotnetTool.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.EntityFrameworkCore.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Foundry.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Garnet.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.GitHub.Models.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Go.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.JavaScript.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Kafka.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Keycloak.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Kubernetes.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Maui.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
  • Aspire.Hosting.Milvus.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.MongoDB.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.MySql.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Nats.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.OpenAI.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Oracle.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.PostgreSQL.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Python.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Qdrant.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.RabbitMQ.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Redis.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.RemoteHost.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs
  • Aspire.Hosting.Seq.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.SqlServer.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Testing.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs (2 hops)
  • Aspire.Hosting.Teststests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs, tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs, via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Valkey.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Hosting.Yarp.Tests — via graph from tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs
  • Aspire.Playground.Tests — via graph from src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs

Jobs (5)

  • cli-starter — via test Aspire.Cli.EndToEnd.Tests
  • deployment-e2e — affected project Aspire.Hosting.Azure
  • extension-e2esrc/Aspire.Hosting/Dashboard/DashboardServiceHost.cs, tests/Aspire.Cli.EndToEnd.Tests/Helpers/CliE2EAutomatorHelpers.cs, tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs, affected project Aspire.Hosting
  • polyglot — affected project Aspire.Hosting.Go
  • typescript-api-compat — affected project Aspire.Hosting

Selection computed for commit b5981a2.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@JamesNK JamesNK marked this pull request as ready for review June 19, 2026 10:42
Copilot AI review requested due to automatic review settings June 19, 2026 10:42
@JamesNK

JamesNK commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

PR Testing Report

Head Commit: b5981a2c03b4e63c75ad69aacdea248691051538
Tested At: 2026-06-19

Changes Analyzed

  • src/Aspire.Hosting/Dashboard/DashboardServiceHost.cs — Extract ResolveEndpoint helper; use DnsSafeHost for IPv6; respect RandomizePorts
  • tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceHostTests.cs — 16 new unit tests for ResolveEndpoint
  • tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs — Updated existing test (randomizePorts: false), new parameterized [Theory] test
  • tests/Aspire.Cli.EndToEnd.Tests/IsolatedInstancesOtelLogsTests.cs — New E2E test
  • tests/Aspire.Cli.EndToEnd.Tests/Helpers/CliE2EAutomatorHelpers.cs--apphost parameter support with QuoteBashArg

Test Scenarios Executed

Suite Tests Status
DashboardServiceHostTestsResolveEndpoint + ResolveScheme unit tests 21 ✅ Passed
DistributedApplicationTests — Integration tests (configured port passthrough + randomize-ports override for localhost/127.0.0.1/[::1]) 4 ✅ Passed
TestingBuilderTestsLoopbackWithDynamicPorts regression (including [::1]:0) 2 ✅ Passed

Notes

  • The product fix is in Aspire.Hosting.dll (NuGet package), not the CLI binary. Dogfood CLI testing cannot validate this fix because aspire new projects reference published NuGet packages, not the PR's built packages. Unit and integration tests are the correct local validation path.
  • The E2E test (IsolatedInstancesOtelLogsTests) validates end-to-end behavior in CI where packages are built from the PR branch.

Overall Result

✅ All 27 tests passed — PR VERIFIED

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@karolz-ms karolz-ms merged commit b90cede into main Jun 19, 2026
678 of 682 checks passed
@karolz-ms karolz-ms deleted the cli-e2e-isolated-instances-otel-logs branch June 19, 2026 16:25
@github-actions github-actions Bot added this to the 13.5 milestone Jun 19, 2026
@aspire-repo-bot

Copy link
Copy Markdown
Contributor

✅ No documentation update needed.

Decision: docs_required → already documented by name

Triggered signals (1): pr_body_has_cli_flag_mention

This PR fixes a port collision bug in DashboardServiceHost: when RandomizePorts is true (set by --isolated mode), the resource service endpoint was not using port 0, causing "address already in use" errors when a second isolated instance started. The fix makes the implementation match the already-documented behavior of --isolated.

Evidence that the triggered signal is already documented:

  • Signal pr_body_has_cli_flag_mention → identifier --isolated
    • reference/cli/commands/aspire-start.mdx (line 93–95): "--isolated — Run in isolated mode with randomized ports and isolated user secrets, allowing multiple instances of the same AppHost to run simultaneously."
    • reference/cli/commands/aspire-run.mdx (line 80–82): same wording, adds "This is useful for parallel testing or comparing different configurations side-by-side."
    • whats-new/aspire-13-2.mdx (line 193): "The new --isolated flag runs an apphost with randomized ports and isolated user secrets, preventing port conflicts and configuration collisions."

The docs already correctly describe the intended behavior that this PR restores. No new user-facing surface was introduced — all 4 remaining changed files are under tests/.

@davidfowl

Copy link
Copy Markdown
Contributor

/backport to release/13.4

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/13.4 (link to workflow run)

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

Labels

area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants