Add CommunityToolkit.Aspire.Hosting.Kind integration#1270
Add CommunityToolkit.Aspire.Hosting.Kind integration#1270andrey-noskov wants to merge 19 commits intoCommunityToolkit:mainfrom
Conversation
Add CommunityToolkit.Aspire.Hosting.Kind for managing Kind clusters as Aspire resources with dashboard integration, health checks, and WaitFor support. Public API: - AddKindCluster() - creates a Kind cluster resource - WithKubernetesVersion() - configures the K8s version - WithWorkerNodes() - configures additional worker nodes - WithReference() - injects kubeconfig into dependent resources - WithKindNetwork() - connects containers to Kind's Docker network Includes unit tests, integration test with [RequiresKind] attribute, and example AppHost. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit b93022434b0572d0dd14722558baaae6d8ba8af0)
Create a public static KindContainerImageTags class with KindNodeImageRepository and DefaultKubernetesVersion constants. Update KindConfigGenerator to reference the new constant. Add tests to verify the constant values. Co-authored-by: andrey-noskov <25082814+andrey-noskov@users.noreply.github.com> Agent-Logs-Url: https://github.com/andrey-noskov/aspire-kind/sessions/08ed515c-3d80-4aa1-8963-69b98fa0ff72 (cherry picked from commit 6b2325ba4343cd801e6f86ef0de61c5bc34e6295)
Co-authored-by: andrey-noskov <25082814+andrey-noskov@users.noreply.github.com> Agent-Logs-Url: https://github.com/andrey-noskov/aspire-kind/sessions/2f66ff2b-e50f-4afd-8f38-9d46998ea4b4 (cherry picked from commit 62fcd44c282f4fa6ef32dd22644b6fdd5ddf27ff)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit fcbc16381753cf3e7d63b5f6927e412aa335fa71)
Co-authored-by: andrey-noskov <25082814+andrey-noskov@users.noreply.github.com> Agent-Logs-Url: https://github.com/andrey-noskov/aspire-kind/sessions/1a2ac257-4765-4e55-b0ab-1e66a90b76a0 (cherry picked from commit 1359dcf7c94749d9180bda31f3c79da8ab0fa17c)
- Add ClusterLifetime enum (Session/Persistent) and ClusterLifetimeAnnotation - Register IHostApplicationLifetime.ApplicationStopping cleanup for Session lifetime - Delete Kind cluster and kubeconfig files on AppHost shutdown - Default to Session (ephemeral) lifetime; Persistent reuses existing clusters Closes microsoft/aspire-kind#1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit eb42868f7e8c26177f8aac5b3fcf56a715a032a5)
Verify that the Kind CLI is installed and on PATH at builder time (inside AddKindCluster) before any cluster operations are attempted. If missing, throw InvalidOperationException with install URL. Add synchronous ProcessHelper.Run overload for use in sync builder methods without requiring sync-over-async. Closes CommunityToolkit#10 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit c6fa4cca03522b1242744de9f9416d9a6b696b03)
…Toolkit#51) Makes GenerateConfig() async: - Renamed to GenerateConfigAsync with CancellationToken parameter - Uses File.WriteAllTextAsync instead of synchronous File.WriteAllText - Updated KindClusterManager caller to await the async method - Updated test to match the new async signature Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit d06e2adcb8d0311a0567c401385e8724b1a3469d)
…mmunityToolkit#52) Revives CommunityToolkit#35 Expands test coverage from 12 to 28 tests: New test file - KindPublicApiTests.cs (8 tests): Null argument validation for every public method. New tests in AddKindClusterTests.cs (8 tests): - KindConfigGenerator: YAML output for 0/3 workers, with/without K8s version - ProcessHelper.Run: stdout capture, non-zero exit codes - Edge cases: zero workers valid, kubeconfig path correctness Adapted to use async GenerateConfigAsync API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit ba3b5d7814442147de79bbb84dd8ce47aa68f1d4)
…ommunityToolkit#53) Revives CommunityToolkit#34 Adds README.md for the Kind hosting integration with: - Prerequisites (Docker, Kind CLI) - Basic usage (AddKindCluster) - All builder extensions (WithWorkerNodes, WithKubernetesVersion, WithClusterLifetime, WithKindNetwork) - WithReference environment variables (KUBECONFIG, K8S_CLUSTER_NAME) - Full end-to-end example Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 9acf4fbf5728b9bd0bccc84ecccbdc0438b051c9)
CommunityToolkit#39) Replace manual string concatenation with typed model classes and YamlDotNet serializer for Kind cluster config generation. This improves type safety, null handling, and extensibility for future features like port mappings and extra mounts. Fixes microsoft/aspire-kind#38 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit e0a5239c8083d76fd76ac128dacfda867908b8eb)
…ommunityToolkit#54) Adds first-class Helm chart deployment as child resources of KindClusterResource, following Aspire's parent-child pattern. New files: - KindDeployedResource: abstract base for deployed resources - KindHelmChartResource: Helm chart resource with values/version/namespace - HelmManager: CLI wrapper (helm upgrade --install --wait) - KindHelmChartResourceBuilderExtensions: AddHelmChart, WithChartVersion, WithHelmValue, WithHelmValuesFile, WithNamespace Helm releases appear in the Aspire dashboard with lifecycle states (NotStarted -> Starting -> Running / FailedToStart) and support WaitFor() dependency ordering. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 315c4adf18d32491bd4b59983631272a0ba32255)
Use RuntimeInformation.IsOSPlatform to select cmd /c on Windows and sh -c on Linux/Mac for ProcessHelper_Run_CapturesStdout and ProcessHelper_Run_InvalidCommand_NonZeroExitCode tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 55fe43d9cfc92143e09c00e7af66931345528693)
…kit#55) Replace helm --wait with an IHealthCheck that polls the Kubernetes API directly using the C# Kubernetes client. This surfaces real-time workload readiness in the Aspire dashboard instead of blocking on Helm. Changes: - Add KubernetesWorkloadHealthCheck (IHealthCheck polling K8s API) - Add KubernetesWorkloadStatusClient (queries Deployments/StatefulSets) - Add KubernetesObjectStatus/KubernetesWorkloadStatus model records - Add WorkloadReadiness with per-kind readiness evaluation - Remove --wait from helm install arguments in HelmManager - Wait for parent Kind cluster before starting helm install - Show 'Waiting' state while cluster initializes - Add KubernetesClient 19.0.2 dependency - Add 63 unit tests covering readiness logic and status projection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 03a9eccc4e861ce6c9627612de3a9639df747d8c)
) ## Summary Replaces the static `ProcessHelper` class with an `IProcessRunner` interface resolved from DI, making all process execution testable without shelling out to real CLI tools. ## Changes The only change that isn't simple plumbing is the removal of the sync codepath. The only consumer was the kind CLI check. I instead moved that call to OnInitializeResource so it could be async, which I think is a better fit for the Aspire model, as we'd then fail to initialize the resource rather than throwing when creating the builder. ## Motivation Follows the pattern from Aspire.Hosting.Azure (IProcessRunner / MockProcessRunner). Enables the compute environment branch to inject FakeProcessRunner in tests to verify pipeline step behavior (image loading, helm install) without requiring Kind/Helm/Docker in CI. ## Testing - 51/51 tests pass - ProcessHelper.Run tests rewritten as DefaultProcessRunner async tests (cherry picked from commit 0937e7e6b1beff962f278e79962cb8ece8deff31)
Add WithKindConfig(Action<KindConfigModel>) extension method using the annotation composition pattern from the main Aspire repo. Multiple calls compose in order during config generation. Expand internal config models to cover the full Kind v1alpha4 spec and make them public: KindConfigModel, KindNodeModel, KindNetworkingModel, KindMountModel, KindPortMappingModel. Rewrite WithKubernetesVersion and WithWorkerNodes as thin wrappers. WithKubernetesVersion uses a dedicated KubernetesVersionAnnotation applied after all config callbacks, so version is set on every node regardless of call order. WithWorkerNodes delegates to WithKindConfig. Remove WorkerNodes and KubernetesVersion properties from KindClusterResource — all config flows through annotations. Closes microsoft/aspire-kind#6 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 2226ef0afc3e3644fcb9160d60f4f748221b9926)
…ire deploy (CommunityToolkit#73) ## Summary Adds `.WithKind()` extension method on the existing `AddKubernetesEnvironment("k8s")`. This makes Kind a deployment target for `aspire publish` (generates Helm charts) and `aspire deploy` (creates a Kind cluster, builds project images, loads them into Kind, and installs the Helm chart). This is separate from the existing `AddKindCluster()` which remains unchanged and serves a different use case (managed cluster dependency visible in the dashboard during F5). ## User scenarios ### Scenario 1: K8s developer (existing, unchanged) ```csharp // Kind cluster as a managed dependency - shows in dashboard, injects KUBECONFIG builder.AddKindCluster("dev-cluster") .WithKubernetesVersion("v1.32.2"); builder.AddProject<Projects.MyOperator>("operator") .WithReference(kind); ``` ### Scenario 2: K8s consumer (new) ```csharp // Kind as a compute environment for publish/deploy builder.AddKubernetesEnvironment("k8s") .WithKind() .WithKubernetesVersion("v1.32.2") .WithWorkerNodes(1); builder.AddContainer("redis", "redis", "7"); builder.AddProject<Projects.MyApi>("api"); ``` Then from the CLI: ```bash aspire publish --output-path ./charts # generates Helm chart aspire deploy # creates cluster + deploys everything ``` ## What happens during aspire deploy 1. **publish-k8s** - Generates Helm chart (Deployments, Services, ConfigMaps) via Aspire.Hosting.Kubernetes 2. **kind-create-cluster** - Creates the Kind cluster (reuses existing if persistent) 3. **build** - Builds container images for project resources (dotnet publish /t:PublishContainer) 4. **kind-load-images** - Loads all images into Kind (kind load docker-image) 5. **kind-helm-install** - Deploys the Helm chart (helm install) ## Design decisions - **Two independent entry points**: AddKindCluster (F5) and AddKubernetesEnvironment().WithKind() (publish/deploy) serve different personas and don't overlap - **Composes with Aspire.Hosting.Kubernetes**: Delegates manifest generation to the first-party K8s package via its public API. Kind adds deploy-specific pipeline steps - **IKindResource interface**: Shared fluent methods (WithKubernetesVersion, WithWorkerNodes, WithClusterLifetime) work on both resource types - **KindEnvironmentResource surrogate**: Links to KubernetesEnvironmentResource via IResourceWithParent, following the Aspire pattern for surrogate resources - **Invisible in F5**: WithKind() uses CreateResourceBuilder in run mode so the Kind environment doesn't appear in the dashboard - **IProcessRunner seam**: Internal interface for process execution, following the pattern from Aspire.Hosting.Azure. Enables testing pipeline steps without shelling out to real CLI tools - **Image name resolution**: Uses ContainerBuildOptionsCallbackAnnotation for project resources, respecting user customization via WithContainerBuildOptions ## Dependencies - Adds Aspire.Hosting.Kubernetes package reference - Adds Verify.XunitV3 for snapshot testing - Adds `verify.tool` to dotnet-tools.json ## E2E verified Both container and project resources deploy successfully to Kind: - AddContainer("nginx", "nginx", "latest") → pulled from registry → pod Running - AddProject<Projects.HelloApi>("api") → dotnet publish /t:PublishContainer → kind load → pod Running → HTTP 200 (cherry picked from commit 67e7e47c97838df24702dff3158c3e0a6754b3dc)
… Kind networking
- Replace regex-based kubeconfig rewriting with YamlDotNet/k8s YAML parser
in KindContainerHelper (renamed from KindContainerKubeconfigRewriter)
- Rework WithKindNetwork to handle both container lifecycle paths:
- ResourceReadyEvent: connects running containers to Kind network
- ResourceStoppedEvent: connects + restarts crashed containers via
ResourceCommandService instead of raw docker start
- Assign predictable container names (EnsureContainerName) so the
docker network connect handler can identify the container
- Handle docker network connect race condition by treating 'already
exists in network' as success
- Split extension methods by resource type:
- KindContainerExtensions: WithReference(Container) + WithKindNetwork
- KindClusterResourceBuilderExtensions: AddKindCluster, config, WithReference<T>
- Add networking model section to README with connectivity matrix
- Update example AppHost with Headlamp and netshoot containers
- Add KindContainerHelperTests for kubeconfig rewriting
- Add Kind-focused solution file and launch profiles
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit c049ea7201015561551e826b9e366f3ef45a07fd)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.sh | bash -s -- 1270Or
iex "& { $(irm https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.ps1) } 1270" |
|
Contributors on this PR: @andrey-noskov @MattKotsenas |
|
@dotnet-policy-service agree company="Microsoft" |
There was a problem hiding this comment.
Pull request overview
Adds a new CommunityToolkit.Aspire.Hosting.Kind hosting integration to manage local Kind (Kubernetes in Docker) clusters, including both (1) an F5 “managed dependency” Kind cluster resource and (2) a KubernetesEnvironmentResource.WithKind() compute-environment path for aspire publish/deploy, with accompanying examples, tests, and repo wiring.
Changes:
- Introduces Kind resources, extension methods, lifecycle hook, Helm chart resource support, and deploy pipeline steps.
- Adds a comprehensive test suite (unit + publish snapshot-style tests + optional integration test gated on Docker+Kind).
- Wires the new integration into repo docs/solutions/workflow and adds supporting package versions/tooling.
Reviewed changes
Copilot reviewed 66 out of 66 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/WithKindTests.cs | Tests WithKind() behavior on KubernetesEnvironmentResource, chaining, parenting, and run-mode visibility. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/TestModuleInitializer.cs | Configures Verify snapshot output location for the Kind test project. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/Snapshots/KindPublishIntegrationTests.PublishWithServiceCustomizationAppliesType#01.verified.yaml | Verified publish output snapshot (deployment). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/Snapshots/KindPublishIntegrationTests.PublishWithServiceCustomizationAppliesType#00.verified.yaml | Verified publish output snapshot (service). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/Snapshots/KindPublishIntegrationTests.PublishMultipleContainersProducesAllDeployments#02.verified.yaml | Verified publish output snapshot (service). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/Snapshots/KindPublishIntegrationTests.PublishMultipleContainersProducesAllDeployments#01.verified.yaml | Verified publish output snapshot (deployment). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/Snapshots/KindPublishIntegrationTests.PublishMultipleContainersProducesAllDeployments#00.verified.yaml | Verified publish output snapshot (values stub). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/Snapshots/KindPublishIntegrationTests.PublishContainerResourceProducesHelmChart#02.verified.yaml | Verified publish output snapshot (deployment). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/Snapshots/KindPublishIntegrationTests.PublishContainerResourceProducesHelmChart#01.verified.yaml | Verified publish output snapshot (values stub). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/Snapshots/KindPublishIntegrationTests.PublishContainerResourceProducesHelmChart#00.verified.yaml | Verified publish output snapshot (Chart.yaml). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/RequiresKindAttribute.cs | Adds a test trait attribute to gate tests on Docker + Kind availability. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/PublishTestBuilder.cs | Helper for constructing publish-mode test builders via pipeline config keys. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KubernetesWorkloadStatusTests.cs | Unit tests for readiness evaluation logic. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KubernetesWorkloadStatusClientTests.cs | Unit tests for translating Kubernetes client objects into readiness status. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KindPublishIntegrationTests.cs | Publish pipeline integration tests that verify generated Helm chart artifacts via Verify. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KindPublicApiTests.cs | Null-check/guardrail tests for public API surface. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KindPipelineStepTests.cs | Tests pipeline step creation, naming, and dependencies for Kind deploy steps. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KindImageLoadingTests.cs | Tests image preload behavior using a fake process runner and build options annotations. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KindHelmChartTests.cs | Tests Helm chart resource creation/configuration and Helm argument generation. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KindContainerHelperTests.cs | Tests kubeconfig rewriting for container-to-kind-network access. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/KindContainerExtensionsTests.cs | Tests container WithReference(kind) behavior (mount/env/network integration). |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/FakeProcessRunner.cs | Test double for recording CLI invocations. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/CommunityToolkit.Aspire.Hosting.Kind.Tests.csproj | Adds Verify.XunitV3 and references src/examples/testing projects. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/AppHostTests.cs | Docker+Kind-gated integration test verifying cluster health. |
| tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/AddKindClusterTests.cs | Comprehensive unit tests for cluster resource defaults, config generation, lifecycle hook registration, etc. |
| src/CommunityToolkit.Aspire.Hosting.Kind/README.md | End-user documentation for both “managed dependency” and “compute environment” scenarios, plus networking model. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KubernetesWorkloadStatusClient.cs | Queries deployments/statefulsets via Kubernetes client and projects replica counts. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KubernetesWorkloadStatus.cs | Defines status records and readiness evaluation extension method. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KubernetesWorkloadHealthCheck.cs | Health check that evaluates workload readiness by querying Kubernetes. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KubernetesEnvironmentKindExtensions.cs | Public WithKind() extension for KubernetesEnvironmentResource and pipeline step wiring. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindNetworkConnectedAnnotation.cs | Annotation used to record whether a container has been connected to the Kind network. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindInfrastructureExtensions.cs | Registers shared Kind infrastructure (e.g., IProcessRunner) in DI. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindHelmChartResourceBuilderExtensions.cs | Public API for adding/configuring Helm chart resources + runtime install logic. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindHelmChartResource.cs | Public resource model for a Helm chart deployed to a Kind cluster. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindHealthCheck.cs | Health check for verifying the Kind control-plane container is running. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindEnvironmentResource.cs | Public surrogate resource for Kind-backed KubernetesEnvironmentResource compute env. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindDeployPipelineSteps.cs | Implements Kind deploy pipeline steps: create cluster, load images, helm install. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindDeployedResource.cs | Base resource type for Kind-deployed resources (e.g., Helm chart). |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindContainerHelper.cs | Rewrites kubeconfig for container connectivity and writes container kubeconfig file. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindContainerExtensions.cs | Public container extensions for Kind referencing and Kind network connectivity workaround. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindConfigModels.cs | Public models representing Kind v1alpha4 config document structure. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindConfigGenerator.cs | Generates Kind config YAML from annotations and defaults. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindConfigAnnotation.cs | Defines internal resource annotations driving config generation (version/workers/custom config). |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindClusterResourceBuilderExtensions.cs | Public API to add/configure Kind clusters and reference injection for non-containers. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindClusterResource.cs | Public resource for a managed Kind cluster (paths, wait support). |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindClusterManager.cs | CLI orchestration for create/delete/export/status checks of Kind clusters. |
| src/CommunityToolkit.Aspire.Hosting.Kind/KindClusterLifecycleHook.cs | Shutdown cleanup logic for session-lifetime clusters. |
| src/CommunityToolkit.Aspire.Hosting.Kind/IProcessRunner.cs | Abstraction + result type for running external processes (real + fake). |
| src/CommunityToolkit.Aspire.Hosting.Kind/IKindResource.cs | Public interface representing a resource targeting/managing a Kind cluster. |
| src/CommunityToolkit.Aspire.Hosting.Kind/HelmManager.cs | CLI orchestration for installing/upgrading Helm releases. |
| src/CommunityToolkit.Aspire.Hosting.Kind/DefaultProcessRunner.cs | Default process runner implementation capturing stdout/stderr asynchronously. |
| src/CommunityToolkit.Aspire.Hosting.Kind/CommunityToolkit.Aspire.Hosting.Kind.csproj | New integration project with required package references and InternalsVisibleTo for tests. |
| src/CommunityToolkit.Aspire.Hosting.Kind/ClusterLifetimeAnnotation.cs | Internal annotation for cluster lifetime selection. |
| src/CommunityToolkit.Aspire.Hosting.Kind/ClusterLifetime.cs | Public enum describing session vs persistent cluster behavior. |
| README.md | Adds Kind integration entry and links to NuGet/docs. |
| examples/kind/CommunityToolkit.Aspire.Hosting.Kind.Publish.AppHost/Properties/launchSettings.json | Launch settings for publish/deploy example AppHost. |
| examples/kind/CommunityToolkit.Aspire.Hosting.Kind.Publish.AppHost/Program.cs | Example showing Kind as compute environment for aspire publish/deploy. |
| examples/kind/CommunityToolkit.Aspire.Hosting.Kind.Publish.AppHost/CommunityToolkit.Aspire.Hosting.Kind.Publish.AppHost.csproj | Publish/deploy example AppHost project. |
| examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost/Properties/launchSettings.json | Launch settings for F5 Kind cluster example AppHost. |
| examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost/Program.cs | Example showing Kind cluster as managed dependency + Helm chart + container connectivity. |
| examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost/CommunityToolkit.Aspire.Hosting.Kind.AppHost.csproj | F5 example AppHost project. |
| dotnet-tools.json | Adds .NET tool manifest (verify.tool). |
| Directory.Packages.props | Adds package versions for Aspire.Hosting.Kubernetes, KubernetesClient, Verify.XunitV3. |
| CommunityToolkit.Aspire.slnx | Adds Kind src/tests and Kind example AppHost to the main solution. |
| CommunityToolkit.Aspire.Hosting.Kind.slnx | Adds a focused solution for the Kind integration (src/tests/examples). |
| .github/workflows/tests.yaml | Adds Kind test project to CI test matrix. |
| process.Start(); | ||
| process.BeginOutputReadLine(); | ||
| process.BeginErrorReadLine(); | ||
|
|
||
| try | ||
| { | ||
| await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); | ||
| } | ||
| catch | ||
| { | ||
| try | ||
| { | ||
| process.Kill(entireProcessTree: true); | ||
| } | ||
| catch (InvalidOperationException) | ||
| { | ||
| // Process already exited. | ||
| } | ||
|
|
||
| throw; | ||
| } | ||
|
|
||
| return new ProcessResult(process.ExitCode, stdout.ToString().TrimEnd(), stderr.ToString().TrimEnd()); | ||
| } |
There was a problem hiding this comment.
DefaultProcessRunner awaits process.WaitForExitAsync(...) but never calls process.WaitForExit() afterwards. When using async stdout/stderr reads (BeginOutputReadLine/BeginErrorReadLine), this can return before all OutputDataReceived/ErrorDataReceived events are drained, leading to truncated output/error text and flaky diagnostics. Consider calling process.WaitForExit() immediately after the await (same pattern used in tests/CommunityToolkit.Aspire.Testing/ProcessTestUtilities.cs).
There was a problem hiding this comment.
I don't think that's true anymore? I'm the one that accidentally caused that bug, let me check...
There was a problem hiding this comment.
Yeah, fixed back in 2020: dotnet/runtime#42585
| using var process = Process.Start(psi); | ||
| if (process is null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| process.WaitForExit(TimeSpan.FromSeconds(5)); | ||
| return process.ExitCode == 0; | ||
| } |
There was a problem hiding this comment.
CheckKindAvailable uses process.WaitForExit(TimeSpan.FromSeconds(5)) but ignores the returned boolean; if the process doesn’t exit in time, accessing process.ExitCode can throw and (more importantly) the kind process may be left running after disposal. Handle the timeout explicitly (e.g., if WaitForExit returns false then try Kill(entireProcessTree: true) and return false).
There was a problem hiding this comment.
We actually shouldn't be using Process.Start directly at all here, we should be using our IProcessRunner.
| private async Task ExportKubeconfigAsync(CancellationToken cancellationToken) | ||
| { | ||
| var result = await _processRunner.RunAsync( | ||
| _logger, | ||
| "kind", | ||
| [ | ||
| "export", | ||
| "kubeconfig", | ||
| $"--name={_resource.Name}", | ||
| $"--kubeconfig={_resource.KubeconfigPath}", | ||
| ], | ||
| cancellationToken: cancellationToken).ConfigureAwait(false); |
There was a problem hiding this comment.
When reusing an already-running cluster, ExportKubeconfigAsync writes to _resource.KubeconfigPath but doesn’t ensure the parent directory exists. If a persistent Kind cluster already exists from a previous session/machine state, this can fail on first run. Consider creating Path.GetDirectoryName(_resource.KubeconfigPath) before invoking kind export kubeconfig.
|
@aaronpowell @davidfowl I'd appreciate your review and your perspective on whether this is the right shape for a CommunityToolkit Kind integration, especially around the local Kubernetes and publish/deploy workflow story. |
|
@aaronpowell would appreciate your help promoting this |
| "cluster", | ||
| $"--name={_resource.Name}", | ||
| $"--config={configPath}", | ||
| $"--kubeconfig={_resource.KubeconfigPath}", |
There was a problem hiding this comment.
Do we need quoting here (and similar places) for paths with spaces?
| catch (Win32Exception) | ||
| { | ||
| throw new InvalidOperationException( | ||
| "Kind CLI not found. Install it from https://kind.sigs.k8s.io/docs/user/quick-start/#installation"); |
There was a problem hiding this comment.
Can / should we provide the Win32Exception as the inner exception in case it has more details?
| /// <summary> | ||
| /// Gets the list of nodes in the cluster. | ||
| /// </summary> | ||
| public List<KindNodeModel> Nodes { get; } = []; |
There was a problem hiding this comment.
While old, it's probably still a good idea to not use concrete collection types in public APIs -- https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/guidelines-for-collections
Also applies to Dictionary down below.
| /// Connects the container to the "kind" Docker network if not already connected. | ||
| /// </summary> | ||
| /// <returns><see langword="true"/> if the connection was made (or was already present); <see langword="false"/> on failure.</returns> | ||
| private static async Task<bool> ConnectToKindNetworkAsync( |
There was a problem hiding this comment.
nit: Might make more sense to name this something like EnsureConnectedToKindNetworkAsync
|
|
||
| var connectResult = await processRunner.RunAsync( | ||
| logger, | ||
| "docker", |
There was a problem hiding this comment.
Flagging for discussion what we should do about podman support? Generally, Aspire supports both. I think if someone is running podman in root mode, everything here should work OK? Rootless would be a different story though.
| - Sets `K8S_CLUSTER_NAME` to the Kind cluster name | ||
| - Connects the container to the Kind Docker network | ||
|
|
||
| > **Note:** The kubeconfig mounted into containers uses the Kind control-plane container name (e.g., `mycluster-control-plane:6443`) instead of `127.0.0.1`, enabling container-to-container communication over the Kind Docker network. |
| .WithKindNetwork(); | ||
| ``` | ||
|
|
||
| > **Note:** `WithKindNetwork` is available on `IResourceBuilder<ContainerResource>`. It connects the container to the `kind` Docker network automatically when the container starts. |
| } | ||
| } | ||
|
|
||
| // ── DefaultProcessRunner tests ────────────────────────────────────── |
| <PropertyGroup> | ||
| <IsPackable>false</IsPackable> | ||
| <IsTestProject>true</IsTestProject> | ||
| <!-- Verify.XunitV3 brings in newer xunit.v3.common/extensibility.core transitively. |
There was a problem hiding this comment.
Flagging this for discussion. I think the "right" thing to do would be to bump the Verify version, but that would require bumping the xunit version for the repo
| @@ -0,0 +1,12 @@ | |||
| <Solution> | |||
There was a problem hiding this comment.
I think this is in the wrong directory
What this PR is about
This PR adds
CommunityToolkit.Aspire.Hosting.Kind, a hosting integration for running real local Kubernetes clusters with Kind (Kubernetes in Docker) as part of an Aspire app host.The goal is to make local development and validation much closer to a real Kubernetes environment. This is useful both for Kubernetes-specific components such as operators, plugins, controllers, and cluster-level services, and for regular application workloads that need to be exercised in a Kubernetes environment before they reach shared or staging clusters.
What is included
AddKindCluster(...)Why this matters
Today, teams typically either manage Kind clusters manually outside Aspire or wait to validate Kubernetes behavior in a shared or remote environment. This integration keeps that workflow inside Aspire's resource model and shortens the feedback loop for local Kubernetes-oriented development.
Context