Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
45 changes: 45 additions & 0 deletions src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,51 @@ public static IResourceBuilder<PostgresDatabaseResource> WithCreationScript(this
return builder;
}

/// <summary>
/// Configures the password that the PostgreSQL resource is used.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="password">The parameter used to provide the password for the PostgreSQL resource.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<PostgresServerResource> WithPassword(this IResourceBuilder<PostgresServerResource> builder, IResourceBuilder<ParameterResource> password)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(password);

builder.Resource.PasswordParameter = password.Resource;
return builder;
}

/// <summary>
/// Configures the user name that the PostgreSQL resource is used.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="userName">The parameter used to provide the user name for the PostgreSQL resource.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<PostgresServerResource> WithUserName(this IResourceBuilder<PostgresServerResource> builder, IResourceBuilder<ParameterResource> userName)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(userName);

builder.Resource.UserNameParameter = userName.Resource;
return builder;
}

/// <summary>
/// Configures the host port that the PostgreSQL resource is exposed on instead of using randomly assigned port.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="port">The port to bind on the host. If <see langword="null"/> is used random port will be assigned.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<PostgresServerResource> WithHostPort(this IResourceBuilder<PostgresServerResource> builder, int? port)
{
ArgumentNullException.ThrowIfNull(builder);
return builder.WithEndpoint(PostgresServerResource.PrimaryEndpointName, endpoint =>
{
endpoint.Port = port;
});
}

private static IEnumerable<ContainerFileSystemItem> WritePgWebBookmarks(IEnumerable<PostgresDatabaseResource> postgresInstances)
{
var bookmarkFiles = new List<ContainerFileSystemItem>();
Expand Down
31 changes: 31 additions & 0 deletions src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -599,4 +599,35 @@ public static IResourceBuilder<RedisInsightResource> WithDataBindMount(this IRes

return builder.WithBindMount(source, "/data");
}

/// <summary>
/// Configures the password that the Redis resource is used.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="password">The parameter used to provide the password for the Redis resource.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<RedisResource> WithPassword(this IResourceBuilder<RedisResource> builder, IResourceBuilder<ParameterResource> password)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(password);

builder.Resource.SetPassword(password.Resource);
return builder;
}

/// <summary>
/// Configures the host port that the Redis resource is exposed on instead of using randomly assigned port.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="port">The port to bind on the host. If <see langword="null"/> is used random port will be assigned.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<RedisResource> WithHostPort(this IResourceBuilder<RedisResource> builder, int port)
{
ArgumentNullException.ThrowIfNull(builder);
return builder.WithEndpoint(RedisResource.PrimaryEndpointName, endpoint =>
{
endpoint.Port = port;
});

}
}
9 changes: 8 additions & 1 deletion src/Aspire.Hosting.Redis/RedisResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public RedisResource(string name, ParameterResource password) : this(name)
/// <summary>
/// Gets the parameter that contains the Redis server password.
/// </summary>
public ParameterResource? PasswordParameter { get; }
public ParameterResource? PasswordParameter { get; private set; }

private ReferenceExpression BuildConnectionString()
{
Expand Down Expand Up @@ -76,4 +76,11 @@ public ReferenceExpression ConnectionStringExpression

return BuildConnectionString().GetValueAsync(cancellationToken);
}

internal void SetPassword(ParameterResource password)
{
ArgumentNullException.ThrowIfNull(password);

PasswordParameter = password;
}
}
30 changes: 30 additions & 0 deletions src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,36 @@ public static IResourceBuilder<SqlServerDatabaseResource> WithCreationScript(thi
return builder;
}

/// <summary>
/// Configures the password that the SqlServer resource is used.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="password">The parameter used to provide the password for the SqlServer resource.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<SqlServerServerResource> WithPassword(this IResourceBuilder<SqlServerServerResource> builder, IResourceBuilder<ParameterResource> password)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(password);

builder.Resource.SetPassword(password.Resource);
return builder;
}

/// <summary>
/// Configures the host port that the SqlServer resource is exposed on instead of using randomly assigned port.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="port">The port to bind on the host. If <see langword="null"/> is used random port will be assigned.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<SqlServerServerResource> WithHostPort(this IResourceBuilder<SqlServerServerResource> builder, int port)
{
ArgumentNullException.ThrowIfNull(builder);
return builder.WithEndpoint(SqlServerServerResource.PrimaryEndpointName, endpoint =>
{
endpoint.Port = port;
});
}

