| File: AzureKubernetesIngressExtensions.cs | Web Access |
| Project: src\src\Aspire.Hosting.Azure.Kubernetes\Aspire.Hosting.Azure.Kubernetes.csproj (Aspire.Hosting.Azure.Kubernetes) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Azure.Kubernetes; using Aspire.Hosting.Kubernetes; namespace Aspire.Hosting; /// <summary> /// Provides extension methods for adding Kubernetes Ingress and Gateway resources to AKS environments. /// </summary> public static class AzureKubernetesIngressExtensions { /// <summary> /// Adds a Kubernetes Ingress resource to the application model, associated with the /// inner Kubernetes environment of the specified AKS environment. The ingress generates /// a <c>networking.k8s.io/v1 Ingress</c> resource in the Helm chart output at publish time. /// </summary> /// <param name="builder">The AKS environment resource builder.</param> /// <param name="name">The name of the ingress resource.</param> /// <returns>A reference to the <see cref="IResourceBuilder{KubernetesIngressResource}"/> for chaining.</returns> /// <ats-returns>The resource builder.</ats-returns> /// <remarks> /// <para> /// This method delegates to the inner <see cref="KubernetesEnvironmentResource"/> of the AKS /// environment. To use an AKS-specific ingress controller (e.g., Azure Application Gateway /// for Containers), call <see cref="KubernetesIngressExtensions.WithIngressClass(IResourceBuilder{KubernetesIngressResource}, string)"/> with the /// appropriate class name. /// </para> /// </remarks> /// <ats-remarks /> /// <example> /// <code> /// var aks = builder.AddAzureKubernetesEnvironment("aks"); /// var ingress = aks.AddIngress("public") /// .WithIngressClass("azure-alb-external"); /// /// var api = builder.AddProject<MyApi>("api"); /// ingress.WithPath("/api", api.GetEndpoint("http")); /// </code> /// </example> [AspireExport] public static IResourceBuilder<KubernetesIngressResource> AddIngress( this IResourceBuilder<AzureKubernetesEnvironmentResource> builder, [ResourceName] string name) { ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(name); var k8sEnvBuilder = builder.ApplicationBuilder.CreateResourceBuilder(builder.Resource.KubernetesEnvironment); return k8sEnvBuilder.AddIngress(name); } /// <summary> /// Adds a Kubernetes Gateway API Gateway resource to the application model, associated with the /// inner Kubernetes environment of the specified AKS environment. /// </summary> /// <param name="builder">The AKS environment resource builder.</param> /// <param name="name">The name of the gateway resource.</param> /// <returns>A reference to the <see cref="IResourceBuilder{KubernetesGatewayResource}"/> for chaining.</returns> /// <ats-returns>The resource builder.</ats-returns> /// <example> /// <code> /// var aks = builder.AddAzureKubernetesEnvironment("aks"); /// var gateway = aks.AddGateway("public") /// .WithGatewayClass("azure-alb-external"); /// /// var api = builder.AddProject<MyApi>("api"); /// gateway.WithRoute("/api", api.GetEndpoint("http")); /// </code> /// </example> [AspireExport] public static IResourceBuilder<KubernetesGatewayResource> AddGateway( this IResourceBuilder<AzureKubernetesEnvironmentResource> builder, [ResourceName] string name) { ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(name); 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] 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); } /// <summary> /// Routes a Kubernetes <see cref="KubernetesGatewayResource"/> through the supplied /// Azure Application Gateway for Containers (AGC) <see cref="AzureKubernetesLoadBalancerResource"/>. /// </summary> /// <ats-summary>Routes a Kubernetes Gateway through an AGC ApplicationLoadBalancer</ats-summary> /// <param name="builder">The gateway resource builder.</param> /// <param name="loadBalancer">The AGC load balancer to route through.</param> /// <returns>A reference to the <see cref="IResourceBuilder{KubernetesGatewayResource}"/> for chaining.</returns> /// <ats-returns>The resource builder.</ats-returns> /// <remarks> /// Writes the AGC routing annotations (<c>alb.networking.azure.io/alb-name</c> and /// <c>alb.networking.azure.io/alb-namespace</c>) onto the rendered Gateway and defaults /// the GatewayClass to <c>azure-alb-external</c> when one has not already been set via /// <c>WithGatewayClass(...)</c>. /// </remarks> /// <example> /// <code> /// var lb = aks.AddLoadBalancer("lb", albSubnet); /// var gateway = aks.AddGateway("public").WithLoadBalancer(lb); /// </code> /// </example> [AspireExport] public static IResourceBuilder<KubernetesGatewayResource> WithLoadBalancer( this IResourceBuilder<KubernetesGatewayResource> builder, IResourceBuilder<AzureKubernetesLoadBalancerResource> loadBalancer) { ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(loadBalancer); var lb = loadBalancer.Resource; // AGC discovers the target ApplicationLoadBalancer via these two annotations on // the Gateway. See: // https://learn.microsoft.com/azure/application-gateway/for-containers/quickstart-deploy-application-gateway-for-containers builder.Resource.GatewayAnnotations["alb.networking.azure.io/alb-name"] = ReferenceExpression.Create($"{lb.AlbName}"); builder.Resource.GatewayAnnotations["alb.networking.azure.io/alb-namespace"] = ReferenceExpression.Create($"{AzureKubernetesLoadBalancerResource.AlbNamespace}"); // Default to the AGC GatewayClass only when one isn't already set so an explicit // WithGatewayClass(...) call before WithLoadBalancer(...) wins. builder.Resource.GatewayClassName ??= ReferenceExpression.Create($"azure-alb-external"); return builder; } /// <summary> /// Routes a Kubernetes <see cref="KubernetesIngressResource"/> through the supplied /// Azure Application Gateway for Containers (AGC) <see cref="AzureKubernetesLoadBalancerResource"/>. /// </summary> /// <ats-summary>Routes a Kubernetes Ingress through an AGC ApplicationLoadBalancer</ats-summary> /// <param name="builder">The ingress resource builder.</param> /// <param name="loadBalancer">The AGC load balancer to route through.</param> /// <returns>A reference to the <see cref="IResourceBuilder{KubernetesIngressResource}"/> for chaining.</returns> /// <ats-returns>The resource builder.</ats-returns> /// <remarks> /// Writes the AGC routing annotations (<c>alb.networking.azure.io/alb-name</c> and /// <c>alb.networking.azure.io/alb-namespace</c>) onto the rendered Ingress and defaults /// the IngressClass to <c>azure-alb-external</c> when one has not already been set via /// <c>WithIngressClass(...)</c>. /// </remarks> [AspireExport("withLoadBalancerOnIngress", MethodName = "withLoadBalancer")] public static IResourceBuilder<KubernetesIngressResource> WithLoadBalancer( this IResourceBuilder<KubernetesIngressResource> builder, IResourceBuilder<AzureKubernetesLoadBalancerResource> loadBalancer) { ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(loadBalancer); var lb = loadBalancer.Resource; builder.Resource.IngressAnnotations["alb.networking.azure.io/alb-name"] = ReferenceExpression.Create($"{lb.AlbName}"); builder.Resource.IngressAnnotations["alb.networking.azure.io/alb-namespace"] = ReferenceExpression.Create($"{AzureKubernetesLoadBalancerResource.AlbNamespace}"); builder.Resource.IngressClassName ??= ReferenceExpression.Create($"azure-alb-external"); return builder; } }