diff --git a/src/frontend/src/content/docs/integrations/compute/kubernetes.mdx b/src/frontend/src/content/docs/integrations/compute/kubernetes.mdx index 2ac23b9bc..670cc8285 100644 --- a/src/frontend/src/content/docs/integrations/compute/kubernetes.mdx +++ b/src/frontend/src/content/docs/integrations/compute/kubernetes.mdx @@ -164,6 +164,105 @@ await service.publishAsKubernetesService(async (resource) => { +## Add custom Kubernetes manifests + +Within a `PublishAsKubernetesService` callback, you can attach arbitrary Kubernetes manifests — such as [cert-manager](https://cert-manager.io/) `Certificate` definitions, additional `ConfigMap` objects, or any other Kubernetes resource — to the generated Helm chart alongside your service. + +The API surface differs by language: + +- **C#**: add subclasses of `BaseKubernetesResource` directly to the `AdditionalResources` collection. Several built-in types (such as `ConfigMap`) are available in the `Aspire.Hosting.Kubernetes.Resources` namespace, and you can subclass `BaseKubernetesResource` to model any custom resource definition (CRD). +- **TypeScript**: call `addManifest` on the resource. It takes the manifest's `apiVersion`, `kind`, and `metadata.name`, and returns a handle for setting labels, annotations, a namespace, and arbitrary field values using dot-notation paths. + + + + +Define a class for the custom resource by deriving from `BaseKubernetesResource`, then add an instance to `AdditionalResources`. The example below models a cert-manager `Certificate` CRD: + +```csharp title="AppHost.cs" +using Aspire.Hosting.Kubernetes.Resources; +using YamlDotNet.Serialization; + +builder.AddContainer("service", "nginx") + .PublishAsKubernetesService(resource => + { + var certificate = new Certificate + { + Metadata = { Name = "service-tls" }, + Spec = + { + SecretName = "service-tls", + DnsNames = { "service.example.com" }, + IssuerRef = { Name = "letsencrypt-prod", Kind = "ClusterIssuer" }, + }, + }; + certificate.Metadata.Labels["example.com/managed-by"] = "aspire"; + resource.AdditionalResources.Add(certificate); + }); + +public sealed class Certificate() : BaseKubernetesResource("cert-manager.io/v1", "Certificate") +{ + [YamlMember(Alias = "spec")] + public CertificateSpec Spec { get; set; } = new(); +} + +public sealed class CertificateSpec +{ + [YamlMember(Alias = "secretName")] + public string SecretName { get; set; } = ""; + + [YamlMember(Alias = "dnsNames")] + public List DnsNames { get; } = []; + + [YamlMember(Alias = "issuerRef")] + public IssuerRef IssuerRef { get; set; } = new(); +} + +public sealed class IssuerRef +{ + [YamlMember(Alias = "name")] + public string Name { get; set; } = ""; + + [YamlMember(Alias = "kind")] + public string Kind { get; set; } = "Issuer"; +} +``` + +For simple cases that map to a built-in type, instantiate the type directly — for example, `new ConfigMap { Metadata = { Name = "extra-config" }, Data = { ["key"] = "value" } }` — and add it to `AdditionalResources`. + + + +```typescript title="apphost.ts" +const service = await builder.addContainer('service', 'nginx'); +await service.publishAsKubernetesService(async (resource) => { + await resource.addManifest('cert-manager.io/v1', 'Certificate', 'service-tls', { + configure: async (manifest) => { + await manifest.withLabel('example.com/managed-by', 'aspire'); + await manifest.withField('spec.secretName', 'service-tls'); + await manifest.withField('spec.dnsNames', ['service.example.com']); + await manifest.withField('spec.issuerRef.name', 'letsencrypt-prod'); + await manifest.withField('spec.issuerRef.kind', 'ClusterIssuer'); + }, + }); +}); +``` + + + +The manifest handle returned by `addManifest` supports the following configuration methods: + +| Method | Description | +|---|---| +| `withNamespace` | Sets the Kubernetes namespace for the manifest. | +| `withLabel` | Adds a metadata label to the manifest. | +| `withAnnotation` | Adds a metadata annotation to the manifest. | +| `withField` | Sets an arbitrary field using a dot-notation path (for example, `spec.maxReplicaCount`). | + +Each manifest is emitted as a separate template file inside the generated Helm chart. + + + ## Publishing and deployment The Kubernetes integration supports both `aspire publish` (generate Helm chart artifacts) and `aspire deploy` (deploy directly to your current cluster context).