private static async Task CreateDatabaseAsync(SqlConnection sqlConnection, SqlServerDatabaseResource sqlDatabase, IServiceProvider serviceProvider, CancellationToken ct)
{
try
Expand Down
9 changes: 8 additions & 1 deletion src/Aspire.Hosting.SqlServer/SqlServerServerResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public SqlServerServerResource(string name, ParameterResource password) : base(n
/// <summary>
/// Gets the parameter that contains the SQL Server password.
/// </summary>
public ParameterResource PasswordParameter { get; }
public ParameterResource PasswordParameter { get; private set; }

private ReferenceExpression ConnectionString =>
ReferenceExpression.Create(
Expand Down Expand Up @@ -76,6 +76,13 @@ public ReferenceExpression ConnectionStringExpression
/// </summary>
public IReadOnlyDictionary<string, string> Databases => _databases;

internal void SetPassword(ParameterResource password)
{
ArgumentNullException.ThrowIfNull(password);

PasswordParameter = password;
}

internal void AddDatabase(SqlServerDatabaseResource database)
{
_databases.TryAdd(database.Name, database.DatabaseName);
Expand Down
57 changes: 57 additions & 0 deletions tests/Aspire.Hosting.Azure.Tests/AzurePostgresExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -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 System.Net.Sockets;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -341,6 +342,62 @@ public async Task AddAzurePostgresFlexibleServerRunAsContainerProducesCorrectCon
Assert.EndsWith("Database=db2Name", db2ConnectionString);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task AddAzurePostgresFlexibleServerRunAsContainerProducesCorrectUserNameAndPasswordAndHost(bool addDbBeforeRunAsContainer)
{
using var builder = TestDistributedApplicationBuilder.Create();

var postgres = builder.AddAzurePostgresFlexibleServer("postgres-data");
var pass = builder.AddParameter("pass", "p@ssw0rd1");
var user = builder.AddParameter("user", "user1");

IResourceBuilder<AzurePostgresFlexibleServerDatabaseResource> db1 = null!;
IResourceBuilder<AzurePostgresFlexibleServerDatabaseResource> db2 = null!;
if (addDbBeforeRunAsContainer)
{
db1 = postgres.AddDatabase("db1");
db2 = postgres.AddDatabase("db2", "db2Name");
}

IResourceBuilder<PostgresServerResource>? innerPostgres = null;
postgres.RunAsContainer(configureContainer: c =>
{
c.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 12455))
.WithHostPort(12455)
.WithPassword(pass)
.WithUserName(user);
innerPostgres = c;
});

if (!addDbBeforeRunAsContainer)
{
db1 = postgres.AddDatabase("db1");
db2 = postgres.AddDatabase("db2", "db2Name");
}

Assert.NotNull(innerPostgres);

var endpoint = Assert.Single(innerPostgres.Resource.Annotations.OfType<EndpointAnnotation>());
Assert.Equal(5432, endpoint.TargetPort);
Assert.False(endpoint.IsExternal);
Assert.Equal("tcp", endpoint.Name);
Assert.Equal(12455, endpoint.Port);
Assert.Equal(ProtocolType.Tcp, endpoint.Protocol);
Assert.Equal("tcp", endpoint.Transport);
Assert.Equal("tcp", endpoint.UriScheme);

Assert.True(postgres.Resource.IsContainer(), "The resource should now be a container resource.");
Assert.Equal("Host=localhost;Port=12455;Username=user1;Password=p@ssw0rd1", await postgres.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None));

var db1ConnectionString = await db1.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None);
Assert.Equal("Host=localhost;Port=12455;Username=user1;Password=p@ssw0rd1;Database=db1", db1ConnectionString);

var db2ConnectionString = await db2.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None);
Assert.Equal("Host=localhost;Port=12455;Username=user1;Password=p@ssw0rd1;Database=db2Name", db2ConnectionString);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
Expand Down
35 changes: 35 additions & 0 deletions tests/Aspire.Hosting.Azure.Tests/AzureRedisExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -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 System.Net.Sockets;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -210,6 +211,40 @@ public async Task AddAzureRedisRunAsContainerProducesCorrectConnectionString()
Assert.Equal($"localhost:12455,password={redisResource.PasswordParameter.Value}", await redis.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None));
}

