diff --git a/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.csproj b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.csproj
index a6b99132..e32acfbb 100644
--- a/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.csproj
+++ b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.csproj
@@ -18,6 +18,7 @@
+
diff --git a/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost/Program.cs b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost/Program.cs
index 5ce0b109..cb536582 100644
--- a/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost/Program.cs
+++ b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost/Program.cs
@@ -32,4 +32,14 @@
sqlserver2.AddDatabase("db12");
+var mysql1 = builder.AddMySql("mysql1")
+ .WithDbGate(c => c.WithHostPort(8068));
+mysql1.AddDatabase("db13");
+mysql1.AddDatabase("db14");
+
+var mysql2 = builder.AddMySql("mysql2")
+ .WithDbGate();
+mysql2.AddDatabase("db15");
+mysql2.AddDatabase("db16");
+
builder.Build().Run();
\ No newline at end of file
diff --git a/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost/Program.cs b/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost/Program.cs
index adec63c4..34bbcb38 100644
--- a/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost/Program.cs
+++ b/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost/Program.cs
@@ -1,7 +1,8 @@
var builder = DistributedApplication.CreateBuilder(args);
var mysql1 = builder.AddMySql("mysql1")
- .WithAdminer(c => c.WithHostPort(8989));
+ .WithAdminer(c => c.WithHostPort(8989))
+ .WithDbGate(c => c.WithHostPort(9999));
mysql1.AddDatabase("db1");
mysql1.AddDatabase("db2");
diff --git a/src/CommunityToolkit.Aspire.Hosting.DbGate/DbGateContainerImageTags.cs b/src/CommunityToolkit.Aspire.Hosting.DbGate/DbGateContainerImageTags.cs
index e8d1e960..ab338331 100644
--- a/src/CommunityToolkit.Aspire.Hosting.DbGate/DbGateContainerImageTags.cs
+++ b/src/CommunityToolkit.Aspire.Hosting.DbGate/DbGateContainerImageTags.cs
@@ -1,11 +1,11 @@
internal static class DbGateContainerImageTags
{
- /// docker.io
+ /// docker.io
public const string Registry = "docker.io";
- /// dbgate/dbgate
+ /// dbgate/dbgate
public const string Image = "dbgate/dbgate";
- /// 6.1.4
+ /// 6.1.4
public const string Tag = "6.1.4";
}
diff --git a/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/CommunityToolkit.Aspire.Hosting.MySql.Extensions.csproj b/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/CommunityToolkit.Aspire.Hosting.MySql.Extensions.csproj
index 0fcf0071..8eebc9bc 100644
--- a/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/CommunityToolkit.Aspire.Hosting.MySql.Extensions.csproj
+++ b/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/CommunityToolkit.Aspire.Hosting.MySql.Extensions.csproj
@@ -12,6 +12,7 @@
+
diff --git a/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/MySqlBuilderExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/MySqlBuilderExtensions.cs
index e8d3e7c9..f8c052cd 100644
--- a/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/MySqlBuilderExtensions.cs
+++ b/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/MySqlBuilderExtensions.cs
@@ -50,6 +50,91 @@ public static IResourceBuilder WithAdminer(this IResourceBu
return builder;
}
+ ///
+ /// Adds an administration and development platform for MySql to the application model using DbGate.
+ ///
+ ///
+ /// This version of the package defaults to the tag of the container image.
+ /// The MySql server resource builder.
+ /// Configuration callback for DbGate container resource.
+ /// The name of the container (Optional).
+ ///
+ /// Use in application host with a MySql resource
+ ///
+ /// var builder = DistributedApplication.CreateBuilder(args);
+ ///
+ /// var mysql = builder.AddMySql("mysql")
+ /// .WithDbGate();
+ /// var db = mysql.AddDatabase("db");
+ ///
+ /// var api = builder.AddProject<Projects.Api>("api")
+ /// .WithReference(db);
+ ///
+ /// builder.Build().Run();
+ ///
+ ///
+ ///
+ /// A reference to the .
+ public static IResourceBuilder WithDbGate(this IResourceBuilder builder, Action>? configureContainer = null, string? containerName = null)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ containerName ??= $"{builder.Resource.Name}-dbgate";
+
+ var dbGateBuilder = DbGateBuilderExtensions.AddDbGate(builder.ApplicationBuilder, containerName);
+
+ dbGateBuilder
+ .WithEnvironment(context => ConfigureDbGateContainer(context, builder.ApplicationBuilder));
+
+ configureContainer?.Invoke(dbGateBuilder);
+
+ return builder;
+ }
+
+ private static void ConfigureDbGateContainer(EnvironmentCallbackContext context, IDistributedApplicationBuilder applicationBuilder)
+ {
+ var mysqlInstances = applicationBuilder.Resources.OfType();
+
+ var counter = 1;
+
+ // Multiple WithDbGate calls will be ignored
+ if (context.EnvironmentVariables.ContainsKey($"LABEL_mysql{counter}"))
+ {
+ return;
+ }
+
+ foreach (var mySqlServerResource in mysqlInstances)
+ {
+ // DbGate assumes MySql is being accessed over a default Aspire container network and hardcodes the resource address
+ 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($"PORT_mysql{counter}", mySqlServerResource.PrimaryEndpoint.TargetPort!.ToString()!);
+ context.EnvironmentVariables.Add($"ENGINE_mysql{counter}", "mysql@dbgate-plugin-mysql");
+
+ counter++;
+ }
+
+ var instancesCount = mysqlInstances.Count();
+ if (instancesCount > 0)
+ {
+ var strBuilder = new StringBuilder();
+ strBuilder.AppendJoin(',', Enumerable.Range(1, instancesCount).Select(i => $"mysql{i}"));
+ var connections = strBuilder.ToString();
+
+ string CONNECTIONS = context.EnvironmentVariables.GetValueOrDefault("CONNECTIONS")?.ToString() ?? string.Empty;
+ if (string.IsNullOrEmpty(CONNECTIONS))
+ {
+ context.EnvironmentVariables["CONNECTIONS"] = connections;
+ }
+ else
+ {
+ context.EnvironmentVariables["CONNECTIONS"] += $",{connections}";
+ }
+ }
+ }
+
internal static void ConfigureAdminerContainer(EnvironmentCallbackContext context, IDistributedApplicationBuilder applicationBuilder)
{
var mysqlInstances = applicationBuilder.Resources.OfType();
diff --git a/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/README.md b/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/README.md
index af823e87..ca7386eb 100644
--- a/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/README.md
+++ b/src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/README.md
@@ -2,7 +2,7 @@
This integration contains extensions for the [MySql hosting package](https://nuget.org/packages/Aspire.Hosting.MySql) for .NET Aspire.
-The integration provides support for running [Adminer](https://github.com/vrana/adminer) to interact with the MySql database.
+The integration provides support for running [Adminer](https://github.com/vrana/adminer) and [DbGate](https://github.com/dbgate/dbgate) to interact with the MySql database.
## Getting Started
@@ -20,6 +20,7 @@ Then, in the _Program.cs_ file of `AppHost`, define an MySql resource, then call
```csharp
var mysql = builder.AddMySql("mysql")
+ .WithDbGate()
.WithAdminer();
```
diff --git a/tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/AddDbGateTests.cs b/tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/AddDbGateTests.cs
index 3059789f..ea6c7490 100644
--- a/tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/AddDbGateTests.cs
+++ b/tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/AddDbGateTests.cs
@@ -221,6 +221,16 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
var sqlserverResource2 = sqlserverResourceBuilder2.Resource;
+ var mysqlResourceBuilder1 = builder.AddMySql("mysql1")
+ .WithDbGate();
+
+ var mysqlResource1 = mysqlResourceBuilder1.Resource;
+
+ var mysqlResourceBuilder2 =builder.AddMySql("mysql2")
+ .WithDbGate();
+
+ var mysqlResource2 = mysqlResourceBuilder2.Resource;
+
using var app = builder.Build();
var appModel = app.Services.GetRequiredService();
@@ -268,7 +278,7 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
item =>
{
Assert.Equal("CONNECTIONS", item.Key);
- Assert.Equal("mongodb1,mongodb2,postgres1,postgres2,redis1,redis2,sqlserver1,sqlserver2", item.Value);
+ Assert.Equal("mongodb1,mongodb2,postgres1,postgres2,redis1,redis2,sqlserver1,sqlserver2,mysql1,mysql2", item.Value);
},
item =>
{
@@ -423,6 +433,66 @@ public async Task WithDbGateShouldAddAnnotationsForMultipleDatabaseTypes()
{
Assert.Equal("ENGINE_sqlserver2", item.Key);
Assert.Equal("mssql@dbgate-plugin-mssql", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("LABEL_mysql1", item.Key);
+ Assert.Equal(mysqlResource1.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("SERVER_mysql1", item.Key);
+ Assert.Equal(mysqlResource1.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("USER_mysql1", item.Key);
+ Assert.Equal("root", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PASSWORD_mysql1", item.Key);
+ Assert.Equal(mysqlResource1.PasswordParameter.Value, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PORT_mysql1", item.Key);
+ Assert.Equal(mysqlResource1.PrimaryEndpoint.TargetPort.ToString(), item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("ENGINE_mysql1", item.Key);
+ Assert.Equal("mysql@dbgate-plugin-mysql", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("LABEL_mysql2", item.Key);
+ Assert.Equal(mysqlResource2.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("SERVER_mysql2", item.Key);
+ Assert.Equal(mysqlResource2.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("USER_mysql2", item.Key);
+ Assert.Equal("root", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PASSWORD_mysql2", item.Key);
+ Assert.Equal(mysqlResource2.PasswordParameter.Value, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PORT_mysql2", item.Key);
+ Assert.Equal(mysqlResource2.PrimaryEndpoint.TargetPort.ToString(), item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("ENGINE_mysql2", item.Key);
+ Assert.Equal("mysql@dbgate-plugin-mysql", item.Value);
});
}
@@ -455,6 +525,12 @@ public void WithDbGateShouldShouldAddOneDbGateResourceForMultipleDatabaseTypes()
builder.AddSqlServer("sqlserver2")
.WithDbGate();
+ builder.AddMySql("mysql1")
+ .WithDbGate();
+
+ builder.AddMySql("mysql2")
+ .WithDbGate();
+
using var app = builder.Build();
var appModel = app.Services.GetRequiredService();
diff --git a/tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/ResourceCreationTests.cs b/tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/ResourceCreationTests.cs
index 997a6374..de7b08fd 100644
--- a/tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/ResourceCreationTests.cs
+++ b/tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/ResourceCreationTests.cs
@@ -159,4 +159,213 @@ public async Task WithAdminerAddsAnnotationsForMultipleMySqlResource()
Assert.Equal("ADMINER_SERVERS", item.Key);
Assert.Equal(envValue, item.Value);
}
+
+ [Fact]
+ public async Task WithDbGateAddsAnnotations()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+
+ var mysqlResourceBuilder = builder.AddMySql("mysql")
+ .WithDbGate();
+
+ var mysqlResource = mysqlResourceBuilder.Resource;
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var dbGateResource = appModel.Resources.OfType().SingleOrDefault();
+
+ Assert.NotNull(dbGateResource);
+
+ Assert.Equal("mysql-dbgate", dbGateResource.Name);
+
+ var envs = await dbGateResource.GetEnvironmentVariableValuesAsync();
+
+ Assert.NotEmpty(envs);
+ Assert.Collection(envs,
+ item =>
+ {
+ Assert.Equal("LABEL_mysql1", item.Key);
+ Assert.Equal(mysqlResource.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("SERVER_mysql1", item.Key);
+ Assert.Equal(mysqlResource.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("USER_mysql1", item.Key);
+ Assert.Equal("root", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PASSWORD_mysql1", item.Key);
+ Assert.Equal(mysqlResource.PasswordParameter.Value, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PORT_mysql1", item.Key);
+ Assert.Equal(mysqlResource.PrimaryEndpoint.TargetPort.ToString(), item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("ENGINE_mysql1", item.Key);
+ Assert.Equal("mysql@dbgate-plugin-mysql", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("CONNECTIONS", item.Key);
+ Assert.Equal("mysql1", item.Value);
+ });
+ }
+
+ [Fact]
+ public void MultipleWithDbGateCallsAddsOneDbGateResource()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+ builder.AddMySql("mysql1").WithDbGate();
+ builder.AddMySql("mysql2").WithDbGate();
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var dbGateResource = appModel.Resources.OfType().SingleOrDefault();
+ Assert.NotNull(dbGateResource);
+
+ Assert.Equal("mysql1-dbgate", dbGateResource.Name);
+ }
+
+ [Fact]
+ public void WithDbGateShouldChangeDbGateHostPort()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+ var mysqlResourceBuilder = builder.AddMySql("mysql")
+ .WithDbGate(c => c.WithHostPort(8068));
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var dbGateResource = appModel.Resources.OfType().SingleOrDefault();
+ Assert.NotNull(dbGateResource);
+
+ var primaryEndpoint = dbGateResource.Annotations.OfType().Single();
+ Assert.Equal(8068, primaryEndpoint.Port);
+ }
+
+ [Fact]
+ public void WithDbGateShouldChangeDbGateContainerImageTag()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+ var mysqlResourceBuilder = builder.AddMySql("mysql")
+ .WithDbGate(c => c.WithImageTag("manualTag"));
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var dbGateResource = appModel.Resources.OfType().SingleOrDefault();
+ Assert.NotNull(dbGateResource);
+
+ var containerImageAnnotation = dbGateResource.Annotations.OfType().Single();
+ Assert.Equal("manualTag", containerImageAnnotation.Tag);
+ }
+
+ [Fact]
+ public async Task WithDbGateAddsAnnotationsForMultipleMySqlResource()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+
+ var mysqlResourceBuilder1 = builder.AddMySql("mysql1")
+ .WithDbGate();
+
+ var mysqlResource1 = mysqlResourceBuilder1.Resource;
+
+ var mysqlResourceBuilder2 = builder.AddMySql("mysql2")
+ .WithDbGate();
+
+ var mysqlResource2 = mysqlResourceBuilder2.Resource;
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var dbGateResource = appModel.Resources.OfType().SingleOrDefault();
+
+ Assert.NotNull(dbGateResource);
+
+ Assert.Equal("mysql1-dbgate", dbGateResource.Name);
+
+ var envs = await dbGateResource.GetEnvironmentVariableValuesAsync();
+
+ Assert.NotEmpty(envs);
+ Assert.Collection(envs,
+ item =>
+ {
+ Assert.Equal("LABEL_mysql1", item.Key);
+ Assert.Equal(mysqlResource1.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("SERVER_mysql1", item.Key);
+ Assert.Equal(mysqlResource1.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("USER_mysql1", item.Key);
+ Assert.Equal("root", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PASSWORD_mysql1", item.Key);
+ Assert.Equal(mysqlResource1.PasswordParameter.Value, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PORT_mysql1", item.Key);
+ Assert.Equal(mysqlResource1.PrimaryEndpoint.TargetPort.ToString(), item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("ENGINE_mysql1", item.Key);
+ Assert.Equal("mysql@dbgate-plugin-mysql", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("LABEL_mysql2", item.Key);
+ Assert.Equal(mysqlResource2.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("SERVER_mysql2", item.Key);
+ Assert.Equal(mysqlResource2.Name, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("USER_mysql2", item.Key);
+ Assert.Equal("root", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PASSWORD_mysql2", item.Key);
+ Assert.Equal(mysqlResource2.PasswordParameter.Value, item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("PORT_mysql2", item.Key);
+ Assert.Equal(mysqlResource2.PrimaryEndpoint.TargetPort.ToString(), item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("ENGINE_mysql2", item.Key);
+ Assert.Equal("mysql@dbgate-plugin-mysql", item.Value);
+ },
+ item =>
+ {
+ Assert.Equal("CONNECTIONS", item.Key);
+ Assert.Equal("mysql1,mysql2", item.Value);
+ });
+ }
}