Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
10 changes: 8 additions & 2 deletions src/Aspire.Hosting.Azure.Sql/AzureSqlExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Azure;
using Aspire.Hosting.Azure.Sql;
using Azure.Provisioning;
using Azure.Provisioning.Expressions;
using Azure.Provisioning.Primitives;
Expand Down Expand Up @@ -132,6 +133,7 @@ public static IResourceBuilder<AzureSqlDatabaseResource> AddDatabase(this IResou
/// </summary>
/// <param name="builder">The builder for the Azure SQL resource.</param>
/// <param name="configureContainer">Callback that exposes underlying container to allow for customization.</param>
/// <param name="configureOptions">Callback to configure underlying container options including password and port.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{AzureSqlServerResource}"/> builder.</returns>
/// <example>
/// The following example creates an Azure SQL Database (server) resource that runs locally in a
Expand All @@ -148,7 +150,7 @@ public static IResourceBuilder<AzureSqlDatabaseResource> AddDatabase(this IResou
/// builder.Build().Run();
/// </code>
/// </example>
public static IResourceBuilder<AzureSqlServerResource> RunAsContainer(this IResourceBuilder<AzureSqlServerResource> builder, Action<IResourceBuilder<SqlServerServerResource>>? configureContainer = null)
public static IResourceBuilder<AzureSqlServerResource> RunAsContainer(this IResourceBuilder<AzureSqlServerResource> builder, Action<IResourceBuilder<SqlServerServerResource>>? configureContainer = null, Action<RunAsContainerOptions>? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(builder);

Expand All @@ -165,7 +167,11 @@ public static IResourceBuilder<AzureSqlServerResource> RunAsContainer(this IReso

RemoveAzureResources(builder.ApplicationBuilder, azureResource, azureDatabases);

var sqlContainer = builder.ApplicationBuilder.AddSqlServer(azureResource.Name);
var runAsContainerOptions = new RunAsContainerOptions();

configureOptions?.Invoke(runAsContainerOptions);

var sqlContainer = builder.ApplicationBuilder.AddSqlServer(azureResource.Name, password: runAsContainerOptions.Password, runAsContainerOptions.Port);

azureResource.SetInnerResource(sqlContainer.Resource);

Expand Down
11 changes: 11 additions & 0 deletions src/Aspire.Hosting.Azure.Sql/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Aspire.Hosting.AzureSqlExtensions.RunAsContainer(Aspire.Hosting.ApplicationModel.IResourceBuilder{Aspire.Hosting.Azure.AzureSqlServerResource},System.Action{Aspire.Hosting.ApplicationModel.IResourceBuilder{Aspire.Hosting.ApplicationModel.SqlServerServerResource}})</Target>
<Left>lib/net8.0/Aspire.Hosting.Azure.Sql.dll</Left>
<Right>lib/net8.0/Aspire.Hosting.Azure.Sql.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
</Suppressions>
24 changes: 24 additions & 0 deletions src/Aspire.Hosting.Azure.Sql/RunAsContainerOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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.ApplicationModel;

namespace Aspire.Hosting.Azure.Sql;

/// <summary>
/// Represents options for running a SQL Server as a container.
/// This class allows configuration of the SQL Server container,
/// including the port to expose and the password for the SQL Server instance.
/// </summary>
public class RunAsContainerOptions
{
/// <summary>
/// Gets or Sets the port for the SQL Server.
/// </summary>
public int? Port { get; set; }

/// <summary>
/// Gets or Sets the password for the SQL Server.
/// </summary>
public IResourceBuilder<ParameterResource>? Password { get; set; }
}
58 changes: 58 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,63 @@ 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(configureOptions: c =>
{
c.Port = 12455;
c.Password = pass;
}, configureContainer: c =>
{
innerSql = c;
c.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 12455));
});

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