[Fact]
public async Task AddAzureRedisRunAsContainerProducesCorrectHostAndPassword()
{
using var builder = TestDistributedApplicationBuilder.Create();
var pass = builder.AddParameter("pass", "p@ssw0rd1");

RedisResource? redisResource = null;
var redis = builder.AddAzureRedis("cache")
.RunAsContainer(c =>
{
redisResource = c.Resource;

c.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 12455))
.WithHostPort(12455)
.WithPassword(pass);
});

Assert.NotNull(redisResource);

var endpoint = Assert.Single(redisResource.Annotations.OfType<EndpointAnnotation>());
Assert.Equal(6379, endpoint.TargetPort);
Assert.False(endpoint.IsExternal);
Assert.Equal("tcp", endpoint.Name);
Assert.Equal(12455, endpoint.Port);
Assert.Equal(ProtocolType.Tcp, endpoint.Protocol);
Assert.Equal("tcp", endpoint.Transport);
Assert.Equal("tcp", endpoint.UriScheme);

Assert.True(redis.Resource.IsContainer(), "The resource should now be a container resource.");

Assert.NotNull(redisResource?.PasswordParameter);
Assert.Equal($"localhost:12455,password=p@ssw0rd1", await redis.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None));
}

[Theory]
[InlineData(true)]
[InlineData(false)]
Expand Down
55 changes: 55 additions & 0 deletions tests/Aspire.Hosting.Azure.Tests/AzureSqlExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -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 System.Net.Sockets;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;
using Xunit;
Expand Down Expand Up @@ -189,6 +190,60 @@ public async Task AddAzureSqlServerRunAsContainerProducesCorrectConnectionString
Assert.EndsWith(";TrustServerCertificate=true;Database=db2Name", db2ConnectionString);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task AddAzureSqlServerRunAsContainerProducesCorrectPasswordAndPort(bool addDbBeforeRunAsContainer)
{
using var builder = TestDistributedApplicationBuilder.Create();

var sql = builder.AddAzureSqlServer("sql");
var pass = builder.AddParameter("pass", "p@ssw0rd1");
IResourceBuilder<AzureSqlDatabaseResource> db1 = null!;
IResourceBuilder<AzureSqlDatabaseResource> db2 = null!;
if (addDbBeforeRunAsContainer)
{
db1 = sql.AddDatabase("db1");
db2 = sql.AddDatabase("db2", "db2Name");
}

IResourceBuilder<SqlServerServerResource>? innerSql = null;
sql.RunAsContainer(configureContainer: c =>
{
c.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 12455))
.WithHostPort(12455)
.WithPassword(pass);
innerSql = c;
});

Assert.NotNull(innerSql);

if (!addDbBeforeRunAsContainer)
{
db1 = sql.AddDatabase("db1");
db2 = sql.AddDatabase("db2", "db2Name");
}

var endpoint = Assert.Single(innerSql.Resource.Annotations.OfType<EndpointAnnotation>());
Assert.Equal(1433, endpoint.TargetPort);
Assert.False(endpoint.IsExternal);
Assert.Equal("tcp", endpoint.Name);
Assert.Equal(12455, endpoint.Port);
Assert.Equal(ProtocolType.Tcp, endpoint.Protocol);
Assert.Equal("tcp", endpoint.Transport);
Assert.Equal("tcp", endpoint.UriScheme);

Assert.True(sql.Resource.IsContainer(), "The resource should now be a container resource.");
var serverConnectionString = await sql.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None);
Assert.Equal("Server=127.0.0.1,12455;User ID=sa;Password=p@ssw0rd1;TrustServerCertificate=true", serverConnectionString);

var db1ConnectionString = await db1.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None);
Assert.StartsWith("Server=127.0.0.1,12455;User ID=sa;Password=p@ssw0rd1;TrustServerCertificate=true;Database=db1", db1ConnectionString);

var db2ConnectionString = await db2.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None);
Assert.StartsWith("Server=127.0.0.1,12455;User ID=sa;Password=p@ssw0rd1;TrustServerCertificate=true;Database=db2Name", db2ConnectionString);
}

[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
Expand Down
Loading