Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Password/user/port parameters api changed
  • Loading branch information
Harold-Morgan committed Jun 5, 2025
commit 7178b44cd867020312c9c4c54cc286e30ab66098
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,16 @@ public static class MinioBuilderExtensions
/// </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="minioConsolePort">The host port for MinioO Admin.</param>
/// <param name="minioPort">The host port for MiniO.</param>
/// <param name="rootUser">The root user for the MiniO server.</param>
/// <param name="rootPassword">The password for the MiniO root user.</param>
/// <param name="port">The host port for MiniO.</param>
/// <param name="rootUser">The parameter used to provide the root user name for the MiniO resource. If <see langword="null"/> a default value will be used.</param>
/// <param name="rootPassword">The parameter used to provide the administrator password for the MiniO resource. If <see langword="null"/> a random password will be generated.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{MinioContainerResource}"/>.</returns>
public static IResourceBuilder<MinioContainerResource> AddMinioContainer(
this IDistributedApplicationBuilder builder,
string name,
[ResourceName] string name,
IResourceBuilder<ParameterResource>? rootUser = null,
IResourceBuilder<ParameterResource>? rootPassword = null,
int minioPort = 9000,
int minioConsolePort = 9001)
int? port = null)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrEmpty(name);
Expand All @@ -39,18 +37,16 @@ public static IResourceBuilder<MinioContainerResource> AddMinioContainer(

var rootUserParameter = rootUser?.Resource ?? new ParameterResource("user", _ => MinioContainerResource.DefaultUserName);

var minioContainer = new MinioContainerResource(name, rootUserParameter, rootPasswordParameter);
var resource = new MinioContainerResource(name, rootUserParameter, rootPasswordParameter);

var builderWithResource = builder
.AddResource(minioContainer)
.AddResource(resource)
.WithImage(MinioContainerImageTags.Image, MinioContainerImageTags.Tag)
.WithImageRegistry(MinioContainerImageTags.Registry)
.WithHttpEndpoint(targetPort: 9000, port: minioPort, name: MinioContainerResource.PrimaryEndpointName)
.WithHttpEndpoint(targetPort: 9001, port: minioConsolePort, name: "console")
.WithEnvironment("MINIO_ADDRESS", $":{minioPort.ToString()}")
.WithEnvironment("MINIO_CONSOLE_ADDRESS", $":{minioConsolePort.ToString()}")
.WithEnvironment(RootUserEnvVarName, minioContainer.RootUser.Value)
.WithEnvironment(RootPasswordEnvVarName, minioContainer.RootPassword.Value)
.WithHttpEndpoint(targetPort: 9000, port: port, name: MinioContainerResource.PrimaryEndpointName)
.WithHttpEndpoint(targetPort: 9001, name: MinioContainerResource.ConsoleEndpointName)
.WithEnvironment(RootUserEnvVarName, resource.RootUser.Value)
.WithEnvironment(RootPasswordEnvVarName, resource.PasswordParameter.Value)
.WithArgs("server", "/data");

var endpoint = builderWithResource.Resource.GetEndpoint(MinioContainerResource.PrimaryEndpointName);
Expand All @@ -74,6 +70,52 @@ public static IResourceBuilder<MinioContainerResource> AddMinioContainer(
return builderWithResource;
}


/// <summary>
/// Configures the user name that the Minio resource uses.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="userName">The parameter used to provide the user name for the PostgreSQL resource.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<MinioContainerResource> WithUserName(this IResourceBuilder<MinioContainerResource> builder, IResourceBuilder<ParameterResource> userName)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(userName);

builder.Resource.RootUser = userName.Resource;
return builder;
}

/// <summary>
/// Configures the password that the MiniO resource is used.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="password">The parameter used to provide the password for the MiniO resource. If <see langword="null"/>, no password will be configured.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<MinioContainerResource> WithPassword(this IResourceBuilder<MinioContainerResource> builder, IResourceBuilder<ParameterResource> password)
{
ArgumentNullException.ThrowIfNull(builder);

builder.Resource.SetPassword(password.Resource);
return builder;
}

/// <summary>
/// Configures the host port that the PGAdmin resource is exposed on instead of using randomly assigned port.
/// </summary>
/// <param name="builder">The resource builder for PGAdmin.</param>
/// <param name="port">The port to bind on the host. If <see langword="null"/> is used, a random port will be assigned.</param>
/// <returns>The resource builder for PGAdmin.</returns>
public static IResourceBuilder<MinioContainerResource> WithHostPort(this IResourceBuilder<MinioContainerResource> builder, int? port)
{
ArgumentNullException.ThrowIfNull(builder);

return builder.WithEndpoint("http", endpoint =>
{
endpoint.Port = port;
});
}

