Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ jobs:
Hosting.Golang.Tests,
Hosting.JavaScript.Extensions.Tests,
Hosting.Java.Tests,
Hosting.K3s.Tests,
Hosting.K3s.IntegrationTests,
Comment thread
edmondshtogu marked this conversation as resolved.
Hosting.k6.Tests,
Hosting.Keycloak.Extensions.Tests,
Hosting.KurrentDB.Tests,
Expand Down Expand Up @@ -81,6 +83,11 @@ jobs:
Sftp.Tests,
SurrealDb.Tests,
]
exclude:
# k3s integration tests require privileged Linux containers.
# GitHub-hosted Windows runners do not support this reliably.
- os: windows-latest
name: Hosting.K3s.IntegrationTests

steps:
- name: Checkout code
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ examples/perl/**/local/*
**cpanfile.snapshot
**/.modules/
**/*.AppHost.TypeScript/nuget.config
<<<<<<< main

**/.k3s/
=======
tsconfig.apphost.json
.ngrok
bun.lock
Expand All @@ -31,3 +35,4 @@ yarn.lock
solr-data
*.lscache
apphost.js
>>>>>>> main
6 changes: 6 additions & 0 deletions CommunityToolkit.Aspire.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
<Project Path="examples/java/CommunityToolkit.Aspire.Hosting.Java.ServiceDefaults/CommunityToolkit.Aspire.Hosting.Java.ServiceDefaults.csproj" />
<Project Path="examples/java/CommunityToolkit.Aspire.Hosting.Java.WebApp/CommunityToolkit.Aspire.Hosting.Java.WebApp.csproj" />
</Folder>
<Folder Name="/examples/k3s/">
<Project Path="examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost/CommunityToolkit.Aspire.Hosting.K3s.AppHost.csproj" />
</Folder>
<Folder Name="/examples/k6/">
<Project Path="examples/k6/CommunityToolkit.Aspire.Hosting.k6.ApiService/CommunityToolkit.Aspire.Hosting.k6.ApiService.csproj" />
<Project Path="examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost/CommunityToolkit.Aspire.Hosting.k6.AppHost.csproj" />
Expand Down Expand Up @@ -214,6 +217,7 @@
<Project Path="src/CommunityToolkit.Aspire.Hosting.Golang/CommunityToolkit.Aspire.Hosting.Golang.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Java/CommunityToolkit.Aspire.Hosting.Java.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.K3s/CommunityToolkit.Aspire.Hosting.K3s.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.k6/CommunityToolkit.Aspire.Hosting.k6.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.KurrentDB/CommunityToolkit.Aspire.Hosting.KurrentDB.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.LavinMQ/CommunityToolkit.Aspire.Hosting.LavinMQ.csproj" />
Expand Down Expand Up @@ -276,6 +280,8 @@
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Golang.Tests/CommunityToolkit.Aspire.Hosting.Golang.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Java.Tests/CommunityToolkit.Aspire.Hosting.Java.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.K3s.Tests/CommunityToolkit.Aspire.Hosting.K3s.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.K3s.IntegrationTests/CommunityToolkit.Aspire.Hosting.K3s.IntegrationTests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.k6.Tests/CommunityToolkit.Aspire.Hosting.k6.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.KurrentDB.Tests/CommunityToolkit.Aspire.Hosting.KurrentDB.Tests.csproj" />
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.4.10" />
<PackageVersion Include="ModelContextProtocol" Version="0.4.0-preview.3" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="0.4.0-preview.3" />
<PackageVersion Include="KubernetesClient" Version="19.0.2" />
<PackageVersion Include="KurrentDB.Client" Version="1.2.0" />
<PackageVersion Include="SSH.NET" Version="2025.1.0" />
</ItemGroup>
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This repository contains the source code for the Aspire Community Toolkit, a col
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| - **Learn More**: [`Hosting.Golang`][golang-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Golang][golang-shields]][golang-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Golang][golang-shields-preview]][golang-nuget-preview] | A hosting integration Golang apps. |
| - **Learn More**: [`Hosting.Java`][java-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Java][java-shields]][java-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Java][java-shields-preview]][java-nuget-preview] | An integration for running Java code in Aspire either using the local JDK or using a container. |
| - **Learn More**: [`Hosting.K3s`][k3s-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.K3s][k3s-shields]][k3s-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.K3s][k3s-shields-preview]][k3s-nuget-preview] | An Aspire hosting integration for [k3s](https://k3s.io/), a lightweight Kubernetes distribution by Rancher. |
| - **Learn More**: [`Hosting.NodeJS.Extensions`][nodejs-ext-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.NodeJS.Extensions][nodejs-ext-shields]][nodejs-ext-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.JavaScript.Extensions][nodejs-ext-shields-preview]][nodejs-ext-nuget-preview] | An integration that contains some additional extensions for running Node.js applications |
| - **Learn More**: [`Hosting.Ollama`][ollama-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Ollama][ollama-shields]][ollama-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Ollama][ollama-shields-preview]][ollama-nuget-preview] | An Aspire hosting integration leveraging the [Ollama](https://ollama.com) container with support for downloading a model on startup. |
| - **Learn More**: [`OllamaSharp`][ollama-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.OllamaSharp][ollamasharp-shields]][ollamasharp-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.OllamaSharp][ollamasharp-shields-preview]][ollamasharp-nuget-preview] | An Aspire client integration for the [OllamaSharp](https://github.com/awaescher/OllamaSharp) package. |
Expand Down Expand Up @@ -106,6 +107,11 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org)
[java-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Java/
[java-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Java?label=nuget%20(preview)
[java-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Java/absoluteLatest
[k3s-integration-docs]: https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-k3s
[k3s-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.K3s
[k3s-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.K3s/
[k3s-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.K3s?label=nuget%20(preview)
[k3s-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.K3s/absoluteLatest
[nodejs-ext-integration-docs]: https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-nodejs-extensions
[nodejs-ext-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions
[nodejs-ext-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Aspire.AppHost.Sdk/13.3.0">

<PropertyGroup>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Hosting.K3s\CommunityToolkit.Aspire.Hosting.K3s.csproj" IsAspireProjectResource="false" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// K3s hosting example
// ──────────────────────────────────────────────────────────────────────────────
// Prerequisites (host machine):
// • A container runtime that supports privileged Linux containers:
// - Linux: Docker Engine 20.10+ or rootful Podman 4.0+
// - macOS / Windows: Docker Desktop (WSL2 / Hyper-V)
// No host-side helm or kubectl required — both run as containers.
//
// What this demonstrates:
// 1. A k3s cluster starts inside a Docker container.
// 2. podinfo is installed via Helm — a lightweight demo app.
// 3. A K3sServiceEndpointResource exposes the podinfo service:
// • Host processes reach it at http://localhost:{port}
// • DCP-network containers reach it at http://host.docker.internal:{port}
// 4. WithDataVolume keeps the cluster state alive across AppHost restarts.
// ──────────────────────────────────────────────────────────────────────────────

var builder = DistributedApplication.CreateBuilder(args);

var cluster = builder
.AddK3sCluster("k8s")
.WithDataVolume()
.WithLifetime(ContainerLifetime.Persistent);

var podinfo = cluster.AddHelmRelease(
name: "podinfo",
chart: "podinfo",
repo: "https://stefanprodan.github.io/podinfo",
version: "6.7.1",
@namespace: "podinfo");

// Expose the podinfo service as an Aspire endpoint resource.
// WaitForCompletion waits for the helm install container to exit with code 0
// before starting the port-forward — no NodePort required.
cluster.AddServiceEndpoint("podinfo-web", "podinfo", servicePort: 9898, @namespace: "podinfo")
.WaitForCompletion(podinfo);

builder.Build().Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:53201;http://localhost:53202",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:53203",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:53204"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:53202",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:53205",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:53206"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { createBuilder, ContainerLifetime } from './.modules/aspire.js';

const builder = await createBuilder();

// ── Runtime path (actually executed) ─────────────────────────────────────────
// Minimal cluster startup — validates that the core add/build/run path works.
const cluster = builder.addK3sCluster('k8s');
const clusterResource = await cluster;
const _apiEndpoint = await clusterResource.apiEndpoint.get();

// ── Compile-time coverage ─────────────────────────────────────────────────────
// Guards with false so these are type-checked but never executed.
// Covers the full exported API surface without requiring Docker/k3s in CI.
const includeCompileOnlyScenarios = false;

if (includeCompileOnlyScenarios) {

// ── Cluster configuration ────────────────────────────────────────────────
const configuredCluster = builder.addK3sCluster('k8s-configured')
.withK3sVersion('v1.32.3-k3s1')
.withPodSubnet('10.42.0.0/16')
.withServiceSubnet('10.43.0.0/16')
.withDisabledComponent('traefik')
.withExtraArg('--write-kubeconfig-mode=644')
.withDataVolume({ name: 'k8s-data' })
.withLifetime(ContainerLifetime.Persistent);

const configuredClusterResource = await configuredCluster;
const _configuredApiEndpoint = await configuredClusterResource.apiEndpoint.get();

// ── Helm release ─────────────────────────────────────────────────────────
const argocd = configuredCluster.addHelmRelease('argocd', 'argo-cd', {
repo: 'https://argoproj.github.io/argo-helm',
version: '7.8.0',
namespace: 'argocd',
})
.withHelmValue('server.insecure', 'true')
.withHelmValuesFile('./deploy/argocd-values.yaml');

const argocdResource = await argocd;
const _argocdParent = await argocdResource.parent.get();
const _argocdReleaseName = await argocdResource.releaseName.get();
const _argocdNamespace = await argocdResource.namespace.get();

// ── K8s manifest / Kustomize overlay ─────────────────────────────────────
const widgetCrd = configuredCluster.addK8sManifest('widget-crd', './k8s/crds/');

const widgetCrdResource = await widgetCrd;
const _crdParent = await widgetCrdResource.parent.get();
const _crdPath = await widgetCrdResource.path.get();

// ── Service endpoint ─────────────────────────────────────────────────────
const ui = configuredCluster.addServiceEndpoint('argocd-ui', 'argocd-server', 443, {
namespace: 'argocd',
});

const uiResource = await ui;
const _uiParent = await uiResource.parent.get();
const _uiServiceName = await uiResource.serviceName.get();
const _uiServicePort = await uiResource.servicePort.get();
const _uiNamespace = await uiResource.namespace.get();
const _uiHostPort = await uiResource.hostPort.get();

// ── WithReference — cluster kubeconfig injection ──────────────────────────
// Project: receives KUBECONFIG=.../.k3s/k8s/local/kubeconfig.yaml
const _projectRef = builder
.addProject('operator', { projectPath: '../WidgetOperator/WidgetOperator.csproj' })
.withReference(configuredCluster);

// Container: receives a bind-mounted kubeconfig and KUBECONFIG=/var/k3s/kubeconfig.yaml
const _containerRef = builder
.addContainer('sidecar', 'myorg/sidecar')
.withReference(configuredCluster);

// ── WithReference — service endpoint URL injection ────────────────────────
// Host: receives services__argocd-ui__url=https://localhost:{port}
const _endpointRef = builder
.addProject('api', { projectPath: '../WidgetApi/WidgetApi.csproj' })
.withReference(ui);
}

await builder.build().run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"appHost": {
"path": "apphost.ts",
"language": "typescript/nodejs"
},
"profiles": {
"https": {
"applicationUrl": "https://localhost:17149;http://localhost:15243",
"environmentVariables": {
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21065",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22170"
}
},
"http": {
"applicationUrl": "http://localhost:15243",
"environmentVariables": {
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19027",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20141",
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
}
}
},
"packages": {
"CommunityToolkit.Aspire.Hosting.K3s": ""
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "validationapphost",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "aspire run",
"build": "tsc",
"dev": "tsc --watch"
},
"dependencies": {
"vscode-jsonrpc": "^8.2.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"nodemon": "^3.1.11",
"tsx": "^4.19.0",
"typescript": "^5.3.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "."
},
"include": [
"apphost.ts",
".modules/**/*.ts"
],
"exclude": [
"node_modules"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AdditionalPackageTags>kubernetes k3s hosting cluster</AdditionalPackageTags>
<Description>An Aspire hosting integration for k3s. Provides AddK3sCluster, AddHelmRelease (via alpine/helm), AddK8sManifest with Kustomize support (via rancher/kubectl), and AddServiceEndpoint for in-process WebSocket port-forwarding. No host-side kubectl or helm required.</Description>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting" />
<PackageReference Include="KubernetesClient" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="CommunityToolkit.Aspire.Hosting.K3s.Tests" />
</ItemGroup>


</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace CommunityToolkit.Aspire.Hosting;

internal static class HelmContainerImageTags
{
internal const string Registry = "docker.io";
internal const string Image = "alpine/helm";
internal const string Tag = "3.17.3";
}
Loading
Loading