Skip to content

Commit

Permalink
feat: add mongodb container and connection support
Browse files Browse the repository at this point in the history
  • Loading branch information
ailtonguitar committed Dec 1, 2023
1 parent 42979ba commit fc7fc08
Show file tree
Hide file tree
Showing 24 changed files with 1,193 additions and 3 deletions.
18 changes: 18 additions & 0 deletions Aspire.sln
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.MySqlConnector.Tests
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProject.IntegrationServiceA", "tests\testproject\TestProject.IntegrationServiceA\TestProject.IntegrationServiceA.csproj", "{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.MongoDB.Driver", "src\Components\Aspire.MongoDB.Driver\Aspire.MongoDB.Driver.csproj", "{20A5A907-A135-4735-B4BF-E13514F360E3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.MongoDB.Driver.Tests", "tests\Aspire.MongoDB.Driver.Tests\Aspire.MongoDB.Driver.Tests.csproj", "{E592E447-BA3C-44FA-86C1-EBEDC864A644}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -430,6 +434,18 @@ Global
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8}.Release|Any CPU.Build.0 = Release|Any CPU
{20A5A907-A135-4735-B4BF-E13514F360E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20A5A907-A135-4735-B4BF-E13514F360E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20A5A907-A135-4735-B4BF-E13514F360E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20A5A907-A135-4735-B4BF-E13514F360E3}.Release|Any CPU.Build.0 = Release|Any CPU
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E592E447-BA3C-44FA-86C1-EBEDC864A644}.Release|Any CPU.Build.0 = Release|Any CPU
{6472D59F-7C04-43DE-AD33-9F20BE3804BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6472D59F-7C04-43DE-AD33-9F20BE3804BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6472D59F-7C04-43DE-AD33-9F20BE3804BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6472D59F-7C04-43DE-AD33-9F20BE3804BF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -504,6 +520,8 @@ Global
{6472D59F-7C04-43DE-AD33-9F20BE3804BF} = {975F6F41-B455-451D-A312-098DE4A167B6}
{CA283D7F-EB95-4353-B196-C409965D2B42} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{C8079F06-304F-49B1-A0C1-45AA3782A923} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{20A5A907-A135-4735-B4BF-E13514F360E3} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{E592E447-BA3C-44FA-86C1-EBEDC864A644} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{DCF2D47A-921A-4900-B5B2-CF97B3531CE8} = {975F6F41-B455-451D-A312-098DE4A167B6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
5 changes: 4 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
</PropertyGroup>
<ItemGroup>
<!-- Azure SDK for .NET dependencies -->
<PackageVersion Include="AspNetCore.HealthChecks.MongoDb" Version="7.0.0" />
<PackageVersion Include="Azure.Data.Tables" Version="12.8.2" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
<PackageVersion Include="Azure.Identity" Version="1.10.4" />
Expand Down Expand Up @@ -71,6 +72,8 @@
<PackageVersion Include="JsonSchema.Net" Version="5.4.0" />
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.1.1" />
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.1.0" />
<PackageVersion Include="MongoDB.Driver" Version="2.22.0" />
<PackageVersion Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="1.3.0" />
<PackageVersion Include="MySqlConnector.DependencyInjection" Version="2.3.1" />
<PackageVersion Include="Npgsql.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
Expand Down Expand Up @@ -99,4 +102,4 @@
<PackageVersion Include="Microsoft.Signed.Wix" Version="1.0.0-v3.14.0.5722" />
<PackageVersion Include="Microsoft.DotNet.Build.Tasks.Installers" Version="8.0.0-beta.23564.4" />
</ItemGroup>
</Project>
</Project>
11 changes: 11 additions & 0 deletions src/Aspire.Hosting/MongoDB/IMongoDBResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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;

/// <summary>
/// Represents a MongoDB resource that requires a connection string.
/// </summary>
public interface IMongoDBResource : IResourceWithConnectionString
{
}
94 changes: 94 additions & 0 deletions src/Aspire.Hosting/MongoDB/MongoDBBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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.Publishing;

namespace Aspire.Hosting;

/// <summary>
/// Provides extension methods for adding MongoDB resources to an <see cref="IDistributedApplicationBuilder"/>.
/// </summary>
public static class MongoDBBuilderExtensions
{
private const int DefaultContainerPort = 27017;
private const string PasswordEnvVarName = "MONGO_INITDB_ROOT_PASSWORD";
private const string UserNameEnvVarName = "MONGO_INITDB_ROOT_USERNAME";

/// <summary>
/// Adds a MongoDB container to the application model. The default image is "mongo" and the tag is "latest".
/// </summary>
/// <returns></returns>
/// <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 MongoDB.</param>
/// <param name="password">The password for the MongoDB root user. Defaults to a random password.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{MongoDBContainerResource}"/>.</returns>
public static IResourceBuilder<MongoDBContainerResource> AddMongoDBContainer(
this IDistributedApplicationBuilder builder,
string name,
int? port = null,
string? password = null)
{
password ??= Guid.NewGuid().ToString("N");

var mongoDBContainer = new MongoDBContainerResource(name, password);

return builder
.AddResource(mongoDBContainer)
.WithManifestPublishingCallback(WriteMongoDBContainerToManifest)
.WithAnnotation(new ServiceBindingAnnotation(ProtocolType.Tcp, port: port, containerPort: DefaultContainerPort)) // Internal port is always 27017.
.WithAnnotation(new ContainerImageAnnotation { Image = "mongo", Tag = "latest" })
.WithEnvironment(PasswordEnvVarName, () => mongoDBContainer.Password)
.WithEnvironment(UserNameEnvVarName, () => mongoDBContainer.UserName);
}

/// <summary>
/// Adds a MongoDB connection to the application model. Connection strings can also be read from the connection string section of the configuration using the name of the resource.
/// </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="connectionString">The MongoDB connection string (optional).</param>
/// <returns>A reference to the <see cref="IResourceBuilder{MongoDBConnectionResource}"/>.</returns>
public static IResourceBuilder<MongoDBConnectionResource> AddMongoDBConnection(this IDistributedApplicationBuilder builder, string name, string? connectionString = null)
{
var mongoDBConnection = new MongoDBConnectionResource(name, connectionString);

return builder
.AddResource(mongoDBConnection)
.WithManifestPublishingCallback(context => context.WriteMongoDBConnectionToManifest(mongoDBConnection));
}

/// <summary>
/// Adds a MongoDB database to the application model.
/// </summary>
/// <param name="builder">The MongoDB 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{MongoDBDatabaseResource}"/>.</returns>
public static IResourceBuilder<MongoDBDatabaseResource> AddDatabase(this IResourceBuilder<MongoDBContainerResource> builder, string name)
{
var mongoDBDatabase = new MongoDBDatabaseResource(name, builder.Resource);

return builder.ApplicationBuilder
.AddResource(mongoDBDatabase)
.WithManifestPublishingCallback(context => context.WriteMongoDBDatabaseToManifest(mongoDBDatabase));
}

private static void WriteMongoDBContainerToManifest(this ManifestPublishingContext context)
{
context.Writer.WriteString("type", "mongodb.server.v0");
}

private static void WriteMongoDBConnectionToManifest(this ManifestPublishingContext context, MongoDBConnectionResource mongoDbConnection)
{
context.Writer.WriteString("type", "mongodb.connection.v0");
context.Writer.WriteString("connectionString", mongoDbConnection.GetConnectionString());
}

private static void WriteMongoDBDatabaseToManifest(this ManifestPublishingContext context, MongoDBDatabaseResource mongoDbDatabase)
{
context.Writer.WriteString("type", "mongodb.database.v0");
context.Writer.WriteString("parent", mongoDbDatabase.Parent.Name);
}
}
20 changes: 20 additions & 0 deletions src/Aspire.Hosting/MongoDB/MongoDBConnectionResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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;

/// <summary>
/// A resource that represents a MongoDB connection.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="connectionString">The MongoDB connection string.</param>
public class MongoDBConnectionResource(string name, string? connectionString) : Resource(name), IMongoDBResource
{
private readonly string? _connectionString = connectionString;

/// <summary>
/// Gets the connection string for the MongoDB server.
/// </summary>
/// <returns>The specified connection string.</returns>
public string? GetConnectionString() => _connectionString;
}
62 changes: 62 additions & 0 deletions src/Aspire.Hosting/MongoDB/MongoDBConnectionStringBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.MongoDB;

internal class MongoDBConnectionStringBuilder
{
private const string Scheme = "mongodb";

private string? _server;
private int _port;
private string? _userName;
private string? _password;

public MongoDBConnectionStringBuilder WithServer(string server)
{
ArgumentNullException.ThrowIfNullOrWhiteSpace(server, nameof(server));

_server = server;

return this;
}

public MongoDBConnectionStringBuilder WithPort(int port)
{
_port = port;

return this;
}

public MongoDBConnectionStringBuilder WithUserName(string userName)
{
ArgumentNullException.ThrowIfNullOrWhiteSpace(userName, nameof(userName));

_userName = userName;

return this;
}

public MongoDBConnectionStringBuilder WithPassword(string password)
{
ArgumentNullException.ThrowIfNullOrWhiteSpace(password, nameof(password));

_password = password;

return this;
}

public string Build()
{
var builder = new UriBuilder
{
Scheme = Scheme,
Host = _server,
Port = _port,
UserName = _userName,
Password = _password
};

return builder.ToString();
}
}
41 changes: 41 additions & 0 deletions src/Aspire.Hosting/MongoDB/MongoDBContainerResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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.MongoDB;

namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// A resource that represents a MongoDB container.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="password">The MongoDB root password.</param>
public class MongoDBContainerResource(string name, string password) : ContainerResource(name), IMongoDBResource
{
private const string DefaultUserName = "root";

public string Password { get; } = password;

public string UserName { get; } = DefaultUserName;

/// <summary>
/// Gets the connection string for the MongoDB server.
/// </summary>
/// <returns>A connection string for the MongoDB server in the form "mongodb://host:port".</returns>
public string? GetConnectionString()
{
if (!this.TryGetAllocatedEndPoints(out var allocatedEndpoints))
{
throw new DistributedApplicationException("Expected allocated endpoints!");
}

var allocatedEndpoint = allocatedEndpoints.Single();

return new MongoDBConnectionStringBuilder()
.WithServer(allocatedEndpoint.Address)
.WithPort(allocatedEndpoint.Port)
.WithUserName(UserName)
.WithPassword(Password)
.Build();
}
}
40 changes: 40 additions & 0 deletions src/Aspire.Hosting/MongoDB/MongoDBDatabaseResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;

namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// A resource that represents a MongoDB database. This is a child resource of a <see cref="MongoDBContainerResource"/>.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="mongoDBContainer">The MongoDB server resource associated with this database.</param>
public class MongoDBDatabaseResource(string name, MongoDBContainerResource mongoDBContainer)
: Resource(name), IMongoDBResource, IResourceWithParent<MongoDBContainerResource>
{
public MongoDBContainerResource Parent => mongoDBContainer;

/// <summary>
/// Gets the connection string for the MongoDB database.
/// </summary>
/// <returns>A connection string for the MongoDB database.</returns>
public string? GetConnectionString()
{
if (Parent.GetConnectionString() is { } connectionString)
{
var builder = new StringBuilder(connectionString);

if (!connectionString.EndsWith('/'))
{
builder.Append('/');
}

builder.Append(Name);

return builder.ToString();
}

throw new DistributedApplicationException("Parent resource connection string was null.");
}
}
25 changes: 25 additions & 0 deletions src/Components/Aspire.MongoDB.Driver/Aspire.MongoDB.Driver.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(NetCurrent)</TargetFramework>
<IsPackable>true</IsPackable>
<PackageTags>$(ComponentDatabasePackageTags) MongoDB</PackageTags>
<Description>A generic MongoDB client that integrates with Aspire.</Description>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\Common\HealthChecksExtensions.cs" Link="HealthChecksExtensions.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" />
<PackageReference Include="MongoDB.Driver" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
</ItemGroup>


</Project>

0 comments on commit fc7fc08

Please sign in to comment.