diff --git a/src/Aspire.Hosting/ApplicationModel/ContainerLifetimeAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/ContainerLifetimeAnnotation.cs
index 5cc5bfc000..f915295662 100644
--- a/src/Aspire.Hosting/ApplicationModel/ContainerLifetimeAnnotation.cs
+++ b/src/Aspire.Hosting/ApplicationModel/ContainerLifetimeAnnotation.cs
@@ -8,15 +8,29 @@ namespace Aspire.Hosting.ApplicationModel;
///
/// Lifetime modes for container resources
///
-public enum ContainerLifetimeType
+public enum ContainerLifetime
{
///
/// The default lifetime behavior should apply. This will create the resource when the AppHost starts and dispose of it when the AppHost shuts down.
///
Default,
///
- /// The resource is persistent and will not be disposed of when the AppHost shuts down.
+ /// Attempt to re-use a previously created resource (based on the container name) if one exists. Do not destroy the container on AppHost shutdown.
///
+ ///
+ /// In the event that a container with the given name does not exist, a new container will always be created based on the
+ /// current configuration.
+ /// When an existing container IS found, Aspire MAY re-use it based on the following criteria:
+ ///
+ /// - If the container WAS NOT originally created by Aspire, the existing container will be re-used.
+ /// - If the container WAS originally created by Aspire:
+ ///
+ /// - And the configuration DOES match the existing container, the existing container will be re-used.
+ /// - And the configuration DOES NOT match the existing container, the existing container will be stopped
+ /// and a new container created in order to apply the updated configuration.
+ ///
+ ///
+ ///
Persistent,
}
@@ -29,5 +43,5 @@ public sealed class ContainerLifetimeAnnotation : IResourceAnnotation
///
/// Gets or sets the lifetime type for the container resource.
///
- public required ContainerLifetimeType LifetimeType { get; set; }
+ public required ContainerLifetime Lifetime { get; set; }
}
\ No newline at end of file
diff --git a/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs b/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs
index 9801d6b1b5..893f65671c 100644
--- a/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs
+++ b/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs
@@ -235,18 +235,18 @@ public static int GetReplicaCount(this IResource resource)
}
///
- /// Gets the lifetime type of the container for the specified resoruce. Defaults to if
+ /// Gets the lifetime type of the container for the specified resoruce. Defaults to if
/// no is found.
///
/// The resource to the get the ContainerLifetimeType for.
- /// The from the for the resource (if the annotation exists). Defaults to if the annotation is not set.
- internal static ContainerLifetimeType GetContainerLifetimeType(this IResource resource)
+ /// The from the for the resource (if the annotation exists). Defaults to if the annotation is not set.
+ internal static ContainerLifetime GetContainerLifetimeType(this IResource resource)
{
if (resource.TryGetLastAnnotation(out var lifetimeAnnotation))
{
- return lifetimeAnnotation.LifetimeType;
+ return lifetimeAnnotation.Lifetime;
}
- return ContainerLifetimeType.Default;
+ return ContainerLifetime.Default;
}
}
diff --git a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs
index 5f6dc2412c..9e37acaeee 100644
--- a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs
+++ b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs
@@ -227,10 +227,10 @@ public static IResourceBuilder WithContainerRuntimeArgs(this IResourceBuil
///
/// The resource type.
/// Builder for the container resource.
- /// The lifetime behavior of the container resource (defaults behavior is )
+ /// The lifetime behavior of the container resource (defaults behavior is )
/// The .
///
- /// Marking a container resource to have a lifetime.
+ /// Marking a container resource to have a lifetime.
///
/// var builder = DistributedApplication.CreateBuilder(args);
/// builder.AddContainer("mycontainer", "myimage")
@@ -238,9 +238,9 @@ public static IResourceBuilder WithContainerRuntimeArgs(this IResourceBuil
///
///
[Experimental("ASPIRECONTAINERLIFETIME001")]
- public static IResourceBuilder WithContainerLifetime(this IResourceBuilder builder, ContainerLifetimeType lifetimeType) where T : ContainerResource
+ public static IResourceBuilder WithLifetime(this IResourceBuilder builder, ContainerLifetime lifetime) where T : ContainerResource
{
- return builder.WithAnnotation(new ContainerLifetimeAnnotation { LifetimeType = lifetimeType }, ResourceAnnotationMutationBehavior.Replace);
+ return builder.WithAnnotation(new ContainerLifetimeAnnotation { Lifetime = lifetime }, ResourceAnnotationMutationBehavior.Replace);
}
private static IResourceBuilder ThrowResourceIsNotContainer(IResourceBuilder builder) where T : ContainerResource
diff --git a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs
index 7b4a32c9ac..7103805aa4 100644
--- a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs
+++ b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs
@@ -1356,7 +1356,7 @@ private void PrepareContainers()
var nameSuffix = string.Empty;
- if (container.GetContainerLifetimeType() == ContainerLifetimeType.Default)
+ if (container.GetContainerLifetimeType() == ContainerLifetime.Default)
{
nameSuffix = GetRandomNameSuffix();
}
@@ -1366,7 +1366,7 @@ private void PrepareContainers()
ctr.Spec.ContainerName = containerObjectName; // Use the same name for container orchestrator (Docker, Podman) resource and DCP object name.
- if (container.GetContainerLifetimeType() == ContainerLifetimeType.Persistent)
+ if (container.GetContainerLifetimeType() == ContainerLifetime.Persistent)
{
ctr.Spec.Persistent = true;
}
diff --git a/src/Aspire.Hosting/Dcp/Model/Container.cs b/src/Aspire.Hosting/Dcp/Model/Container.cs
index 6e2c9a09d0..f54a08e93e 100644
--- a/src/Aspire.Hosting/Dcp/Model/Container.cs
+++ b/src/Aspire.Hosting/Dcp/Model/Container.cs
@@ -63,6 +63,13 @@ internal sealed class ContainerSpec
[JsonPropertyName("networks")]
public List? Networks { get; set; }
+
+ ///
+ /// Optional lifecycle key for the resource (used to identify changes to persistent resources requiring a restart).
+ /// If unset, DCP will calculate a default lifecycle key based on a hash of various resource spec properties.
+ ///
+ [JsonPropertyName("lifecycleKey")]
+ public string? LifecycleKey { get; set; }
}
internal sealed class BuildContext
@@ -305,6 +312,12 @@ internal sealed class ContainerStatus : V1Status
[JsonPropertyName("healthProbeResults")]
public List? HealthProbeResults { get; set;}
+ ///
+ /// The lifecycle key for the resource (used to identify changes to persistent resources requiring a restart).
+ ///
+ [JsonPropertyName("lifecycleKey")]
+ public string? LifecycleKey { get; set; }
+
// Note: the ContainerStatus has "Message" property that represents a human-readable information about Container state.
// It is provided by V1Status base class.
}
diff --git a/src/Aspire.Hosting/PublicAPI.Unshipped.txt b/src/Aspire.Hosting/PublicAPI.Unshipped.txt
index 17ce2b8d4a..f0927d2b6d 100644
--- a/src/Aspire.Hosting/PublicAPI.Unshipped.txt
+++ b/src/Aspire.Hosting/PublicAPI.Unshipped.txt
@@ -21,11 +21,11 @@ Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent.Resource.get -> A
Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent.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.Default = 0 -> Aspire.Hosting.ApplicationModel.ContainerLifetimeType
-Aspire.Hosting.ApplicationModel.ContainerLifetimeType.Persistent = 1 -> Aspire.Hosting.ApplicationModel.ContainerLifetimeType
+Aspire.Hosting.ApplicationModel.ContainerLifetimeAnnotation.Lifetime.get -> Aspire.Hosting.ApplicationModel.ContainerLifetime
+Aspire.Hosting.ApplicationModel.ContainerLifetimeAnnotation.Lifetime.set -> void
+Aspire.Hosting.ApplicationModel.ContainerLifetime
+Aspire.Hosting.ApplicationModel.ContainerLifetime.Default = 0 -> Aspire.Hosting.ApplicationModel.ContainerLifetime
+Aspire.Hosting.ApplicationModel.ContainerLifetime.Persistent = 1 -> Aspire.Hosting.ApplicationModel.ContainerLifetime
Aspire.Hosting.ApplicationModel.ConnectionStringReference.ConnectionName.get -> string?
Aspire.Hosting.ApplicationModel.ConnectionStringReference.ConnectionName.set -> void
Aspire.Hosting.ApplicationModel.CustomResourceSnapshot.HealthStatus.get -> Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?
@@ -74,7 +74,7 @@ Aspire.Hosting.IDistributedApplicationBuilder.Eventing.get -> Aspire.Hosting.Eve
static Aspire.Hosting.ApplicationModel.ResourceExtensions.GetEnvironmentVariableValuesAsync(this Aspire.Hosting.ApplicationModel.IResourceWithEnvironment! resource, Aspire.Hosting.DistributedApplicationOperation applicationOperation = Aspire.Hosting.DistributedApplicationOperation.Run) -> System.Threading.Tasks.ValueTask!>
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! targetStates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
-static Aspire.Hosting.ContainerResourceBuilderExtensions.WithContainerLifetime(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, Aspire.Hosting.ApplicationModel.ContainerLifetimeType lifetimeType) -> Aspire.Hosting.ApplicationModel.IResourceBuilder!
+static Aspire.Hosting.ContainerResourceBuilderExtensions.WithLifetime(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, Aspire.Hosting.ApplicationModel.ContainerLifetime lifetime) -> Aspire.Hosting.ApplicationModel.IResourceBuilder!
static Aspire.Hosting.ParameterResourceBuilderExtensions.AddParameter(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, Aspire.Hosting.ApplicationModel.ParameterDefault! value, bool secret = false, bool persist = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder!
static Aspire.Hosting.ParameterResourceBuilderExtensions.AddParameter(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! value, bool secret = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder!
static Aspire.Hosting.ProjectResourceBuilderExtensions.WithEndpointsInEnvironment(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, System.Func! filter) -> Aspire.Hosting.ApplicationModel.IResourceBuilder!