From 2e73adcd51c044935423bcf55745a6cf18906c48 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Thu, 31 Jul 2025 17:45:31 -0500 Subject: [PATCH 1/3] External Services with URL Parameter fails to generate manifests Need to check for publish mode before calling GetValueAsync on the URL parameter Fix #10789 --- src/Aspire.Hosting/ResourceBuilderExtensions.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Aspire.Hosting/ResourceBuilderExtensions.cs b/src/Aspire.Hosting/ResourceBuilderExtensions.cs index 364d0c793c9..13c148cc7e5 100644 --- a/src/Aspire.Hosting/ResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ResourceBuilderExtensions.cs @@ -179,12 +179,14 @@ public static IResourceBuilder WithEnvironment(this IResourceBuilder bu { builder.WithEnvironment(async context => { - var url = await externalService.Resource.UrlParameter.GetValueAsync(context.CancellationToken).ConfigureAwait(false); - // In publish mode we can't validate the parameter value so we'll just use it without validating. - if (!context.ExecutionContext.IsPublishMode && !ExternalServiceResource.UrlIsValidForExternalService(url, out var _, out var message)) + if (!context.ExecutionContext.IsPublishMode) { - throw new DistributedApplicationException($"The URL parameter '{externalService.Resource.UrlParameter.Name}' for the external service '{externalService.Resource.Name}' is invalid: {message}"); + var url = await externalService.Resource.UrlParameter.GetValueAsync(context.CancellationToken).ConfigureAwait(false); + if (!ExternalServiceResource.UrlIsValidForExternalService(url, out var _, out var message)) + { + throw new DistributedApplicationException($"The URL parameter '{externalService.Resource.UrlParameter.Name}' for the external service '{externalService.Resource.Name}' is invalid: {message}"); + } } context.EnvironmentVariables[name] = externalService.Resource.UrlParameter; From d8f90d927efc07ec3226800ea2ec57b6de8bca92 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Thu, 31 Jul 2025 17:55:49 -0500 Subject: [PATCH 2/3] Add test --- .../ExternalServiceTests.cs | 17 +++++++++++++++++ ...ceWithParameterPublishManifest.verified.json | 11 +++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/Aspire.Hosting.Tests/Snapshots/ExternalServiceTests.ExternalServiceWithParameterPublishManifest.verified.json diff --git a/tests/Aspire.Hosting.Tests/ExternalServiceTests.cs b/tests/Aspire.Hosting.Tests/ExternalServiceTests.cs index 51933630ebf..7af05c9dc42 100644 --- a/tests/Aspire.Hosting.Tests/ExternalServiceTests.cs +++ b/tests/Aspire.Hosting.Tests/ExternalServiceTests.cs @@ -444,6 +444,23 @@ public async Task ExternalServiceWithParameterHttpHealthCheckResolvesUrlAsync() Assert.Contains(healthCheckKey, result.Entries.Keys); } + [Fact] + public async Task ExternalServiceWithParameterPublishManifest() + { + using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); + + var urlParam = builder.AddParameter("external-url"); + var externalService = builder.AddExternalService("external", urlParam); + + var project = builder.AddProject("project") + .WithReference(externalService) + .WithEnvironment("EXTERNAL_SERVICE", externalService); + + var manifest = await ManifestUtils.GetManifest(project.Resource); + + await Verify(manifest.ToString(), extension: "json"); + } + private sealed class TestProject : IProjectMetadata { public string ProjectPath => "testproject"; diff --git a/tests/Aspire.Hosting.Tests/Snapshots/ExternalServiceTests.ExternalServiceWithParameterPublishManifest.verified.json b/tests/Aspire.Hosting.Tests/Snapshots/ExternalServiceTests.ExternalServiceWithParameterPublishManifest.verified.json new file mode 100644 index 00000000000..73910c77b68 --- /dev/null +++ b/tests/Aspire.Hosting.Tests/Snapshots/ExternalServiceTests.ExternalServiceWithParameterPublishManifest.verified.json @@ -0,0 +1,11 @@ +{ + "type": "project.v0", + "path": "testproject", + "env": { + "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true", + "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true", + "OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY": "in_memory", + "services__external__default__0": "{external-url.value}", + "EXTERNAL_SERVICE": "{external-url.value}" + } +} \ No newline at end of file From 51e9742f84a9b239be833c996f66f81b759a2e85 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 1 Aug 2025 11:03:09 -0500 Subject: [PATCH 3/3] Fix test to not use Verify --- .../Aspire.Hosting.Tests/ExternalServiceTests.cs | 15 ++++++++++++++- ...viceWithParameterPublishManifest.verified.json | 11 ----------- 2 files changed, 14 insertions(+), 12 deletions(-) delete mode 100644 tests/Aspire.Hosting.Tests/Snapshots/ExternalServiceTests.ExternalServiceWithParameterPublishManifest.verified.json diff --git a/tests/Aspire.Hosting.Tests/ExternalServiceTests.cs b/tests/Aspire.Hosting.Tests/ExternalServiceTests.cs index 7af05c9dc42..a4c1acb6956 100644 --- a/tests/Aspire.Hosting.Tests/ExternalServiceTests.cs +++ b/tests/Aspire.Hosting.Tests/ExternalServiceTests.cs @@ -458,7 +458,20 @@ public async Task ExternalServiceWithParameterPublishManifest() var manifest = await ManifestUtils.GetManifest(project.Resource); - await Verify(manifest.ToString(), extension: "json"); + Assert.Equal( + """ + { + "type": "project.v0", + "path": "testproject", + "env": { + "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true", + "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true", + "OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY": "in_memory", + "services__external__default__0": "{external-url.value}", + "EXTERNAL_SERVICE": "{external-url.value}" + } + } + """, manifest.ToString()); } private sealed class TestProject : IProjectMetadata diff --git a/tests/Aspire.Hosting.Tests/Snapshots/ExternalServiceTests.ExternalServiceWithParameterPublishManifest.verified.json b/tests/Aspire.Hosting.Tests/Snapshots/ExternalServiceTests.ExternalServiceWithParameterPublishManifest.verified.json deleted file mode 100644 index 73910c77b68..00000000000 --- a/tests/Aspire.Hosting.Tests/Snapshots/ExternalServiceTests.ExternalServiceWithParameterPublishManifest.verified.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "project.v0", - "path": "testproject", - "env": { - "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true", - "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true", - "OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY": "in_memory", - "services__external__default__0": "{external-url.value}", - "EXTERNAL_SERVICE": "{external-url.value}" - } -} \ No newline at end of file