Skip to content

Commit

Permalink
Consolidate MySql resources. (dotnet#2127)
Browse files Browse the repository at this point in the history
* Consolidate MySql resources.

* Update src/Aspire.Hosting/MySql/MySqlBuilderExtensions.cs

Co-authored-by: James Newton-King <[email protected]>

---------

Co-authored-by: James Newton-King <[email protected]>
  • Loading branch information
mitchdenny and JamesNK authored Feb 8, 2024
1 parent 90caa5a commit 07e3868
Show file tree
Hide file tree
Showing 8 changed files with 36 additions and 100 deletions.
11 changes: 0 additions & 11 deletions src/Aspire.Hosting/MySql/IMySqlParentResource.cs

This file was deleted.

49 changes: 21 additions & 28 deletions src/Aspire.Hosting/MySql/MySqlBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,41 @@ public static class MySqlBuilderExtensions
private const string PasswordEnvVarName = "MYSQL_ROOT_PASSWORD";

/// <summary>
/// Adds a MySQL container to the application model. The default image is "mysql" and the tag is "latest".
/// Adds a MySQL server resource to the application model. For local development a container is used.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
/// <param name="port">The host port for MySQL.</param>
/// <param name="password">The password for the MySQL root user. Defaults to a random password.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<MySqlContainerResource> AddMySqlContainer(this IDistributedApplicationBuilder builder, string name, int? port = null, string? password = null)
public static IResourceBuilder<MySqlServerResource> AddMySql(this IDistributedApplicationBuilder builder, string name, int? port = null, string? password = null)
{
password ??= Guid.NewGuid().ToString("N");
var mySqlContainer = new MySqlContainerResource(name, password);
return builder.AddResource(mySqlContainer)
.WithManifestPublishingCallback(context => WriteMySqlContainerResourceToManifest(context, mySqlContainer))
password = password ?? Guid.NewGuid().ToString("N");
var resource = new MySqlServerResource(name, password);
return builder.AddResource(resource)
.WithManifestPublishingCallback(WriteMySqlContainerToManifest)
.WithAnnotation(new EndpointAnnotation(ProtocolType.Tcp, port: port, containerPort: 3306)) // Internal port is always 3306.
.WithAnnotation(new ContainerImageAnnotation { Image = "mysql", Tag = "latest" })
.WithEnvironment(context =>
{
if (context.PublisherName == "manifest")
{
context.EnvironmentVariables.Add(PasswordEnvVarName, $"{{{mySqlContainer.Name}.inputs.password}}");
context.EnvironmentVariables.Add(PasswordEnvVarName, $"{{{resource.Name}.inputs.password}}");
}
else
{
context.EnvironmentVariables.Add(PasswordEnvVarName, mySqlContainer.Password);
context.EnvironmentVariables.Add(PasswordEnvVarName, resource.Password);
}
});
}

/// <summary>
/// Adds a MySQL server resource to the application model. For local development a container is used.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<MySqlServerResource> AddMySql(this IDistributedApplicationBuilder builder, string name)
{
var password = Guid.NewGuid().ToString("N");
var mySqlContainer = new MySqlServerResource(name, password);
return builder.AddResource(mySqlContainer)
.WithManifestPublishingCallback(WriteMySqlContainerToManifest)
.WithAnnotation(new EndpointAnnotation(ProtocolType.Tcp, containerPort: 3306)) // Internal port is always 3306.
.WithAnnotation(new ContainerImageAnnotation { Image = "mysql", Tag = "latest" })
.WithEnvironment(PasswordEnvVarName, () => mySqlContainer.Password);
}

/// <summary>
/// Adds a MySQL database to the application model.
/// </summary>
/// <param name="builder">The MySQL server resource builder.</param>
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<MySqlDatabaseResource> AddDatabase(this IResourceBuilder<IMySqlParentResource> builder, string name)
public static IResourceBuilder<MySqlDatabaseResource> AddDatabase(this IResourceBuilder<MySqlServerResource> builder, string name)
{
var mySqlDatabase = new MySqlDatabaseResource(name, builder.Resource);
return builder.ApplicationBuilder.AddResource(mySqlDatabase)
Expand All @@ -82,7 +65,7 @@ public static IResourceBuilder<MySqlDatabaseResource> AddDatabase(this IResource
/// <param name="hostPort">The host port for the application ui.</param>
/// <param name="containerName">The name of the container (Optional).</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<T> WithPhpMyAdmin<T>(this IResourceBuilder<T> builder, int? hostPort = null, string? containerName = null) where T : IMySqlParentResource
public static IResourceBuilder<T> WithPhpMyAdmin<T>(this IResourceBuilder<T> builder, int? hostPort = null, string? containerName = null) where T : MySqlServerResource
{
if (builder.ApplicationBuilder.Resources.OfType<PhpMyAdminContainerResource>().Any())
{
Expand Down Expand Up @@ -114,7 +97,17 @@ private static void WriteMySqlDatabaseToManifest(ManifestPublishingContext conte
context.Writer.WriteString("parent", mySqlDatabase.Parent.Name);
}

private static void WriteMySqlContainerResourceToManifest(ManifestPublishingContext context, MySqlContainerResource resource)
/// <summary>
/// Changes resource to be published as a container.
/// </summary>
/// <param name="builder">The <see cref="MySqlServerResource"/> builder.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<MySqlServerResource> PublishAsContainer(this IResourceBuilder<MySqlServerResource> builder)
{
return builder.WithManifestPublishingCallback(context => WriteMySqlContainerResourceToManifest(context, builder.Resource));
}

private static void WriteMySqlContainerResourceToManifest(ManifestPublishingContext context, MySqlServerResource resource)
{
context.WriteContainer(resource);
context.Writer.WriteString( // "connectionString": "...",
Expand Down
33 changes: 0 additions & 33 deletions src/Aspire.Hosting/MySql/MySqlContainerResource.cs

This file was deleted.

8 changes: 4 additions & 4 deletions src/Aspire.Hosting/MySql/MySqlDatabaseResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// A resource that represents a MySQL database. This is a child resource of a <see cref="MySqlContainerResource"/>.
/// A resource that represents a MySQL database. This is a child resource of a <see cref="MySqlServerResource"/>.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="mySqlParentResource">The MySQL parent resource associated with this database.</param>
public class MySqlDatabaseResource(string name, IMySqlParentResource mySqlParentResource) : Resource(name), IResourceWithParent<IMySqlParentResource>, IResourceWithConnectionString
/// <param name="parent">The MySQL parent resource associated with this database.</param>
public class MySqlDatabaseResource(string name, MySqlServerResource parent) : Resource(name), IResourceWithParent<MySqlServerResource>, IResourceWithConnectionString
{
public IMySqlParentResource Parent { get; } = mySqlParentResource;
public MySqlServerResource Parent { get; } = parent;

/// <summary>
/// Gets the connection string for the MySQL database.
Expand Down
2 changes: 1 addition & 1 deletion src/Aspire.Hosting/MySql/MySqlServerResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Aspire.Hosting.ApplicationModel;
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="password">The MySQL server root password.</param>
public class MySqlServerResource(string name, string password) : Resource(name), IMySqlParentResource
public class MySqlServerResource(string name, string password) : ContainerResource(name), IResourceWithConnectionString
{
public string Password { get; } = password;

Expand Down
19 changes: 3 additions & 16 deletions src/Aspire.Hosting/MySql/PhpMyAdminConfigWriterHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
{
var adminResource = appModel.Resources.OfType<PhpMyAdminContainerResource>().Single();
var serverFileMount = adminResource.Annotations.OfType<VolumeMountAnnotation>().Single(v => v.Target == "/etc/phpmyadmin/config.user.inc.php");
var mySqlInstances = appModel.Resources.OfType<IMySqlParentResource>();
var mySqlInstances = appModel.Resources.OfType<MySqlServerResource>();

if (appModel.Resources.OfType<PhpMyAdminContainerResource>().SingleOrDefault() is not { } myAdminResource)
{
Expand All @@ -35,13 +35,7 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
{
context.EnvironmentVariables.Add("PMA_HOST", $"host.docker.internal:{endpoint.Port}");
context.EnvironmentVariables.Add("PMA_USER", "root");
var password = singleInstance switch
{
MySqlServerResource psr => psr.Password,
MySqlContainerResource pcr => pcr.Password,
_ => throw new InvalidOperationException("MySql resource is neither MySqlServerResource or MySqlContainerResource.")
};
context.EnvironmentVariables.Add("PMA_PASSWORD", password);
context.EnvironmentVariables.Add("PMA_PASSWORD", singleInstance.Password);
}));
}
}
Expand All @@ -58,20 +52,13 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
{
if (mySqlInstance.TryGetAllocatedEndPoints(out var allocatedEndpoints))
{
var password = mySqlInstance switch
{
MySqlServerResource psr => psr.Password,
MySqlContainerResource pcr => pcr.Password,
_ => throw new InvalidOperationException("MySql resource is neither MySqlServerResource or MySqlContainerResource.")
};

var endpoint = allocatedEndpoints.Where(ae => ae.Name == "tcp").Single();
writer.WriteLine("$i++;");
writer.WriteLine($"$cfg['Servers'][$i]['host'] = 'host.docker.internal:{endpoint.Port}';");
writer.WriteLine($"$cfg['Servers'][$i]['verbose'] = '{mySqlInstance.Name}';");
writer.WriteLine($"$cfg['Servers'][$i]['auth_type'] = 'cookie';");
writer.WriteLine($"$cfg['Servers'][$i]['user'] = 'root';");
writer.WriteLine($"$cfg['Servers'][$i]['password'] = '{password}';");
writer.WriteLine($"$cfg['Servers'][$i]['password'] = '{mySqlInstance.Password}';");
writer.WriteLine($"$cfg['Servers'][$i]['AllowNoPassword'] = true;");
writer.WriteLine();
}
Expand Down
14 changes: 7 additions & 7 deletions tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ public class AddMySqlTests
public void AddMySqlContainerWithDefaultsAddsAnnotationMetadata()
{
var appBuilder = DistributedApplication.CreateBuilder();
appBuilder.AddMySqlContainer("mysql");
appBuilder.AddMySql("mysql");

var app = appBuilder.Build();

var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();

var containerResource = Assert.Single(appModel.Resources.OfType<MySqlContainerResource>());
var containerResource = Assert.Single(appModel.Resources.OfType<MySqlServerResource>());
Assert.Equal("mysql", containerResource.Name);

var manifestAnnotation = Assert.Single(containerResource.Annotations.OfType<ManifestPublishingCallbackAnnotation>());
Expand Down Expand Up @@ -63,7 +63,7 @@ public void AddMySqlContainerWithDefaultsAddsAnnotationMetadata()
public void AddMySqlAddsAnnotationMetadata()
{
var appBuilder = DistributedApplication.CreateBuilder();
appBuilder.AddMySqlContainer("mysql", 1234, "pass");
appBuilder.AddMySql("mysql", 1234, "pass");

var app = appBuilder.Build();

Expand Down Expand Up @@ -111,7 +111,7 @@ public void AddMySqlAddsAnnotationMetadata()
public void MySqlCreatesConnectionString()
{
var appBuilder = DistributedApplication.CreateBuilder();
appBuilder.AddMySqlContainer("mysql")
appBuilder.AddMySql("mysql")
.WithAnnotation(
new AllocatedEndpointAnnotation("mybinding",
ProtocolType.Tcp,
Expand All @@ -134,7 +134,7 @@ public void MySqlCreatesConnectionString()
public void MySqlCreatesConnectionStringWithDatabase()
{
var appBuilder = DistributedApplication.CreateBuilder();
appBuilder.AddMySqlContainer("mysql")
appBuilder.AddMySql("mysql")
.WithAnnotation(
new AllocatedEndpointAnnotation("mybinding",
ProtocolType.Tcp,
Expand All @@ -148,7 +148,7 @@ public void MySqlCreatesConnectionStringWithDatabase()

var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();

var mySqlResource = Assert.Single(appModel.Resources.OfType<MySqlContainerResource>());
var mySqlResource = Assert.Single(appModel.Resources.OfType<MySqlServerResource>());
var mySqlConnectionString = mySqlResource.GetConnectionString();
var mySqlDatabaseResource = Assert.Single(appModel.Resources.OfType<MySqlDatabaseResource>());
var dbConnectionString = mySqlDatabaseResource.GetConnectionString();
Expand All @@ -162,7 +162,7 @@ public void WithMySqlTwiceEndsUpWithOneAdminContainer()
{
var builder = DistributedApplication.CreateBuilder();
builder.AddMySql("mySql").WithPhpMyAdmin();
builder.AddMySqlContainer("mySql2").WithPhpMyAdmin();
builder.AddMySql("mySql2").WithPhpMyAdmin();

Assert.Single(builder.Resources.OfType<PhpMyAdminContainerResource>());
}
Expand Down

0 comments on commit 07e3868

Please sign in to comment.