Skip to content

Add CommunityToolkit.Aspire.Hosting.Kind integration#1270

Open
andrey-noskov wants to merge 19 commits intoCommunityToolkit:mainfrom
andrey-noskov:submission/kind-hosting-history
Open

Add CommunityToolkit.Aspire.Hosting.Kind integration#1270
andrey-noskov wants to merge 19 commits intoCommunityToolkit:mainfrom
andrey-noskov:submission/kind-hosting-history

Conversation

@andrey-noskov
Copy link
Copy Markdown

@andrey-noskov andrey-noskov commented Apr 9, 2026

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

  • Kind cluster resource and lifecycle support via AddKindCluster(...)
  • Helm chart deployment support for Kind-managed clusters
  • Aspire publish/deploy support targeting Kind
  • example AppHost projects
  • unit and integration test coverage
  • required repo wiring for package metadata, solution files, root README, and test matrix

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

andrey-noskov and others added 19 commits April 9, 2026 09:08
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>
Copilot AI review requested due to automatic review settings April 9, 2026 18:21
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.sh | bash -s -- 1270

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/CommunityToolkit/Aspire/main/eng/scripts/dogfood-pr.ps1) } 1270"

@andrey-noskov
Copy link
Copy Markdown
Author

Contributors on this PR: @andrey-noskov @MattKotsenas

@andrey-noskov
Copy link
Copy Markdown
Author

@dotnet-policy-service agree company="Microsoft"

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +67 to +90
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());
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@MattKotsenas MattKotsenas Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's true anymore? I'm the one that accidentally caused that bug, let me check...

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, fixed back in 2020: dotnet/runtime#42585

Comment on lines +45 to +53
using var process = Process.Start(psi);
if (process is null)
{
return false;
}

process.WaitForExit(TimeSpan.FromSeconds(5));
return process.ExitCode == 0;
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually shouldn't be using Process.Start directly at all here, we should be using our IProcessRunner.

Comment on lines +145 to +156
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);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@andrey-noskov
Copy link
Copy Markdown
Author

@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.

@tamirdresher
Copy link
Copy Markdown

@aaronpowell would appreciate your help promoting this

"cluster",
$"--name={_resource.Name}",
$"--config={configPath}",
$"--kubeconfig={_resource.KubeconfigPath}",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; } = [];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Might make more sense to name this something like EnsureConnectedToKindNetworkAsync


var connectResult = await processRunner.RunAsync(
logger,
"docker",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here about note

.WithKindNetwork();
```

> **Note:** `WithKindNetwork` is available on `IResourceBuilder<ContainerResource>`. It connects the container to the `kind` Docker network automatically when the container starts.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

}
}

// ── DefaultProcessRunner tests ──────────────────────────────────────
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks left over?

<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<!-- Verify.XunitV3 brings in newer xunit.v3.common/extensibility.core transitively.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is in the wrong directory

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants