Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,58 @@ public static IResourceBuilder<KubernetesGatewayResource> AddGateway(
var k8sEnvBuilder = builder.ApplicationBuilder.CreateResourceBuilder(builder.Resource.KubernetesEnvironment);
return k8sEnvBuilder.AddGateway(name);
}

/// <summary>
/// Adds an external Helm chart to be installed in the AKS environment's inner Kubernetes
/// environment. The chart is installed via <c>helm upgrade --install</c> as a pipeline step
/// after the main application Helm chart is deployed.
/// </summary>
/// <param name="builder">The AKS environment resource builder.</param>
/// <param name="name">The name of the Helm chart resource.</param>
/// <param name="chartReference">
/// The Helm chart reference. Can be an OCI registry URL (e.g., <c>oci://quay.io/jetstack/charts/cert-manager</c>)
/// or a chart name from an added repository.
/// </param>
/// <param name="chartVersion">The chart version to install.</param>
/// <returns>A resource builder for the Helm chart resource.</returns>
/// <remarks>
/// <para>
/// This method delegates to the inner <see cref="KubernetesEnvironmentResource"/> of the AKS
/// environment, so the returned <see cref="KubernetesHelmChartResource"/> can be configured
/// with the same <see cref="KubernetesHelmChartExtensions.WithHelmValue"/>,
/// <see cref="KubernetesHelmChartExtensions.WithNamespace"/>,
/// <see cref="KubernetesHelmChartExtensions.WithReleaseName"/>, and
/// <see cref="KubernetesHelmChartExtensions.WithDestroy"/> extensions used with
/// non-AKS Kubernetes environments.
/// </para>
/// <para>
/// External Helm charts are <em>not</em> uninstalled by <c>aspire destroy</c> by default,
/// because they may be shared with workloads outside the Aspire app. Opt in by chaining
/// <see cref="KubernetesHelmChartExtensions.WithDestroy"/>.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// var aks = builder.AddAzureKubernetesEnvironment("aks");
///
/// // Install cert-manager
/// aks.AddHelmChart("cert-manager", "oci://quay.io/jetstack/charts/cert-manager", "1.17.0")
/// .WithHelmValue("crds.enabled", "true");
/// </code>
/// </example>
[AspireExport(Description = "Adds an external Helm chart to an AKS environment")]
public static IResourceBuilder<KubernetesHelmChartResource> AddHelmChart(
this IResourceBuilder<AzureKubernetesEnvironmentResource> builder,
[ResourceName] string name,
string chartReference,
string chartVersion)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrEmpty(name);
ArgumentException.ThrowIfNullOrEmpty(chartReference);
ArgumentException.ThrowIfNullOrEmpty(chartVersion);

var k8sEnvBuilder = builder.ApplicationBuilder.CreateResourceBuilder(builder.Resource.KubernetesEnvironment);
return k8sEnvBuilder.AddHelmChart(name, chartReference, chartVersion);
}
}
16 changes: 11 additions & 5 deletions src/Aspire.Hosting.Kubernetes/HelmChartOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ namespace Aspire.Hosting.Kubernetes;
[AspireExport(ExposeMethods = true)]
public sealed partial class HelmChartOptions
{
private const int KubernetesNamespaceMaxLength = 63;
private const int HelmReleaseNameMaxLength = 53;
internal const int KubernetesNamespaceMaxLength = 63;
internal const int HelmReleaseNameMaxLength = 53;
private const int HelmChartNameMaxLength = 250;
private const int HelmChartDescriptionMaxLength = 1024;

Expand All @@ -39,7 +39,7 @@ internal HelmChartOptions(IResourceBuilder<KubernetesEnvironmentResource> enviro
public HelmChartOptions WithNamespace(string @namespace)
{
ArgumentException.ThrowIfNullOrWhiteSpace(@namespace);
ValidateDnsLabel(@namespace, "Kubernetes namespace", KubernetesNamespaceMaxLength, nameof(@namespace));
ValidateNamespace(@namespace, nameof(@namespace));

var expression = ReferenceExpression.Create($"{@namespace}");
EnvironmentBuilder.WithAnnotation(new KubernetesNamespaceAnnotation(expression), ResourceAnnotationMutationBehavior.Replace);
Expand Down Expand Up @@ -83,7 +83,7 @@ internal HelmChartOptions WithNamespace([AspireUnion(typeof(string), typeof(IRes
public HelmChartOptions WithReleaseName(string releaseName)
{
ArgumentException.ThrowIfNullOrWhiteSpace(releaseName);
ValidateDnsLabel(releaseName, "Helm release name", HelmReleaseNameMaxLength, nameof(releaseName));
ValidateReleaseName(releaseName, nameof(releaseName));

var expression = ReferenceExpression.Create($"{releaseName}");
EnvironmentBuilder.WithAnnotation(new HelmReleaseNameAnnotation(expression), ResourceAnnotationMutationBehavior.Replace);
Expand Down Expand Up @@ -255,7 +255,7 @@ internal HelmChartOptions WithChartDescription([AspireUnion(typeof(string), type
};
}

private static void ValidateDnsLabel(string value, string target, int maxLength, string paramName)
internal static void ValidateDnsLabel(string value, string target, int maxLength, string paramName)
{
if (value.Length > maxLength)
{
Expand All @@ -268,6 +268,12 @@ private static void ValidateDnsLabel(string value, string target, int maxLength,
}
}

internal static void ValidateNamespace(string @namespace, string paramName)
=> ValidateDnsLabel(@namespace, "Kubernetes namespace", KubernetesNamespaceMaxLength, paramName);

internal static void ValidateReleaseName(string releaseName, string paramName)
=> ValidateDnsLabel(releaseName, "Helm release name", HelmReleaseNameMaxLength, paramName);

// Matches Helm's own chart-version validation, which uses the lenient SemVer parser
// (Masterminds/semver/v3 NewVersion) — see helm/helm pkg/chart/v2/metadata.go isValidSemver.
// Helm accepts a leading "v" and partial versions (e.g. "v1", "1", "1.2"), coercing them
Expand Down
Loading
Loading