diff --git a/src/Aspire.Hosting/MySql/IMySqlParentResource.cs b/src/Aspire.Hosting/MySql/IMySqlParentResource.cs deleted file mode 100644 index eb72e185ec..0000000000 --- a/src/Aspire.Hosting/MySql/IMySqlParentResource.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Aspire.Hosting.ApplicationModel; - -/// -/// Represents a MySQL parent resource (container or server) that produces a connection string. -/// -public interface IMySqlParentResource : IResourceWithConnectionString, IResourceWithEnvironment -{ -} diff --git a/src/Aspire.Hosting/MySql/MySqlBuilderExtensions.cs b/src/Aspire.Hosting/MySql/MySqlBuilderExtensions.cs index f99fa21c80..ba05fdcb6a 100644 --- a/src/Aspire.Hosting/MySql/MySqlBuilderExtensions.cs +++ b/src/Aspire.Hosting/MySql/MySqlBuilderExtensions.cs @@ -17,58 +17,41 @@ public static class MySqlBuilderExtensions private const string PasswordEnvVarName = "MYSQL_ROOT_PASSWORD"; /// - /// 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. /// /// The . /// The name of the resource. This name will be used as the connection string name when referenced in a dependency. /// The host port for MySQL. /// The password for the MySQL root user. Defaults to a random password. /// A reference to the . - public static IResourceBuilder AddMySqlContainer(this IDistributedApplicationBuilder builder, string name, int? port = null, string? password = null) + public static IResourceBuilder 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); } }); } - /// - /// Adds a MySQL server resource to the application model. For local development a container is used. - /// - /// The . - /// The name of the resource. This name will be used as the connection string name when referenced in a dependency. - /// A reference to the . - public static IResourceBuilder 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); - } - /// /// Adds a MySQL database to the application model. /// /// The MySQL server resource builder. /// The name of the resource. This name will be used as the connection string name when referenced in a dependency. /// A reference to the . - public static IResourceBuilder AddDatabase(this IResourceBuilder builder, string name) + public static IResourceBuilder AddDatabase(this IResourceBuilder builder, string name) { var mySqlDatabase = new MySqlDatabaseResource(name, builder.Resource); return builder.ApplicationBuilder.AddResource(mySqlDatabase) @@ -82,7 +65,7 @@ public static IResourceBuilder AddDatabase(this IResource /// The host port for the application ui. /// The name of the container (Optional). /// A reference to the . - public static IResourceBuilder WithPhpMyAdmin(this IResourceBuilder builder, int? hostPort = null, string? containerName = null) where T : IMySqlParentResource + public static IResourceBuilder WithPhpMyAdmin(this IResourceBuilder builder, int? hostPort = null, string? containerName = null) where T : MySqlServerResource { if (builder.ApplicationBuilder.Resources.OfType().Any()) { @@ -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) + /// + /// Changes resource to be published as a container. + /// + /// The builder. + /// A reference to the . + public static IResourceBuilder PublishAsContainer(this IResourceBuilder builder) + { + return builder.WithManifestPublishingCallback(context => WriteMySqlContainerResourceToManifest(context, builder.Resource)); + } + + private static void WriteMySqlContainerResourceToManifest(ManifestPublishingContext context, MySqlServerResource resource) { context.WriteContainer(resource); context.Writer.WriteString( // "connectionString": "...", diff --git a/src/Aspire.Hosting/MySql/MySqlContainerResource.cs b/src/Aspire.Hosting/MySql/MySqlContainerResource.cs deleted file mode 100644 index ea0e2daf87..0000000000 --- a/src/Aspire.Hosting/MySql/MySqlContainerResource.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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.Utils; - -namespace Aspire.Hosting.ApplicationModel; - -/// -/// A resource that represents a MySQL container. -/// -/// The name of the resource. -/// The MySQL server root password. -public class MySqlContainerResource(string name, string password) : ContainerResource(name), IMySqlParentResource -{ - public string Password { get; } = password; - - /// - /// Gets the connection string for the MySQL server. - /// - /// A connection string for the MySQL server in the form "Server=host;Port=port;User ID=root;Password=password". - public string? GetConnectionString() - { - if (!this.TryGetAllocatedEndPoints(out var allocatedEndpoints)) - { - throw new DistributedApplicationException("Expected allocated endpoints!"); - } - - var allocatedEndpoint = allocatedEndpoints.Single(); // We should only have one endpoint for MySQL. - - var connectionString = $"Server={allocatedEndpoint.Address};Port={allocatedEndpoint.Port};User ID=root;Password=\"{PasswordUtil.EscapePassword(Password)}\";"; - return connectionString; - } -} diff --git a/src/Aspire.Hosting/MySql/MySqlDatabaseResource.cs b/src/Aspire.Hosting/MySql/MySqlDatabaseResource.cs index ed6100dee4..3e594841f0 100644 --- a/src/Aspire.Hosting/MySql/MySqlDatabaseResource.cs +++ b/src/Aspire.Hosting/MySql/MySqlDatabaseResource.cs @@ -4,13 +4,13 @@ namespace Aspire.Hosting.ApplicationModel; /// -/// A resource that represents a MySQL database. This is a child resource of a . +/// A resource that represents a MySQL database. This is a child resource of a . /// /// The name of the resource. -/// The MySQL parent resource associated with this database. -public class MySqlDatabaseResource(string name, IMySqlParentResource mySqlParentResource) : Resource(name), IResourceWithParent, IResourceWithConnectionString +/// The MySQL parent resource associated with this database. +public class MySqlDatabaseResource(string name, MySqlServerResource parent) : Resource(name), IResourceWithParent, IResourceWithConnectionString { - public IMySqlParentResource Parent { get; } = mySqlParentResource; + public MySqlServerResource Parent { get; } = parent; /// /// Gets the connection string for the MySQL database. diff --git a/src/Aspire.Hosting/MySql/MySqlServerResource.cs b/src/Aspire.Hosting/MySql/MySqlServerResource.cs index dff9920f9f..6fca1053b6 100644 --- a/src/Aspire.Hosting/MySql/MySqlServerResource.cs +++ b/src/Aspire.Hosting/MySql/MySqlServerResource.cs @@ -10,7 +10,7 @@ namespace Aspire.Hosting.ApplicationModel; /// /// The name of the resource. /// The MySQL server root password. -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; diff --git a/src/Aspire.Hosting/MySql/PhpMyAdminConfigWriterHook.cs b/src/Aspire.Hosting/MySql/PhpMyAdminConfigWriterHook.cs index 57bf52b206..7e3f204863 100644 --- a/src/Aspire.Hosting/MySql/PhpMyAdminConfigWriterHook.cs +++ b/src/Aspire.Hosting/MySql/PhpMyAdminConfigWriterHook.cs @@ -11,7 +11,7 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C { var adminResource = appModel.Resources.OfType().Single(); var serverFileMount = adminResource.Annotations.OfType().Single(v => v.Target == "/etc/phpmyadmin/config.user.inc.php"); - var mySqlInstances = appModel.Resources.OfType(); + var mySqlInstances = appModel.Resources.OfType(); if (appModel.Resources.OfType().SingleOrDefault() is not { } myAdminResource) { @@ -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); })); } } @@ -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(); } diff --git a/tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs b/tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs index 5dfbfc2e0f..2ed4953698 100644 --- a/tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs +++ b/tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs @@ -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(); - var containerResource = Assert.Single(appModel.Resources.OfType()); + var containerResource = Assert.Single(appModel.Resources.OfType()); Assert.Equal("mysql", containerResource.Name); var manifestAnnotation = Assert.Single(containerResource.Annotations.OfType()); @@ -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(); @@ -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, @@ -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, @@ -148,7 +148,7 @@ public void MySqlCreatesConnectionStringWithDatabase() var appModel = app.Services.GetRequiredService(); - var mySqlResource = Assert.Single(appModel.Resources.OfType()); + var mySqlResource = Assert.Single(appModel.Resources.OfType()); var mySqlConnectionString = mySqlResource.GetConnectionString(); var mySqlDatabaseResource = Assert.Single(appModel.Resources.OfType()); var dbConnectionString = mySqlDatabaseResource.GetConnectionString(); @@ -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()); } diff --git a/tests/Aspire.Hosting.Tests/MySql/MySqlContainerResourceTests.cs b/tests/Aspire.Hosting.Tests/MySql/MySqlConnectionStringBuilderTests.cs similarity index 100% rename from tests/Aspire.Hosting.Tests/MySql/MySqlContainerResourceTests.cs rename to tests/Aspire.Hosting.Tests/MySql/MySqlConnectionStringBuilderTests.cs