Kubernetes publish: WithHttpProbe generates lowercase scheme ("http"), rejected by kube-apiserver
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
## Description
When using `WithHttpProbe` with Kubernetes publishing (`aspire deploy` or `aspire publish`), the generated Helm chart emits `httpGet.scheme: "http"` (lowercase). The Kubernetes API rejects this value because the `scheme` field is an OpenAPI enum that only accepts `"HTTP"` / `"HTTPS"` (uppercase). This makes `helm install` fail during `aspire deploy`.
## Reproduction Steps
Minimal `apphost.cs`:
```csharp
var builder = DistributedApplication.CreateBuilder(args);
builder.AddKubernetesEnvironment("env");
var api = builder.AddProject("api")
.WithHttpEndpoint(targetPort: 8080)
#pragma warning disable ASPIREPROBES001
.WithHttpProbe(ProbeType.Liveness, "/health", endpointName: "http")
.WithHttpProbe(ProbeType.Readiness, "/alive", endpointName: "http");
#pragma warning restore ASPIREPROBES001
builder.Build().Run();
```
Then:
```bash
aspire deploy # or: aspire publish -o ./out
```
## Expected Behavior
The generated `deployment.yaml` should produce a valid probe that `helm install` / `kubectl apply` accepts:
```yaml
livenessProbe:
httpGet:
scheme: "HTTP" # uppercase, matches K8s OpenAPI enum
path: "/health"
port: 8080
```
## Actual Behavior
The generated chart emits lowercase `scheme: "http"`, and `helm install` fails:
```yaml
livenessProbe:
httpGet:
scheme: "http" # lowercase — rejected by K8s API
```
Error from `aspire deploy`:
```
helm-deploy-k8s ✗ Helm deployment failed:
Error: server-side apply failed for object default/api-deployment apps/v1, Kind=Deployment:
Deployment.apps "api-deployment" is invalid:
spec.template.spec.containers[0].livenessProbe.httpGet.scheme:
Unsupported value: "http": supported values: "HTTP", "HTTPS"
```
This is **not** k3s/k3d-specific. I verified by applying directly to the Kubernetes API (bypassing helm):
```bash
$ kubectl apply -f - <
ENUM: HTTP, HTTPS
```
## Root Cause
In `src/Aspire.Hosting.Kubernetes/KubernetesResource.cs`, the `ProcessProbes()` method assigns the probe scheme directly from the endpoint mapping without case conversion:
```csharp
// KubernetesResource.cs, ProcessProbes(), line ~325
probe = new ProbeV1()
{
HttpGet = new()
{
Path = endpointProbeAnnotation.Path,
Port = GetEndpointValue(endpointMapping, EndpointProperty.TargetPort),
Scheme = endpointMapping.Scheme, // ← lowercase "http" passed through as-is
},
};
```
The `endpointMapping.Scheme` originates from `endpoint.UriScheme`, which is a .NET URI scheme. Per RFC 3986 §3.1, the canonical form of URI schemes is lowercase, and .NET's `Uri.UriSchemeHttp = "http"` / `Uri.UriSchemeHttps = "https"` follow this convention.
However, Kubernetes defines `URIScheme` as a Go enum where the values are uppercase:
```go
// staging/src/k8s.io/api/core/v1/types.go (upstream Kubernetes)
const (
URISchemeHTTP URIScheme = "HTTP"
URISchemeHTTPS URIScheme = "HTTPS"
)
```
This enum is reflected into the OpenAPI schema (`"enum": ["HTTP", "HTTPS"]`) and enforced by the kube-apiserver admission validation. So there's a **case mismatch between .NET's URI scheme convention (lowercase) and Kubernetes' enum values (uppercase)**, and Aspire passes the value through without adapting the case.
## Why the Unit Tests Don't Catch This
The existing test `PublishAsync_ResourceWithProbes` in `KubernetesPublisherTests.cs` uses Verify.net snapshot testing. The verified snapshot (`PublishAsync_ResourceWithProbes#01.verified.yaml`) also contains `scheme: "http"` (lowercase):
```yaml
httpGet:
scheme: "http"
path: "/health"
```
Since the test only compares generated YAML against a snapshot (and does not perform a real `helm install` or `kubectl apply` against a Kubernetes API), the invalid value passes the test. The snapshot itself encodes the bug.
## Suggested Fix
Add `.ToUpperInvariant()` in `ProcessProbes()`:
```csharp
Scheme = endpointMapping.Scheme.ToUpperInvariant(),
```
And update the verified snapshots (`scheme: "http"` → `scheme: "HTTP"`) in:
- `tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ResourceWithProbes#01.verified.yaml`
- `tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_ResourceWithProbes#02.verified.yaml`
## Workaround
Post-process the generated chart with `sed` before `helm install`:
```bash
find ./aspire-output/templates -name "deployment.yaml" \
-exec sed -i 's/scheme: "http"/scheme: "HTTP"/g; s/scheme: "https"/scheme: "HTTPS"/g' {} +
```
## Environment
- Aspire: `13.4.3` (`Aspire.Hosting.Kubernetes` `13.4.3-preview.1.26305.13`)
- .NET: `10.0.201`
- Kubernetes: `v1.35.5` (k3s; verified not k3s-specific — see reproduction)
- Verified the bug is still present on `main` (commit checked: `ProcessProbes` has no case conversion)
## Related
- `WithHttpProbe` was introduced in #11081
- K8s upstream enum: [`kubernetes/api/core/v1/types.go`](https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/api/core/v1/types.go) (`URISchemeHTTP = "HTTP"`)
Contributor guide
Assessment
This issue has not been assessed yet.