Add Kubernetes deploy pipeline with Helm engine#15723
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15723Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15723" |
|
Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
|
|
Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
|
0005428 to
f979f8a
Compare
There was a problem hiding this comment.
Pull request overview
Adds end-to-end aspire deploy support for Kubernetes by introducing a pluggable deployment-engine pipeline (defaulting to Helm), wiring container registry + OTLP/dashboard behavior into Kubernetes publishing, and expanding test coverage (unit snapshot tests + new CLI E2E tests).
Changes:
- Introduces a Helm-based Kubernetes deployment engine that adds prepare/deploy/summary/uninstall pipeline steps and supports deploy-time resolution of parameters, secrets, cross-references, and container image registry prefixes.
- Adds Kubernetes environment configuration APIs/annotations (namespace, release name, chart version), plus default dashboard generation and OTLP wiring.
- Expands tests substantially: new Helm expression string-output coverage, updated Kubernetes publisher snapshots, and multiple new KinD+Helm CLI E2E tests.
Reviewed changes
Copilot reviewed 110 out of 110 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Shared/Docker/Dockerfile.e2e | Disables stabilized package versions for pack/bundle during E2E image builds. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ResourceWithProbes#00.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ResourceWithProbes#01.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ResourceWithProbes#02.verified.yaml | Adds/updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#00.verified.yaml | Updates expected generated Helm chart snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#01.verified.yaml | Updates expected generated values snapshot (incl. dashboard + OTLP). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#02.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#03.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#04.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#05.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#06.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesSpecialResourceName#07.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpressionWithParameterCondition#01.verified.yaml | Updates expected generated values snapshot (dashboard config + TLS placeholders). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpressionWithParameterCondition#02.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpressionWithParameterCondition#03.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpressionWithParameterCondition#04.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpressionWithParameterCondition#05.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpressionWithParameterCondition#06.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpression#01.verified.yaml | Updates expected generated values snapshot (dashboard config). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpression#02.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpression#03.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpression#04.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpression#05.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_HandlesConditionalReferenceExpression#06.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#01.verified.yaml | Updates expected generated values snapshot (dashboard config + OTLP + secrets placeholders). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#02.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#03.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#04.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#05.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#06.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#07.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#08.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#09.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_GeneratesValidHelmChart#10.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#00.verified.yaml | Updates expected generated Helm chart snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#01.verified.yaml | Updates expected generated values snapshot (dashboard config + OTLP). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#02.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#03.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#04.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#05.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#06.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#07.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_CustomWorkloadAndResourceType#08.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ConditionalWithParameterBranch_UsesIfElseSyntax#01.verified.yaml | Updates expected generated values snapshot (dashboard config + TLS placeholders). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ConditionalWithParameterBranch_UsesIfElseSyntax#02.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ConditionalWithParameterBranch_UsesIfElseSyntax#03.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ConditionalWithParameterBranch_UsesIfElseSyntax#04.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ConditionalWithParameterBranch_UsesIfElseSyntax#05.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ConditionalWithParameterBranch_UsesIfElseSyntax#06.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#01.verified.yaml | Updates expected generated values snapshot (dashboard config + OTLP + placeholders). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#02.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#03.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#04.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#05.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#06.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#07.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#08.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesWithProjectResources#09.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#00.verified.yaml | Updates expected generated Helm chart snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#01.verified.yaml | Updates expected generated values snapshot (dashboard config). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#02.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#03.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#04.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#05.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#06.verified.yaml | Updates expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#07.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#08.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.KubernetesMapsPortsForBaitAndSwitchResources#09.verified.yaml | Adds expected generated Kubernetes YAML snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.PublishingKubernetesEnvironmentPublishesFile#01.verified.yaml | Updates expected environment values snapshot to include dashboard config. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env1/values.verified.yaml | Updates expected multi-env values snapshot (dashboard config + OTLP). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env1/templates/ServiceA/config.verified.yaml | Updates expected generated config template snapshot (string-safe numeric output + OTLP). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env1/templates/env1-dashboard/config.verified.yaml | Adds expected dashboard config template snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env1/templates/env1-dashboard/deployment.verified.yaml | Adds expected dashboard deployment template snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env1/templates/env1-dashboard/service.verified.yaml | Adds expected dashboard service template snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env2/values.verified.yaml | Updates expected multi-env values snapshot (dashboard config + OTLP). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env2/templates/ServiceB/config.verified.yaml | Updates expected generated config template snapshot (string-safe numeric output + OTLP). |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env2/templates/env2-dashboard/config.verified.yaml | Adds expected dashboard config template snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env2/templates/env2-dashboard/deployment.verified.yaml | Adds expected dashboard deployment template snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesEnvironmentResourceTests.MultipleKubernetesEnvironmentsSupported/env2/templates/env2-dashboard/service.verified.yaml | Adds expected dashboard service template snapshot. |
| tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs | Updates publisher tests to include dashboard templates in expected files. |
| tests/Aspire.Hosting.Kubernetes.Tests/HelmExtensionsTests.cs | Adds coverage for EnsureStringOutput / toString behavior. |
| tests/Aspire.Hosting.Kubernetes.Tests/Aspire.Hosting.Kubernetes.Tests.csproj | Adds shared test helper compile include. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployBasicApiServiceTests.cs | New KinD+Helm E2E test for basic Kubernetes deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithRedisTests.cs | New KinD+Helm E2E test validating Redis-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithValkeyTests.cs | New KinD+Helm E2E test validating Valkey-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithGarnetTests.cs | New KinD+Helm E2E test validating Garnet-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithRabbitMQTests.cs | New KinD+Helm E2E test validating RabbitMQ-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithPostgresTests.cs | New KinD+Helm E2E test validating PostgreSQL-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithMySqlTests.cs | New KinD+Helm E2E test validating MySQL-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithSqlServerTests.cs | New KinD+Helm E2E test validating SQL Server-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithMongoDBTests.cs | New KinD+Helm E2E test validating MongoDB-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/KubernetesDeployWithNatsTests.cs | New (quarantined) KinD+Helm E2E test validating NATS-backed app deploy. |
| tests/Aspire.Cli.EndToEnd.Tests/Helpers/KubernetesDeployTestHelpers.cs | Adds shared terminal automation helpers for KinD+Helm based Kubernetes deploy E2E tests. |
| src/Aspire.Hosting.Kubernetes/Resources/RollingUpdateStatefulSetStrategyV1.cs | Makes maxUnavailable nullable for StatefulSet rolling update strategy YAML. |
| src/Aspire.Hosting.Kubernetes/KubernetesResource.cs | Improves Helm parameter handling (deferred resolution, values key mapping, image registry resolution) and removes https service discovery env vars. |
| src/Aspire.Hosting.Kubernetes/KubernetesPublishingContext.cs | Includes dashboard in publish output and captures deploy-time resolution mappings/cross-references/image refs. |
| src/Aspire.Hosting.Kubernetes/KubernetesInfrastructure.cs | Wires dashboard deployment target + container registry resolution + OTLP environment injection for Kubernetes environments. |
| src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentResource.cs | Adds dashboard support and deploy pipeline wiring (engine expansion + dependencies). |
| src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentExtensions.cs | Adds default Helm engine, .WithHelm(...), and dashboard configuration APIs. |
| src/Aspire.Hosting.Kubernetes/KubernetesAspireDashboardResourceBuilderExtensions.cs | Adds dashboard builder helpers and customization (host port, forwarded headers). |
| src/Aspire.Hosting.Kubernetes/KubernetesAspireDashboardResource.cs | Adds a Kubernetes-specific dashboard resource type with endpoint references. |
| src/Aspire.Hosting.Kubernetes/HelmChartOptions.cs | Adds fluent configuration options for namespace/release/chart version with validation. |
| src/Aspire.Hosting.Kubernetes/Extensions/ResourceExtensions.cs | Ensures Helm numeric conversions render as strings via toString to keep ConfigMap/Secret values string-typed. |
| src/Aspire.Hosting.Kubernetes/Extensions/HelmExtensions.cs | Adds EnsureStringOutput helper and exposes regex used for numeric conversions. |
| src/Aspire.Hosting.Kubernetes/Deployment/HelmDeploymentEngine.cs | Introduces Helm deployment pipeline steps including prepare/deploy/summary/uninstall and deploy-time values override generation. |
| src/Aspire.Hosting.Kubernetes/Annotations/KubernetesNamespaceAnnotation.cs | Adds annotation for namespace configuration via ReferenceExpression. |
| src/Aspire.Hosting.Kubernetes/Annotations/KubernetesDeploymentEngineAnnotation.cs | Adds annotation-based extension point for pluggable deployment engines. |
| src/Aspire.Hosting.Kubernetes/Annotations/HelmReleaseNameAnnotation.cs | Adds annotation for Helm release name via ReferenceExpression. |
| src/Aspire.Hosting.Kubernetes/Annotations/HelmChartVersionAnnotation.cs | Adds annotation for Helm chart version via ReferenceExpression. |
|
Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
|
|
Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
|
There was a problem hiding this comment.
Left 3 inline comments covering the service summary lookup, Kubernetes dashboard host-port behavior, and deploy-time Helm validation.
Also, the KubernetesDeploymentEngineAnnotation feels pretty pipeline-internal for a public extension point, and WithHostPort (I know it's an existing API naming convention) but is a Docker-Compose-ish name for something that really needs clearer Kubernetes semantics.
7f47bc1 to
5726a6c
Compare
|
Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
|
9db6c3a to
be920c6
Compare
|
Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
|
TypeScript AppHost support for Helm APIsTested the K8s deploy pipeline locally - works great! One gap I noticed: The new Helm deployment APIs (WithHelm, WithNamespace, WithReleaseName, WithChartVersion) don't have TypeScript equivalents yet. The TS K8s API only exposes withProperties for chart-level settings, but the Helm deploy engine annotations aren't wired into the TypeScript code generation. Would be great to add these as a follow-up so TypeScript AppHosts can also use the Helm deploy pipeline: |
Testing SummaryTested locally on Docker Desktop Kubernetes (2-node cluster, Helm 4.1.4). What works well
Gap: TypeScript AppHost can't deploy to K8sTried creating a TypeScript AppHost with addKubernetesEnvironment + withContainerRegistry. The deploy pipeline runs but produces only 2 no-op steps (pipeline-execution + deploy). No build/push/helm steps are created. The root cause: withHelm() is what registers the HelmDeploymentEngine which creates the pipeline steps. Since withHelm() has no TypeScript equivalent, TS AppHosts can't drive K8s deploys. This is the main follow-up item -- exposing the Helm deployment engine APIs in the TypeScript codegen. |
Follow-up: WithHelm TypeScript codegenWithHelm has [AspireExport]\ (line 82 of KubernetesEnvironmentExtensions.cs) but it doesn't appear in the generated TypeScript modules. The likely reason: \HelmChartOptions\ (the callback parameter type) is not marked with [AspireExport], so the codegen can't generate the TS equivalent for the \Action\ parameter. Adding [AspireExport]\ to \HelmChartOptions\ and its methods (\WithNamespace, \WithReleaseName, \WithChartVersion) should unblock TS codegen. |
Port Mapping VerificationDeployed with multiple endpoint configurations and verified all port mappings are correct in the live K8s cluster:
Env var expressions also correct -- HTTP_PORTS lists all target ports, service references use Helm expressions. Minor issue: endpoint name length validationK8s port names must be <= 15 chars (IANA service name format). Names like 'custom-port-only' (16 chars) cause \helm upgrade --install\ to fail with:
Consider validating endpoint names against this K8s constraint at publish time for a better error experience. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The tests now run inside the Dockerfile.e2e container where the CLI is pre-built from source (x.y.z-dev), instead of installing from the PR shell script at runtime. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Docker container from Dockerfile.e2e does not include kubectl. The bare runner had it pre-installed, but inside the container it was missing, causing 'kubectl: command not found' at the ConfigMap apply step. Download kubectl alongside kind and helm in InstallKindAndHelmAsync so the helper is self-contained regardless of environment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When running inside a Docker container with socket forwarding, KinD's kubeconfig uses 127.0.0.1:<port> (host loopback) which is unreachable from inside the container. After cluster creation, detect the Docker environment (/.dockerenv), join the 'kind' network, and switch to the internal kubeconfig which uses Docker DNS names instead of localhost. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two fixes for KubernetesDeploy E2E tests running in Docker containers: 1. Pre-install kind, helm, and kubectl in Dockerfile.e2e so they don't need to be downloaded at test runtime (~2-3 min saved per test). InstallKindAndHelmAsync now skips downloads when tools are on PATH. 2. After kind create cluster, detect Docker environment (/.dockerenv) and switch to KinD's internal kubeconfig + join the 'kind' network so kubectl can reach the API server via Docker DNS instead of the host's 127.0.0.1 loopback (unreachable from inside a container). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Docker container approach (CreateDockerTestTerminal) doesn't work for K8s deploy tests because aspire deploy uses docker buildx with a docker-container driver, which can't access the test container's filesystem for build context. Revert to CreateTestTerminal which runs directly on the GitHub Actions runner. Keep the kubectl install improvement in InstallKindAndHelmAsync (skips download if already on PATH via command -v). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix XML doc: move <example> out of <remarks> to top-level sibling - Fix GetSteps lookup: print-summary steps are owned by the environment resource, not the deployment target - Add post-deploy instructions step with dashboard port-forward command, helm status/get-all, and helm uninstall commands - Revert KubernetesDeploy tests to bare-terminal (Docker container approach incompatible with docker buildx image builds) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The release name now defaults to IHostEnvironment.EnvironmentName (from aspire deploy -e <name>) instead of the resource name from AddKubernetesEnvironment(). This matches user expectations since the -e flag is the primary way to differentiate deployments. Also refactored namespace/release resolution into shared helpers to eliminate duplication across HelmDeploy, PrintInstructions, and HelmUninstall methods. Improved summary presentation: split instructions into separate concise key-value entries matching the established pattern used by ACA and Docker Compose (emoji keys, inline markdown values). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix service name mismatch in PrintResourceSummaryAsync: use ToServiceName() so kubectl queries match the deployed service name - Throw on helm deploy failure so the pipeline step fails and dependent print-summary steps do not run - Use MatchEvaluator in Regex.Replace for Chart.yaml version to prevent regex backreference corruption from parameter-backed values - Validate resolved release name and namespace at deploy time using DNS label rules, catching invalid environment names early - Add ServicePort to EndpointMapping so WithHostPort flows the exposed port into the Kubernetes Service manifest Port field while keeping TargetPort as the container port - Add missing ArgumentNullException.ThrowIfNull in WithHostPort Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WithHostPort implied Docker-style host port mapping, which doesn't apply in Kubernetes where Services are cluster-internal by default. - Rename WithHostPort → WithServicePort with docs explaining it sets the Kubernetes Service port (useful behind ingress) - Add WithOtlpServicePort(grpcPort, httpPort) for customizing the OTLP endpoint Service ports (e.g. standard 4317/4318) - Re-add ServicePort plumbing in EndpointMapping so the APIs flow ExposedPort into the Service manifest Port field while keeping TargetPort as the container port Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address Eric's review feedback: eliminate the custom annotation type and align with how Docker Compose handles deployment engines. - Delete KubernetesDeploymentEngineAnnotation — replaced by a DeploymentEngineStepsFactory delegate property on KubernetesEnvironmentResource - Move per-resource print-summary steps to KubernetesResource deployment targets via PipelineStepAnnotation (matching the DockerComposeServiceResource pattern) - Merge the two PipelineStepAnnotations on the environment resource into a single one that handles publish + engine + deployment target step expansion (matching DockerComposeEnvironmentResource pattern) - WithHelm() and EnsureDefaultHelmEngine() now set the delegate property instead of adding/replacing an annotation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…gets GetSteps was looking for print-summary steps on the environment resource (this), but they now live on the deployment target (KubernetesResource). Updated to match the Docker Compose pattern which uses context.GetSteps(deploymentTarget, 'print-summary'). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add [AspireExport(ExposeMethods = true)] to HelmChartOptions so WithNamespace, WithReleaseName, WithChartVersion are visible from TypeScript callbacks in withHelm() - Add ASPIREEXPORT012 analyzer rule that warns when callback parameter types (Action<T>) in [AspireExport] methods lack [AspireExport] on the context type, scoped to same-assembly types only - Add [AspireExport(ExposeProperties = true)] to ResourceUrlAnnotation (pre-existing gap caught by the new analyzer rule) - Add KubernetesDeployTypeScriptTests E2E test that creates a project from the Express/React starter template, adds Aspire.Hosting.Kubernetes, modifies apphost.ts with addKubernetesEnvironment + withHelm callback, and deploys to a KinD cluster Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
d5eedb4 to
f70d859
Compare
- ASPIREEXPORT012: Skip callback context types with no public instance methods/properties (fixes false positive on empty TestContext in analyzer unit tests) - ASPIREEXPORT012: Scope to same-assembly types only (fixes false positives on external ContainerApp/ContainerAppJob types) - Replace Semver NuGet package with GeneratedRegex for chart version validation (package reference was lost during rebase) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert [AspireExport] on ResourceUrlAnnotation (caused snapshot mismatches across all codegen tests — out of scope for this PR) - ASPIREEXPORT012: Also skip types with [AspireDto] (they're already exported for serialization, not callback contexts) - Use shared SmolSemVer (SemVersion.TryParse) instead of regex for chart version validation — the type is already available from Aspire.Hosting via project reference Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…alidation) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The test was running inside a Docker container, but KinD creates clusters on the host via the mounted Docker socket. This caused kubectl to fail connecting to the API server (localhost inside the container != localhost on the host). Switched to CreateTestTerminal() matching the pattern used by all other K8s deploy E2E tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Parameters must be created with addParameter() before being passed into the withHelm callback. Also use separate await for addKubernetesEnvironment and withHelm since the fluent chain crosses async boundaries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🎬 CLI E2E Test Recordings — 68 recordings uploaded (commit View recordings
📹 Recordings uploaded automatically from CI run #24281920579 |
| /// Substitutes Helm value expressions (e.g., <c>{{ .Values.secrets.cache.password }}</c>) in a template | ||
| /// string with resolved values from the lookup dictionary. | ||
| /// </summary> | ||
| internal static string ResolveHelmExpressions(string template, Dictionary<string, string> resolvedLookup) |
There was a problem hiding this comment.
Why do we do this resolution and not helm itself?
davidfowl
left a comment
There was a problem hiding this comment.
Small nits for follow ups. HUGE steps forward now lets polish and clean up!
JamesNK
left a comment
There was a problem hiding this comment.
Post-merge review: 5 issues found (1 correctness, 1 potential bug, 1 reproducibility, 1 maintainability, 1 observability).
| var dashboardServiceName = environment.Dashboard.Resource.Name.ToKubernetesResourceName() + "-service"; | ||
| context.Summary.Add( | ||
| "📊 Dashboard", | ||
| new MarkdownString($"`kubectl port-forward -n {@namespace} svc/{dashboardServiceName} 18888:18888` then open [http://localhost:18888](http://localhost:18888)")); |
There was a problem hiding this comment.
The port-forward instruction hard-codes 18888:18888, but the Service port is customizable via WithServicePort(). If someone calls .WithDashboard(d => d.WithServicePort(80)), the generated kubectl port-forward instruction will reference the wrong Service port. Consider resolving the actual service port from the dashboard endpoint configuration.
| { | ||
| await uninstallTask.CompleteAsync( | ||
| new MarkdownString($"Helm release **{releaseName}** uninstalled from namespace **{@namespace}**"), | ||
| CompletionState.Completed, |
There was a problem hiding this comment.
When helm uninstall exits with a non-zero code, this calls FailAsync() but does not throw — so the pipeline step's Action delegate returns normally and the pipeline treats the step as successful. Compare with HelmDeployAsync (line ~437) which throws InvalidOperationException on failure. The catch block at line 571 also rethrows, creating an inconsistency: exceptions from helm uninstall propagate but non-zero exit codes don't.
If this is intentional best-effort teardown, a comment clarifying that would help. If not, add a throw after the FailAsync call.
| var resource = new KubernetesAspireDashboardResource(name); | ||
|
|
||
| return builder.CreateResourceBuilder(resource) | ||
| .WithImage("mcr.microsoft.com/dotnet/nightly/aspire-dashboard") |
There was a problem hiding this comment.
.WithImage("mcr.microsoft.com/dotnet/nightly/aspire-dashboard") has no tag, which means deploys pull whatever latest points to from the nightly feed. This makes Kubernetes deployments non-reproducible and ties production Helm charts to a nightly image. A pinned tag matching the Aspire SDK version would be more appropriate for generated Helm charts.
| } | ||
|
|
||
| [GeneratedRegex("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")] | ||
| private static partial Regex DnsLabelPattern(); |
There was a problem hiding this comment.
The DnsLabelPattern() generated regex is duplicated here and in HelmDeploymentEngine.cs (line 108). If one is updated and the other isn't, build-time validation (in HelmChartOptions) and deploy-time validation (in HelmDeploymentEngine) would diverge. Consider extracting to a shared helper.
| if (resolvedValue is null) | ||
| { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
If captured.Parameter.GetValueAsync() returns null, the entry is silently skipped with no warning. A required parameter that has no value at deploy time will produce an incomplete override file, leading to cryptic Helm deployment failures (e.g., empty secrets). Consider logging a warning here so the root cause is easier to diagnose.
* Add Kubernetes deploy pipeline with Helm engine
Implement end-to-end aspire deploy support for Kubernetes environments
using an annotation-driven deployment engine architecture with Helm as
the default engine.
New features:
- KubernetesDeploymentEngineAnnotation for pluggable deployment engines
- HelmDeploymentEngine with pipeline steps: prepare, helm-deploy,
print-summary, and helm-uninstall
- HelmChartConfiguration builder with dual overloads (string + parameter)
for namespace, release name, and chart version
- WithHelm() extension method on KubernetesEnvironmentResource
- Container registry wiring in KubernetesInfrastructure
- Pipeline step dependency wiring (build → push → prepare → deploy → summary)
Design decisions:
- Uses helm upgrade --install for idempotent deployments
- Config annotations use ReferenceExpression for both literal and
parameter-backed values (enabling deploy-time prompting)
- Helm is the default engine added by AddKubernetesEnvironment()
- WithAnnotation Replace behavior prevents annotation stacking
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix: remove unused using directive in KubernetesDeployTests
Remove 'using Aspire.TestUtilities' which is unnecessary since
TestTempDirectory is in the global namespace. This was causing
IDE0005 build errors in CI where warnings are treated as errors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix values.yaml secret/parameter population during deploy
During aspire publish, secrets and parameters without defaults are written
as empty placeholders in values.yaml (correct for distributable charts).
During aspire deploy, these need actual resolved values.
Changes:
- KubernetesResource.AllocateParameter: Always store ParameterResource
reference (previously dropped for secrets and params without defaults)
- KubernetesPublishingContext: Accept environment resource, capture
secret/unresolved parameter mappings to CapturedHelmValues during publish
while preserving empty placeholders in values.yaml
- KubernetesEnvironmentResource: Add CapturedHelmValues list to store
parameter-to-values.yaml mappings between publish and deploy steps
- HelmDeploymentEngine.PrepareAsync: Resolve captured values via
GetValueAsync() and write values-deploy.yaml override file
- HelmDeploymentEngine.HelmDeployAsync: Pass -f values-deploy.yaml after
-f values.yaml so resolved secrets take precedence via Helm merge
This mirrors Docker Compose's CapturedEnvironmentVariables pattern:
capture parameter references during publish, resolve during deploy.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix cross-resource secret references and rename deploy values file
Cross-resource secret references (e.g., server referencing cache's
password) were entirely skipped from values.yaml because their Value
string contained Helm expressions ({{ .Values.secrets.cache.password }}).
This caused 'nil pointer' errors in Helm templates because the
secrets.server section was completely missing from values.yaml.
Changes:
- AddValuesToHelmSectionAsync: entries with ValueContainsHelmExpression
now write empty placeholders instead of being skipped, and capture
the template string as CapturedHelmCrossReference for deploy-time
resolution
- KubernetesEnvironmentResource: add CapturedHelmCrossReference record
and CapturedHelmCrossReferences list
- HelmDeploymentEngine: two-phase resolution at deploy time:
1) resolve direct ParameterResource values
2) substitute Helm expressions in cross-reference templates with
resolved values from phase 1
- Rename override file from values-deploy.yaml to values.{envName}.yaml
to mirror Docker Compose's .env.{envName} naming convention
- Add ResolveHelmExpressions() with regex-based substitution
- 9 new tests: cross-reference resolution, expression substitution,
file naming, unresolved expression preservation
- Updated 7 snapshots reflecting new empty placeholder entries
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Resolve container image references with registry prefix at deploy time
During publish, Kubernetes image parameters are written as 'server:latest'
to values.yaml. At deploy time, we now resolve the full registry-prefixed
image name (e.g., 'myregistry.azurecr.io/myrepo/server:latest') using the
same ContainerImageReference pattern as Docker Compose.
Changes:
- Added ImageResource property to HelmValue for tracking the source resource
- GetContainerImageName sets ImageResource on project/Dockerfile resources
- KubernetesPublishingContext captures CapturedHelmImageReferences during publish
- HelmDeploymentEngine Phase 3 resolves image references via ContainerImageReference
- Added CapturedHelmImageReference record to KubernetesEnvironmentResource
- 5 new tests covering image capture, resolution, and registry prefix
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix values.yaml key mismatch for parameter-backed secrets
When a container resource has an environment variable (e.g., REDIS_PASSWORD)
backed by a ParameterResource (e.g., cache-password), the Helm template
expression references the parameter name (cache_password) but values.yaml
was using the env var name (REDIS_PASSWORD). This caused nil pointer errors
at deploy time because Helm couldn't find the expected key.
Added HelmValue.ValuesKey property that preserves the parameter's formatted
name from AllocateParameter. AddValuesToHelmSectionAsync now uses ValuesKey
when present, ensuring values.yaml keys match the Helm expression paths.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add cross-resource secret resolution tests
Verify that the ValuesKey fix correctly resolves the key mismatch
between parameter names and environment variable names. Tests cover:
- values.yaml keys match Helm template expression paths
- Phase 1 + Phase 2 override file resolution produces fully resolved values
- Full E2E publish-then-resolve flow with shared secret parameter
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix ReferenceExpression {0} passthrough losing HelmValue metadata
When Redis provides its password via WithReference, it wraps the
ParameterResource in a ReferenceExpression($"{PasswordParameter}").
The {0} passthrough optimization in ProcessValueAsync was calling
.ToString() on the inner result, converting the HelmValue (with
ValuesKey="cache_password") to a plain string. This lost the
ValuesKey, causing the values.yaml key to fall back to the env var
name (CACHE_PASSWORD) instead of the parameter name (cache_password),
which didn't match the Helm template reference.
Fix: preserve the inner object when at the top level (embedded=false),
only convert to string when embedded in a larger format string. This
also correctly preserves integer type information for port parameters,
which now render as {{ .Values.parameters.X.port_http | int }} instead
of being quoted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix ConfigMap integer values: use toString instead of int pipe
Kubernetes ConfigMap data values must be strings. When the {0}
passthrough fix preserves HelmValue type information, port parameters
render with '| int' pipe which produces a number in the ConfigMap
template. K8s rejects this with 'cannot unmarshal number into Go
struct field ConfigMap.data of type string'.
Fix: in ToConfigMap, replace '| int' (and other numeric pipes) with
'| toString' so the value stays a string in the ConfigMap context.
The '| int' pipe is still used correctly in Service/Deployment specs
where numeric values are expected.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Aspire Dashboard support to Kubernetes environment
- Create KubernetesAspireDashboardResource with HTTP (18888), OTLP gRPC (18889), and OTLP HTTP (18890) endpoints
- Create KubernetesAspireDashboardResourceBuilderExtensions with CreateDashboard, WithHostPort, and WithForwardedHeaders
- Add DashboardEnabled property and Dashboard resource to KubernetesEnvironmentResource (enabled by default)
- Add WithDashboard(bool) and WithDashboard(Action<>) extension methods to KubernetesEnvironmentExtensions
- Wire ConfigureOtlp in KubernetesInfrastructure: sets OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL, and OTEL_SERVICE_NAME on resources with OtlpExporterAnnotation
- Dashboard deployed in unsecured auth mode (DASHBOARD__FRONTEND__AUTHMODE=Unsecured, DASHBOARD__OTLP__AUTHMODE=Unsecured)
- Include dashboard resource in pipeline steps and configuration for build/deploy coordination
- Add KnownOtelConfigNames shared file to K8s project
- Add 6 new tests covering dashboard creation, OTLP configuration, dashboard disable, endpoint verification
- Update 13 snapshot files to include OTLP env vars in generated K8s manifests
- All 72 tests pass
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix CS0436 build error: remove redundant KnownOtelConfigNames shared file
KnownOtelConfigNames is already accessible from Aspire.Hosting via
InternalsVisibleTo. Including the shared file copy caused a type conflict
that was promoted to error in Release builds (TreatWarningsAsErrors).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove HTTPS service discovery variables in Kubernetes publishing
Containers in Kubernetes don't have TLS certificates - TLS termination
is handled externally by ingress controllers or service mesh. This mirrors
the Docker Compose behavior where RemoveHttpsServiceDiscoveryVariables
filters out services__*__https__* entries after environment callbacks run.
Without this fix, the Blazor webfrontend tries to connect to apiservice
via https://apiservice-service:8080 but apiservice only listens on HTTP,
causing connection failures.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Include dashboard resource in Kubernetes publish output
The KubernetesPublishingContext only iterated model.Resources, which
doesn't include the dashboard resource. This meant OTLP env vars
pointed to env-dashboard-service but no dashboard deployment/service
was generated. Mirror the Docker Compose pattern: prepend the dashboard
resource to the iteration when DashboardEnabled is true.
Updated all publisher test expectedFiles arrays to include dashboard
templates and regenerated snapshots.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Kubernetes deploy E2E tests (baseline, Redis, Postgres)
Add E2E CLI tests that exercise 'aspire deploy' to KinD clusters:
- KubernetesDeployTestHelpers: shared helpers for KinD setup, project
scaffolding, interactive deploy, and deployment verification
- DeployBasicApiService: baseline test with a plain API endpoint
- DeployWithRedis: Redis SET+GET verification via /test-deployment
- DeployWithPostgres: PostgreSQL SELECT 1 verification
Tests use Hex1b terminal automation to answer parameter prompts
interactively (registryendpoint, namespace, chartversion) and verify
deployments via port-forwarded /test-deployment endpoints.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add remaining K8s deploy E2E tests (RabbitMQ, MongoDB, MySQL, SQL Server, Garnet, Valkey, NATS)
Complete the K8s deploy E2E test suite with 7 additional resource tests:
- DeployWithRabbitMQ: queue declare+delete via RabbitMQ.Client
- DeployWithMongoDB: insert+find document via MongoDB.Driver
- DeployWithMySql: SELECT 1 via MySqlConnector
- DeployWithSqlServer: SELECT 1 via Microsoft.Data.SqlClient
- DeployWithGarnet: SET+GET via StackExchange.Redis (Redis-compatible)
- DeployWithValkey: SET+GET via StackExchange.Redis (Redis-compatible)
- DeployWithNats: connection state check via NATS.Client.Core
Total: 10 E2E tests covering the full aspire deploy → KinD workflow
with real /test-deployment endpoints performing actual operations.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove quarantine from K8s deploy E2E tests
These tests should run in normal CI, not be quarantined.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix KinD cluster creation for containerd v2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Split K8s deploy tests into one class per file for CI parallelization
Each test class becomes a separate CI job via SplitTestsOnCI=true.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use aspire new + aspire add for project scaffolding in E2E tests
Hardcoded SDK version 10.0.0-dev doesn't match the PR build's CLI.
Switch to aspire new (empty apphost) + aspire add for hosting packages
+ dotnet new web for ApiService, so SDK version is automatically correct.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use Starter template instead of EmptyAppHost for K8s deploy E2E tests
The EmptyAppHost template doesn't create a solution file, ApiService project, or
project references. Switch to the Starter template (no Redis) which provides the
full project structure (AppHost + ApiService + ServiceDefaults + solution) out of
the box. This eliminates the need for dotnet new web, dotnet sln add, and
dotnet add reference commands — we just aspire add hosting packages, add client
NuGet packages, and inject our custom code into the existing source files.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add pragma to suppress ASPIRECOMPUTE003 experimental diagnostic in K8s deploy tests
AddContainerRegistry is marked [Experimental('ASPIRECOMPUTE003')] and
TreatWarningsAsErrors promotes it to a build error. Add #pragma warning disable
at the top of the generated AppHost.cs code in all 10 test files.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Keep ASPIRE_PLAYGROUND set so deploy prompts for parameters
Unsetting ASPIRE_PLAYGROUND caused aspire deploy to run non-interactively,
skipping parameter prompts entirely. The pipeline then failed because
parameters like chartversion, namespace, and registryendpoint had no values.
Leave ASPIRE_PLAYGROUND set so the interactive prompts appear.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix parameter prompt order: match code declaration order not alphabetical
The deploy command prompts for parameters in the order they are declared
in AppHost.cs (registryendpoint, namespace, chartversion), not alphabetical.
The tests were waiting for chartversion first which never appeared, causing
a timeout. Reorder all 10 test files to match the actual prompt order.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add --prerelease flag to dotnet add package for client packages
In CI, the PR NuGet feed only has prerelease versions of Aspire client
packages (e.g. 13.3.0-pr.15723.*). Without --prerelease, dotnet add
package fails with NU1103 because it only looks for stable versions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix StatefulSet maxUnavailable: 0 bug, fix RabbitMQ async API, quarantine NATS
- Make MaxUnavailable nullable (int?) in RollingUpdateStatefulSetStrategyV1
so it's omitted from YAML when not set (Kubernetes rejects 0)
- Fix RabbitMQ test: CreateChannel -> CreateChannelAsync (7.x API change)
- Quarantine NATS K8s deploy test: NATS hosting passes ParameterResource
as container args which K8s publishing can't resolve (#15789)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Increase verification retries for database containers
Database containers (Postgres, MySQL, SQL Server) need 60-120s to fully
initialize in CI. Increase retry loop from 10x5s to 30x5s (150s total)
and add HTTP status code to curl output for diagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add pod logs and env diagnostics to verification step
Show server pod logs and connection-related environment variables
before attempting port-forward + curl. This will help diagnose
why Postgres/MySQL/SQL Server return HTTP 500.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use server-level references for Postgres/MySQL/SQL Server K8s tests
AddDatabase() databases are not created in K8s deploy mode because
the ResourceReadyEvent CREATE DATABASE callbacks only fire during
Aspire orchestration, not in deployed Helm charts (#15795).
Work around by referencing the server directly (uses default database)
instead of AddDatabase(). This still validates the full deploy pipeline.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix MySQL SELECT 1 type comparison (long vs int)
MySQL's ExecuteScalar returns a long, not int, for SELECT 1.
Use Convert.ToInt32 for all database tests to handle any numeric type.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename K8s test methods to DeployK8s* and add stabilization override
Rename all Kubernetes deploy E2E test methods from DeployXYZ to
DeployK8sXYZ to make them obviously Kubernetes-related in CI.
Add /p:StabilizePackageVersion=false to Dockerfile.e2e Stage 1
build to ensure consistent -dev suffixed packages regardless of
whether the branch is stabilized for release.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix ASPIREEXPORT011: remove redundant export IDs
The convention-derived names match the explicit IDs, so remove
the redundant first argument from AspireExport attributes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add retry logic for KinD/Helm downloads to handle transient CDN failures
The SQL Server test failed because GitHub CDN returned HTML instead of the
KinD binary. Added a retry loop (3 attempts with ELF validation) for both
KinD and Helm downloads.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add version assertions to K8s deploy E2E tests
Assert CLI version has prerelease suffix (aspire --version) and
template version has prerelease suffix (Using project templates version:)
in every K8s deploy test. Runs in both CI and local environments.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address Kubernetes review feedback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Switch KubernetesDeploy E2E tests to Docker container terminal
The tests now run inside the Dockerfile.e2e container where the
CLI is pre-built from source (x.y.z-dev), instead of installing
from the PR shell script at runtime.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add kubectl install to KubernetesDeployTestHelpers
The Docker container from Dockerfile.e2e does not include kubectl.
The bare runner had it pre-installed, but inside the container it
was missing, causing 'kubectl: command not found' at the ConfigMap
apply step.
Download kubectl alongside kind and helm in InstallKindAndHelmAsync
so the helper is self-contained regardless of environment.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix kubectl networking inside Docker container
When running inside a Docker container with socket forwarding,
KinD's kubeconfig uses 127.0.0.1:<port> (host loopback) which
is unreachable from inside the container. After cluster creation,
detect the Docker environment (/.dockerenv), join the 'kind'
network, and switch to the internal kubeconfig which uses Docker
DNS names instead of localhost.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Pre-install K8s tools in Dockerfile.e2e and fix API server networking
Two fixes for KubernetesDeploy E2E tests running in Docker containers:
1. Pre-install kind, helm, and kubectl in Dockerfile.e2e so they don't
need to be downloaded at test runtime (~2-3 min saved per test).
InstallKindAndHelmAsync now skips downloads when tools are on PATH.
2. After kind create cluster, detect Docker environment (/.dockerenv)
and switch to KinD's internal kubeconfig + join the 'kind' network
so kubectl can reach the API server via Docker DNS instead of the
host's 127.0.0.1 loopback (unreachable from inside a container).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert KubernetesDeploy tests to bare-terminal approach
The Docker container approach (CreateDockerTestTerminal) doesn't
work for K8s deploy tests because aspire deploy uses docker buildx
with a docker-container driver, which can't access the test
container's filesystem for build context. Revert to CreateTestTerminal
which runs directly on the GitHub Actions runner.
Keep the kubectl install improvement in InstallKindAndHelmAsync
(skips download if already on PATH via command -v).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Trigger CI rebuild
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback and add deployment instructions
- Fix XML doc: move <example> out of <remarks> to top-level sibling
- Fix GetSteps lookup: print-summary steps are owned by the
environment resource, not the deployment target
- Add post-deploy instructions step with dashboard port-forward
command, helm status/get-all, and helm uninstall commands
- Revert KubernetesDeploy tests to bare-terminal (Docker container
approach incompatible with docker buildx image builds)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Default Helm release name to deployment environment name
The release name now defaults to IHostEnvironment.EnvironmentName
(from aspire deploy -e <name>) instead of the resource name from
AddKubernetesEnvironment(). This matches user expectations since
the -e flag is the primary way to differentiate deployments.
Also refactored namespace/release resolution into shared helpers
to eliminate duplication across HelmDeploy, PrintInstructions,
and HelmUninstall methods.
Improved summary presentation: split instructions into separate
concise key-value entries matching the established pattern used
by ACA and Docker Compose (emoji keys, inline markdown values).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback: fix deploy issues and wire WithHostPort
- Fix service name mismatch in PrintResourceSummaryAsync: use
ToServiceName() so kubectl queries match the deployed service name
- Throw on helm deploy failure so the pipeline step fails and
dependent print-summary steps do not run
- Use MatchEvaluator in Regex.Replace for Chart.yaml version to
prevent regex backreference corruption from parameter-backed values
- Validate resolved release name and namespace at deploy time using
DNS label rules, catching invalid environment names early
- Add ServicePort to EndpointMapping so WithHostPort flows the
exposed port into the Kubernetes Service manifest Port field
while keeping TargetPort as the container port
- Add missing ArgumentNullException.ThrowIfNull in WithHostPort
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Trigger CI rebuild
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename WithHostPort to WithServicePort and add WithOtlpServicePort
WithHostPort implied Docker-style host port mapping, which doesn't
apply in Kubernetes where Services are cluster-internal by default.
- Rename WithHostPort → WithServicePort with docs explaining it
sets the Kubernetes Service port (useful behind ingress)
- Add WithOtlpServicePort(grpcPort, httpPort) for customizing the
OTLP endpoint Service ports (e.g. standard 4317/4318)
- Re-add ServicePort plumbing in EndpointMapping so the APIs
flow ExposedPort into the Service manifest Port field while
keeping TargetPort as the container port
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove compat suppression file (fix is now in main)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Replace KubernetesDeploymentEngineAnnotation with delegate property
Address Eric's review feedback: eliminate the custom annotation type
and align with how Docker Compose handles deployment engines.
- Delete KubernetesDeploymentEngineAnnotation — replaced by a
DeploymentEngineStepsFactory delegate property on
KubernetesEnvironmentResource
- Move per-resource print-summary steps to KubernetesResource
deployment targets via PipelineStepAnnotation (matching the
DockerComposeServiceResource pattern)
- Merge the two PipelineStepAnnotations on the environment resource
into a single one that handles publish + engine + deployment target
step expansion (matching DockerComposeEnvironmentResource pattern)
- WithHelm() and EnsureDefaultHelmEngine() now set the delegate
property instead of adding/replacing an annotation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix print-summary step dependency wiring after move to deployment targets
GetSteps was looking for print-summary steps on the environment
resource (this), but they now live on the deployment target
(KubernetesResource). Updated to match the Docker Compose pattern
which uses context.GetSteps(deploymentTarget, 'print-summary').
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add TypeScript AppHost support for K8s deploy and enhance ATS analyzer
- Add [AspireExport(ExposeMethods = true)] to HelmChartOptions so
WithNamespace, WithReleaseName, WithChartVersion are visible from
TypeScript callbacks in withHelm()
- Add ASPIREEXPORT012 analyzer rule that warns when callback parameter
types (Action<T>) in [AspireExport] methods lack [AspireExport] on
the context type, scoped to same-assembly types only
- Add [AspireExport(ExposeProperties = true)] to ResourceUrlAnnotation
(pre-existing gap caught by the new analyzer rule)
- Add KubernetesDeployTypeScriptTests E2E test that creates a project
from the Express/React starter template, adds Aspire.Hosting.Kubernetes,
modifies apphost.ts with addKubernetesEnvironment + withHelm callback,
and deploys to a KinD cluster
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix analyzer and build issues from CI failures
- ASPIREEXPORT012: Skip callback context types with no public
instance methods/properties (fixes false positive on empty
TestContext in analyzer unit tests)
- ASPIREEXPORT012: Scope to same-assembly types only (fixes
false positives on external ContainerApp/ContainerAppJob types)
- Replace Semver NuGet package with GeneratedRegex for chart
version validation (package reference was lost during rebase)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix CI codegen failures and use SmolSemVer for chart validation
- Revert [AspireExport] on ResourceUrlAnnotation (caused snapshot
mismatches across all codegen tests — out of scope for this PR)
- ASPIREEXPORT012: Also skip types with [AspireDto] (they're already
exported for serialization, not callback contexts)
- Use shared SmolSemVer (SemVersion.TryParse) instead of regex for
chart version validation — the type is already available from
Aspire.Hosting via project reference
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Restore CompatibilitySuppressions.xml from main (needed for package validation)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix TypeScript K8s deploy test: use bare terminal instead of Docker
The test was running inside a Docker container, but KinD creates
clusters on the host via the mounted Docker socket. This caused
kubectl to fail connecting to the API server (localhost inside the
container != localhost on the host). Switched to CreateTestTerminal()
matching the pattern used by all other K8s deploy E2E tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix TypeScript K8s test: register parameters before withHelm callback
Parameters must be created with addParameter() before being passed
into the withHelm callback. Also use separate await for
addKubernetesEnvironment and withHelm since the fluent chain
crosses async boundaries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Mitch Denny <mitch@mitchdeny.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Implements end-to-end
aspire deploysupport for Kubernetes environments using an annotation-driven deployment engine architecture with Helm as the default engine.What's new
Annotation-driven deployment engine:
KubernetesDeploymentEngineAnnotation— pluggable callback for creating deployment pipeline stepsKubernetesNamespaceAnnotation,HelmReleaseNameAnnotation,HelmChartVersionAnnotation) usingReferenceExpressionfor both literal and parameter-backed valuesHelm deployment engine:
HelmDeploymentEnginecreates 4 pipeline steps:prepare-{name}— resolves chart values and updates Chart.yaml versionhelm-deploy-{name}— runshelm upgrade --installwith namespace/release configprint-{resource}-summary— retrieves service endpoints via kubectlhelm-uninstall-{name}— teardown stepBuilder API:
Container registry wiring:
KubernetesInfrastructurenow resolves container registries for image push supportPipeline step ordering
Design decisions
helm upgrade --installfor idempotent deploymentsAddKubernetesEnvironment)WithAnnotation(..., Replace)prevents annotation stacking on repeatedWithHelmcallsKubernetesDeploymentEngineAnnotationextension pointTesting
KubernetesDeployTests.csMicrosoft Reviewers: Open in CodeFlow