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

Update container lifetime API and add schema for lifecycleKey property #5630

Merged
merged 2 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
20 changes: 17 additions & 3 deletions src/Aspire.Hosting/ApplicationModel/ContainerLifetimeAnnotation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,29 @@ namespace Aspire.Hosting.ApplicationModel;
/// <summary>
/// Lifetime modes for container resources
/// </summary>
public enum ContainerLifetimeType
public enum ContainerLifetime
{
/// <summary>
/// The default lifetime behavior should apply. This will create the resource when the AppHost starts and dispose of it when the AppHost shuts down.
/// </summary>
Default,
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// In the event that a container with the given name does not exist, a new container will always be created based on the
/// current <see cref="ContainerResource"/> configuration.
/// <para>When an existing container IS found, Aspire MAY re-use it based on the following criteria:</para>
/// <list type="bullet">
/// <item>If the container WAS NOT originally created by Aspire, the existing container will be re-used.</item>
/// <item>If the container WAS originally created by Aspire:
/// <list type="bullet">
/// <item>And the <see cref="ContainerResource"/> configuration DOES match the existing container, the existing container will be re-used.</item>
/// <item>And the <see cref="ContainerResource"/> 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.</item>
/// </list></item>
/// </list>
/// </remarks>
Persistent,
}

Expand All @@ -29,5 +43,5 @@ public sealed class ContainerLifetimeAnnotation : IResourceAnnotation
/// <summary>
/// Gets or sets the lifetime type for the container resource.
/// </summary>
public required ContainerLifetimeType LifetimeType { get; set; }
public required ContainerLifetime Lifetime { get; set; }
}
10 changes: 5 additions & 5 deletions src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,18 +235,18 @@ public static int GetReplicaCount(this IResource resource)
}

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

return ContainerLifetimeType.Default;
return ContainerLifetime.Default;
}
}
8 changes: 4 additions & 4 deletions src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,20 +227,20 @@ public static IResourceBuilder<T> WithContainerRuntimeArgs<T>(this IResourceBuil
/// </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.Default"/>)</param>
/// <param name="lifetime">The lifetime behavior of the container resource (defaults behavior is <see cref="ContainerLifetime.Default"/>)</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
/// <example>
/// Marking a container resource to have a <see cref="ContainerLifetimeType.Persistent"/> lifetime.
/// Marking a container resource to have a <see cref="ContainerLifetime.Persistent"/> lifetime.
/// <code language="csharp">
/// var builder = DistributedApplication.CreateBuilder(args);
/// builder.AddContainer("mycontainer", "myimage")
/// .WithContainerLifetime(ContainerLifetimeType.Persistent);
/// </code>
/// </example>
[Experimental("ASPIRECONTAINERLIFETIME001")]
public static IResourceBuilder<T> WithContainerLifetime<T>(this IResourceBuilder<T> builder, ContainerLifetimeType lifetimeType) where T : ContainerResource
public static IResourceBuilder<T> WithLifetime<T>(this IResourceBuilder<T> 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<T> ThrowResourceIsNotContainer<T>(IResourceBuilder<T> builder) where T : ContainerResource
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Hosting/Dcp/ApplicationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1356,7 +1356,7 @@ private void PrepareContainers()

var nameSuffix = string.Empty;

if (container.GetContainerLifetimeType() == ContainerLifetimeType.Default)
if (container.GetContainerLifetimeType() == ContainerLifetime.Default)
{
nameSuffix = GetRandomNameSuffix();
}
Expand All @@ -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;
}
Expand Down
13 changes: 13 additions & 0 deletions src/Aspire.Hosting/Dcp/Model/Container.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ internal sealed class ContainerSpec

[JsonPropertyName("networks")]
public List<ContainerNetworkConnection>? Networks { get; set; }

/// <summary>
/// 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.
/// </summary>
[JsonPropertyName("lifecycleKey")]
public string? LifecycleKey { get; set; }
}

internal sealed class BuildContext
Expand Down Expand Up @@ -305,6 +312,12 @@ internal sealed class ContainerStatus : V1Status
[JsonPropertyName("healthProbeResults")]
public List<HealthProbeResult>? HealthProbeResults { get; set;}

/// <summary>
/// The lifecycle key for the resource (used to identify changes to persistent resources requiring a restart).
/// </summary>
[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.
}
Expand Down
12 changes: 6 additions & 6 deletions src/Aspire.Hosting/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -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<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.ContainerResourceBuilderExtensions.WithLifetime<T>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>! builder, Aspire.Hosting.ApplicationModel.ContainerLifetime lifetime) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>!
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<Aspire.Hosting.ApplicationModel.ParameterResource!>!
static Aspire.Hosting.ParameterResourceBuilderExtensions.AddParameter(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! value, bool secret = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.ParameterResource!>!
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!>!
Expand Down