File: KubernetesAspireDashboardResourceBuilderExtensions.cs
Web Access
Project: src\src\Aspire.Hosting.Kubernetes\Aspire.Hosting.Kubernetes.csproj (Aspire.Hosting.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.Kubernetes;
 
namespace Aspire.Hosting;
 
/// <summary>
/// Provides extension methods for creating Aspire Dashboard resources in the application model.
/// </summary>
public static class KubernetesAspireDashboardResourceBuilderExtensions
{
    /// <summary>
    /// Creates a new Aspire Dashboard resource builder with the specified name.
    /// </summary>
    /// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> instance.</param>
    /// <param name="name">The name of the Aspire Dashboard resource.</param>
    /// <returns>A reference to the <see cref="IResourceBuilder{KubernetesAspireDashboardResource}"/>.</returns>
    /// <remarks>
    /// This method initializes a new Aspire Dashboard resource with HTTP (port 18888),
    /// OTLP gRPC (port 18889), and OTLP HTTP (port 18890) endpoints. The dashboard is
    /// configured for unsecured cluster-internal access by default so it can be used behind
    /// an ingress controller or other Kubernetes networking layer.
    /// </remarks>
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="builder"/> is <c>null</c>.</exception>
    /// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is <c>null</c> or empty.</exception>
    internal static IResourceBuilder<KubernetesAspireDashboardResource> CreateDashboard(
        this IDistributedApplicationBuilder builder,
        string name)
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentException.ThrowIfNullOrEmpty(name);
 
        var resource = new KubernetesAspireDashboardResource(name);
 
        return builder.CreateResourceBuilder(resource)
                      .WithImage("mcr.microsoft.com/dotnet/nightly/aspire-dashboard")
                      .WithHttpEndpoint(targetPort: 18888)
                      // Expose the HTTP endpoint so ingress or explicit host port mapping can route browser traffic to the dashboard.
                      .WithEndpoint("http", e => e.IsExternal = true)
                      .WithHttpEndpoint(name: "otlp-grpc", targetPort: 18889)
                      .WithHttpEndpoint(name: "otlp-http", targetPort: 18890);
    }
 
    /// <summary>
    /// Sets the Kubernetes Service port for the Aspire Dashboard HTTP endpoint.
    /// </summary>
    /// <param name="builder">The <see cref="IResourceBuilder{KubernetesAspireDashboardResource}"/> instance to configure.</param>
    /// <param name="port">The Service port number. Cluster-internal clients will connect to this port,
    /// which routes to the dashboard's container port (18888). If <c>null</c>, the Service port
    /// defaults to the container port.</param>
    /// <returns>The <see cref="IResourceBuilder{KubernetesAspireDashboardResource}"/> instance for chaining.</returns>
    /// <ats-returns>The resource builder.</ats-returns>
    /// <remarks>
    /// This sets the <c>port</c> field on the generated Kubernetes Service while keeping the
    /// <c>targetPort</c> as the original container port. This is useful when placing the dashboard
    /// behind an ingress controller that expects a specific Service port (for example, port 80).
    /// To access the dashboard from outside the cluster, use <c>kubectl port-forward</c> or
    /// configure the environment's <see cref="KubernetesEnvironmentResource.DefaultServiceType"/>
    /// to <c>NodePort</c> or <c>LoadBalancer</c>.
    /// </remarks>
    [AspireExport]
    public static IResourceBuilder<KubernetesAspireDashboardResource> WithServicePort(
        this IResourceBuilder<KubernetesAspireDashboardResource> builder,
        int? port = null)
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return builder.WithEndpoint("http", e =>
        {
            e.Port = port;
            e.IsExternal = port is not null;
        });
    }
 
    /// <summary>
    /// Sets the Kubernetes Service ports for the Aspire Dashboard OTLP endpoints.
    /// </summary>
    /// <param name="builder">The <see cref="IResourceBuilder{KubernetesAspireDashboardResource}"/> instance to configure.</param>
    /// <param name="grpcPort">The Service port for the OTLP gRPC endpoint. If <c>null</c>, the Service port
    /// defaults to the container port (18889).</param>
    /// <param name="httpPort">The Service port for the OTLP HTTP endpoint. If <c>null</c>, the Service port
    /// defaults to the container port (18890).</param>
    /// <returns>The <see cref="IResourceBuilder{KubernetesAspireDashboardResource}"/> instance for chaining.</returns>
    /// <ats-returns>The resource builder.</ats-returns>
    /// <remarks>
    /// This sets the <c>port</c> field on the generated Kubernetes Service for the OTLP endpoints
    /// while keeping the <c>targetPort</c> as the original container port. Application resources
    /// in the cluster send telemetry to these Service ports. Use standard OTLP ports (4317 for gRPC,
    /// 4318 for HTTP) if your services are configured with those defaults.
    /// </remarks>
    [AspireExport]
    public static IResourceBuilder<KubernetesAspireDashboardResource> WithOtlpServicePort(
        this IResourceBuilder<KubernetesAspireDashboardResource> builder,
        int? grpcPort = null,
        int? httpPort = null)
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        if (grpcPort is not null)
        {
            builder = builder.WithEndpoint("otlp-grpc", e => e.Port = grpcPort);
        }
 
        if (httpPort is not null)
        {
            builder = builder.WithEndpoint("otlp-http", e => e.Port = httpPort);
        }
 
        return builder;
    }
 
    /// <summary>
    /// Configures whether forwarded headers processing is enabled for the Aspire dashboard container.
    /// </summary>
    /// <param name="builder">The <see cref="IResourceBuilder{KubernetesAspireDashboardResource}"/> instance.</param>
    /// <param name="enabled">True to enable forwarded headers, false to disable.</param>
    /// <returns>The same <see cref="IResourceBuilder{KubernetesAspireDashboardResource}"/> to allow chaining.</returns>
    /// <ats-returns>The resource builder.</ats-returns>
    /// <remarks>
    /// This sets the <c>ASPIRE_DASHBOARD_FORWARDEDHEADERS_ENABLED</c> environment variable inside the dashboard
    /// container. When enabled, the dashboard will process <c>X-Forwarded-Host</c> and <c>X-Forwarded-Proto</c>
    /// headers which is required when the dashboard is accessed through a reverse proxy or ingress controller.
    /// </remarks>
    [AspireExport]
    public static IResourceBuilder<KubernetesAspireDashboardResource> WithForwardedHeaders(
        this IResourceBuilder<KubernetesAspireDashboardResource> builder,
        bool enabled = true)
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return builder.WithEnvironment("ASPIRE_DASHBOARD_FORWARDEDHEADERS_ENABLED", enabled ? "true" : "false");
    }
}