Skip to content

plugin-kube

Placement runtime (out-of-process over gRPC)
Source github.com/opencharly/charly/candy/plugin-kube
Version 2026.174.1200
Candy plugin-kube

This plugin is not listed in charly/charly.yml’s compiled_plugins:. It is not part of the shipped binary: charly builds and loads it out-of-process over gRPC when a plan references one of its words (the coexist path).

The reserved words this plugin serves:

  • k8s — deploy class
  • kube — verb class

OUT-OF-TREE charly plugin owning ALL Kubernetes cluster interaction: the kube cluster-probe check VERB, the deploy:k8s SUBSTRATE (the target: k8s workload deploy, F1 — kubectl apply -k on the host-generated Kustomize tree), AND the k3s post-provision finalization (S3, FINAL/K5 unit 6 — relocated wholesale from charly/k3s_post.go: the guest-forward kubeconfig rewrite + the ~/.kube/config merge) the k3s-server / target:k8s deploy seam needs — a standalone Go module (go.mod + main.go) that speaks the Kubernetes API (client-go dynamic + clientcmd) over go-plugin gRPC via the charly plugin SDK (github.com/opencharly/sdk). charly’s loader fetches this candy’s repo, go-builds the provider binary on the HOST, and serves it OUT-OF-PROCESS via LocalTransport — so the k8s.io/client-go + k8s.io/apimachinery dependency lives HERE, out of charly’s core go.mod (the goadb-analog of candy/plugin-adb), while kube: authoring stays unchanged: the kube: <method> sugar desugars to plugin/plugin_input — the method + every kube-exclusive modifier ride the input map, validated against this plugin’s own #KubeInput — and dispatches through the provider registry exactly like a built-in (ResolveVerb → grpcProvider → invokeVerbProvider hands it the full desugared #Op, after the host pre-resolves any cluster: profile to a concrete kube_context in the input map); target: k8s resolves to this plugin’s deploy:k8s provider over the E3b reverse channel — THIS plugin’s own preresolve.go (F6, FINAL/K5 unit 6a; dispatched via the generalized deploy_preresolve.go:wireDeployPreresolver seam) resolves the cluster template + image Capabilities into the egress-validated Kustomize tree, reaching the host’s “deploy-entity-resolve” + “k8s-generate- kustomize” HostBuild seams, and this plugin applies it. Provides the full 13-method probe surface (nodes/wait-nodes/pods/wait-ready/ingress/ingressclass/storageclass/ service/lb-external-ip/addons/apply/delete/raw), the deploy:k8s substrate, PLUS the internal k3s-post-provision deploy seam (this plugin’s own k3s_post.go: the guest-forward kubeconfig rewrite via the “deploy-entity-resolve” HostBuild seam, then the kubeconfig merge into ~/.kube/config). The R10 consumers are the check-k3s bed (the kube: verb, via the k3s-server candy) and the check-k8s-deploy bed (the deploy:k8s substrate).

The CUE schema below is the authoritative grammar for this plugin’s input. It is the same single source that generates the plugin’s Go parameter types and answers the runtime Describe RPC, so this page cannot disagree with either.

// This out-of-tree plugin's OWN CUE schema, served over the Describe channel — the
// typed plugin_input for the `kube` cluster-probe check verb. It is the SINGLE
// SOURCE for this plugin's params, used two ways (the same contract core `spec` and
// the http plugin use):
//
// 1. GENERATE the Go param struct — `cue exp gengotypes` (driven by task cue:gen,
// which wraps this with `package params` + `@go(params)`) emits
// ../params/cue_types_gen.go, so the provider decodes plugin_input into a TYPED
// struct, never a hand-parsed map.
// 2. VALIDATE authored input AT RUNTIME — the plugin serves this source over the
// Describe channel; the host splices it onto the base (base ++ plugin) and
// validates every authored `kube:` step's plugin_input against #KubeInput.
//
// Since the schema-compaction cutover the per-verb fields left core #Op: a step's
// `kube: <method>` sugar desugars to the internal plugin/plugin_input pair, the
// method name rides the input's `method` key (the former core #KubeMethod enum),
// and every kube-exclusive modifier (name/namespace/label/cluster/manifest/
// kube_kind/kube_context/kubeconfig/kube_count/kube_resource/kube_group/
// kube_version/json/artifact_key/deploy_name) lives HERE. Only the genuinely SHARED
// step modifiers (timeout, the exit_status/stdout/stderr matchers, context, …) stay
// on core #Op, read off the step Op by the provider. The host preresolver still
// rewrites a `cluster:` profile to a concrete `kube_context` — now into the input map
// (charly/k8s_config.go) — and charly/k8s_plugin.go synthesizes the internal
// {method: k3s-post-provision, artifact_key, deploy_name} input (S3, FINAL/K5 unit 6 —
// the k3s post-provision finalization — kubeconfig retrieval, guest-forward rewrite,
// and the kubeconfig merge — moved wholesale into this plugin from charly/k3s_post.go;
// the VM-forward resolution reaches the host ONLY via the generic
// "deploy-entity-resolve" HostBuild seam, over an InvokeWithExecutor-carried broker).
//
// SELF-CONTAINED: it references NO base def, so it compiles standalone (the SDK's
// serve-side check + gengotypes) AND splices onto the base (base ++ plugin is a
// def-name collision check, not a base-reference resolver).
//
// The plugin ALSO serves deploy:k8s (the `target: k8s` substrate) — that capability
// keeps its authoring contract on core #Deploy / #K8s and carries NO plugin_input,
// so no input def for it lives here.
// #KubeInput is the `kube` verb's plugin_input: the method name plus its
// method-exclusive modifiers.
#KubeInput: {
// method — the kube method name (the former core #KubeMethod enum plus the
// internal k3s-post-provision the host synthesizes; the verb's PRIMARY input
// field, so `kube: nodes` desugars to {method: "nodes"}).
method: ("nodes" | "wait-nodes" | "pods" | "wait-ready" | "ingress" | "ingressclass" | "storageclass" | "service" | "lb-external-ip" | "addons" | "apply" | "delete" | "raw" | "k3s-post-provision") @go(Method,type=string)
// name / namespace / label — resource identity + selector.
name?: string
namespace?: string
label?: string
// cluster — a kind:k8s cluster template name; the HOST preresolves it to a
// concrete kube_context (findK8sSpec needs the project loader) and leaves the
// authored key in place, so the input def admits both.
cluster?: string
// manifest — the multi-doc YAML path (apply/delete).
manifest?: string
// kube_kind / kube_count — wait-ready's workload kind + wait-nodes' Ready count.
kube_kind?: string @go(KubeKind)
kube_count?: int @go(KubeCount,type=int)
// kubeconfig / kube_context — the cluster-selection pair (kubeconfig path +
// context) an authored step may set explicitly.
kubeconfig?: string
kube_context?: string @go(KubeContext)
// kube_resource / kube_group / kube_version / json — the raw escape hatch's
// GVR + JSON output toggle.
kube_resource?: string @go(KubeResource)
kube_group?: string @go(KubeGroup)
kube_version?: string @go(KubeVersion)
json?: bool @go(JSON)
// artifact_key / deploy_name — the k3s-post-provision payload (S3, FINAL/K5 unit 6):
// artifact_key is the ENTITY-scoped identity (the shared per-VM cluster cache dir +
// kubeconfig context); deploy_name is the real per-deploy (domain) identity the
// guest-forward port-forward lookup keys off. See charly/k8s_plugin.go.
artifact_key?: string @go(ArtifactKey)
deploy_name?: string @go(DeployName)
}

See also the candy reference for this candy’s install surface.