From 25687b6c5c111a5e7f2bb9b2a4ef5ca9f31a091a Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 29 Mar 2024 01:24:53 -0700 Subject: [PATCH] - In order for tools to better describe multiple endpoints int the manifest, we must include port information. At runtime most of the port information is dynamically generated and thus not described (for anything but containers), that doesn't work well when trying to deploy to various environments. We allocate ports in situations where there are none to match the runtime behavior. (#3274) - Change also renames ContainerPort to TargetPort everywhere. - Added `WithExternalHttpEndpoints` to mark all http and https endpoints as external. --- playground/AWS/AWS.AppHost/Program.cs | 1 + .../AWS/AWS.AppHost/aspire-manifest.json | 6 +- .../EventHubs.AppHost/Program.cs | 1 + .../AzureSearch.AppHost/Program.cs | 1 + .../AzureStorageEndToEnd.AppHost/Program.cs | 1 + .../aspire-manifest.json | 6 +- .../CosmosEndToEnd.AppHost/Program.cs | 1 + .../aspire-manifest.json | 6 +- .../DatabaseMigration.AppHost/Program.cs | 1 + .../aspire-manifest.json | 8 +- .../OpenAIEndToEnd.AppHost/Program.cs | 1 + .../aspire-manifest.json | 6 +- .../ParameterEndToEnd.AppHost/Program.cs | 1 + .../aspire-manifest.json | 6 +- .../PostgresEndToEnd.AppHost/Program.cs | 1 + .../aspire-manifest.json | 18 +- .../ProxylessEndToEnd.AppHost/Program.cs | 2 +- .../aspire-manifest.json | 2 +- .../SqlServerEndToEnd.AppHost/Program.cs | 1 + .../aspire-manifest.json | 8 +- playground/Stress/Stress.AppHost/Program.cs | 2 +- playground/TestShop/AppHost/Program.cs | 1 + .../TestShop/AppHost/aspire-manifest.json | 59 ++-- .../bicep/BicepSample.AppHost/Program.cs | 1 + .../BicepSample.AppHost/aspire-manifest.json | 8 +- playground/cdk/CdkSample.AppHost/Program.cs | 1 + .../CdkSample.AppHost/aspire-manifest.json | 6 +- playground/dapr/AppHost/aspire-manifest.json | 6 +- playground/mongo/Mongo.AppHost/Program.cs | 1 + .../mongo/Mongo.AppHost/aspire-manifest.json | 8 +- playground/mysql/MySqlDb.AppHost/Program.cs | 1 + .../MySqlDb.AppHost/aspire-manifest.json | 8 +- playground/nats/Nats.AppHost/Program.cs | 1 + .../nats/Nats.AppHost/aspire-manifest.json | 8 +- playground/orleans/OrleansAppHost/Program.cs | 1 + .../OrleansAppHost/aspire-manifest.json | 18 +- playground/seq/Seq.AppHost/Program.cs | 1 + .../seq/Seq.AppHost/aspire-manifest.json | 8 +- playground/signalr/SignalRAppHost/Program.cs | 1 + .../SignalRAppHost/aspire-manifest.json | 6 +- .../AzureCosmosDBExtensions.cs | 2 +- .../AzureStorageExtensions.cs | 6 +- .../KafkaBuilderExtensions.cs | 2 +- .../MongoDBBuilderExtensions.cs | 4 +- .../MySqlBuilderExtensions.cs | 4 +- .../NatsBuilderExtensions.cs | 2 +- .../OracleDatabaseBuilderExtensions.cs | 2 +- .../PostgresBuilderExtensions.cs | 4 +- .../RabbitMQBuilderExtensions.cs | 4 +- .../RedisBuilderExtensions.cs | 4 +- .../SeqBuilderExtensions.cs | 2 +- .../SqlServerBuilderExtensions.cs | 2 +- .../ApplicationModel/EndpointAnnotation.cs | 10 +- .../EndpointAnnotationExtensions.cs | 37 --- src/Aspire.Hosting/Dcp/ApplicationExecutor.cs | 4 +- .../ExecutableResourceBuilderExtensions.cs | 2 +- .../Publishing/ManifestPublishingContext.cs | 53 ++- .../Publishing/PortAllocator.cs | 29 ++ .../ResourceBuilderExtensions.cs | 108 +++--- .../DistributedApplicationTests.cs | 2 +- .../Kafka/AddKafkaTests.cs | 4 +- .../ManifestGenerationTests.cs | 30 +- .../MongoDB/AddMongoDBTests.cs | 6 +- .../MySql/AddMySqlTests.cs | 8 +- .../Aspire.Hosting.Tests/Nats/AddNatsTests.cs | 6 +- .../Oracle/AddOracleDatabaseTests.cs | 10 +- .../Aspire.Hosting.Tests/PortAllocatorTest.cs | 37 +++ .../Postgres/AddPostgresTests.cs | 14 +- .../RabbitMQ/AddRabbitMQTests.cs | 14 +- .../Redis/AddRedisTests.cs | 6 +- .../ResourceNotificationTests.cs | 2 +- .../SqlServer/AddSqlServerTests.cs | 6 +- .../Utils/ManifestUtils.cs | 29 ++ .../Aspire.Hosting.Tests/WithEndpointTests.cs | 308 +++++++++++++++++- .../TestProject.AppHost/TestProgram.cs | 4 +- .../TestProject.AppHost/aspire-manifest.json | 30 +- 76 files changed, 727 insertions(+), 294 deletions(-) delete mode 100644 src/Aspire.Hosting/ApplicationModel/EndpointAnnotationExtensions.cs create mode 100644 src/Aspire.Hosting/Publishing/PortAllocator.cs create mode 100644 tests/Aspire.Hosting.Tests/PortAllocatorTest.cs diff --git a/playground/AWS/AWS.AppHost/Program.cs b/playground/AWS/AWS.AppHost/Program.cs index d672be1607..72c95c139e 100644 --- a/playground/AWS/AWS.AppHost/Program.cs +++ b/playground/AWS/AWS.AppHost/Program.cs @@ -21,6 +21,7 @@ // .WithReference(awsConfig); builder.AddProject("Frontend") + .WithExternalHttpEndpoints() // Demonstrating binding all of the output variables to a section in IConfiguration. By default they are bound to the AWS::Resources prefix. // The prefix is configurable by the optional configSection parameter. .WithReference(awsResources) diff --git a/playground/AWS/AWS.AppHost/aspire-manifest.json b/playground/AWS/AWS.AppHost/aspire-manifest.json index e19c5e7191..6fc8616af0 100644 --- a/playground/AWS/AWS.AppHost/aspire-manifest.json +++ b/playground/AWS/AWS.AppHost/aspire-manifest.json @@ -23,12 +23,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/playground/AspireEventHub/EventHubs.AppHost/Program.cs b/playground/AspireEventHub/EventHubs.AppHost/Program.cs index 0289774251..4a97f0c5e0 100644 --- a/playground/AspireEventHub/EventHubs.AppHost/Program.cs +++ b/playground/AspireEventHub/EventHubs.AppHost/Program.cs @@ -12,6 +12,7 @@ .WithReference(blob); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(eventHub); builder.Build().Run(); diff --git a/playground/AzureSearchEndToEnd/AzureSearch.AppHost/Program.cs b/playground/AzureSearchEndToEnd/AzureSearch.AppHost/Program.cs index 739568fbef..254c1b047f 100644 --- a/playground/AzureSearchEndToEnd/AzureSearch.AppHost/Program.cs +++ b/playground/AzureSearchEndToEnd/AzureSearch.AppHost/Program.cs @@ -6,6 +6,7 @@ var azureSearch = builder.AddAzureSearch("search"); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(azureSearch); // This project is only added in playground projects to support development/debugging diff --git a/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Program.cs b/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Program.cs index ae0834f703..0f4c2e9b62 100644 --- a/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Program.cs +++ b/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Program.cs @@ -10,6 +10,7 @@ var blobs = storage.AddBlobs("blobs"); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(blobs); // This project is only added in playground projects to support development/debugging diff --git a/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/aspire-manifest.json b/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/aspire-manifest.json index 164c98c9dd..483ce4f4d9 100644 --- a/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/aspire-manifest.json +++ b/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/aspire-manifest.json @@ -25,12 +25,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs b/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs index 2632cc228f..2900210872 100644 --- a/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs +++ b/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/Program.cs @@ -8,6 +8,7 @@ .RunAsEmulator(); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(db); // This project is only added in playground projects to support development/debugging diff --git a/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/aspire-manifest.json b/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/aspire-manifest.json index 3194d7928c..e58262b4e9 100644 --- a/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/aspire-manifest.json +++ b/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/aspire-manifest.json @@ -21,12 +21,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/playground/DatabaseMigration/DatabaseMigration.AppHost/Program.cs b/playground/DatabaseMigration/DatabaseMigration.AppHost/Program.cs index 3b2f14c340..ef455af8dc 100644 --- a/playground/DatabaseMigration/DatabaseMigration.AppHost/Program.cs +++ b/playground/DatabaseMigration/DatabaseMigration.AppHost/Program.cs @@ -6,6 +6,7 @@ var db1 = builder.AddSqlServer("sql1").AddDatabase("db1"); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(db1); builder.AddProject("migration") diff --git a/playground/DatabaseMigration/DatabaseMigration.AppHost/aspire-manifest.json b/playground/DatabaseMigration/DatabaseMigration.AppHost/aspire-manifest.json index ae98da2c03..67b9fce7a8 100644 --- a/playground/DatabaseMigration/DatabaseMigration.AppHost/aspire-manifest.json +++ b/playground/DatabaseMigration/DatabaseMigration.AppHost/aspire-manifest.json @@ -13,7 +13,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1433 + "targetPort": 1433 } } }, @@ -34,12 +34,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } }, diff --git a/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/Program.cs b/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/Program.cs index b349fb0e46..9d4e54b455 100644 --- a/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/Program.cs +++ b/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/Program.cs @@ -9,6 +9,7 @@ ); builder.AddProject("webstory") + .WithExternalHttpEndpoints() .WithReference(openai) .WithEnvironment("OpenAI__DeploymentName", deploymentAndModelName); diff --git a/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/aspire-manifest.json b/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/aspire-manifest.json index 94ce89f997..43689938dd 100644 --- a/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/aspire-manifest.json +++ b/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/aspire-manifest.json @@ -23,12 +23,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/Program.cs b/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/Program.cs index f995f96202..0958554c4b 100644 --- a/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/Program.cs +++ b/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/Program.cs @@ -22,6 +22,7 @@ var insertionrows = builder.AddParameter("insertionrows"); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithEnvironment("InsertionRows", insertionrows) .WithReference(db); diff --git a/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/aspire-manifest.json b/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/aspire-manifest.json index 04f5e0a74c..78d771e575 100644 --- a/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/aspire-manifest.json +++ b/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/aspire-manifest.json @@ -38,12 +38,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/Program.cs b/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/Program.cs index f686a15398..7c17e4c45e 100644 --- a/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/Program.cs +++ b/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/Program.cs @@ -22,6 +22,7 @@ var db10 = builder.AddPostgres("pg10").WithPgAdmin().PublishAsConnectionString().AddDatabase("db10"); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(db1) .WithReference(db2) .WithReference(db3) diff --git a/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/aspire-manifest.json b/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/aspire-manifest.json index d19dc823d6..c6e34bc2f9 100644 --- a/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/aspire-manifest.json +++ b/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/aspire-manifest.json @@ -15,7 +15,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } }, @@ -38,7 +38,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } }, @@ -61,7 +61,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } }, @@ -88,7 +88,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } }, @@ -111,7 +111,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } }, @@ -134,7 +134,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } }, @@ -187,12 +187,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } }, diff --git a/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/Program.cs b/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/Program.cs index 007defd04a..1ce368a3bb 100644 --- a/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/Program.cs +++ b/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/Program.cs @@ -17,7 +17,7 @@ .WithReference(redis); builder.AddProject("api2", launchProfileName: null) - .WithEndpoint(13456, "http") + .WithHttpEndpoint(port: 13456) .WithReference(redis); // This project is only added in playground projects to support development/debugging diff --git a/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/aspire-manifest.json b/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/aspire-manifest.json index 5a2e9f36e5..62952cb05e 100644 --- a/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/aspire-manifest.json +++ b/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/aspire-manifest.json @@ -9,7 +9,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 6379 + "targetPort": 6379 } } }, diff --git a/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/Program.cs b/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/Program.cs index 2139dc7250..2b6b7fbcbb 100644 --- a/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/Program.cs +++ b/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/Program.cs @@ -7,6 +7,7 @@ var db2 = builder.AddSqlServer("sql2").PublishAsContainer().AddDatabase("db2"); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(db1) .WithReference(db2); diff --git a/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/aspire-manifest.json b/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/aspire-manifest.json index 2fadd89455..c1a0ecaccd 100644 --- a/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/aspire-manifest.json +++ b/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/aspire-manifest.json @@ -26,7 +26,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1433 + "targetPort": 1433 } } }, @@ -48,12 +48,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } }, diff --git a/playground/Stress/Stress.AppHost/Program.cs b/playground/Stress/Stress.AppHost/Program.cs index d9316e88aa..6be76f2599 100644 --- a/playground/Stress/Stress.AppHost/Program.cs +++ b/playground/Stress/Stress.AppHost/Program.cs @@ -13,7 +13,7 @@ for (var i = 0; i < 30; i++) { var port = 5180 + i; - serviceBuilder.WithHttpEndpoint(port, $"http-{port}"); + serviceBuilder.WithHttpEndpoint(port, name: $"http-{port}"); } builder.AddProject("stress-telemetryservice"); diff --git a/playground/TestShop/AppHost/Program.cs b/playground/TestShop/AppHost/Program.cs index f5f7ea6d54..586c121b6c 100644 --- a/playground/TestShop/AppHost/Program.cs +++ b/playground/TestShop/AppHost/Program.cs @@ -23,6 +23,7 @@ .WithReference(messaging); builder.AddProject("frontend") + .WithExternalHttpEndpoints() .WithReference(basketService) .WithReference(catalogService); diff --git a/playground/TestShop/AppHost/aspire-manifest.json b/playground/TestShop/AppHost/aspire-manifest.json index 9a5997fdca..aa91f5dce0 100644 --- a/playground/TestShop/AppHost/aspire-manifest.json +++ b/playground/TestShop/AppHost/aspire-manifest.json @@ -15,7 +15,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } }, @@ -34,7 +34,7 @@ ], "volumes": [ { - "name": "basketcache-data", + "name": "TestShop.AppHost-basketcache-data", "target": "/data", "readOnly": false } @@ -44,7 +44,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 6379 + "targetPort": 6379 } } }, @@ -70,20 +70,43 @@ } } }, + "rabbitmq-password": { + "type": "parameter.v0", + "value": "{rabbitmq-password.inputs.value}", + "inputs": { + "value": { + "type": "string", + "secret": true + } + } + }, "messaging": { "type": "container.v0", - "connectionString": "amqp://guest:{messaging-password.value}@{messaging.bindings.tcp.host}:{messaging.bindings.tcp.port}", - "image": "rabbitmq:3", + "connectionString": "amqp://guest:{rabbitmq-password.value}@{messaging.bindings.tcp.host}:{messaging.bindings.tcp.port}", + "image": "rabbitmq:3-management", + "volumes": [ + { + "name": "TestShop.AppHost-messaging-data", + "target": "/var/lib/rabbitmq", + "readOnly": false + } + ], "env": { "RABBITMQ_DEFAULT_USER": "guest", - "RABBITMQ_DEFAULT_PASS": "{messaging-password.value}" + "RABBITMQ_DEFAULT_PASS": "{rabbitmq-password.value}" }, "bindings": { "tcp": { "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5672 + "targetPort": 5672 + }, + "management": { + "scheme": "http", + "protocol": "tcp", + "transport": "http", + "targetPort": 15672 } } }, @@ -126,12 +149,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } }, @@ -205,22 +230,6 @@ } } } - }, - "messaging-password": { - "type": "parameter.v0", - "value": "{messaging-password.inputs.value}", - "inputs": { - "value": { - "type": "string", - "secret": true, - "default": { - "generate": { - "minLength": 22, - "special": false - } - } - } - } } } } \ No newline at end of file diff --git a/playground/bicep/BicepSample.AppHost/Program.cs b/playground/bicep/BicepSample.AppHost/Program.cs index 8f9802633b..f2aae2b42e 100644 --- a/playground/bicep/BicepSample.AppHost/Program.cs +++ b/playground/bicep/BicepSample.AppHost/Program.cs @@ -57,6 +57,7 @@ var signalr = builder.AddAzureSignalR("signalr"); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(sqlServer) .WithReference(pg) .WithReference(cosmosDb) diff --git a/playground/bicep/BicepSample.AppHost/aspire-manifest.json b/playground/bicep/BicepSample.AppHost/aspire-manifest.json index 433c841873..3e9afe47ef 100644 --- a/playground/bicep/BicepSample.AppHost/aspire-manifest.json +++ b/playground/bicep/BicepSample.AppHost/aspire-manifest.json @@ -23,7 +23,7 @@ }, "test0": { "type": "azure.bicep.v0", - "path": "../../../../../Users/midenn/AppData/Local/Temp/tmpjkt1f3.tmp.bicep" + "path": "../../../../../../Users/davifowl/AppData/Local/Temp/tmppj1k21.tmp.bicep" }, "kv3": { "type": "azure.bicep.v0", @@ -176,12 +176,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/playground/cdk/CdkSample.AppHost/Program.cs b/playground/cdk/CdkSample.AppHost/Program.cs index 30aa2f807a..5dcd0a9757 100644 --- a/playground/cdk/CdkSample.AppHost/Program.cs +++ b/playground/cdk/CdkSample.AppHost/Program.cs @@ -89,6 +89,7 @@ }); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(signalr) .WithReference(blobs) .WithReference(sqldb) diff --git a/playground/cdk/CdkSample.AppHost/aspire-manifest.json b/playground/cdk/CdkSample.AppHost/aspire-manifest.json index f5400c9149..20e78d455a 100644 --- a/playground/cdk/CdkSample.AppHost/aspire-manifest.json +++ b/playground/cdk/CdkSample.AppHost/aspire-manifest.json @@ -194,12 +194,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } }, diff --git a/playground/dapr/AppHost/aspire-manifest.json b/playground/dapr/AppHost/aspire-manifest.json index 2e5118bbe5..1ec4ffc6a2 100644 --- a/playground/dapr/AppHost/aspire-manifest.json +++ b/playground/dapr/AppHost/aspire-manifest.json @@ -17,7 +17,8 @@ "path": "../ServiceA/DaprServiceA.csproj", "env": { "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true", - "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true" + "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true", + "ASPNETCORE_FORWARDEDHEADERS_ENABLED": "true" }, "bindings": { "http": { @@ -48,7 +49,8 @@ "path": "../ServiceB/DaprServiceB.csproj", "env": { "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true", - "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true" + "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true", + "ASPNETCORE_FORWARDEDHEADERS_ENABLED": "true" }, "bindings": { "http": { diff --git a/playground/mongo/Mongo.AppHost/Program.cs b/playground/mongo/Mongo.AppHost/Program.cs index 42767b79e5..7d3e8fc729 100644 --- a/playground/mongo/Mongo.AppHost/Program.cs +++ b/playground/mongo/Mongo.AppHost/Program.cs @@ -8,6 +8,7 @@ .PublishAsContainer(); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(db); // This project is only added in playground projects to support development/debugging diff --git a/playground/mongo/Mongo.AppHost/aspire-manifest.json b/playground/mongo/Mongo.AppHost/aspire-manifest.json index 6d54b2991a..accf42f3e1 100644 --- a/playground/mongo/Mongo.AppHost/aspire-manifest.json +++ b/playground/mongo/Mongo.AppHost/aspire-manifest.json @@ -9,7 +9,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 27017 + "targetPort": 27017 } } }, @@ -26,12 +26,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/playground/mysql/MySqlDb.AppHost/Program.cs b/playground/mysql/MySqlDb.AppHost/Program.cs index 20f1b3e6aa..78e6aaa23a 100644 --- a/playground/mysql/MySqlDb.AppHost/Program.cs +++ b/playground/mysql/MySqlDb.AppHost/Program.cs @@ -11,6 +11,7 @@ .AddDatabase(catalogDbName); builder.AddProject("apiservice") + .WithExternalHttpEndpoints() .WithReference(catalogDb); builder.Build().Run(); diff --git a/playground/mysql/MySqlDb.AppHost/aspire-manifest.json b/playground/mysql/MySqlDb.AppHost/aspire-manifest.json index ec0b7c4877..e86ca23895 100644 --- a/playground/mysql/MySqlDb.AppHost/aspire-manifest.json +++ b/playground/mysql/MySqlDb.AppHost/aspire-manifest.json @@ -20,7 +20,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 3306 + "targetPort": 3306 } } }, @@ -41,12 +41,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } }, diff --git a/playground/nats/Nats.AppHost/Program.cs b/playground/nats/Nats.AppHost/Program.cs index 100b340df3..ecd1da87d2 100644 --- a/playground/nats/Nats.AppHost/Program.cs +++ b/playground/nats/Nats.AppHost/Program.cs @@ -4,6 +4,7 @@ .WithJetStream(); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(nats); builder.AddProject("backend") diff --git a/playground/nats/Nats.AppHost/aspire-manifest.json b/playground/nats/Nats.AppHost/aspire-manifest.json index fd5fc58988..bdf3efd7f0 100644 --- a/playground/nats/Nats.AppHost/aspire-manifest.json +++ b/playground/nats/Nats.AppHost/aspire-manifest.json @@ -12,7 +12,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 4222 + "targetPort": 4222 } } }, @@ -29,12 +29,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } }, diff --git a/playground/orleans/OrleansAppHost/Program.cs b/playground/orleans/OrleansAppHost/Program.cs index eaf8c3bbae..31436147ba 100644 --- a/playground/orleans/OrleansAppHost/Program.cs +++ b/playground/orleans/OrleansAppHost/Program.cs @@ -17,6 +17,7 @@ builder.AddProject("silo") .WithReference(orleans) + .WithExternalHttpEndpoints() .WithReplicas(3); // This project is only added in playground projects to support development/debugging diff --git a/playground/orleans/OrleansAppHost/aspire-manifest.json b/playground/orleans/OrleansAppHost/aspire-manifest.json index c7fcf03d2a..c1db76a4a2 100644 --- a/playground/orleans/OrleansAppHost/aspire-manifest.json +++ b/playground/orleans/OrleansAppHost/aspire-manifest.json @@ -29,31 +29,35 @@ "Orleans__GrainStorage__Default__ProviderType": "AzureBlobStorage", "Orleans__GrainStorage__Default__ServiceKey": "grainstate", "ConnectionStrings__grainstate": "{grainstate.connectionString}", - "Orleans__ClusterId": "84bf51f8b9c14c58ac7119216fde5c20", + "Orleans__ClusterId": "02348447ebc64775888d944ac74b95ef", "Orleans__EnableDistributedTracing": "true", - "Orleans__Endpoints__SiloPort": "{silo.bindings.orleans-silo.port}", - "Orleans__Endpoints__GatewayPort": "{silo.bindings.orleans-gateway.port}" + "Orleans__Endpoints__SiloPort": "{silo.bindings.orleans-silo.targetPort}", + "Orleans__Endpoints__GatewayPort": "{silo.bindings.orleans-gateway.targetPort}" }, "bindings": { "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "orleans-silo": { "scheme": "tcp", "protocol": "tcp", - "transport": "tcp" + "transport": "tcp", + "targetPort": 8000 }, "orleans-gateway": { "scheme": "tcp", "protocol": "tcp", - "transport": "tcp" + "transport": "tcp", + "targetPort": 8001 } } } diff --git a/playground/seq/Seq.AppHost/Program.cs b/playground/seq/Seq.AppHost/Program.cs index 26bb1e5edc..60071dc5eb 100644 --- a/playground/seq/Seq.AppHost/Program.cs +++ b/playground/seq/Seq.AppHost/Program.cs @@ -6,6 +6,7 @@ var seq = builder.AddSeq("seq"); builder.AddProject("api") + .WithExternalHttpEndpoints() .WithReference(seq); // This project is only added in playground projects to support development/debugging diff --git a/playground/seq/Seq.AppHost/aspire-manifest.json b/playground/seq/Seq.AppHost/aspire-manifest.json index 19657294e1..faa82133d7 100644 --- a/playground/seq/Seq.AppHost/aspire-manifest.json +++ b/playground/seq/Seq.AppHost/aspire-manifest.json @@ -12,7 +12,7 @@ "scheme": "http", "protocol": "tcp", "transport": "http", - "containerPort": 80 + "targetPort": 80 } } }, @@ -29,12 +29,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/playground/signalr/SignalRAppHost/Program.cs b/playground/signalr/SignalRAppHost/Program.cs index 40cc0d0b50..127aa7cb30 100644 --- a/playground/signalr/SignalRAppHost/Program.cs +++ b/playground/signalr/SignalRAppHost/Program.cs @@ -3,6 +3,7 @@ var signalr = builder.AddAzureSignalR("signalr1"); builder.AddProject("webfrontend") + .WithExternalHttpEndpoints() .WithReference(signalr); builder.Build().Run(); diff --git a/playground/signalr/SignalRAppHost/aspire-manifest.json b/playground/signalr/SignalRAppHost/aspire-manifest.json index f7b40899e0..9adb37253f 100644 --- a/playground/signalr/SignalRAppHost/aspire-manifest.json +++ b/playground/signalr/SignalRAppHost/aspire-manifest.json @@ -22,12 +22,14 @@ "http": { "scheme": "http", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true }, "https": { "scheme": "https", "protocol": "tcp", - "transport": "http" + "transport": "http", + "external": true } } } diff --git a/src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBExtensions.cs b/src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBExtensions.cs index 29b6a798f5..f7b2b961b8 100644 --- a/src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBExtensions.cs +++ b/src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBExtensions.cs @@ -92,7 +92,7 @@ public static IResourceBuilder RunAsEmulator(this IResour return builder; } - builder.WithEndpoint(name: "emulator", containerPort: 8081) + builder.WithEndpoint(name: "emulator", targetPort: 8081) .WithAnnotation(new ContainerImageAnnotation { Image = "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator", Tag = "latest" }); if (configureContainer != null) diff --git a/src/Aspire.Hosting.Azure.Storage/AzureStorageExtensions.cs b/src/Aspire.Hosting.Azure.Storage/AzureStorageExtensions.cs index 125205e6bc..d2b39e6285 100644 --- a/src/Aspire.Hosting.Azure.Storage/AzureStorageExtensions.cs +++ b/src/Aspire.Hosting.Azure.Storage/AzureStorageExtensions.cs @@ -95,9 +95,9 @@ public static IResourceBuilder RunAsEmulator(this IResourc return builder; } - builder.WithEndpoint(name: "blob", containerPort: 10000) - .WithEndpoint(name: "queue", containerPort: 10001) - .WithEndpoint(name: "table", containerPort: 10002) + builder.WithEndpoint(name: "blob", targetPort: 10000) + .WithEndpoint(name: "queue", targetPort: 10001) + .WithEndpoint(name: "table", targetPort: 10002) .WithAnnotation(new ContainerImageAnnotation { Image = "mcr.microsoft.com/azure-storage/azurite", Tag = "3.29.0" }); if (configureContainer != null) diff --git a/src/Aspire.Hosting.Kafka/KafkaBuilderExtensions.cs b/src/Aspire.Hosting.Kafka/KafkaBuilderExtensions.cs index bb4de60ce8..c4870fd05e 100644 --- a/src/Aspire.Hosting.Kafka/KafkaBuilderExtensions.cs +++ b/src/Aspire.Hosting.Kafka/KafkaBuilderExtensions.cs @@ -24,7 +24,7 @@ public static IResourceBuilder AddKafka(this IDistributedAp { var kafka = new KafkaServerResource(name); return builder.AddResource(kafka) - .WithEndpoint(containerPort: KafkaBrokerPort, hostPort: port, name: KafkaServerResource.PrimaryEndpointName) + .WithEndpoint(targetPort: KafkaBrokerPort, port: port, name: KafkaServerResource.PrimaryEndpointName) .WithImage("confluentinc/confluent-local", "7.6.0") .WithEnvironment(context => ConfigureKafkaContainer(context, kafka)); } diff --git a/src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs b/src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs index 06536209c7..61c9e940f4 100644 --- a/src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs +++ b/src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs @@ -28,7 +28,7 @@ public static IResourceBuilder AddMongoDB(this IDistribut return builder .AddResource(mongoDBContainer) - .WithEndpoint(hostPort: port, containerPort: DefaultContainerPort, name: MongoDBServerResource.PrimaryEndpointName) + .WithEndpoint(port: port, targetPort: DefaultContainerPort, name: MongoDBServerResource.PrimaryEndpointName) .WithImage(MongoDBContainerImageTags.Image, MongoDBContainerImageTags.Tag); } @@ -66,7 +66,7 @@ public static IResourceBuilder WithMongoExpress(this IResourceBuilder b builder.ApplicationBuilder.AddResource(mongoExpressContainer) .WithImage("mongo-express", "1.0.2-20") .WithEnvironment(context => ConfigureMongoExpressContainer(context, builder.Resource)) - .WithHttpEndpoint(containerPort: 8081, hostPort: hostPort, name: containerName) + .WithHttpEndpoint(targetPort: 8081, port: hostPort, name: containerName) .ExcludeFromManifest(); return builder; diff --git a/src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs b/src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs index 2e68d436f5..a2d22349c9 100644 --- a/src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs +++ b/src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs @@ -29,7 +29,7 @@ public static IResourceBuilder AddMySql(this IDistributedAp var resource = new MySqlServerResource(name, passwordParameter); return builder.AddResource(resource) - .WithEndpoint(hostPort: port, containerPort: 3306, name: MySqlServerResource.PrimaryEndpointName) // Internal port is always 3306. + .WithEndpoint(port: port, targetPort: 3306, name: MySqlServerResource.PrimaryEndpointName) // Internal port is always 3306. .WithImage(MySqlContainerImageTags.Image, MySqlContainerImageTags.Tag) .WithEnvironment(context => { @@ -75,7 +75,7 @@ public static IResourceBuilder WithPhpMyAdmin(this IResourceBuilder bui var phpMyAdminContainer = new PhpMyAdminContainerResource(containerName); builder.ApplicationBuilder.AddResource(phpMyAdminContainer) .WithImage("phpmyadmin", "5.2") - .WithHttpEndpoint(containerPort: 80, hostPort: hostPort, name: containerName) + .WithHttpEndpoint(targetPort: 80, port: hostPort, name: containerName) .WithBindMount(Path.GetTempFileName(), "/etc/phpmyadmin/config.user.inc.php") .ExcludeFromManifest(); diff --git a/src/Aspire.Hosting.Nats/NatsBuilderExtensions.cs b/src/Aspire.Hosting.Nats/NatsBuilderExtensions.cs index 8885fb4f1a..1b28ef4178 100644 --- a/src/Aspire.Hosting.Nats/NatsBuilderExtensions.cs +++ b/src/Aspire.Hosting.Nats/NatsBuilderExtensions.cs @@ -22,7 +22,7 @@ public static IResourceBuilder AddNats(this IDistributedAppl { var nats = new NatsServerResource(name); return builder.AddResource(nats) - .WithEndpoint(containerPort: 4222, hostPort: port, name: NatsServerResource.PrimaryEndpointName) + .WithEndpoint(targetPort: 4222, port: port, name: NatsServerResource.PrimaryEndpointName) .WithImage(NatsContainerImageTags.Image, NatsContainerImageTags.Tag); } diff --git a/src/Aspire.Hosting.Oracle/OracleDatabaseBuilderExtensions.cs b/src/Aspire.Hosting.Oracle/OracleDatabaseBuilderExtensions.cs index 4a5c752ece..a46150c03b 100644 --- a/src/Aspire.Hosting.Oracle/OracleDatabaseBuilderExtensions.cs +++ b/src/Aspire.Hosting.Oracle/OracleDatabaseBuilderExtensions.cs @@ -42,7 +42,7 @@ public static IResourceBuilder AddOracle(this IDis var oracleDatabaseServer = new OracleDatabaseServerResource(name, passwordParameter); return builder.AddResource(oracleDatabaseServer) - .WithEndpoint(hostPort: port, containerPort: 1521, name: OracleDatabaseServerResource.PrimaryEndpointName) + .WithEndpoint(port: port, targetPort: 1521, name: OracleDatabaseServerResource.PrimaryEndpointName) .WithImage("database/free", "23.3.0.0") .WithImageRegistry("container-registry.oracle.com") .WithEnvironment(context => diff --git a/src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs b/src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs index bc4745813e..1d95311530 100644 --- a/src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs +++ b/src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs @@ -35,7 +35,7 @@ public static IResourceBuilder AddPostgres(this IDistrib var postgresServer = new PostgresServerResource(name, userName?.Resource, passwordParameter); return builder.AddResource(postgresServer) - .WithEndpoint(hostPort: port, containerPort: 5432, name: PostgresServerResource.PrimaryEndpointName) // Internal port is always 5432. + .WithEndpoint(port: port, targetPort: 5432, name: PostgresServerResource.PrimaryEndpointName) // Internal port is always 5432. .WithImage(PostgresContainerImageTags.Image, PostgresContainerImageTags.Tag) .WithEnvironment("POSTGRES_HOST_AUTH_METHOD", "scram-sha-256") .WithEnvironment("POSTGRES_INITDB_ARGS", "--auth-host=scram-sha-256 --auth-local=scram-sha-256") @@ -84,7 +84,7 @@ public static IResourceBuilder WithPgAdmin(this IResourceBuilder builde var pgAdminContainer = new PgAdminContainerResource(containerName); builder.ApplicationBuilder.AddResource(pgAdminContainer) .WithImage("dpage/pgadmin4", "8.3") - .WithHttpEndpoint(containerPort: 80, hostPort: hostPort, name: containerName) + .WithHttpEndpoint(targetPort: 80, port: hostPort, name: containerName) .WithEnvironment(SetPgAdminEnvironmentVariables) .WithBindMount(Path.GetTempFileName(), "/pgadmin4/servers.json") .ExcludeFromManifest(); diff --git a/src/Aspire.Hosting.RabbitMQ/RabbitMQBuilderExtensions.cs b/src/Aspire.Hosting.RabbitMQ/RabbitMQBuilderExtensions.cs index feb9159f60..0977e11d17 100644 --- a/src/Aspire.Hosting.RabbitMQ/RabbitMQBuilderExtensions.cs +++ b/src/Aspire.Hosting.RabbitMQ/RabbitMQBuilderExtensions.cs @@ -36,7 +36,7 @@ public static IResourceBuilder AddRabbitMQ(this IDistrib var rabbitMq = new RabbitMQServerResource(name, userName?.Resource, passwordParameter); var rabbitmq = builder.AddResource(rabbitMq) .WithImage(RabbitMQContainerImageTags.Image, RabbitMQContainerImageTags.Tag) - .WithEndpoint(hostPort: port, containerPort: 5672, name: RabbitMQServerResource.PrimaryEndpointName) + .WithEndpoint(port: port, targetPort: 5672, name: RabbitMQServerResource.PrimaryEndpointName) .WithEnvironment(context => { context.EnvironmentVariables["RABBITMQ_DEFAULT_USER"] = rabbitMq.UserNameReference; @@ -127,7 +127,7 @@ public static IResourceBuilder WithManagementPlugin(this if (handled) { - builder.WithHttpEndpoint(containerPort: 15672, name: RabbitMQServerResource.ManagementEndpointName); + builder.WithHttpEndpoint(targetPort: 15672, name: RabbitMQServerResource.ManagementEndpointName); return builder; } diff --git a/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs b/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs index 79c95541ae..cfa6496f7c 100644 --- a/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs +++ b/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs @@ -27,7 +27,7 @@ public static IResourceBuilder AddRedis(this IDistributedApplicat { var redis = new RedisResource(name); return builder.AddResource(redis) - .WithEndpoint(hostPort: port, containerPort: 6379, name: RedisResource.PrimaryEndpointName) + .WithEndpoint(port: port, targetPort: 6379, name: RedisResource.PrimaryEndpointName) .WithImage(RedisContainerImageTags.Image, RedisContainerImageTags.Tag); } @@ -52,7 +52,7 @@ public static IResourceBuilder WithRedisCommander(this IResourceB var resource = new RedisCommanderResource(containerName); builder.ApplicationBuilder.AddResource(resource) .WithImage("rediscommander/redis-commander", "latest") - .WithHttpEndpoint(containerPort: 8081, hostPort: hostPort, name: containerName) + .WithHttpEndpoint(targetPort: 8081, port: hostPort, name: containerName) .ExcludeFromManifest(); return builder; diff --git a/src/Aspire.Hosting.Seq/SeqBuilderExtensions.cs b/src/Aspire.Hosting.Seq/SeqBuilderExtensions.cs index fdeba93b44..e1e24caee8 100644 --- a/src/Aspire.Hosting.Seq/SeqBuilderExtensions.cs +++ b/src/Aspire.Hosting.Seq/SeqBuilderExtensions.cs @@ -28,7 +28,7 @@ public static IResourceBuilder AddSeq( { var seqResource = new SeqResource(name); var resourceBuilder = builder.AddResource(seqResource) - .WithHttpEndpoint(hostPort: port, containerPort: 80, name: SeqResource.PrimaryEndpointName) + .WithHttpEndpoint(port: port, targetPort: 80, name: SeqResource.PrimaryEndpointName) .WithImage("datalust/seq", "2024.1") .WithEnvironment("ACCEPT_EULA", "Y"); diff --git a/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs b/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs index 64da515f5f..f15ac9734d 100644 --- a/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs +++ b/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs @@ -26,7 +26,7 @@ public static IResourceBuilder AddSqlServer(this IDistr var sqlServer = new SqlServerServerResource(name, passwordParameter); return builder.AddResource(sqlServer) - .WithEndpoint(hostPort: port, containerPort: 1433, name: SqlServerServerResource.PrimaryEndpointName) + .WithEndpoint(port: port, targetPort: 1433, name: SqlServerServerResource.PrimaryEndpointName) .WithImage(SqlServerContainerImageTags.Image, SqlServerContainerImageTags.Tag) .WithImageRegistry(SqlServerContainerImageTags.Registry) .WithEnvironment("ACCEPT_EULA", "Y") diff --git a/src/Aspire.Hosting/ApplicationModel/EndpointAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/EndpointAnnotation.cs index 3ebc67a2aa..e7a4a39ad6 100644 --- a/src/Aspire.Hosting/ApplicationModel/EndpointAnnotation.cs +++ b/src/Aspire.Hosting/ApplicationModel/EndpointAnnotation.cs @@ -24,11 +24,11 @@ public sealed class EndpointAnnotation : IResourceAnnotation /// Transport that is being used (e.g. http, http2, http3 etc). /// Name of the service. /// Desired port for the service. - /// If the endpoint is used for the container, this is the port the container process is listening on. + /// This is the port the resource is listening on. If the endpoint is used for the container, it is the container port. /// Indicates that this endpoint should be exposed externally at publish time. /// The name of the environment variable that will be set to the port number of this endpoint. /// Specifies if the endpoint will be proxied by DCP. Defaults to true. - public EndpointAnnotation(ProtocolType protocol, string? uriScheme = null, string? transport = null, string? name = null, int? port = null, int? containerPort = null, bool? isExternal = null, string? env = null, bool isProxied = true) + public EndpointAnnotation(ProtocolType protocol, string? uriScheme = null, string? transport = null, string? name = null, int? port = null, int? targetPort = null, bool? isExternal = null, string? env = null, bool isProxied = true) { // If the URI scheme is null, we'll adopt either udp:// or tcp:// based on the // protocol. If the name is null, we'll use the URI scheme as the default. This @@ -45,7 +45,7 @@ public EndpointAnnotation(ProtocolType protocol, string? uriScheme = null, strin _transport = transport; Name = name; Port = port; - ContainerPort = containerPort ?? port; + TargetPort = targetPort ?? port; IsExternal = isExternal ?? false; EnvironmentVariable = env; IsProxied = isProxied; @@ -67,12 +67,12 @@ public EndpointAnnotation(ProtocolType protocol, string? uriScheme = null, strin public int? Port { get; set; } /// - /// If the endpoint is used for the container, this is the port the container process is listening on. + /// This is the port the resource is listening on. If the endpoint is used for the container, it is the container port. /// /// /// Defaults to . /// - public int? ContainerPort { get; set; } + public int? TargetPort { get; set; } /// /// If a service is URI-addressable, this property will contain the URI scheme to use for constructing service URI. diff --git a/src/Aspire.Hosting/ApplicationModel/EndpointAnnotationExtensions.cs b/src/Aspire.Hosting/ApplicationModel/EndpointAnnotationExtensions.cs deleted file mode 100644 index 7fa680e48e..0000000000 --- a/src/Aspire.Hosting/ApplicationModel/EndpointAnnotationExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Aspire.Hosting.ApplicationModel; - -/// -/// Provides extension methods for . -/// -public static class EndpointAnnotationExtensions -{ - /// - /// Sets the transport to HTTP/2 and the URI scheme to HTTPS for the specified object. - /// - /// The object to modify. - /// The modified object. - public static EndpointAnnotation AsHttp2(this EndpointAnnotation endpoint) - { - ArgumentNullException.ThrowIfNull(endpoint); - - endpoint.Transport = "http2"; - endpoint.UriScheme = "https"; - return endpoint; - } - - /// - /// Sets the property to true for the specified object. - /// - /// The object to modify. - /// The modified object. - public static EndpointAnnotation AsExternal(this EndpointAnnotation endpoint) - { - ArgumentNullException.ThrowIfNull(endpoint); - - endpoint.IsExternal = true; - return endpoint; - } -} diff --git a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs index 5e82e0ad06..f9f6d395db 100644 --- a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs +++ b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs @@ -1653,12 +1653,12 @@ private void AddServicesProducedInfo(IResource modelResource, IAnnotationHolder if (modelResource.IsContainer()) { - if (sp.EndpointAnnotation.ContainerPort is null) + if (sp.EndpointAnnotation.TargetPort is null) { throw new InvalidOperationException($"The endpoint for container resource {modelResourceName} must specify the ContainerPort"); } - sp.DcpServiceProducerAnnotation.Port = sp.EndpointAnnotation.ContainerPort; + sp.DcpServiceProducerAnnotation.Port = sp.EndpointAnnotation.TargetPort; } else if (!sp.EndpointAnnotation.IsProxied) { diff --git a/src/Aspire.Hosting/ExecutableResourceBuilderExtensions.cs b/src/Aspire.Hosting/ExecutableResourceBuilderExtensions.cs index a8d84bacc9..2a21ea8030 100644 --- a/src/Aspire.Hosting/ExecutableResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ExecutableResourceBuilderExtensions.cs @@ -79,6 +79,6 @@ private static async Task WriteExecutableAsDockerfileResourceAsync(ManifestPubli } await context.WriteEnvironmentVariablesAsync(executable).ConfigureAwait(false); - context.WriteBindings(executable, emitContainerPort: true); + context.WriteBindings(executable); } } diff --git a/src/Aspire.Hosting/Publishing/ManifestPublishingContext.cs b/src/Aspire.Hosting/Publishing/ManifestPublishingContext.cs index bc0ea47bc7..feefc15821 100644 --- a/src/Aspire.Hosting/Publishing/ManifestPublishingContext.cs +++ b/src/Aspire.Hosting/Publishing/ManifestPublishingContext.cs @@ -31,6 +31,8 @@ public sealed class ManifestPublishingContext(DistributedApplicationExecutionCon /// public Utf8JsonWriter Writer { get; } = writer; + private PortAllocator PortAllocator { get; } = new(); + /// /// Gets cancellation token for this operation. /// @@ -242,7 +244,7 @@ public async Task WriteContainerAsync(ContainerResource container) WriteContainerMounts(container); await WriteEnvironmentVariablesAsync(container).ConfigureAwait(false); - WriteBindings(container, emitContainerPort: true); + WriteBindings(container); } /// @@ -264,8 +266,7 @@ public void WriteConnectionString(IResource resource) /// collection. /// /// The that contains annotations. - /// Flag to determine whether container port is emitted. - public void WriteBindings(IResource resource, bool emitContainerPort = false) + public void WriteBindings(IResource resource) { if (resource.TryGetEndpoints(out var endpoints)) { @@ -277,9 +278,49 @@ public void WriteBindings(IResource resource, bool emitContainerPort = false) Writer.WriteString("protocol", endpoint.Protocol.ToString().ToLowerInvariant()); Writer.WriteString("transport", endpoint.Transport); - if (emitContainerPort && endpoint.ContainerPort is { } containerPort) + int? targetPort = (resource, endpoint.UriScheme, endpoint.TargetPort) switch + { + // The port was specified so use it + (_, _, int port) => port, + + // Project resources get their default port from the deployment tool + // ideally we would default to a known port but we don't know it at this point + (ProjectResource, var scheme, null) when scheme is "http" or "https" => null, + + // Allocate a dynamic port + _ => PortAllocator.AllocatePort() + }; + + int? exposedPort = (endpoint.UriScheme, endpoint.Port, targetPort) switch + { + // Exposed port and target port are the same, we don't need to mention the exposed port + (_, int p0, int p1) when p0 == p1 => null, + + // Port was specified, so use it + (_, int port, _) => port, + + // We have a target port, not need to specify an exposedPort + // it will default to the targetPort + (_, null, int port) => null, + + // Let the tool infer the default http and https ports + ("http", null, null) => null, + ("https", null, null) => null, + + // Other schemes just allocate a port + _ => PortAllocator.AllocatePort() + }; + + if (exposedPort is int ep) + { + PortAllocator.AddUsedPort(ep); + Writer.WriteNumber("port", ep); + } + + if (targetPort is int tp) { - Writer.WriteNumber("containerPort", containerPort); + PortAllocator.AddUsedPort(tp); + Writer.WriteNumber("targetPort", tp); } if (endpoint.IsExternal) @@ -387,7 +428,7 @@ public void WritePortBindingEnvironmentVariables(IResource resource) continue; } - Writer.WriteString(endpoint.EnvironmentVariable, $"{{{resource.Name}.bindings.{endpoint.Name}.port}}"); + Writer.WriteString(endpoint.EnvironmentVariable, $"{{{resource.Name}.bindings.{endpoint.Name}.targetPort}}"); } } } diff --git a/src/Aspire.Hosting/Publishing/PortAllocator.cs b/src/Aspire.Hosting/Publishing/PortAllocator.cs new file mode 100644 index 0000000000..40f36fbebb --- /dev/null +++ b/src/Aspire.Hosting/Publishing/PortAllocator.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Aspire.Hosting.Publishing; + +// Used for the manifest publisher to dynamically allocate ports +internal sealed class PortAllocator(int startPort = 8000) +{ + private int _allocatedPortStart = startPort; + private readonly HashSet _usedPorts = []; + + public int AllocatePort() + { + while (true) + { + if (!_usedPorts.Contains(_allocatedPortStart)) + { + return _allocatedPortStart; + } + + _allocatedPortStart++; + } + } + + public void AddUsedPort(int port) + { + _usedPorts.Add(port); + } +} diff --git a/src/Aspire.Hosting/ResourceBuilderExtensions.cs b/src/Aspire.Hosting/ResourceBuilderExtensions.cs index d184a26349..8c8a2f6f84 100644 --- a/src/Aspire.Hosting/ResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ResourceBuilderExtensions.cs @@ -335,30 +335,6 @@ private static void ApplyEndpoints(this IResourceBuilder builder, IResourc } } - /// - /// Exposes an endpoint on a resource. This endpoint reference can be retrieved using . - /// The endpoint name will be the scheme name if not specified. - /// - /// The resource type. - /// The resource builder. - /// An optional host port. - /// An optional scheme e.g. (http/https). Defaults to "tcp" if not specified. - /// An optional name of the endpoint. Defaults to the scheme name if not specified. - /// An optional name of the environment variable to inject. - /// Specifies if the endpoint will be proxied by DCP. Defaults to true. - /// The . - /// Throws an exception if an endpoint with the same name already exists on the specified resource. - public static IResourceBuilder WithEndpoint(this IResourceBuilder builder, int? hostPort = null, string? scheme = null, string? name = null, string? env = null, bool isProxied = true) where T : IResource - { - if (builder.Resource.Annotations.OfType().Any(sb => string.Equals(sb.Name, name, StringComparisons.EndpointAnnotationName))) - { - throw new DistributedApplicationException($"Endpoint '{name}' already exists. Endpoint names are case-insensitive."); - } - - var annotation = new EndpointAnnotation(ProtocolType.Tcp, uriScheme: scheme, name: name, port: hostPort, env: env, isProxied: isProxied); - return builder.WithAnnotation(annotation); - } - /// /// Changes an existing creates a new endpoint if it doesn't exist and invokes callback to modify the defaults. /// @@ -393,53 +369,21 @@ public static IResourceBuilder WithEndpoint(this IResourceBuilder build return builder; } - /// - /// Exposes an HTTP endpoint on a resource. This endpoint reference can be retrieved using . - /// The endpoint name will be "http" if not specified. - /// - /// The resource type. - /// The resource builder. - /// An optional host port. - /// An optional name of the endpoint. Defaults to "http" if not specified. - /// An optional name of the environment variable to inject. - /// The . - /// Throws an exception if an endpoint with the same name already exists on the specified resource. - public static IResourceBuilder WithHttpEndpoint(this IResourceBuilder builder, int? hostPort = null, string? name = null, string? env = null) where T : IResource - { - return builder.WithEndpoint(hostPort: hostPort, scheme: "http", name: name, env: env); - } - - /// - /// Exposes an HTTPS endpoint on a resource. This endpoint reference can be retrieved using . - /// The endpoint name will be "https" if not specified. - /// - /// The resource type. - /// The resource builder. - /// An optional host port. - /// An optional name of the endpoint. Defaults to "https" if not specified. - /// An optional name of the environment variable to inject. - /// The . - /// Throws an exception if an endpoint with the same name already exists on the specified resource. - public static IResourceBuilder WithHttpsEndpoint(this IResourceBuilder builder, int? hostPort = null, string? name = null, string? env = null) where T : IResource - { - return builder.WithEndpoint(hostPort: hostPort, scheme: "https", name: name, env: env); - } - /// /// Exposes an endpoint on a resource. This endpoint reference can be retrieved using . /// The endpoint name will be the scheme name if not specified. /// /// The resource type. /// The resource builder. - /// The container port. - /// An optional host port. + /// This is the port the resource is listening on. If the endpoint is used for the container, it is the container port. + /// An optional port. This is the port that will be given to other resource to communicate with this resource. /// An optional scheme e.g. (http/https). Defaults to "tcp" if not specified. /// An optional name of the endpoint. Defaults to the scheme name if not specified. /// An optional name of the environment variable to inject. /// Specifies if the endpoint will be proxied by DCP. Defaults to true. /// The . /// Throws an exception if an endpoint with the same name already exists on the specified resource. - public static IResourceBuilder WithEndpoint(this IResourceBuilder builder, int containerPort, int? hostPort = null, string? scheme = null, string? name = null, string? env = null, bool isProxied = true) where T : IResource + public static IResourceBuilder WithEndpoint(this IResourceBuilder builder, int? port = null, int? targetPort = null, string? scheme = null, string? name = null, string? env = null, bool isProxied = true) where T : IResource { if (builder.Resource.Annotations.OfType().Any(sb => string.Equals(sb.Name, name, StringComparisons.EndpointAnnotationName))) { @@ -450,8 +394,8 @@ public static IResourceBuilder WithEndpoint(this IResourceBuilder build protocol: ProtocolType.Tcp, uriScheme: scheme, name: name, - port: hostPort, - containerPort: containerPort, + port: port, + targetPort: targetPort, env: env, isProxied: isProxied); @@ -464,16 +408,16 @@ public static IResourceBuilder WithEndpoint(this IResourceBuilder build /// /// The resource type. /// The resource builder. - /// The container port. - /// An optional host port. + /// This is the port the resource is listening on. If the endpoint is used for the container, it is the container port. + /// An optional port. This is the port that will be given to other resource to communicate with this resource. /// An optional name of the endpoint. Defaults to "http" if not specified. /// An optional name of the environment variable to inject. /// Specifies if the endpoint will be proxied by DCP. Defaults to true. /// The . /// Throws an exception if an endpoint with the same name already exists on the specified resource. - public static IResourceBuilder WithHttpEndpoint(this IResourceBuilder builder, int containerPort, int? hostPort = null, string? name = null, string? env = null, bool isProxied = true) where T : IResource + public static IResourceBuilder WithHttpEndpoint(this IResourceBuilder builder, int? port = null, int? targetPort = null, string? name = null, string? env = null, bool isProxied = true) where T : IResource { - return builder.WithEndpoint(containerPort: containerPort, hostPort: hostPort, scheme: "http", name: name, env: env, isProxied: isProxied); + return builder.WithEndpoint(targetPort: targetPort, port: port, scheme: "http", name: name, env: env, isProxied: isProxied); } /// @@ -482,20 +426,44 @@ public static IResourceBuilder WithHttpEndpoint(this IResourceBuilder b /// /// The resource type. /// The resource builder. - /// The container port. - /// An optional host port. + /// This is the port the resource is listening on. If the endpoint is used for the container, it is the container port. + /// An optional host port. /// An optional name of the endpoint. Defaults to "https" if not specified. /// An optional name of the environment variable to inject. /// Specifies if the endpoint will be proxied by DCP. Defaults to true. /// The . /// Throws an exception if an endpoint with the same name already exists on the specified resource. - public static IResourceBuilder WithHttpsEndpoint(this IResourceBuilder builder, int containerPort, int? hostPort = null, string? name = null, string? env = null, bool isProxied = true) where T : IResource + public static IResourceBuilder WithHttpsEndpoint(this IResourceBuilder builder, int? port = null, int? targetPort = null, string? name = null, string? env = null, bool isProxied = true) where T : IResource + { + return builder.WithEndpoint(targetPort: targetPort, port: port, scheme: "https", name: name, env: env, isProxied: isProxied); + } + + /// + /// Marks existing http or https endpoints on a resource as external. + /// + /// The resource type. + /// The resource builder. + /// + public static IResourceBuilder WithExternalHttpEndpoints(this IResourceBuilder builder) where T : IResourceWithEndpoints { - return builder.WithEndpoint(containerPort: containerPort, hostPort: hostPort, scheme: "https", name: name, env: env, isProxied: isProxied); + if (!builder.Resource.TryGetAnnotationsOfType(out var endpoints)) + { + return builder; + } + + foreach (var endpoint in endpoints) + { + if (endpoint.UriScheme == "http" || endpoint.UriScheme == "https") + { + endpoint.IsExternal = true; + } + } + + return builder; } /// - /// Gets an by name from the resource. These endpoints are declared either using or by launch settings (for project resources). + /// Gets an by name from the resource. These endpoints are declared either using or by launch settings (for project resources). /// The can be used to resolve the address of the endpoint in . /// /// The resource type. diff --git a/tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs b/tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs index 73e2cc0193..8fc808fe11 100644 --- a/tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs +++ b/tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs @@ -252,7 +252,7 @@ public async Task SpecifyingEnvPortInEndpointFlowsToEnv() .WithHttpEndpoint(name: "http0", env: "PORT0"); testProgram.AppBuilder.AddContainer("redis0", "redis") - .WithEndpoint(containerPort: 6379, name: "tcp", env: "REDIS_PORT"); + .WithEndpoint(targetPort: 6379, name: "tcp", env: "REDIS_PORT"); await using var app = testProgram.Build(); diff --git a/tests/Aspire.Hosting.Tests/Kafka/AddKafkaTests.cs b/tests/Aspire.Hosting.Tests/Kafka/AddKafkaTests.cs index 1d4cd26df1..e9b8e3c1aa 100644 --- a/tests/Aspire.Hosting.Tests/Kafka/AddKafkaTests.cs +++ b/tests/Aspire.Hosting.Tests/Kafka/AddKafkaTests.cs @@ -24,7 +24,7 @@ public void AddKafkaContainerWithDefaultsAddsAnnotationMetadata() Assert.Equal("kafka", containerResource.Name); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(9092, endpoint.ContainerPort); + Assert.Equal(9092, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Null(endpoint.Port); @@ -79,7 +79,7 @@ public async Task VerifyManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 9092 + "targetPort": 9092 } } } diff --git a/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs b/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs index 1cb7a21b01..154b6db07c 100644 --- a/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs +++ b/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs @@ -85,7 +85,7 @@ public void EnsureWorkerProjectDoesNotGetBindingsGenerated() public void EnsureExecutablesWithDockerfileProduceDockerfilev0Manifest() { using var program = CreateTestProgramJsonDocumentManifestPublisher(includeNodeApp: true); - program.NodeAppBuilder!.WithHttpsEndpoint(containerPort: 3000, env: "HTTPS_PORT") + program.NodeAppBuilder!.WithHttpsEndpoint(targetPort: 3000, env: "HTTPS_PORT") .PublishAsDockerFile(); // Build AppHost so that publisher can be resolved. @@ -109,9 +109,9 @@ public void EnsureExecutablesWithDockerfileProduceDockerfilev0Manifest() Assert.True(nodeapp.TryGetProperty("env", out var env)); Assert.True(nodeapp.TryGetProperty("bindings", out var bindings)); - Assert.Equal(3000, bindings.GetProperty("https").GetProperty("containerPort").GetInt32()); + Assert.Equal(3000, bindings.GetProperty("https").GetProperty("targetPort").GetInt32()); Assert.Equal("https", bindings.GetProperty("https").GetProperty("scheme").GetString()); - Assert.Equal("{nodeapp.bindings.https.port}", env.GetProperty("HTTPS_PORT").GetString()); + Assert.Equal("{nodeapp.bindings.https.targetPort}", env.GetProperty("HTTPS_PORT").GetString()); } [Fact] @@ -155,7 +155,7 @@ public void EnsureContainerWithEndpointsEmitsContainerPort() var grafana = resources.GetProperty("grafana"); var bindings = grafana.GetProperty("bindings"); var httpBinding = bindings.GetProperty("http"); - Assert.Equal(3000, httpBinding.GetProperty("containerPort").GetInt32()); + Assert.Equal(3000, httpBinding.GetProperty("targetPort").GetInt32()); } [Fact] @@ -456,9 +456,9 @@ public void NodeAppIsExecutableResource() using var program = CreateTestProgramJsonDocumentManifestPublisher(); program.AppBuilder.AddNodeApp("nodeapp", "..\\foo\\app.js") - .WithHttpEndpoint(hostPort: 5031, env: "PORT"); + .WithHttpEndpoint(port: 5031, env: "PORT"); program.AppBuilder.AddNpmApp("npmapp", "..\\foo") - .WithHttpEndpoint(hostPort: 5032, env: "PORT"); + .WithHttpEndpoint(port: 5032, env: "PORT"); // Build AppHost so that publisher can be resolved. program.Build(); @@ -482,7 +482,7 @@ static void AssertNodeResource(TestProgram program, string resourceName, JsonEle Assert.Equal("http", httpBinding.GetProperty("scheme").GetString()); var env = jsonElement.GetProperty("env"); - Assert.Equal($$"""{{{resourceName}}.bindings.http.port}""", env.GetProperty("PORT").GetString()); + Assert.Equal($$"""{{{resourceName}}.bindings.http.targetPort}""", env.GetProperty("PORT").GetString()); Assert.Equal(program.AppBuilder.Environment.EnvironmentName.ToLowerInvariant(), env.GetProperty("NODE_ENV").GetString()); var command = jsonElement.GetProperty("command"); @@ -648,7 +648,7 @@ public void VerifyTestProgramFullManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1433 + "targetPort": 1433 } } }, @@ -669,7 +669,7 @@ public void VerifyTestProgramFullManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 3306 + "targetPort": 3306 } } }, @@ -686,7 +686,7 @@ public void VerifyTestProgramFullManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 6379 + "targetPort": 6379 } } }, @@ -706,7 +706,7 @@ public void VerifyTestProgramFullManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } }, @@ -727,7 +727,7 @@ public void VerifyTestProgramFullManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5672 + "targetPort": 5672 } } }, @@ -740,7 +740,7 @@ public void VerifyTestProgramFullManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 27017 + "targetPort": 27017 } } }, @@ -760,7 +760,7 @@ public void VerifyTestProgramFullManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1521 + "targetPort": 1521 } } }, @@ -780,7 +780,7 @@ public void VerifyTestProgramFullManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 9092 + "targetPort": 9092 } } }, diff --git a/tests/Aspire.Hosting.Tests/MongoDB/AddMongoDBTests.cs b/tests/Aspire.Hosting.Tests/MongoDB/AddMongoDBTests.cs index 284b10b199..83834ad756 100644 --- a/tests/Aspire.Hosting.Tests/MongoDB/AddMongoDBTests.cs +++ b/tests/Aspire.Hosting.Tests/MongoDB/AddMongoDBTests.cs @@ -27,7 +27,7 @@ public void AddMongoDBContainerWithDefaultsAddsAnnotationMetadata() Assert.Equal("mongodb", containerResource.Name); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(27017, endpoint.ContainerPort); + Assert.Equal(27017, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Null(endpoint.Port); @@ -55,7 +55,7 @@ public void AddMongoDBContainerAddsAnnotationMetadata() Assert.Equal("mongodb", containerResource.Name); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(27017, endpoint.ContainerPort); + Assert.Equal(27017, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Equal(9813, endpoint.Port); @@ -161,7 +161,7 @@ public async Task VerifyManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 27017 + "targetPort": 27017 } } } diff --git a/tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs b/tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs index cafc58398b..2a7922fe4f 100644 --- a/tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs +++ b/tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs @@ -32,7 +32,7 @@ public async Task AddMySqlContainerWithDefaultsAddsAnnotationMetadata() Assert.Null(containerAnnotation.Registry); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(3306, endpoint.ContainerPort); + Assert.Equal(3306, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Null(endpoint.Port); @@ -72,7 +72,7 @@ public async Task AddMySqlAddsAnnotationMetadata() Assert.Null(containerAnnotation.Registry); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(3306, endpoint.ContainerPort); + Assert.Equal(3306, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Equal(1234, endpoint.Port); @@ -154,7 +154,7 @@ public async Task VerifyManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 3306 + "targetPort": 3306 } } } @@ -192,7 +192,7 @@ public async Task VerifyManifestWithPasswordParameter() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 3306 + "targetPort": 3306 } } } diff --git a/tests/Aspire.Hosting.Tests/Nats/AddNatsTests.cs b/tests/Aspire.Hosting.Tests/Nats/AddNatsTests.cs index 83265d1a4d..2912ad5214 100644 --- a/tests/Aspire.Hosting.Tests/Nats/AddNatsTests.cs +++ b/tests/Aspire.Hosting.Tests/Nats/AddNatsTests.cs @@ -26,7 +26,7 @@ public void AddNatsContainerWithDefaultsAddsAnnotationMetadata() Assert.Equal("nats", containerResource.Name); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(4222, endpoint.ContainerPort); + Assert.Equal(4222, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Null(endpoint.Port); @@ -65,7 +65,7 @@ public void AddNatsContainerAddsAnnotationMetadata() Assert.Equal("-js -sd /data".Split(' '), args); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(4222, endpoint.ContainerPort); + Assert.Equal(4222, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Equal(1234, endpoint.Port); @@ -107,7 +107,7 @@ public async Task VerifyManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 4222 + "targetPort": 4222 } } } diff --git a/tests/Aspire.Hosting.Tests/Oracle/AddOracleDatabaseTests.cs b/tests/Aspire.Hosting.Tests/Oracle/AddOracleDatabaseTests.cs index d9139b9c52..e7a14565d5 100644 --- a/tests/Aspire.Hosting.Tests/Oracle/AddOracleDatabaseTests.cs +++ b/tests/Aspire.Hosting.Tests/Oracle/AddOracleDatabaseTests.cs @@ -30,7 +30,7 @@ public async Task AddOracleWithDefaultsAddsAnnotationMetadata() Assert.Equal("container-registry.oracle.com", containerAnnotation.Registry); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(1521, endpoint.ContainerPort); + Assert.Equal(1521, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Null(endpoint.Port); @@ -70,7 +70,7 @@ public async Task AddOracleAddsAnnotationMetadata() Assert.Equal("container-registry.oracle.com", containerAnnotation.Registry); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(1521, endpoint.ContainerPort); + Assert.Equal(1521, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Equal(1234, endpoint.Port); @@ -153,7 +153,7 @@ public async Task AddDatabaseToOracleDatabaseAddsAnnotationMetadata() Assert.Equal("container-registry.oracle.com", containerAnnotation.Registry); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(1521, endpoint.ContainerPort); + Assert.Equal(1521, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Equal(1234, endpoint.Port); @@ -194,7 +194,7 @@ public async Task VerifyManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1521 + "targetPort": 1521 } } } @@ -232,7 +232,7 @@ public async Task VerifyManifestWithPasswordParameter() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1521 + "targetPort": 1521 } } } diff --git a/tests/Aspire.Hosting.Tests/PortAllocatorTest.cs b/tests/Aspire.Hosting.Tests/PortAllocatorTest.cs new file mode 100644 index 0000000000..902736731a --- /dev/null +++ b/tests/Aspire.Hosting.Tests/PortAllocatorTest.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Aspire.Hosting.Publishing; +using Xunit; + +namespace Aspire.Hosting.Tests; + +public class PortAllocatorTest +{ + [Fact] + public void CanAllocatePorts() + { + var allocator = new PortAllocator(1000); + var port1 = allocator.AllocatePort(); + allocator.AddUsedPort(port1); + var port2 = allocator.AllocatePort(); + + Assert.Equal(1000, port1); + Assert.Equal(1001, port2); + } + + [Fact] + public void SkipUsedPorts() + { + var allocator = new PortAllocator(1000); + allocator.AddUsedPort(1000); + allocator.AddUsedPort(1001); + allocator.AddUsedPort(1003); + var port1 = allocator.AllocatePort(); + allocator.AddUsedPort(port1); + var port2 = allocator.AllocatePort(); + + Assert.Equal(1002, port1); + Assert.Equal(1004, port2); + } +} diff --git a/tests/Aspire.Hosting.Tests/Postgres/AddPostgresTests.cs b/tests/Aspire.Hosting.Tests/Postgres/AddPostgresTests.cs index 8ab2475287..8445d0e248 100644 --- a/tests/Aspire.Hosting.Tests/Postgres/AddPostgresTests.cs +++ b/tests/Aspire.Hosting.Tests/Postgres/AddPostgresTests.cs @@ -32,7 +32,7 @@ public async Task AddPostgresWithDefaultsAddsAnnotationMetadata() Assert.Null(containerAnnotation.Registry); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(5432, endpoint.ContainerPort); + Assert.Equal(5432, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Null(endpoint.Port); @@ -87,7 +87,7 @@ public async Task AddPostgresAddsAnnotationMetadata() Assert.Null(containerAnnotation.Registry); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(5432, endpoint.ContainerPort); + Assert.Equal(5432, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Equal(1234, endpoint.Port); @@ -179,7 +179,7 @@ public async Task AddDatabaseToPostgresAddsAnnotationMetadata() Assert.Null(containerAnnotation.Registry); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(5432, endpoint.ContainerPort); + Assert.Equal(5432, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Equal(1234, endpoint.Port); @@ -238,7 +238,7 @@ public async Task VerifyManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } } @@ -281,7 +281,7 @@ public async Task VerifyManifestWithParameters() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } } @@ -307,7 +307,7 @@ public async Task VerifyManifestWithParameters() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } } @@ -333,7 +333,7 @@ public async Task VerifyManifestWithParameters() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } } } diff --git a/tests/Aspire.Hosting.Tests/RabbitMQ/AddRabbitMQTests.cs b/tests/Aspire.Hosting.Tests/RabbitMQ/AddRabbitMQTests.cs index da08c1d772..661d97d083 100644 --- a/tests/Aspire.Hosting.Tests/RabbitMQ/AddRabbitMQTests.cs +++ b/tests/Aspire.Hosting.Tests/RabbitMQ/AddRabbitMQTests.cs @@ -32,7 +32,7 @@ public void AddRabbitMQContainerWithDefaultsAddsAnnotationMetadata(bool withMana Assert.Equal("rabbit", containerResource.Name); var primaryEndpoint = Assert.Single(containerResource.Annotations.OfType().Where(e => e.Name == "tcp")); - Assert.Equal(5672, primaryEndpoint.ContainerPort); + Assert.Equal(5672, primaryEndpoint.TargetPort); Assert.False(primaryEndpoint.IsExternal); Assert.Equal("tcp", primaryEndpoint.Name); Assert.Null(primaryEndpoint.Port); @@ -43,7 +43,7 @@ public void AddRabbitMQContainerWithDefaultsAddsAnnotationMetadata(bool withMana if (withManagementPlugin) { var mangementEndpoint = Assert.Single(containerResource.Annotations.OfType().Where(e => e.Name == "management")); - Assert.Equal(15672, mangementEndpoint.ContainerPort); + Assert.Equal(15672, mangementEndpoint.TargetPort); Assert.False(primaryEndpoint.IsExternal); Assert.Equal("management", mangementEndpoint.Name); Assert.Null(mangementEndpoint.Port); @@ -182,7 +182,7 @@ public async Task VerifyManifest(bool withManagementPlugin) "scheme": "http", "protocol": "tcp", "transport": "http", - "containerPort": 15672 + "targetPort": 15672 } """ : ""; @@ -200,7 +200,7 @@ public async Task VerifyManifest(bool withManagementPlugin) "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5672 + "targetPort": 5672 }{{managementBinding}} } } @@ -234,7 +234,7 @@ public async Task VerifyManifestWithParameters() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5672 + "targetPort": 5672 } } } @@ -258,7 +258,7 @@ public async Task VerifyManifestWithParameters() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5672 + "targetPort": 5672 } } } @@ -282,7 +282,7 @@ public async Task VerifyManifestWithParameters() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5672 + "targetPort": 5672 } } } diff --git a/tests/Aspire.Hosting.Tests/Redis/AddRedisTests.cs b/tests/Aspire.Hosting.Tests/Redis/AddRedisTests.cs index 87544e5840..5d2b6ad3be 100644 --- a/tests/Aspire.Hosting.Tests/Redis/AddRedisTests.cs +++ b/tests/Aspire.Hosting.Tests/Redis/AddRedisTests.cs @@ -26,7 +26,7 @@ public void AddRedisContainerWithDefaultsAddsAnnotationMetadata() Assert.Equal("myRedis", containerResource.Name); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(6379, endpoint.ContainerPort); + Assert.Equal(6379, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Null(endpoint.Port); @@ -54,7 +54,7 @@ public void AddRedisContainerAddsAnnotationMetadata() Assert.Equal("myRedis", containerResource.Name); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(6379, endpoint.ContainerPort); + Assert.Equal(6379, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Equal(9813, endpoint.Port); @@ -103,7 +103,7 @@ public async Task VerifyManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 6379 + "targetPort": 6379 } } } diff --git a/tests/Aspire.Hosting.Tests/ResourceNotificationTests.cs b/tests/Aspire.Hosting.Tests/ResourceNotificationTests.cs index 8e2eccc232..5572a10a17 100644 --- a/tests/Aspire.Hosting.Tests/ResourceNotificationTests.cs +++ b/tests/Aspire.Hosting.Tests/ResourceNotificationTests.cs @@ -14,7 +14,7 @@ public void InitialStateCanBeSpecified() var builder = DistributedApplication.CreateBuilder(); var custom = builder.AddResource(new CustomResource("myResource")) - .WithEndpoint(name: "ep", scheme: "http", hostPort: 8080) + .WithEndpoint(name: "ep", scheme: "http", port: 8080) .WithEnvironment("x", "1000") .WithInitialState(new() { diff --git a/tests/Aspire.Hosting.Tests/SqlServer/AddSqlServerTests.cs b/tests/Aspire.Hosting.Tests/SqlServer/AddSqlServerTests.cs index f524b8c749..ff788312c2 100644 --- a/tests/Aspire.Hosting.Tests/SqlServer/AddSqlServerTests.cs +++ b/tests/Aspire.Hosting.Tests/SqlServer/AddSqlServerTests.cs @@ -26,7 +26,7 @@ public async Task AddSqlServerContainerWithDefaultsAddsAnnotationMetadata() Assert.Equal("sqlserver", containerResource.Name); var endpoint = Assert.Single(containerResource.Annotations.OfType()); - Assert.Equal(1433, endpoint.ContainerPort); + Assert.Equal(1433, endpoint.TargetPort); Assert.False(endpoint.IsExternal); Assert.Equal("tcp", endpoint.Name); Assert.Null(endpoint.Port); @@ -125,7 +125,7 @@ public async Task VerifyManifest() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1433 + "targetPort": 1433 } } } @@ -165,7 +165,7 @@ public async Task VerifyManifestWithPasswordParameter() "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1433 + "targetPort": 1433 } } } diff --git a/tests/Aspire.Hosting.Tests/Utils/ManifestUtils.cs b/tests/Aspire.Hosting.Tests/Utils/ManifestUtils.cs index 84e290bd6c..8dedb19232 100644 --- a/tests/Aspire.Hosting.Tests/Utils/ManifestUtils.cs +++ b/tests/Aspire.Hosting.Tests/Utils/ManifestUtils.cs @@ -28,6 +28,35 @@ public static async Task GetManifest(IResource resource) return resourceNode; } + public static async Task GetManifests(IResource[] resources) + { + using var ms = new MemoryStream(); + var writer = new Utf8JsonWriter(ms); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Publish); + var context = new ManifestPublishingContext(executionContext, Path.Combine(Environment.CurrentDirectory, "manifest.json"), writer); + + var results = new List(); + + foreach (var r in resources) + { + writer.WriteStartObject(); + await context.WriteResourceAsync(r); + writer.WriteEndObject(); + writer.Flush(); + ms.Position = 0; + var obj = JsonNode.Parse(ms); + Assert.NotNull(obj); + var resourceNode = obj[r.Name]; + Assert.NotNull(resourceNode); + results.Add(resourceNode); + + ms.Position = 0; + writer.Reset(ms); + } + + return [.. results]; + } + public static async Task<(JsonNode ManifestNode, string BicepText)> GetManifestWithBicep(IResource resource) { var manifestNode = await GetManifest(resource); diff --git a/tests/Aspire.Hosting.Tests/WithEndpointTests.cs b/tests/Aspire.Hosting.Tests/WithEndpointTests.cs index f7c4c46a1d..4dac7720a6 100644 --- a/tests/Aspire.Hosting.Tests/WithEndpointTests.cs +++ b/tests/Aspire.Hosting.Tests/WithEndpointTests.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 Aspire.Hosting.Utils; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -105,7 +106,7 @@ public void CanAddEndpointsWithContainerPortAndEnv() { using var testProgram = CreateTestProgram(); testProgram.AppBuilder.AddExecutable("foo", "foo", ".") - .WithHttpEndpoint(containerPort: 3001, name: "mybinding", env: "PORT"); + .WithHttpEndpoint(targetPort: 3001, name: "mybinding", env: "PORT"); var app = testProgram.Build(); @@ -117,7 +118,7 @@ public void CanAddEndpointsWithContainerPortAndEnv() var endpoints = resource.Annotations.OfType().ToArray(); Assert.Single(endpoints); Assert.Equal("mybinding", endpoints[0].Name); - Assert.Equal(3001, endpoints[0].ContainerPort); + Assert.Equal(3001, endpoints[0].TargetPort); Assert.Equal("http", endpoints[0].UriScheme); Assert.Equal("PORT", endpoints[0].EnvironmentVariable); } @@ -125,7 +126,7 @@ public void CanAddEndpointsWithContainerPortAndEnv() [Fact] public void GettingContainerHostNameFailsIfNoContainerHostNameSet() { - var builder = DistributedApplication.CreateBuilder(); + using var builder = TestDistributedApplicationBuilder.Create(); var container = builder.AddContainer("app", "image") .WithEndpoint("ep", e => { @@ -140,6 +141,307 @@ public void GettingContainerHostNameFailsIfNoContainerHostNameSet() Assert.Equal("The endpoint \"ep\" has no associated container host name.", ex.Message); } + [Fact] + public void WithExternalHttpEndpointsMarkExistingHttpEndpointsAsExternal() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container = builder.AddContainer("app", "image") + .WithEndpoint(name: "ep0") + .WithHttpEndpoint(name: "ep1") + .WithHttpsEndpoint(name: "ep2") + .WithExternalHttpEndpoints(); + + var ep0 = container.GetEndpoint("ep0"); + var ep1 = container.GetEndpoint("ep1"); + var ep2 = container.GetEndpoint("ep2"); + + Assert.False(ep0.EndpointAnnotation.IsExternal); + Assert.True(ep1.EndpointAnnotation.IsExternal); + Assert.True(ep2.EndpointAnnotation.IsExternal); + } + + // Existing code... + + [Fact] + public async Task VerifyManifestWithBothDifferentPortAndTargetPort() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container = builder.AddContainer("app", "image") + .WithEndpoint(name: "ep0", port: 8080, targetPort: 3000); + + var manifest = await ManifestUtils.GetManifest(container.Resource); + var expectedManifest = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "ep0": { + "scheme": "tcp", + "protocol": "tcp", + "transport": "tcp", + "port": 8080, + "targetPort": 3000 + } + } + } + """; + + Assert.Equal(expectedManifest, manifest.ToString()); + } + + [Fact] + public async Task VerifyManifestWithHttpPortWithTargetPort() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container = builder.AddContainer("app", "image") + .WithHttpEndpoint(name: "h1", targetPort: 3001); + + var manifest = await ManifestUtils.GetManifest(container.Resource); + var expectedManifest = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "h1": { + "scheme": "http", + "protocol": "tcp", + "transport": "http", + "targetPort": 3001 + } + } + } + """; + + Assert.Equal(expectedManifest, manifest.ToString()); + } + + [Fact] + public async Task VerifyManifestWithHttpsAndTargetPort() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container = builder.AddContainer("app", "image") + .WithHttpsEndpoint(name: "h2", targetPort: 3001); + + var manifest = await ManifestUtils.GetManifest(container.Resource); + var expectedManifest = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "h2": { + "scheme": "https", + "protocol": "tcp", + "transport": "http", + "targetPort": 3001 + } + } + } + """; + + Assert.Equal(expectedManifest, manifest.ToString()); + } + + [Fact] + public async Task VerifyManifestContainerWithHttpEndpointAndNoPortsAllocatesPort() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container = builder.AddContainer("app", "image") + .WithHttpEndpoint(name: "h3"); + + var manifest = await ManifestUtils.GetManifest(container.Resource); + var expectedManifest = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "h3": { + "scheme": "http", + "protocol": "tcp", + "transport": "http", + "targetPort": 8000 + } + } + } + """; + + Assert.Equal(expectedManifest, manifest.ToString()); + } + + [Fact] + public async Task VerifyManifestContainerWithHttpsEndpointAllocatesPort() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container = builder.AddContainer("app", "image") + .WithHttpsEndpoint(name: "h4"); + + var manifest = await ManifestUtils.GetManifest(container.Resource); + var expectedManifest = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "h4": { + "scheme": "https", + "protocol": "tcp", + "transport": "http", + "targetPort": 8000 + } + } + } + """; + + Assert.Equal(expectedManifest, manifest.ToString()); + } + + [Fact] + public async Task VerifyManifestWithHttpEndpointAndPortOnlySetsTargetPort() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container = builder.AddContainer("app", "image") + .WithHttpEndpoint(name: "otlp", port: 1004); + + var manifest = await ManifestUtils.GetManifest(container.Resource); + var expectedManifest = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "otlp": { + "scheme": "http", + "protocol": "tcp", + "transport": "http", + "targetPort": 1004 + } + } + } + """; + + Assert.Equal(expectedManifest, manifest.ToString()); + } + + [Fact] + public async Task VerifyManifestWithTcpEndpointAndNoPortAllocatesPort() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container = builder.AddContainer("app", "image") + .WithEndpoint(name: "custom"); + + var manifest = await ManifestUtils.GetManifest(container.Resource); + var expectedManifest = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "custom": { + "scheme": "tcp", + "protocol": "tcp", + "transport": "tcp", + "targetPort": 8000 + } + } + } + """; + + Assert.Equal(expectedManifest, manifest.ToString()); + } + + [Fact] + public async Task VerifyManifestProjectWithHttpEndpointDoesNotAllocatePort() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var project = builder.AddProject("proj") + .WithHttpEndpoint(name: "hp") + .WithHttpsEndpoint(name: "hps"); + + var manifest = await ManifestUtils.GetManifest(project.Resource); + var s = manifest.ToString(); + var expectedManifest = + """ + { + "type": "project.v0", + "path": "projectpath", + "env": { + "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true", + "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true" + }, + "bindings": { + "hp": { + "scheme": "http", + "protocol": "tcp", + "transport": "http" + }, + "hps": { + "scheme": "https", + "protocol": "tcp", + "transport": "http" + } + } + } + """; + + Assert.Equal(expectedManifest, manifest.ToString()); + } + + [Fact] + public async Task VerifyManifestPortAllocationIsGlobal() + { + using var builder = TestDistributedApplicationBuilder.Create(); + var container0 = builder.AddContainer("app0", "image") + .WithEndpoint(name: "custom"); + + var container1 = builder.AddContainer("app1", "image") + .WithEndpoint(name: "custom"); + + var manifests = await ManifestUtils.GetManifests([container0.Resource, container1.Resource]); + var expectedManifest0 = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "custom": { + "scheme": "tcp", + "protocol": "tcp", + "transport": "tcp", + "targetPort": 8000 + } + } + } + """; + + var expectedManifest1 = + """ + { + "type": "container.v0", + "image": "image:latest", + "bindings": { + "custom": { + "scheme": "tcp", + "protocol": "tcp", + "transport": "tcp", + "targetPort": 8001 + } + } + } + """; + + Assert.Equal(expectedManifest0, manifests[0].ToString()); + Assert.Equal(expectedManifest1, manifests[1].ToString()); + } + private static TestProgram CreateTestProgram(string[]? args = null) => TestProgram.Create(args); + sealed class TestProject : IProjectMetadata + { + public string ProjectPath => "projectpath"; + + public LaunchSettings? LaunchSettings { get; } = new(); + } } diff --git a/tests/testproject/TestProject.AppHost/TestProgram.cs b/tests/testproject/TestProject.AppHost/TestProgram.cs index 235945587c..84fe285b5b 100644 --- a/tests/testproject/TestProject.AppHost/TestProgram.cs +++ b/tests/testproject/TestProject.AppHost/TestProgram.cs @@ -51,10 +51,10 @@ private TestProgram(string[] args, Assembly assembly, bool includeIntegrationSer var scriptPath = Path.Combine(path, "app.js"); NodeAppBuilder = AppBuilder.AddNodeApp("nodeapp", scriptPath) - .WithHttpEndpoint(hostPort: 5031, env: "PORT"); + .WithHttpEndpoint(port: 5031, env: "PORT"); NpmAppBuilder = AppBuilder.AddNpmApp("npmapp", path) - .WithHttpEndpoint(hostPort: 5032, env: "PORT"); + .WithHttpEndpoint(port: 5032, env: "PORT"); } if (includeIntegrationServices) diff --git a/tests/testproject/TestProject.AppHost/aspire-manifest.json b/tests/testproject/TestProject.AppHost/aspire-manifest.json index 6f5b9bafb6..06a96e418b 100644 --- a/tests/testproject/TestProject.AppHost/aspire-manifest.json +++ b/tests/testproject/TestProject.AppHost/aspire-manifest.json @@ -119,7 +119,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1433 + "targetPort": 1433 } }, "connectionString": "Server={sqlservercontainer.bindings.tcp.host},{sqlservercontainer.bindings.tcp.port};User ID=sa;Password={sqlservercontainer.inputs.password};TrustServerCertificate=true;", @@ -146,7 +146,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 3306 + "targetPort": 3306 } }, "connectionString": "Server={mysqlcontainer.bindings.tcp.host};Port={mysqlcontainer.bindings.tcp.port};User ID=root;Password={mysqlcontainer.inputs.password}", @@ -170,7 +170,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 6379 + "targetPort": 6379 } }, "connectionString": "{rediscontainer.bindings.tcp.host}:{rediscontainer.bindings.tcp.port}" @@ -188,7 +188,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } }, "connectionString": "Host={postgrescontainer.bindings.tcp.host};Port={postgrescontainer.bindings.tcp.port};Username=postgres;Password={postgrescontainer.inputs.password};", @@ -216,13 +216,13 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5672 + "targetPort": 5672 }, "management": { "scheme": "http", "protocol": "tcp", "transport": "http", - "containerPort": 15672 + "targetPort": 15672 } }, "connectionString": "amqp://guest:{rabbitmqcontainer.inputs.password}@{rabbitmqcontainer.bindings.management.host}:{rabbitmqcontainer.bindings.management.port}", @@ -249,7 +249,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 9092 + "targetPort": 9092 } }, "connectionString": "{kafkacontainer.bindings.tcp.host}:{kafkacontainer.bindings.tcp.port}" @@ -262,7 +262,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 27017 + "targetPort": 27017 } }, "connectionString": "{mongodbcontainer.bindings.tcp.host}:{mongodbcontainer.bindings.tcp.port}" @@ -279,7 +279,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 1433 + "targetPort": 1433 } }, "connectionString": "Server={sqlserverabstract.bindings.tcp.host},{sqlserverabstract.bindings.tcp.port};User ID=sa;Password={sqlserverabstract.inputs.password};TrustServerCertificate=true;", @@ -306,7 +306,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 3306 + "targetPort": 3306 } }, "connectionString": "Server={mysqlabstract.bindings.tcp.host};Port={mysqlabstract.bindings.tcp.port};User ID=root;Password={mysqlabstract.inputs.password}", @@ -330,7 +330,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 6379 + "targetPort": 6379 } }, "connectionString": "{redisabstract.bindings.tcp.host}:{redisabstract.bindings.tcp.port}" @@ -348,7 +348,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5432 + "targetPort": 5432 } }, "connectionString": "Host={postgresabstract.bindings.tcp.host};Port={postgresabstract.bindings.tcp.port};Username=postgres;Password={postgresabstract.inputs.password};", @@ -376,13 +376,13 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 5672 + "targetPort": 5672 }, "management": { "scheme": "http", "protocol": "tcp", "transport": "http", - "containerPort": 15672 + "targetPort": 15672 } }, "connectionString": "amqp://guest:{rabbitmqabstract.inputs.password}@{rabbitmqabstract.bindings.management.host}:{rabbitmqabstract.bindings.management.port}", @@ -409,7 +409,7 @@ "scheme": "tcp", "protocol": "tcp", "transport": "tcp", - "containerPort": 9092 + "targetPort": 9092 } }, "connectionString": "{kafkaabstract.bindings.tcp.host}:{kafkaabstract.bindings.tcp.port}"