Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
33 changes: 33 additions & 0 deletions src/Aspire.Hosting/ApplicationModel/ContainerLifetimeAnnotation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// Lifetime modes for container resources
/// </summary>
public enum ContainerLifetimeType
{
/// <summary>
/// The resource is tied to the lifetime of the AppHost.
/// </summary>
AppHost,
/// <summary>
/// The resource is persistent and will not be disposed of when the AppHost shuts down.
/// </summary>
Persistent,
}

/// <summary>
/// Annotation that controls the lifetime of a container resource (AppHost based lifetime or persistent)
/// </summary>
[DebuggerDisplay("Type = {GetType().Name,nq}")]
public sealed class ContainerLifetimeAnnotation : IResourceAnnotation
{
/// <summary>
/// Gets or sets the lifetime type for the container resource.
/// </summary>
public required ContainerLifetimeType LifetimeType { get; set; }
}
22 changes: 19 additions & 3 deletions src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ public static bool TryGetEnvironmentVariables(this IResource resource, [NotNullW
/// var container = builder.AddContainer("elasticsearch", "library/elasticsearch", "8.14.0")
/// .WithEnvironment("discovery.type", "single-node")
/// .WithEnvironment("xpack.security.enabled", "true");
///
///
/// var env = await container.Resource.GetEnvironmentVariableValuesAsync();
///
///
/// Assert.Collection(env,
/// env =>
/// {
Expand Down Expand Up @@ -179,7 +179,7 @@ public static IEnumerable<EndpointReference> GetEndpoints(this IResourceWithEndp
/// </summary>
/// <param name="resource">The <see cref="IResourceWithEndpoints"/> which contains <see cref="EndpointAnnotation"/> annotations.</param>
/// <param name="endpointName">The name of the endpoint.</param>
/// <returns>An <see cref="EndpointReference"/> object representing the endpoint reference
/// <returns>An <see cref="EndpointReference"/> object representing the endpoint reference
/// for the specified endpoint.</returns>
public static EndpointReference GetEndpoint(this IResourceWithEndpoints resource, string endpointName)
{
Expand Down Expand Up @@ -233,4 +233,20 @@ public static int GetReplicaCount(this IResource resource)
return 1;
}
}

/// <summary>
/// Gets the lifetime type of the container for the specified resoruce. Defaults to <see cref="ContainerLifetimeType.AppHost"/> if
/// no <see cref="ContainerLifetimeAnnotation"/> is found.
/// </summary>
/// <param name="resource">The resource to the get the ContainerLifetimeType for.</param>
/// <returns></returns>
public static ContainerLifetimeType GetContainerLifetimeType(this IResource resource)
{
if (resource.TryGetLastAnnotation<ContainerLifetimeAnnotation>(out var lifetimeAnnotation))
{
return lifetimeAnnotation.LifetimeType;
}

return ContainerLifetimeType.AppHost;
}
}
12 changes: 12 additions & 0 deletions src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,18 @@ public static IResourceBuilder<T> WithContainerRuntimeArgs<T>(this IResourceBuil
return builder.WithAnnotation(annotation);
}

/// <summary>
/// Sets the lifetime behavior of the container resource.
/// </summary>
/// <typeparam name="T">The resource type.</typeparam>
/// <param name="builder">Builder for the container resource.</param>
/// <param name="lifetimeType">The lifetime behavior of the container resource (defaults behavior is <see cref="ContainerLifetimeType.AppHost"/>)</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<T> WithContainerLifetime<T>(this IResourceBuilder<T> builder, ContainerLifetimeType lifetimeType) where T : ContainerResource
{
return builder.WithAnnotation(new ContainerLifetimeAnnotation { LifetimeType = lifetimeType }, ResourceAnnotationMutationBehavior.Replace);
}

private static IResourceBuilder<T> ThrowResourceIsNotContainer<T>(IResourceBuilder<T> builder) where T : ContainerResource
{
throw new InvalidOperationException($"The resource '{builder.Resource.Name}' does not have a container image specified. Use WithImage to specify the container image and tag.");
Expand Down
14 changes: 13 additions & 1 deletion src/Aspire.Hosting/Dcp/ApplicationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1327,11 +1327,23 @@ private void PrepareContainers()
throw new InvalidOperationException();
}

var nameSuffix = GetRandomNameSuffix();
var nameSuffix = string.Empty;

if (container.GetContainerLifetimeType() == ContainerLifetimeType.AppHost)
{
nameSuffix = GetRandomNameSuffix();
}

var containerObjectName = GetObjectNameForResource(container, nameSuffix);
var ctr = Container.Create(containerObjectName, containerImageName);

ctr.Spec.ContainerName = containerObjectName; // Use the same name for container orchestrator (Docker, Podman) resource and DCP object name.

if (container.GetContainerLifetimeType() == ContainerLifetimeType.Persistent)
{
ctr.Spec.Persistent = true;
}

ctr.Annotate(CustomResource.ResourceNameAnnotation, container.Name);
ctr.Annotate(CustomResource.OtelServiceNameAnnotation, container.Name);
ctr.Annotate(CustomResource.OtelServiceInstanceIdAnnotation, nameSuffix);
Expand Down
9 changes: 9 additions & 0 deletions src/Aspire.Hosting/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ Aspire.Hosting.ApplicationModel.BeforeStartEvent
Aspire.Hosting.ApplicationModel.BeforeStartEvent.BeforeStartEvent(System.IServiceProvider! services, Aspire.Hosting.ApplicationModel.DistributedApplicationModel! model) -> void
Aspire.Hosting.ApplicationModel.BeforeStartEvent.Model.get -> Aspire.Hosting.ApplicationModel.DistributedApplicationModel!
Aspire.Hosting.ApplicationModel.BeforeStartEvent.Services.get -> System.IServiceProvider!
Aspire.Hosting.ApplicationModel.ContainerLifetimeAnnotation
Aspire.Hosting.ApplicationModel.ContainerLifetimeAnnotation.ContainerLifetimeAnnotation() -> void
Aspire.Hosting.ApplicationModel.ContainerLifetimeAnnotation.LifetimeType.get -> Aspire.Hosting.ApplicationModel.ContainerLifetimeType
Aspire.Hosting.ApplicationModel.ContainerLifetimeAnnotation.LifetimeType.set -> void
Aspire.Hosting.ApplicationModel.ContainerLifetimeType
Aspire.Hosting.ApplicationModel.ContainerLifetimeType.AppHost = 0 -> Aspire.Hosting.ApplicationModel.ContainerLifetimeType
Aspire.Hosting.ApplicationModel.ContainerLifetimeType.Persistent = 1 -> Aspire.Hosting.ApplicationModel.ContainerLifetimeType
Aspire.Hosting.ApplicationModel.ResourceNotificationService.ResourceNotificationService(Microsoft.Extensions.Logging.ILogger<Aspire.Hosting.ApplicationModel.ResourceNotificationService!>! logger, Microsoft.Extensions.Hosting.IHostApplicationLifetime! hostApplicationLifetime) -> void
Aspire.Hosting.DistributedApplicationBuilder.Eventing.get -> Aspire.Hosting.Eventing.IDistributedApplicationEventing!
Aspire.Hosting.Eventing.DistributedApplicationEventing
Expand All @@ -34,9 +41,11 @@ Aspire.Hosting.Eventing.IDistributedApplicationEventing.Unsubscribe(Aspire.Hosti
Aspire.Hosting.Eventing.IDistributedApplicationResourceEvent
Aspire.Hosting.Eventing.IDistributedApplicationResourceEvent.Resource.get -> Aspire.Hosting.ApplicationModel.IResource!
Aspire.Hosting.IDistributedApplicationBuilder.Eventing.get -> Aspire.Hosting.Eventing.IDistributedApplicationEventing!
static Aspire.Hosting.ApplicationModel.ResourceExtensions.GetContainerLifetimeType(this Aspire.Hosting.ApplicationModel.IResource! resource) -> Aspire.Hosting.ApplicationModel.ContainerLifetimeType
static Aspire.Hosting.ApplicationModel.ResourceExtensions.GetEnvironmentVariableValuesAsync(this Aspire.Hosting.ApplicationModel.IResourceWithEnvironment! resource, Aspire.Hosting.DistributedApplicationOperation applicationOperation = Aspire.Hosting.DistributedApplicationOperation.Run) -> System.Threading.Tasks.ValueTask<System.Collections.Generic.Dictionary<string!, string!>!>
Aspire.Hosting.ApplicationModel.ResourceNotificationService.WaitForResourceAsync(string! resourceName, string? targetState = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Aspire.Hosting.ApplicationModel.ResourceNotificationService.WaitForResourceAsync(string! resourceName, System.Collections.Generic.IEnumerable<string!>! targetStates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string!>!
static Aspire.Hosting.ContainerResourceBuilderExtensions.WithContainerLifetime<T>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>! builder, Aspire.Hosting.ApplicationModel.ContainerLifetimeType lifetimeType) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>!
static Aspire.Hosting.ProjectResourceBuilderExtensions.WithEndpointsInEnvironment(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.ProjectResource!>! builder, System.Func<Aspire.Hosting.ApplicationModel.EndpointAnnotation!, bool>! filter) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.ProjectResource!>!
Aspire.Hosting.DistributedApplicationExecutionContext.DistributedApplicationExecutionContext(Aspire.Hosting.DistributedApplicationExecutionContextOptions! options) -> void
Aspire.Hosting.DistributedApplicationExecutionContext.ServiceProvider.get -> System.IServiceProvider!
Expand Down