Skip to content

Fix blocking ParameterResource.Value calls to prevent deadlocks in Aspire 9.4+ #763

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 30, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private static IResourceBuilder<T> WithJolokiaHealthCheck<T>(
{
Uri baseUri = new Uri(endpoint.Url, UriKind.Absolute);
string userName = (await builder.Resource.UserNameReference.GetValueAsync(ct))!;
string password = builder.Resource.PasswordParameter.Value;
string password = (await ((IValueProvider)builder.Resource.PasswordParameter).GetValueAsync(ct))!;
basicAuthentication = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}"));
uri = new UriBuilder(baseUri)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ public static IResourceBuilder<MinioContainerResource> AddMinioContainer(
.WithImageRegistry(MinioContainerImageTags.Registry)
.WithHttpEndpoint(targetPort: 9000, port: port, name: MinioContainerResource.PrimaryEndpointName)
.WithHttpEndpoint(targetPort: consoleTargetPort, name: MinioContainerResource.ConsoleEndpointName)
.WithEnvironment(RootUserEnvVarName, resource.RootUser.Value)
.WithEnvironment(RootPasswordEnvVarName, resource.PasswordParameter.Value)
.WithEnvironment(RootUserEnvVarName, $"{resource.RootUser}")
.WithEnvironment(RootPasswordEnvVarName, $"{resource.PasswordParameter}")
.WithArgs("server", "/data", "--console-address", $":{consoleTargetPort}");

var endpoint = builderWithResource.Resource.GetEndpoint(MinioContainerResource.PrimaryEndpointName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private static void ConfigureDbGateContainer(EnvironmentCallbackContext context,
context.EnvironmentVariables.Add($"LABEL_mysql{counter}", mySqlServerResource.Name);
context.EnvironmentVariables.Add($"SERVER_mysql{counter}", mySqlServerResource.Name);
context.EnvironmentVariables.Add($"USER_mysql{counter}", "root");
context.EnvironmentVariables.Add($"PASSWORD_mysql{counter}", mySqlServerResource.PasswordParameter.Value);
context.EnvironmentVariables.Add($"PASSWORD_mysql{counter}", mySqlServerResource.PasswordParameter);
context.EnvironmentVariables.Add($"PORT_mysql{counter}", mySqlServerResource.PrimaryEndpoint.TargetPort!.ToString()!);
context.EnvironmentVariables.Add($"ENGINE_mysql{counter}", "mysql@dbgate-plugin-mysql");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,14 @@

foreach (var postgresServer in postgresInstances)
{
var user = postgresServer.UserNameParameter?.Value ?? "postgres";
var userParameter = postgresServer.UserNameParameter ?? ReferenceExpression.Create("postgres");

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.PostgreSQL.Extensions.Tests-ubuntu-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.PostgreSQL.Extensions.Tests-ubuntu-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.Adminer.Tests-ubuntu-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.Adminer.Tests-ubuntu-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.DbGate.Tests-ubuntu-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.DbGate.Tests-ubuntu-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.PostgreSQL.Extensions.Tests-windows-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.PostgreSQL.Extensions.Tests-windows-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.DbGate.Tests-windows-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / run-tests / Hosting.DbGate.Tests-windows-latest

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Expected interpolated string

Check failure on line 108 in src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/PostgresBuilderExtensions.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Expected interpolated string

// DbGate assumes Postgres is being accessed over a default Aspire container network and hardcodes the resource address
// This will need to be refactored once updated service discovery APIs are available
context.EnvironmentVariables.Add($"LABEL_postgres{counter}", postgresServer.Name);
context.EnvironmentVariables.Add($"SERVER_postgres{counter}", postgresServer.Name);
context.EnvironmentVariables.Add($"USER_postgres{counter}", user);
context.EnvironmentVariables.Add($"PASSWORD_postgres{counter}", postgresServer.PasswordParameter.Value);
context.EnvironmentVariables.Add($"USER_postgres{counter}", userParameter);
context.EnvironmentVariables.Add($"PASSWORD_postgres{counter}", postgresServer.PasswordParameter);
context.EnvironmentVariables.Add($"PORT_postgres{counter}", postgresServer.PrimaryEndpoint.TargetPort!.ToString()!);
context.EnvironmentVariables.Add($"ENGINE_postgres{counter}", "postgres@dbgate-plugin-postgres");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ private static void ConfigureDbGateContainer(EnvironmentCallbackContext context,

// DbGate assumes Redis is being accessed over a default Aspire container network and hardcodes the resource address
var redisUrl = redisResource.PasswordParameter is not null ?
$"redis://:{redisResource.PasswordParameter.Value}@{redisResource.Name}:{redisResource.PrimaryEndpoint.TargetPort}" : $"redis://{redisResource.Name}:{redisResource.PrimaryEndpoint.TargetPort}";
ReferenceExpression.Create($"redis://:{redisResource.PasswordParameter}@{redisResource.Name}:{redisResource.PrimaryEndpoint.TargetPort?.ToString()}") :
ReferenceExpression.Create($"redis://{redisResource.Name}:{redisResource.PrimaryEndpoint.TargetPort?.ToString()}");

context.EnvironmentVariables.Add($"LABEL_redis{counter}", redisResource.Name);
context.EnvironmentVariables.Add($"URL_redis{counter}", redisUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private static void ConfigureDbGateContainer(EnvironmentCallbackContext context,
context.EnvironmentVariables.Add($"LABEL_sqlserver{counter}", sqlServerResource.Name);
context.EnvironmentVariables.Add($"SERVER_sqlserver{counter}", sqlServerResource.Name);
context.EnvironmentVariables.Add($"USER_sqlserver{counter}", "sa");
context.EnvironmentVariables.Add($"PASSWORD_sqlserver{counter}", sqlServerResource.PasswordParameter.Value);
context.EnvironmentVariables.Add($"PASSWORD_sqlserver{counter}", sqlServerResource.PasswordParameter);
context.EnvironmentVariables.Add($"PORT_sqlserver{counter}", sqlServerResource.PrimaryEndpoint.TargetPort!.ToString()!);
context.EnvironmentVariables.Add($"ENGINE_sqlserver{counter}", "mssql@dbgate-plugin-mssql");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net.Sockets;
using Aspire.Components.Common.Tests;
using Aspire.Hosting;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;

namespace CommunityToolkit.Aspire.Hosting.DbGate.Tests;
Expand Down Expand Up @@ -295,10 +296,11 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
Assert.Equal("USER_postgres1", item.Key);
Assert.Equal("postgres", item.Value);
},
item =>
async item =>
{
Assert.Equal("PASSWORD_postgres1", item.Key);
Assert.Equal(postgresResource1.PasswordParameter.Value, item.Value);
var expectedPassword = await ((IValueProvider)postgresResource1.PasswordParameter).GetValueAsync(default);
Assert.Equal(expectedPassword, item.Value);
},
item =>
{
Expand All @@ -325,10 +327,11 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
Assert.Equal("USER_postgres2", item.Key);
Assert.Equal("postgres", item.Value);
},
item =>
async item =>
{
Assert.Equal("PASSWORD_postgres2", item.Key);
Assert.Equal(postgresResource2.PasswordParameter.Value, item.Value);
var expectedPassword = await ((IValueProvider)postgresResource2.PasswordParameter).GetValueAsync(default);
Assert.Equal(expectedPassword, item.Value);
},
item =>
{
Expand All @@ -345,12 +348,12 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
Assert.Equal("LABEL_redis1", item.Key);
Assert.Equal(redisResource1.Name, item.Value);
},
item =>
async item =>
{
var redisUrl = redisResource1.PasswordParameter is not null ?
$"redis://:{redisResource1.PasswordParameter.Value}@{redisResource1.Name}:{redisResource1.PrimaryEndpoint.TargetPort}" : $"redis://{redisResource1.Name}:{redisResource1.PrimaryEndpoint.TargetPort}";
var expectedRedisUrl = redisResource1.PasswordParameter is not null ?
$"redis://:{await ((IValueProvider)redisResource1.PasswordParameter).GetValueAsync(default)}@{redisResource1.Name}:{redisResource1.PrimaryEndpoint.TargetPort}" : $"redis://{redisResource1.Name}:{redisResource1.PrimaryEndpoint.TargetPort}";
Assert.Equal("URL_redis1", item.Key);
Assert.Equal(redisUrl, item.Value);
Assert.Equal(expectedRedisUrl, item.Value);
},
item =>
{
Expand All @@ -362,12 +365,12 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
Assert.Equal("LABEL_redis2", item.Key);
Assert.Equal(redisResource2.Name, item.Value);
},
item =>
async item =>
{
var redisUrl = redisResource2.PasswordParameter is not null ?
$"redis://:{redisResource2.PasswordParameter.Value}@{redisResource2.Name}:{redisResource2.PrimaryEndpoint.TargetPort}" : $"redis://{redisResource2.Name}:{redisResource2.PrimaryEndpoint.TargetPort}";
var expectedRedisUrl = redisResource2.PasswordParameter is not null ?
$"redis://:{await ((IValueProvider)redisResource2.PasswordParameter).GetValueAsync(default)}@{redisResource2.Name}:{redisResource2.PrimaryEndpoint.TargetPort}" : $"redis://{redisResource2.Name}:{redisResource2.PrimaryEndpoint.TargetPort}";
Assert.Equal("URL_redis2", item.Key);
Assert.Equal(redisUrl, item.Value);
Assert.Equal(expectedRedisUrl, item.Value);
},
item =>
{
Expand All @@ -389,10 +392,11 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
Assert.Equal("USER_sqlserver1", item.Key);
Assert.Equal("sa", item.Value);
},
item =>
async item =>
{
Assert.Equal("PASSWORD_sqlserver1", item.Key);
Assert.Equal(sqlserverResource1.PasswordParameter.Value, item.Value);
var expectedPassword = await ((IValueProvider)sqlserverResource1.PasswordParameter).GetValueAsync(default);
Assert.Equal(expectedPassword, item.Value);
},
item =>
{
Expand All @@ -419,10 +423,11 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
Assert.Equal("USER_sqlserver2", item.Key);
Assert.Equal("sa", item.Value);
},
item =>
async item =>
{
Assert.Equal("PASSWORD_sqlserver2", item.Key);
Assert.Equal(sqlserverResource2.PasswordParameter.Value, item.Value);
var expectedPassword = await ((IValueProvider)sqlserverResource2.PasswordParameter).GetValueAsync(default);
Assert.Equal(expectedPassword, item.Value);
},
item =>
{
Expand All @@ -449,10 +454,11 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
Assert.Equal("USER_mysql1", item.Key);
Assert.Equal("root", item.Value);
},
item =>
async item =>
{
Assert.Equal("PASSWORD_mysql1", item.Key);
Assert.Equal(mysqlResource1.PasswordParameter.Value, item.Value);
var expectedPassword = await ((IValueProvider)mysqlResource1.PasswordParameter).GetValueAsync(default);
Assert.Equal(expectedPassword, item.Value);
},
item =>
{
Expand All @@ -479,10 +485,11 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
Assert.Equal("USER_mysql2", item.Key);
Assert.Equal("root", item.Value);
},
item =>
async item =>
{
Assert.Equal("PASSWORD_mysql2", item.Key);
Assert.Equal(mysqlResource2.PasswordParameter.Value, item.Value);
var expectedPassword = await ((IValueProvider)mysqlResource2.PasswordParameter).GetValueAsync(default);
Assert.Equal(expectedPassword, item.Value);
},
item =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ public void WithAuthTokenStringParameterEnvironmentVariable()
var context = new EnvironmentCallbackContext(new DistributedApplicationExecutionContext(new DistributedApplicationExecutionContextOptions(DistributedApplicationOperation.Run)));
environment.Callback(context);

Assert.Equal("your-ngrok-auth-token", ((ParameterResource)context.EnvironmentVariables["NGROK_AUTHTOKEN"]).Value);
var parameterResource = (ParameterResource)context.EnvironmentVariables["NGROK_AUTHTOKEN"];
Assert.NotNull(parameterResource);
// Verify it's the expected parameter resource without calling .Value
Assert.Equal("ngrok-authtoken", parameterResource.Name);
}

[Fact]
Expand Down
Loading