Skip to content

Publishers crash or silently drop resources when references cross resource boundaries #15900

@davidfowl

Description

@davidfowl

Problem

Publishers crash or silently drop resources when references cross resource boundaries. This affects all compute environment implementations:

  • Docker Compose (DockerComposeInfrastructure)
  • Azure Container Apps (AzureContainerAppsInfrastructure)
  • Azure App Service
  • Kubernetes
  • Any 3rd-party implementation that follows the same BeforeStart + GetComputeResources + ResourceMapping pattern

The root pattern is broken and needs to change.

Root cause

All publishers follow the same pattern:

  1. Subscribe to BeforeStartEvent
  2. Iterate GetComputeResources() (which filters out build-only, excluded, cross-environment resources)
  3. Build a ResourceMapping dictionary from only those filtered resources
  4. Resolve environment variables — which can reference ANY resource, including filtered ones
  5. 💥 KeyNotFoundException when a callback references a resource not in the mapping

GetComputeResources() makes a premature decision about which resources matter. It conflates "should this resource be deployed" with "should this resource exist in the resolution graph." These are different questions.

Affected publishers

Publisher Mapping Crash site
Docker Compose DockerComposeEnvironmentResource.ResourceMapping DockerComposeServiceResourceExtensions.ProcessValueAsync:22
Azure Container Apps ContainerAppEnvironmentContext._containerApps BaseContainerAppContext:227
Azure App Service Similar pattern Similar crash
Kubernetes Similar pattern Similar crash
3rd-party Anyone using GetComputeResources() Same

Scenario 1: Build-only container referenced by another resource

A JavaScript app without a PublishAs* method is build-only. When another resource references it, the publisher crashes.

var frontend = builder.AddViteApp("frontend", "./frontend")
    .WithHttpEndpoint(name: "http", targetPort: 3000);

builder.AddContainer("api", "apiimage")
    .WithReference(frontend.GetEndpoint("http"));

Result: KeyNotFoundException during publish-compose step.

Scenario 2: Excluded resource referenced by another resource

var excluded = builder.AddContainer("auth", "authimage")
    .WithHttpEndpoint(name: "http", targetPort: 8080)
    .ExcludeFromManifest();

builder.AddContainer("api", "apiimage")
    .WithReference(excluded.GetEndpoint("http"));

Result: KeyNotFoundException — excluded resource not in mapping.

Scenario 3: Cross-environment resource referenced

var compose1 = builder.AddDockerComposeEnvironment("compose1");
var compose2 = builder.AddDockerComposeEnvironment("compose2");

var db = builder.AddContainer("db", "postgres")
    .WithHttpEndpoint(name: "http", targetPort: 5432)
    .WithComputeEnvironment(compose2);

builder.AddContainer("api", "apiimage")
    .WithComputeEnvironment(compose1)
    .WithReference(db.GetEndpoint("http"));

Result: KeyNotFoundExceptiondb not in compose1's mapping.

Scenario 4: Build-only container with no consumer (silent omission)

// User forgot .PublishAsStaticWebsite()
builder.AddViteApp("frontend", "./frontend")
    .WithExternalHttpEndpoints();

builder.AddContainer("api", "apiimage");

Result: PIPELINE SUCCEEDED ✅ but frontend is silently missing from output. No error.

Proposed fix

Dependent on #11787 — infrastructure processing runs in BeforeStartEvent, which is too early.

We need to change the pattern that all publishers follow:

  1. Move infrastructure processing from BeforeStart into pipeline steps — proper ordering, other subscribers can add annotations first
  2. Build the full resource mapping — ALL resources get registered (endpoints only), not just compute resources. References never crash.
  3. Separate "resolvable" from "deployable" — all resources are resolvable (in the mapping), only compute resources are deployable (get DeploymentTargetAnnotation, appear in output)
  4. Validate the resource graph — after resolution, check for invalid states:
    • Build-only resource not consumed and not explicitly excluded → error with actionable message
    • Excluded resource referenced by a deployable resource → error with actionable message
  5. Update all publishers — Docker Compose first, then replicate to ACA, App Service, K8s
  6. Document the pattern for 3rd-party implementors

Test cases

4 test cases on branch davidfowl/fix-resource-exclusion in DockerComposeResourceExclusionTests.cs:

  • ReferencedResourceExcludedFromPublish_ShouldNotCrash
  • ReferencedResourceTargetingDifferentEnvironment_ShouldNotCrash
  • ReferencedBuildOnlyContainer_ShouldNotCrash
  • UnreferencedBuildOnlyContainer_ShouldFailWithClearError

Related

Metadata

Metadata

Assignees

Labels

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

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions