diff --git a/playground/mongo/Mongo.AppHost/Program.cs b/playground/mongo/Mongo.AppHost/Program.cs index 0b347e63849..e76f2c911cc 100644 --- a/playground/mongo/Mongo.AppHost/Program.cs +++ b/playground/mongo/Mongo.AppHost/Program.cs @@ -9,7 +9,7 @@ builder.AddProject("api") .WithExternalHttpEndpoints() - .WithReference(db); + .WithReference(db).WaitFor(db); #if !SKIP_DASHBOARD_REFERENCE // This project is only added in playground projects to support development/debugging diff --git a/src/Aspire.Hosting.MongoDB/Aspire.Hosting.MongoDB.csproj b/src/Aspire.Hosting.MongoDB/Aspire.Hosting.MongoDB.csproj index b8f0a0f64fd..2558c18177e 100644 --- a/src/Aspire.Hosting.MongoDB/Aspire.Hosting.MongoDB.csproj +++ b/src/Aspire.Hosting.MongoDB/Aspire.Hosting.MongoDB.csproj @@ -6,6 +6,7 @@ aspire integration hosting MongoDB $(SharedDir)MongoDB_300px.png MongoDB support for .NET Aspire. + $(NoWarn);CS8002 @@ -20,4 +21,8 @@ + + + + diff --git a/src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs b/src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs index c86f2b4d3f0..421dccd19ac 100644 --- a/src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs +++ b/src/Aspire.Hosting.MongoDB/MongoDBBuilderExtensions.cs @@ -4,6 +4,7 @@ using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.MongoDB; using Aspire.Hosting.Utils; +using Microsoft.Extensions.DependencyInjection; namespace Aspire.Hosting; @@ -29,11 +30,27 @@ public static IResourceBuilder AddMongoDB(this IDistribut var mongoDBContainer = new MongoDBServerResource(name); + string? connectionString = null; + + builder.Eventing.Subscribe(mongoDBContainer, async (@event, ct) => + { + connectionString = await mongoDBContainer.ConnectionStringExpression.GetValueAsync(ct).ConfigureAwait(false); + + if (connectionString == null) + { + throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{mongoDBContainer.Name}' resource but the connection string was null."); + } + }); + + var healthCheckKey = $"{name}_check"; + builder.Services.AddHealthChecks().AddMongoDb(sp => connectionString ?? throw new InvalidOperationException("Connection string is unavailable"), name: healthCheckKey); + return builder .AddResource(mongoDBContainer) .WithEndpoint(port: port, targetPort: DefaultContainerPort, name: MongoDBServerResource.PrimaryEndpointName) .WithImage(MongoDBContainerImageTags.Image, MongoDBContainerImageTags.Tag) - .WithImageRegistry(MongoDBContainerImageTags.Registry); + .WithImageRegistry(MongoDBContainerImageTags.Registry) + .WithHealthCheck(healthCheckKey); } /// diff --git a/src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs b/src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs index ab1d110a316..a097297fca6 100644 --- a/src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs +++ b/src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs @@ -56,7 +56,7 @@ public static IResourceBuilder AddPostgres(this IDistrib if (connectionString == null) { - throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{postgresServer}' resource but the connection string was null."); + throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{postgresServer.Name}' resource but the connection string was null."); } var lookup = builder.Resources.OfType().ToDictionary(d => d.Name); diff --git a/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs b/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs index 397478b3f19..d95519100d6 100644 --- a/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs +++ b/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs @@ -47,7 +47,7 @@ public static IResourceBuilder AddRedis(this IDistributedApplicat if (connectionString == null) { - throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{redis}' resource but the connection string was null."); + throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{redis.Name}' resource but the connection string was null."); } }); diff --git a/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs b/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs index 585316a4e19..0bcdd6f3848 100644 --- a/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs +++ b/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs @@ -38,7 +38,7 @@ public static IResourceBuilder AddSqlServer(this IDistr if (connectionString == null) { - throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{sqlServer}' resource but the connection string was null."); + throw new DistributedApplicationException($"ConnectionStringAvailableEvent was published for the '{sqlServer.Name}' resource but the connection string was null."); } var lookup = builder.Resources.OfType().ToDictionary(d => d.Name); diff --git a/tests/Aspire.Hosting.MongoDB.Tests/MongoDbFunctionalTests.cs b/tests/Aspire.Hosting.MongoDB.Tests/MongoDbFunctionalTests.cs index e290195a326..cdf596fb10f 100644 --- a/tests/Aspire.Hosting.MongoDB.Tests/MongoDbFunctionalTests.cs +++ b/tests/Aspire.Hosting.MongoDB.Tests/MongoDbFunctionalTests.cs @@ -11,6 +11,8 @@ using Xunit; using Xunit.Abstractions; using Polly; +using Aspire.Hosting.ApplicationModel; +using Microsoft.Extensions.Diagnostics.HealthChecks; namespace Aspire.Hosting.MongoDB.Tests; @@ -26,6 +28,45 @@ public class MongoDbFunctionalTests(ITestOutputHelper testOutputHelper) new() { Name = "Schindler's List"}, ]; + [Fact] + [RequiresDocker] + public async Task VerifyWaitForOnMongoBlocksDependentResources() + { + var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3)); + using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(testOutputHelper); + + var healthCheckTcs = new TaskCompletionSource(); + builder.Services.AddHealthChecks().AddAsyncCheck("blocking_check", () => + { + return healthCheckTcs.Task; + }); + + var resource = builder.AddMongoDB("resource") + .WithHealthCheck("blocking_check"); + + var dependentResource = builder.AddMongoDB("dependentresource") + .WaitFor(resource); + + using var app = builder.Build(); + + var pendingStart = app.StartAsync(cts.Token); + + var rns = app.Services.GetRequiredService(); + + await rns.WaitForResourceAsync(resource.Resource.Name, KnownResourceStates.Running, cts.Token); + + await rns.WaitForResourceAsync(dependentResource.Resource.Name, KnownResourceStates.Waiting, cts.Token); + + healthCheckTcs.SetResult(HealthCheckResult.Healthy()); + + await rns.WaitForResourceAsync(resource.Resource.Name, (re => re.Snapshot.HealthStatus == HealthStatus.Healthy), cts.Token); + + await rns.WaitForResourceAsync(dependentResource.Resource.Name, KnownResourceStates.Running, cts.Token); + + await pendingStart; + await app.StopAsync(); + } + [Fact] [RequiresDocker] public async Task VerifyMongoDBResource()