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

Add WithVolumeMount Extension Method to Container Resource Class #1447

Closed
13 changes: 10 additions & 3 deletions samples/eShopLite/AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
var builder = DistributedApplication.CreateBuilder(args);

var catalogDb = builder.AddPostgres("postgres").AddDatabase("catalogdb");
var catalogDb = builder.AddPostgresContainer("postgres")
.WithNamedVolume("VolumeMount.example.data");

var basketCache = builder.AddRedis("basketcache");
// Add another Postgres container with the same named volume to see if named volumes name validation works correctly
builder.AddPostgresContainer("randomdb")
.WithNamedVolume("VolumeMount.example.data");

var basketCache = builder.AddRedisContainer("basketcache")
.WithNamedVolume("basketcachvolume");

var catalogService = builder.AddProject<Projects.CatalogService>("catalogservice")
.WithReference(catalogDb)
.WithReplicas(2);

var messaging = builder.AddRabbitMQ("messaging");
var messaging = builder.AddRabbitMQContainer("messaging")
.WithNamedVolume("messagingvolume");

var basketService = builder.AddProject("basketservice", @"..\BasketService\BasketService.csproj")
.WithReference(basketCache)
Expand Down
54 changes: 54 additions & 0 deletions src/Aspire.Hosting/Dcp/ApplicationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -509,12 +509,66 @@ private void PrepareContainers()
}
}

// Ensure no duplicate target volumes are mounted.
ValidateNamedVolumes(ctr);

var containerAppResource = new AppResource(container, ctr);
AddServicesProducedInfo(container, ctr, containerAppResource);
_appResources.Add(containerAppResource);
}
}

private static void ValidateNamedVolumes(Container? container)
{

if(container is null)
{
return;
}

var volumeList = container.Spec.VolumeMounts;

if(volumeList is null)
{
return;
}

if(volumeList.Count == 0)
{
return;
}

var volumeNameToMountCount = new Dictionary<string, int>();

var namedVolumes = volumeList.Where(v => v.Type == Model.VolumeMountType.Named).ToArray();

foreach(VolumeMount namedVolume in namedVolumes)
{
if(namedVolume.Source is null)
{
continue;
}

if(volumeNameToMountCount.TryGetValue(namedVolume.Source, out var value))
{
volumeNameToMountCount[namedVolume.Source] = ++value;
}
else
{
volumeNameToMountCount.Add(namedVolume.Source, 1);
}
}

foreach(var volumeName in volumeNameToMountCount.Keys)
{
if (volumeNameToMountCount[volumeName] > 1)
{
throw new InvalidOperationException($"Volume {volumeName} is mounted more than once in container {container.Metadata.Name}");
}
}

}

private async Task CreateContainersAsync(IEnumerable<AppResource> containerResources, CancellationToken cancellationToken)
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,31 @@ public static IResourceBuilder<T> WithVolumeMount<T>(this IResourceBuilder<T> bu
return builder.WithAnnotation(annotation);
}

public static IResourceBuilder<T> WithNamedVolume<T>(this IResourceBuilder<T> builder, string volumeName) where T : ContainerResource
{
ArgumentNullException.ThrowIfNullOrWhiteSpace(volumeName, nameof(volumeName));

// Mapping of parent resource types to their volume paths
var volumePaths = new Dictionary<Type, string>
{
{ typeof(SqlServerContainerResource), "/var/opt/mssql" },
{ typeof(MongoDBContainerResource), "/data/db" },
{ typeof(RedisContainerResource), "/data" },
{ typeof(PostgresContainerResource), "/var/lib/postgresql/data" },
{ typeof(RabbitMQContainerResource), "/var/lib/rabbitmq" },
{ typeof(MySqlContainerResource), "/var/lib/mysql" }
};

Type resourceType = typeof(T);

if (volumePaths.TryGetValue(resourceType, out var volumePath))
{
builder.WithVolumeMount(volumeName, volumePath, VolumeMountType.Named);
}

return builder;
}

/// <summary>
/// Adds the arguments to be passed to a container resource when the container is started.
/// </summary>
Expand Down