Skip to content
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

Consolidate MySql resources. #2127

Merged
merged 2 commits into from
Feb 8, 2024
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
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