/// <summary>
/// Adds a named volume for the data folder to a Minio container resource.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,33 @@
/// <summary>
/// A resource that represents a MiniO storage
/// </summary>
/// <param name="name">The name of the resource</param>
/// <param name="rootUser"> A parameter that contains the MiniO server admin user name, or null to</param>
/// <param name="rootPassword"> A parameter that contains the Minio server admin password</param>
public sealed class MinioContainerResource(
string name,
ParameterResource rootUser,
ParameterResource rootPassword) : ContainerResource(name),
IResourceWithConnectionString
public sealed class MinioContainerResource : ContainerResource, IResourceWithConnectionString
{
internal const string PrimaryEndpointName = "http";
internal const string ConsoleEndpointName = "console";
internal const string DefaultUserName = "minioadmin";

/// <summary>
/// Initializes a new instance of the <see cref="MinioContainerResource"/> class.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="user">A parameter that contains the Minio server root user name.</param>
/// <param name="password">A parameter that contains the Minio server root password.</param>
public MinioContainerResource(string name, ParameterResource user, ParameterResource password) : base(name)
{
RootUser = user;
PasswordParameter = password;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use a primary constructor for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it looks much cleaner now, thanks!


/// <summary>
/// The MiniO root user.
/// </summary>
public ParameterResource RootUser { get; set; } = rootUser;
public ParameterResource RootUser { get; set; }

/// <summary>
/// The MiniO root password.
/// </summary>
public ParameterResource RootPassword { get; } = rootPassword;
public ParameterResource PasswordParameter { get; set; }

private EndpointReference? _primaryEndpoint;

Expand All @@ -36,17 +42,40 @@ public sealed class MinioContainerResource(
/// Gets the connection string expression for the Minio
/// </summary>
public ReferenceExpression ConnectionStringExpression => GetConnectionString();


/// <summary>
/// Gets the connection string for the MiniO server.
/// </summary>
/// <param name="cancellationToken"> A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
/// <returns>A connection string for the PostgreSQL server in the form "Host=host;Port=port;Username=postgres;Password=password".</returns>
public ValueTask<string?> GetConnectionStringAsync(CancellationToken cancellationToken = default)
{
if (this.TryGetLastAnnotation<ConnectionStringRedirectAnnotation>(out var connectionStringAnnotation))
{
return connectionStringAnnotation.Resource.GetConnectionStringAsync(cancellationToken);
}

return ConnectionStringExpression.GetValueAsync(cancellationToken);
}

/// <summary>
/// Gets the connection string for the MiniO server.
/// </summary>
private ReferenceExpression GetConnectionString()
{
var builder = new ReferenceExpressionBuilder();

builder.Append(
$"Endpoint=http://{PrimaryEndpoint.Property(EndpointProperty.Host)}:{PrimaryEndpoint.Property(EndpointProperty.Port)}");

builder.Append($";AccessKey={RootUser.Value}");
builder.Append($";SecretKey={RootPassword.Value}");
builder.Append($";SecretKey={PasswordParameter.Value}");

return builder.Build();
}

internal void SetPassword(ParameterResource password)
{
PasswordParameter = password;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public async Task StorageGetsCreatedAndUsable()
.AddMinioContainer("minio",
distributedApplicationBuilder.AddParameter("username", rootUser),
rootPasswordParameter,
minioPort: port);
port: port);

await using var app = await distributedApplicationBuilder.BuildAsync();

Expand Down Expand Up @@ -77,7 +77,7 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
var minio = builder1.AddMinioContainer("minio",
builder1.AddParameter("username", rootUser),
rootPasswordParameter,
minioPort: port);
port: port);

if (useVolume)
{
Expand Down Expand Up @@ -134,7 +134,7 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
var minio2 = builder2.AddMinioContainer("minio",
builder2.AddParameter("username", rootUser),
rootPasswordParameter2,
minioPort: port);
port: port);

if (useVolume)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,44 @@ public void WithDataBindMountShouldThrowWhenSourceIsNull()
var exception = Assert.Throws<ArgumentNullException>(action);
Assert.Equal(nameof(source), exception.ParamName);
}

[Fact]
public void VerifyMinioContainerResourceWithHostPort()
{
var builder = DistributedApplication.CreateBuilder();
builder.AddMinioContainer("minio")
.WithHostPort(1000);

var resource = Assert.Single(builder.Resources.OfType<MinioContainerResource>());
var endpoint = Assert.Single(resource.Annotations.OfType<EndpointAnnotation>(), x => x.Name == "http");
Assert.Equal(1000, endpoint.Port);
}

[Fact]
public async Task VerifyMinioContainerResourceWithPassword()
{
var builder = DistributedApplication.CreateBuilder();
var password = "p@ssw0rd1";
var pass = builder.AddParameter("pass", password);
var minio = builder.AddMinioContainer("minio")
.WithPassword(pass)
.WithEndpoint("http", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 2000));

var connectionString = await minio.Resource.GetConnectionStringAsync();
Assert.Equal("Endpoint=http://localhost:2000;AccessKey=minioadmin;SecretKey=p@ssw0rd1", connectionString);
}

[Fact]
public async Task VerifyMinioContainerResourceWithUserName()
{
var builder = DistributedApplication.CreateBuilder();
var user = "user1";
var pass = builder.AddParameter("user", user);
var postgres = builder.AddMinioContainer("minio")
.WithUserName(pass)
.WithEndpoint("http", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 2000));

var connectionString = await postgres.Resource.GetConnectionStringAsync();
Assert.Equal($"Endpoint=http://localhost:2000;AccessKey=user1;SecretKey={postgres.Resource.PasswordParameter.Value}", connectionString);
}
}