diff --git a/marketplace b/marketplace index 1c156511..76b8c065 160000 --- a/marketplace +++ b/marketplace @@ -1 +1 @@ -Subproject commit 1c1565112c1034927cc25421330cb40dee56d864 +Subproject commit 76b8c0653965f0e0b9c396cedddd7fc8f21829b9 diff --git a/src/content/docs/guides/kubernetes.md b/src/content/docs/guides/kubernetes.md index a9937d85..43aceb37 100644 --- a/src/content/docs/guides/kubernetes.md +++ b/src/content/docs/guides/kubernetes.md @@ -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 @@ -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 | diff --git a/src/content/docs/recipes/distros/layer-nerdctl.md b/src/content/docs/recipes/distros/layer-nerdctl.md index 76cac1d4..3305ef3a 100644 --- a/src/content/docs/recipes/distros/layer-nerdctl.md +++ b/src/content/docs/recipes/distros/layer-nerdctl.md @@ -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 @@ -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. @@ -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. diff --git a/src/content/docs/recipes/kubernetes/check-k8s.md b/src/content/docs/recipes/kubernetes/check-k8s.md index 3c0b1908..94f345bc 100644 --- a/src/content/docs/recipes/kubernetes/check-k8s.md +++ b/src/content/docs/recipes/kubernetes/check-k8s.md @@ -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-` kubeconfig context, which a `kube:` step addresses with `kube_context:`. See [`/charly-kubernetes:kind`](/recipes/kubernetes/kind/). @@ -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 @@ -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` @@ -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 diff --git a/src/content/docs/recipes/kubernetes/kind.md b/src/content/docs/recipes/kubernetes/kind.md index 69ddd78a..fde00464 100644 --- a/src/content/docs/recipes/kubernetes/kind.md +++ b/src/content/docs/recipes/kubernetes/kind.md @@ -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. @@ -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 @@ -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 diff --git a/src/content/docs/recipes/kubernetes/kubernetes.md b/src/content/docs/recipes/kubernetes/kubernetes.md index 6ecea0c1..1d6dbdca 100644 --- a/src/content/docs/recipes/kubernetes/kubernetes.md +++ b/src/content/docs/recipes/kubernetes/kubernetes.md @@ -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." --- @@ -13,7 +13,7 @@ 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/). @@ -21,11 +21,11 @@ A **local-cluster sibling** reuses this same Kustomize + `kubectl` machinery: `k | Action | Command | Description | |---|---|---| -| Add Kubernetes deploy | `charly fleet add ` | Read BoxConfig + the deploy node + the `kind: kubernetes` cluster template; emit `.opencharly/k8s//` Kustomize tree | -| Source-less deploy | `charly fleet from-box [name] --cluster ` | Deploy from OCI labels only — no `charly.yml` needed (see Part F.10) | -| Sync to cluster | `charly fleet sync ` | `kubectl apply -k .opencharly/k8s//overlays/default` | -| Show generated manifests | `charly fleet show ` | `kubectl kustomize …` — see what would apply | -| Delete Kubernetes deploy | `charly fleet del ` | Remove overlay dir; base stays if other instances reference it | +| Add Kubernetes deploy | `charly deploy add ` | Read BoxConfig + the deploy node + the `kind: kubernetes` cluster template; emit `.opencharly/k8s//` Kustomize tree | +| Source-less deploy | `charly deploy from-box [name] --cluster ` | Deploy from OCI labels only — no `charly.yml` needed (see Part F.10) | +| Re-apply to cluster | `charly deploy add ` | 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 ` | `kubectl kustomize …` — see what would apply | +| Delete Kubernetes deploy | `charly deploy del ` | Remove overlay dir; base stays if other instances reference it | ## The three-layer model @@ -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//overlays/` (or `charly fleet sync `). +Apply: `kubectl apply -k .opencharly/k8s//overlays/` (or re-run `charly deploy add `). **Egress validation.** Every manifest is validated through `validateEgressValue`/`writeYAML` (candy/plugin-kube/materialize.go) before it @@ -154,18 +154,18 @@ 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 @@ -173,14 +173,14 @@ charly fleet sync openclaw # kubectl apply -k ... - `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 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`)