Skip to content
Merged
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
15 changes: 9 additions & 6 deletions src/content/docs/guides/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ A local-cluster sibling reuses the same Kustomize + `kubectl` machinery without
upstream `kind` tool, served by the `deploy:kindcluster` provider beside `deploy:kubernetes` in
`plugin-kube` ([kind](/recipes/kubernetes/kind/)).

kind provisions its node containers on the operator's own container engine, and charly's engine
word maps 1:1 to kind's `KIND_EXPERIMENTAL_PROVIDER`. Three disposable beds witness the three
engines — podman (`check-kindcluster`), docker (`check-kindcluster-docker`) and rootless nerdctl
(`check-kindcluster-nerdctl`) — differing only in `engine:` and a collision-free cluster name and
kubeconfig context, since a kind cluster name is unique only within an engine.
kind provisions its node containers on the operator's own container engine. charly's engine
word maps to kind's `KIND_EXPERIMENTAL_PROVIDER` through the engine capability data
(`podman` → `podman`, `docker` → `docker`, `containerd` → `nerdctl`), and any word outside
the engine vocabulary is refused. Three disposable beds witness the three engines — podman
(`check-kindcluster`), docker (`check-kindcluster-docker`) and rootless containerd
(`check-kindcluster-nerdctl`, whose entity name keeps kind's `nerdctl` provider word) — differing
only in `engine:` and a collision-free cluster name and kubeconfig context, since a kind cluster
name is unique only within an engine.

## k3s runs in a VM, not a pod

Expand Down Expand Up @@ -69,7 +72,7 @@ Every bed below is declared in charly's own `charly.yml`:
| `check-kubevirt-operator` | the KubeVirt + CDI platform install on a disposable k3s VM |
| `check-kindcluster` | kind on the podman engine |
| `check-kindcluster-docker` | kind on the docker engine |
| `check-kindcluster-nerdctl` | kind on the rootless nerdctl engine |
| `check-kindcluster-nerdctl` | kind on the rootless containerd engine (the entity name keeps kind's `nerdctl` provider word) |
| `check-kindcluster-workload` | the `deploy:kindcluster` workload leg — a locally-built image loaded into the kind node and applied through the shared Kustomize generator |
| `check-kindcluster-lock` | teardown lock inheritance — a kindcluster root with a deploy-level member, torn down together |
| `check-kind-host-vm` | the kind-host profile applied into a disposable eval-vm guest, off the operator's workstation |
Expand Down
14 changes: 9 additions & 5 deletions src/content/docs/recipes/distros/layer-nerdctl.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description: "nerdctl (containerd CLI) + the rootless containerd/buildkit/CNI st
# layer-nerdctl

nerdctl (the containerd CLI) plus the rootless containerd + buildkit + CNI
stack, as ONE candy. Composed by a box that wants the opt-in `engine: nerdctl`
stack, as ONE candy. Composed by a box that wants the opt-in `engine: containerd`
engine.

## What it installs
Expand Down Expand Up @@ -83,9 +83,12 @@ specific deploy needs the publish side, set it deliberately in

## Engine capability

The `engine: nerdctl` word is served by `opencharly/plugin-nerdctl`
The `engine: containerd` word is served by `opencharly/plugin-nerdctl`
(out-of-process) or compiled in; every engine op delegates to
`container.InvokeEngineOp("nerdctl", …)` in the spec module. nerdctl has no
`container.InvokeEngineOp("containerd", …)` in the spec module. `containerd` is
the engine WORD; `nerdctl` is the CLI BINARY that drives it — the spec-owned
`#EngineBinaries` map (`containerd → nerdctl`) is the one home for that mapping.
nerdctl has no
`--keep-id`, so the start-plan runs the workload as `--user 0:0` (uid 0 == the
host user under rootless), guaranteeing uid-identical sharing; a non-zero uid
needs the subuid caveat.
Expand All @@ -106,5 +109,6 @@ needs the subuid caveat.

## Use when

Working with the nerdctl engine, the layer-nerdctl candy, the rootless
containerd/buildkit checklist, or an `engine: nerdctl` deploy.
Working with the containerd engine (driven through the nerdctl CLI), the
layer-nerdctl candy, the rootless containerd/buildkit checklist, or an
`engine: containerd` deploy.
9 changes: 5 additions & 4 deletions src/content/docs/recipes/kubernetes/check-k8s.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ The cluster-probe verb is spelled `kube`; the deploy KIND is spelled

The **local-cluster sibling** is `kindcluster` (`kind: kindcluster`, a
`kindcluster:` entity): kind provisions the cluster as node containers on the
operator's own engine (docker / rootless podman / rootless nerdctl) instead of
operator's own engine (docker / rootless podman / rootless containerd, where
containerd is driven through its nerdctl CLI) instead of
a VM. It is the SAME `kube:` probe surface — a `kindcluster:` deploy writes a
`kind-<cluster>` kubeconfig context, which a `kube:` step addresses with
`kube_context:`. See [`/charly-kubernetes:kind`](/recipes/kubernetes/kind/).
Expand Down Expand Up @@ -77,7 +78,7 @@ resolves the `cluster:` profile to a concrete kubeconfig context via the generic
4. None given → current-context of the default kubeconfig (matches
`kubectl` with no flags).

`charly fleet add vm:k3s-srv` (or any deploy whose layers include
`charly deploy add vm:k3s-srv` (or any deploy whose layers include
`k3s-server`) provisions a cluster whose kubeconfig is merged into the
default kubeconfig under a context named after the deploy (the plugin-side
`k3s-post-provision` finalization dispatched by `candy/plugin-fleet`'s
Expand Down Expand Up @@ -228,7 +229,7 @@ charly's core binary.
PLUGIN-SIDE `hostConfigResolveVmState` → `sdk/loaderkit.ResolveVmStateViaExecutor`
read (the config-resolve HostBuild seam is DELETED, K-wave 2 cone R2 bank D —
the SAME read `candy/plugin-vm`'s own `hostConfigResolve` + `candy/plugin-deploy-vm`'s
`resolvePriorVmState` use, R3 — a FIX-ROUND regression fix: a direct
`resolvePriorVmState` use, R2 — a FIX-ROUND regression fix: a direct
`deploykit.LoadDeployConfigForRead` call from this out-of-process plugin
silently found nothing every time, since `deploykit.DeployStateHost` is
wired only by charly-core's own `init()`), then calls `mergeKubeconfig`
Expand Down Expand Up @@ -263,7 +264,7 @@ all were removed when the verb was externalized.
run), the plan-step vocabulary, and how the provider registry dispatches
declarative verbs.
- [`/charly-kubernetes:kubernetes`](/recipes/kubernetes/kubernetes/) — deploying images to a Kubernetes cluster
(`kind: kubernetes` cluster templates, Kustomize generation, `charly fleet`).
(`kind: kubernetes` cluster templates, Kustomize generation, `charly deploy`).
- [`/charly-internals:plugin`](/recipes/internals/plugin/) — the Provider model and the out-of-process
plugin dispatch the `kube:` verb rides on.
- [`/charly-infrastructure:k3s`](/recipes/infrastructure/k3s/) — the k3s-server / k3s-agent candies whose
Expand Down
10 changes: 6 additions & 4 deletions src/content/docs/recipes/kubernetes/kind.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ a `target: local` deploy on the host (or a box that runs kind inside).

## Engine selection — KIND_EXPERIMENTAL_PROVIDER

The engine is charly's own engine word (`podman`/`docker`/`nerdctl`), which
maps 1:1 to kind's `KIND_EXPERIMENTAL_PROVIDER`. The `kindcluster:` deploy's
The engine is charly's own engine word (`podman`/`docker`/`containerd`), mapped
to kind's `KIND_EXPERIMENTAL_PROVIDER` through the engine capability data
(`podman` → `podman`, `docker` → `docker`, `containerd` → `nerdctl`); a word
outside the engine vocabulary is refused. The `kindcluster:` deploy's
`engine:` field (or the resolved `engine.run`) drives it; there is no
separate selector.

Expand All @@ -42,7 +44,7 @@ separate selector.
|--------|--------------|
| `docker` | A reachable daemon (rootless or rootful). Nothing else. |
| rootless `podman` | cgroup v2 + a log driver kind can tail (`k8s-file`); raised pids/inotify limits. |
| rootless `nerdctl` | Rootless containerd running (`containerd-rootless-setuptool.sh install`). |
| rootless `containerd` | Rootless containerd running (`containerd-rootless-setuptool.sh install`); the nerdctl CLI drives it. |

kindnet is kind's CNI; there is **no Traefik and no ServiceLB**, so the
`kube: addons` check verb (which waits on the k3s addon stack) does NOT
Expand All @@ -65,7 +67,7 @@ kind nodes have their own image store, separate from the host engine's. A
locally-built image must be loaded in — and `kind load docker-image` is
BROKEN on podman (measured: "image not present locally" despite the image
existing). The provider uses the engine-agnostic `kind load image-archive`
path, so it works on docker, podman, and nerdctl.
path, so it works on docker, podman, and containerd.

## Teardown

Expand Down
28 changes: 14 additions & 14 deletions src/content/docs/recipes/kubernetes/kubernetes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "kubernetes"
description: "`charly fleet add`, `charly fleet from-box`, Kustomize manifest generation, cluster profiles, Kubernetes deployments, `deploy:` block in deploy spec, or OCI-label capabilities."
description: "`charly deploy add`, `charly deploy from-box`, Kustomize manifest generation, cluster profiles, Kubernetes deployments, `deploy:` block in deploy spec, or OCI-label capabilities."
---

<!-- DO-NOT-EDIT — generated by `charly docs generate`; the opencharly/docs repo's deploy workflow regenerates this tree against its pinned charly checkout. -->
Expand All @@ -13,19 +13,19 @@ description: "`charly fleet add`, `charly fleet from-box`, Kustomize manifest ge

OpenCharly can deploy built images to a Kubernetes cluster by emitting a Kustomize `base/` + `overlays/` tree. The deployment schema stays **target-agnostic** — the deploy node describes *what the workload needs* (kind, replica, resources, exposure, storage, probes); a per-cluster **`kind: kubernetes` cluster template** (the `kubernetes:` entity, which absorbed the former cluster-profile file) supplies the Kubernetes-specific knobs (storage class, ingress class, cert issuer, secret backend).

Every box runtime contract is baked into OCI labels at build time, so **a Kubernetes deploy is possible without access to `charly.yml`** — the `charly fleet from-box` verb reads capabilities from the pushed image alone.
Every box runtime contract is baked into OCI labels at build time, so **a Kubernetes deploy is possible without access to `charly.yml`** — the `charly deploy from-box` verb reads capabilities from the pushed image alone.

A **local-cluster sibling** reuses this same Kustomize + `kubectl` machinery: `kindcluster` (`kind: kindcluster`) runs kind on the operator's engine and is served by the `deploy:kindcluster` provider beside `deploy:kubernetes` in `candy/plugin-kube`. See [`/charly-kubernetes:kind`](/recipes/kubernetes/kind/).

## Quick reference

| Action | Command | Description |
|---|---|---|
| Add Kubernetes deploy | `charly fleet add <name> <ref>` | Read BoxConfig + the deploy node + the `kind: kubernetes` cluster template; emit `.opencharly/k8s/<name>/` Kustomize tree |
| Source-less deploy | `charly fleet from-box <registry/name:tag> [name] --cluster <name>` | Deploy from OCI labels only — no `charly.yml` needed (see Part F.10) |
| Sync to cluster | `charly fleet sync <name>` | `kubectl apply -k .opencharly/k8s/<name>/overlays/default` |
| Show generated manifests | `charly fleet show <name>` | `kubectl kustomize …` — see what would apply |
| Delete Kubernetes deploy | `charly fleet del <name>` | Remove overlay dir; base stays if other instances reference it |
| Add Kubernetes deploy | `charly deploy add <name> <ref>` | Read BoxConfig + the deploy node + the `kind: kubernetes` cluster template; emit `.opencharly/k8s/<name>/` Kustomize tree |
| Source-less deploy | `charly deploy from-box <registry/name:tag> [name] --cluster <name>` | Deploy from OCI labels only — no `charly.yml` needed (see Part F.10) |
| Re-apply to cluster | `charly deploy add <name>` | Re-materialize the tree and `kubectl apply -k` it against the operator's kubeconfig — the apply IS the deploy (`candy/plugin-kube/deploy.go`) |
| Show generated manifests | `charly deploy show <name>` | `kubectl kustomize …` — see what would apply |
| Delete Kubernetes deploy | `charly deploy del <name>` | Remove overlay dir; base stays if other instances reference it |

## The three-layer model

Expand Down Expand Up @@ -145,7 +145,7 @@ New cluster = write a new `kind: kubernetes` template; zero deploy changes.
└── kustomization.yaml # namespace override + patches from deploy.patches:
```

Apply: `kubectl apply -k .opencharly/k8s/<name>/overlays/<instance>` (or `charly fleet sync <name>`).
Apply: `kubectl apply -k .opencharly/k8s/<name>/overlays/<instance>` (or re-run `charly deploy add <name>`).

**Egress validation.** Every manifest is validated through `validateEgressValue`/`writeYAML`
(candy/plugin-kube/materialize.go) before it
Expand All @@ -154,33 +154,33 @@ is written: workload / service / pvc / ingress against the `#K8sObject` envelope
`kustomization.yaml` against `#Kustomization`. A structurally-broken manifest fails
deploy generation instead of reaching the cluster. Owned by [`/charly-internals:egress`](/recipes/internals/egress/).

## Source-less deploy — `charly fleet from-box`
## Source-less deploy — `charly deploy from-box`

Proves the self-contained image invariant: a deploy pipeline with **no access to `charly.yml`** can still produce a correct Kustomize tree.

```bash
# On a machine that doesn't have the source repo:
charly fleet from-box quay.io/myorg/openclaw:v2 openclaw \
charly deploy from-box quay.io/myorg/openclaw:v2 openclaw \
--cluster production --namespace apps
# Reads: OCI labels (capabilities) + the `production` kind:kubernetes cluster template
# + ~/.config/charly/charly.yml (if present, for per-machine overrides)
# Emits: .opencharly/k8s/openclaw/base/ + overlays/default/
charly fleet sync openclaw # kubectl apply -k ...
charly deploy add openclaw # kubectl apply -k ...
```

## Relevant code

- `spec/spec/cue_types_gen.go` (generated) — `KubernetesDeploy` (`KubernetesDeployConfig`, the deploy's `deploy:` deploy-knobs block: namespace / workload override / patches / raw; the former `charly/k8s_config.go` is DELETED, K-wave 2)
- `spec/spec/cue_types_gen.go` (generated) — `KubernetesSpec` (the `kind: kubernetes` cluster template; absorbed the former `ClusterProfile` / `clusters/*.yaml`)
- `candy/plugin-kube/preresolve.go` — the PLUGIN-side `deploy:kubernetes` preresolver (F6/FINAL-K5-unit-6a, relocated from the DELETED `charly/k8s_deploy_preresolve.go`). `target: kubernetes` is an EXTERNAL deploy substrate (F1): it resolves out-of-process to candy/plugin-kube's `deploy:kubernetes` provider (beside its `kube:` verb), NOT a compiled-in deploy target. The preresolver resolves the image Capabilities + the `kind: kubernetes` cluster template by self-loading the project PLUGIN-SIDE (`sdk/loaderkit.ResolveKubernetesEntityViaExecutor`, K-wave W3a A3-phase-2 — no HostBuild round trip remains for this lookup), runs `materializeKustomize` (`materialize.go`) — which InvokeProviders the compiled-in `verb:k8sgen`/`OpEmit` for the manifest docs and `verb:egress`/`OpValidate` to validate each one, entirely peer-to-peer, no host round trip — and ships the egress-validated overlay path in `DeployVenue.Substrate`; the plugin runs `kubectl --context <ctx> apply -k`. The user-facing surface (schema, cluster templates, Kustomize output) is unchanged. See [`/charly-internals:install-plan`](/recipes/internals/install-plan/) + [`/charly-internals:plugin`](/recipes/internals/plugin/).
- `charly/k8s_generate.go` / `host_build_k8s_generate.go` are DELETED — the former "k8s-generate-kustomize" HostBuild seam they served is retired (K5-A item 6): the Kustomize GENERATION now runs entirely inside `candy/plugin-kube/materialize.go` (see above), and `candy/plugin-fleet/deploy_from_box.go`'s `charly fleet from-box` path calls `materializeKustomize` the same way. The generator itself still lives in `candy/plugin-k8sgen/k8sgen.go`, reached over `verb:k8sgen` peer dispatch, not a core shim
- `charly/k8s_generate.go` / `host_build_k8s_generate.go` are DELETED — the former "k8s-generate-kustomize" HostBuild seam they served is retired (K5-A item 6): the Kustomize GENERATION now runs entirely inside `candy/plugin-kube/materialize.go` (see above), and `candy/plugin-fleet/deploy_from_box.go`'s `charly deploy from-box` path calls `materializeKustomize` the same way. The generator itself still lives in `candy/plugin-k8sgen/k8sgen.go`, reached over `verb:k8sgen` peer dispatch, not a core shim
- `candy/plugin-k8sgen/` — the compiled-in Kustomize GENERATOR candy (M13): `k8sgen.go` (the ported workload/service/pvc/ingress/kustomization builders + the workload-kind heuristic + `GenerateTree`), serving `verb:k8sgen`/`OpEmit` over `spec.KubernetesGenInput` → `spec.KubernetesGenReply` manifest docs; no client-go (split from the heavy external plugin-kube so it resolves in the project-less from-box path)
- `candy/plugin-fleet/deploy_from_box.go` — the plugin-side `charly fleet from-box` Kubernetes path (`--cluster`), calling `materializeKustomize` the same way (`charly/fleet_from_box_cmd.go` is DELETED, K-wave 2 cone R2 — the pod path lives in `candy/plugin-fleet/from_box_pod.go`)
- `candy/plugin-fleet/deploy_from_box.go` — the plugin-side `charly deploy from-box` Kubernetes path (`--cluster`), calling `materializeKustomize` the same way (`charly/fleet_from_box_cmd.go` is DELETED, K-wave 2 cone R2 — the pod path lives in `candy/plugin-fleet/from_box_pod.go`)
- `sdk/deploykit/capabilities.go` — `CapabilityLabelMap` + completeness check, over `spec.BoxMetadata` (`charly/capabilities.go`, this row's former citation, no longer exists — see [`/charly-internals:capabilities`](/recipes/internals/capabilities/))

## Related skills

- [`/charly-core:deploy`](/recipes/core/deploy/) — unified `charly fleet add`/`del` verb; Kubernetes is one of the five deploy substrates
- [`/charly-core:deploy`](/recipes/core/deploy/) — unified `charly deploy add`/`del` verb; Kubernetes is one of the five deploy substrates
- [`/charly-internals:capabilities`](/recipes/internals/capabilities/) — OCI label contract the Kubernetes generator reads from
- [`/charly-internals:install-plan`](/recipes/internals/install-plan/) — shared IR across build + deploy targets
- [`/charly-internals:egress`](/recipes/internals/egress/) — the CUE egress gate that validates every generated manifest before write (`#K8sObject` / `#Kustomization`)
Loading