Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions KubeOps.slnx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<Solution>
<Folder Name="/examples/">
<Project Path="examples\AspireAppHost\AspireAppHost.csproj" />
<Project Path="examples\ConversionWebhookOperator\ConversionWebhookOperator.csproj" />
<Project Path="examples\Operator\Operator.csproj" />
<Project Path="examples\WebhookOperator\WebhookOperator.csproj" />
Expand Down Expand Up @@ -29,6 +30,8 @@
</Folder>
<Folder Name="/src/">
<Project Path="src\KubeOps.Abstractions\KubeOps.Abstractions.csproj" />
<Project Path="src\KubeOps.Aspire.Hosting\KubeOps.Aspire.Hosting.csproj" />
<Project Path="src\KubeOps.Aspire\KubeOps.Aspire.csproj" />
<Project Path="src\KubeOps.Cli\KubeOps.Cli.csproj" />
<Project Path="src\KubeOps.Generator\KubeOps.Generator.csproj" />
<Project Path="src\KubeOps.KubernetesClient\KubeOps.KubernetesClient.csproj" />
Expand All @@ -41,6 +44,8 @@
<Folder Name="/test/">
<Project Path="test/KubeOps.Generator.Test.Entities/KubeOps.Generator.Test.Entities.csproj" />
<Project Path="test\KubeOps.Abstractions.Test\KubeOps.Abstractions.Test.csproj" />
<Project Path="test\KubeOps.Aspire.Hosting.Test\KubeOps.Aspire.Hosting.Test.csproj" />
<Project Path="test\KubeOps.Aspire.Test\KubeOps.Aspire.Test.csproj" />
<Project Path="test\KubeOps.Cli.Test\KubeOps.Cli.Test.csproj" />
<Project Path="test\KubeOps.Generator.Test\KubeOps.Generator.Test.csproj" />
<Project Path="test\KubeOps.KubernetesClient.Test\KubeOps.KubernetesClient.Test.csproj" />
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ The SDK is designed to be modular. You can include only the packages you need:
| Package | Description |
| -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [KubeOps.Abstractions](./src/KubeOps.Abstractions/README.md) | Defines core interfaces, attributes (like `[KubernetesEntity]`), and base classes used across the SDK. Essential for defining your custom resources and controllers. |
| [KubeOps.Aspire](./src/KubeOps.Aspire/README.md) | [.NET Aspire](https://learn.microsoft.com/dotnet/aspire/) service defaults for an operator: a single `AddKubeOpsServiceDefaults()` call wiring up OpenTelemetry, service discovery, HTTP resilience, and health checks. |
| [KubeOps.Aspire.Hosting](./src/KubeOps.Aspire.Hosting/README.md) | [.NET Aspire](https://learn.microsoft.com/dotnet/aspire/) hosting integration. Adds `AddKubeOps<TProject>(...)` so a KubeOps operator can be orchestrated as a resource inside an Aspire AppHost. |
| [KubeOps.Cli](./src/KubeOps.Cli/README.md) | A [.NET Tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools) providing commands for scaffolding projects, generating [Custom Resource Definitions (CRDs)](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/), and more. |
| [KubeOps.Generator](./src/KubeOps.Generator/README.md) | Contains [Roslyn Source Generators](https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview) to automate boilerplate code generation for CRDs and controllers based on your definitions. |
| [KubeOps.KubernetesClient](./src/KubeOps.KubernetesClient/README.md) | Provides an enhanced client for interacting with the [Kubernetes API](https://kubernetes.io/docs/reference/kubernetes-api/), built on top of the official `KubernetesClient` library. Offers convenience methods for common operator tasks. |
Expand Down
122 changes: 122 additions & 0 deletions docs/docs/operator/aspire.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: .NET Aspire
description: Orchestrate and observe a KubeOps operator with .NET Aspire
sidebar_position: 9.5
---

# .NET Aspire Integration

[.NET Aspire](https://learn.microsoft.com/dotnet/aspire/) is an opinionated stack for building observable, production-ready distributed applications. KubeOps ships two packages that let you treat an operator as a first-class Aspire resource:

| Package | Side | Purpose |
| --- | --- | --- |
| [`KubeOps.Aspire.Hosting`](../packages/aspire-hosting) | AppHost | Adds `AddKubeOps<TProject>(...)` so the operator is orchestrated as a resource. |
| [`KubeOps.Aspire`](../packages/aspire) | Operator | Adds `AddKubeOpsServiceDefaults()` for OpenTelemetry, service discovery, resilience and health checks. |

Together they give you a local dashboard with the operator's logs, traces and metrics, automatic service discovery to other resources, and a single entry point to run the whole stack.

## The two halves of an Aspire integration

Aspire integrations always come in two parts, and KubeOps follows that convention:

1. **Hosting integration** &mdash; code that runs in the *AppHost* project and describes the application model.
2. **Service defaults** &mdash; a small amount of wiring inside the *operator* project itself.

The service-defaults call is the one piece you add to the operator. This is the idiomatic Aspire pattern (`AddServiceDefaults()`); the hosting side then injects the configuration (telemetry endpoint, service discovery variables) automatically.

## Operator project

Install `KubeOps.Aspire` and call `AddKubeOpsServiceDefaults()` on the host builder, **after** `AddKubernetesOperator()`:

```csharp
using KubeOps.Aspire;
using KubeOps.Operator;

using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

builder.Services
.AddKubernetesOperator()
.RegisterComponents();

builder.AddKubeOpsServiceDefaults();

using var host = builder.Build();
await host.RunAsync();
```

`AddKubeOpsServiceDefaults()` configures:

- **OpenTelemetry** logging (with scopes and formatted messages), metrics (runtime + `HttpClient`) and tracing. It subscribes to the operator's `ActivitySource`, which KubeOps registers under the operator name (see [Logging, Tracing, and OpenTelemetry](./logging)).
- **OTLP export** &mdash; enabled automatically when the `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable is present. Aspire sets this for you, so traces and metrics show up in the dashboard with no extra code.
- **Service discovery** &mdash; `HttpClient` instances resolve logical Aspire resource names (e.g. `https://apiservice`).
- **HTTP resilience** &mdash; the standard resilience handler (retries, circuit breaker, timeouts) is applied to all `HttpClient` instances.
- **Health checks** &mdash; a default `self` liveness check tagged `live`.

:::tip Operator name
The OpenTelemetry service name and the tracing source name must match `OperatorSettings.Name` — otherwise the operator's reconciliation traces are never captured. Call `AddKubeOpsServiceDefaults()` **after** `AddKubernetesOperator()` so KubeOps can resolve the configured name automatically. If you must call it earlier, pass the name explicitly (and keep it in sync with `OperatorSettings.Name`):

```csharp
builder.AddKubeOpsServiceDefaults("my-operator");
```
:::

## AppHost project

Create an [Aspire AppHost](https://learn.microsoft.com/dotnet/aspire/fundamentals/app-host-overview) project, reference `KubeOps.Aspire.Hosting`, and add the operator with `AddKubeOps`:

```csharp
var builder = DistributedApplication.CreateBuilder(args);

var apiService = builder.AddProject<Projects.ApiService>("apiservice");

builder.AddKubeOps<Projects.Operator>("operator")
.WithReference(apiService);

builder.Build().Run();
```

`AddKubeOps<TProject>` is a thin, KubeOps-flavoured wrapper around the built-in `AddProject<TProject>`. It returns the standard `IResourceBuilder<ProjectResource>`, so every Aspire extension works as usual:

- `WithReference(apiService)` injects the service-discovery configuration so the operator can call `apiservice` by name.
- `WithEnvironment(...)`, `WaitFor(...)`, `WithReplicas(...)` and friends all apply unchanged.

:::note Project reference
When referencing `KubeOps.Aspire.Hosting` from an AppHost, mark it as a normal code reference so the AppHost SDK does not treat it as a resource:

```xml
<ProjectReference Include="..\..\src\KubeOps.Aspire.Hosting\KubeOps.Aspire.Hosting.csproj"
IsAspireProjectResource="false" />
```
:::

## Running locally

Run the AppHost; the Aspire dashboard opens and starts the operator alongside any other resources:

```bash
dotnet run --project examples/AspireAppHost
```

The operator connects to your current cluster using the standard Kubernetes configuration resolution (`KUBECONFIG`, in-cluster config). The dashboard shows structured logs, the reconciliation traces emitted from the operator's `ActivitySource`, and runtime/HTTP metrics.

## Health endpoints

`KubeOps.Aspire` is usable from a plain console operator and therefore does **not** force an ASP.NET Core dependency. The default health checks are registered in the service collection, but exposing them over HTTP requires an HTTP server.

If your operator already hosts webhooks via [`KubeOps.Operator.Web`](../packages/operator-web), map the endpoints on the `WebApplication`:

```csharp
app.MapHealthChecks("/health");
app.MapHealthChecks("/alive", new HealthCheckOptions
{
Predicate = registration => registration.Tags.Contains("live"),
});
```

These line up with the standard Kubernetes liveness/readiness probe conventions and the Aspire dashboard's health reporting.

## Deployment

Because the operator is an ordinary project resource in the Aspire application model, you can publish the whole stack with the [Aspire CLI](https://learn.microsoft.com/dotnet/aspire/cli/overview) or community tooling such as [Aspir8](https://github.com/prom3theu5/aspirational-manifests) to generate Kubernetes manifests. For hand-rolled manifests and RBAC, see [Deployment](./deployment) and [RBAC](./rbac).
17 changes: 17 additions & 0 deletions docs/docs/packages/aspire-hosting.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: KubeOps.Aspire.Hosting
description: .NET Aspire hosting integration for KubeOps operators
sidebar_position: 4.4
hide_title: true
---

import AspireHosting from "../../../src/KubeOps.Aspire.Hosting/README.md";

<AspireHosting />

## Related Packages

- [KubeOps.Aspire](./aspire) - Service defaults for the operator project
- [KubeOps.Operator](./operator) - Main operator engine

See the [.NET Aspire guide](../operator/aspire) for the full walkthrough.
17 changes: 17 additions & 0 deletions docs/docs/packages/aspire.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: KubeOps.Aspire
description: .NET Aspire service defaults for KubeOps operators
sidebar_position: 4.3
hide_title: true
---

import Aspire from "../../../src/KubeOps.Aspire/README.md";

<Aspire />

## Related Packages

- [KubeOps.Aspire.Hosting](./aspire-hosting) - AppHost integration for orchestrating the operator
- [KubeOps.Operator](./operator) - Main operator engine

See the [.NET Aspire guide](../operator/aspire) for the full walkthrough.
5 changes: 5 additions & 0 deletions docs/docs/packages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ KubeOps is designed to be modular, allowing you to include only the packages you
- [KubeOps.Operator](./operator) - Main operator engine
- [KubeOps.Operator.Web](./operator-web) - ASP.NET Core integration

## Hosting & Orchestration

- [KubeOps.Aspire.Hosting](./aspire-hosting) - .NET Aspire AppHost integration
- [KubeOps.Aspire](./aspire) - .NET Aspire service defaults for the operator

## Development Tools

- [KubeOps.Cli](./cli) - Command-line tools
Expand Down
26 changes: 26 additions & 0 deletions examples/AspireAppHost/AspireAppHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<Sdk Name="Aspire.AppHost.Sdk" Version="13.3.5" />

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<UserSecretsId>kubeops-aspire-apphost</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.3.5" />
</ItemGroup>

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

</Project>
9 changes: 9 additions & 0 deletions examples/AspireAppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

var builder = DistributedApplication.CreateBuilder(args);

builder.AddKubeOps<Projects.Operator>("operator");

builder.Build().Run();
2 changes: 2 additions & 0 deletions examples/Operator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

#if DEBUG
using KubeOps.Abstractions.Crds;
#endif
using KubeOps.Operator;

using Microsoft.Extensions.Hosting;
Expand Down
22 changes: 22 additions & 0 deletions src/KubeOps.Aspire.Hosting/KubeOps.Aspire.Hosting.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
<PackageId>KubeOps.Aspire.Hosting</PackageId>
<PackageTags>Kubernetes Operator SDK Aspire Hosting AppHost</PackageTags>
<PackageDescription>
.NET Aspire hosting integration for KubeOps operators. Adds an
AddKubeOps extension to the distributed application builder so a KubeOps
operator project can be orchestrated as a resource in a .NET Aspire
AppHost, wired up with references, environment variables and telemetry.
</PackageDescription>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting" Version="13.3.5" />
</ItemGroup>

</Project>
39 changes: 39 additions & 0 deletions src/KubeOps.Aspire.Hosting/KubeOpsHostingExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using Aspire.Hosting.ApplicationModel;

namespace Aspire.Hosting;

/// <summary>
/// Extension methods for adding KubeOps operator projects to a
/// <see cref="IDistributedApplicationBuilder"/>.
/// </summary>
public static class KubeOpsHostingExtensions
{
/// <summary>
/// Adds a KubeOps operator project to the distributed application model.
/// </summary>
/// <remarks>
/// This is a KubeOps-flavoured wrapper around
/// <see cref="ProjectResourceBuilderExtensions.AddProject{TProject}(IDistributedApplicationBuilder, string)"/>.
/// It returns the standard <see cref="ProjectResource"/> builder, so all the usual
/// Aspire extensions (<c>WithReference</c>, <c>WithEnvironment</c>, <c>WaitFor</c>, ...)
/// apply unchanged. Pair it with <c>AddKubeOpsServiceDefaults()</c> from the
/// <c>KubeOps.Aspire</c> package in the operator project to enable telemetry and
/// service discovery.
/// </remarks>
/// <typeparam name="TProject">
/// The operator project metadata type generated by the Aspire AppHost SDK
/// (e.g. <c>Projects.MyOperator</c>).
/// </typeparam>
/// <param name="builder">The distributed application builder.</param>
/// <param name="name">The resource name of the operator.</param>
/// <returns>An <see cref="IResourceBuilder{ProjectResource}"/> for further configuration.</returns>
public static IResourceBuilder<ProjectResource> AddKubeOps<TProject>(
this IDistributedApplicationBuilder builder,
[ResourceName] string name)
where TProject : IProjectMetadata, new()
=> builder.AddProject<TProject>(name);
}
22 changes: 22 additions & 0 deletions src/KubeOps.Aspire.Hosting/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# KubeOps.Aspire.Hosting

`KubeOps.Aspire.Hosting` is the [.NET Aspire](https://learn.microsoft.com/dotnet/aspire/) **hosting** integration for KubeOps operators. It lets you orchestrate a KubeOps operator project as a resource inside a .NET Aspire AppHost.

It is the AppHost-side counterpart to the [`KubeOps.Aspire`](https://www.nuget.org/packages/KubeOps.Aspire) service-defaults integration.

## Usage

In your Aspire AppHost project:

```csharp
var apiService = builder.AddProject<Projects.ApiService>("apiservice");

builder.AddKubeOps<Projects.Operator>("operator")
.WithReference(apiService);
```

`AddKubeOps<TProject>` is a thin, KubeOps-flavoured wrapper around the built-in `AddProject<TProject>` and returns the standard `IResourceBuilder<ProjectResource>`, so every Aspire extension (`WithReference`, `WithEnvironment`, `WaitFor`, ...) works as usual.

To complete the loop, add `AddKubeOpsServiceDefaults()` from the `KubeOps.Aspire` package in the operator project. This enables OpenTelemetry export to the Aspire dashboard and service discovery for the references wired up above.

See the [.NET Aspire guide](https://dotnet.github.io/dotnet-operator-sdk/docs/operator/aspire) for the full picture.
34 changes: 34 additions & 0 deletions src/KubeOps.Aspire/KubeOps.Aspire.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
<PackageId>KubeOps.Aspire</PackageId>
<PackageTags>Kubernetes Operator SDK Aspire OpenTelemetry ServiceDiscovery</PackageTags>
<PackageDescription>
.NET Aspire service defaults for KubeOps operators. Provides a single
AddKubeOpsServiceDefaults extension that wires up OpenTelemetry (logging,
metrics and tracing with OTLP export), service discovery, HTTP resilience
and default health checks so an operator integrates cleanly with a
.NET Aspire AppHost.
</PackageDescription>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.8" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.8" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="10.6.0" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.6.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\KubeOps.Abstractions\KubeOps.Abstractions.csproj" />
</ItemGroup>

</Project>
Loading
Loading