You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Today the only way to run kubectl from a Jetpack playbook is !shell / !command / !external (grep confirms zerokubectl/helm/kustomize references anywhere in src/). examples/github_release_advanced.yml even shows the gap: it can discover a k8s version via !github_release but cannot apply it. There is no idempotent, declarative k8s task — no server-side diff for changed_when, no first-class -k kustomize, no structured get -o json exposed to later tasks.
Proposed module
A new !kubectl module implementing the standard two-trait module model:
action: get should default to -o json and expose the parsed object under result (via the existing save: machinery) so later tasks can branch on it.
Manifest: inline OR bring-your-own file
The manifest source must support both:
inline — a literal YAML block.
BYO file — a path to an existing YAML in the automation repo, so users with manifests already on disk don't have to inline them. This is the common case.
Recommended design: manifest: accepts a string; if it resolves to an existing file it is loaded as YAML, otherwise the string is treated as inline YAML. File lookup should reuse the same mechanism !copy uses for src (find_file_path, src/modules/files/copy.rs:~90) — resolved relative to the role/playbook/repo root. Anchoring to the repo root matters: it lets a playbook in playbooks/foo/ reference manifest: deploy/manifests/x.yaml at the repo root rather than a CWD-relative path — which is exactly the "automation repository" root introduced in #46 (JET_REPO_ROOT), so this issue depends on / composes with #46. (Less-magic alternative if file-existence detection is rejected: two explicit keys — manifest: inline, manifest_file: path. Either resolves the BYO case.)
Exactly one of manifest / kustomize may be set; specifying both is a validation error.
Implementation anchors
Registration (6 edits, one file):src/registry/list.rs — import + enum variant Kubectl + the four match arms (get_module/get_name/get_with/evaluate) + the category() match. The !kubectl YAML tag is the enum variant lowercased via #[serde(rename_all = "lowercase")] (list.rs:84-86), enforced by every_module_tag_is_lowercase_snake (list.rs:391).
Structured args: follow the FileAttributesInput Input/Evaluated-pair + deny_unknown_fields + template() constructor pattern (src/tasks/files.rs:27-41). manifest: and wait_for: are nested.
Reuse verbatim from src/modules/commands/shell.rs: handle.remote.run_unsafe (src/handle/remote.rs:166), CheckRc, cmd_info (src/connection/command.rs:36), build_results_map (shell.rs:173-186), save: → update_variables (src/inventory/hosts.rs:205), and failed_when/changed_when (shell.rs:129-151).
Inherit for free: all with:/and: modifiers (condition/items/delegate_to/sudo/skip_if_exists) are applied by the FSM (src/playbooks/task_fsm.rs:316-410); sudo via src/handle/remote.rs:207.
Idempotency / changed_when (the real value over !shell)
apply should compute changed_when from a server-side dry-run diff, not "always changed": kubectl apply --dry-run=server -o name vs the applied set. delete should be changed_when: false when the resource is already absent (kubectl get rc != 0).
Caveat
CommandResult { cmd, out, rc } (src/connection/command.rs:23) captures combined stdout+stderr with no separate stderr (src/connection/local.rs:148). For get -o json that is fine (stdout), but flag if any flow needs stderr isolation — that may require extending CommandResult.
Tests (TDD)
Unit-test arg → argv construction (mirror how proxmox_lxc tests only the templating layer, no subprocess): assert action: kustomize + kustomize: x yields ["apply","-k","x"], etc.
Unit-test manifest-source resolution: an inline block is passed through; a string that resolves to an existing file is loaded from disk; both manifest + kustomize set → error.
Integration (needs a cluster): apply a Namespace from an inline manifest and from a BYO file, assert changed_when true on first run / false on second; get returns the object under result.
Problem
Today the only way to run
kubectlfrom a Jetpack playbook is!shell/!command/!external(grep confirms zerokubectl/helm/kustomizereferences anywhere insrc/).examples/github_release_advanced.ymleven shows the gap: it can discover a k8s version via!github_releasebut cannot apply it. There is no idempotent, declarative k8s task — no server-side diff forchanged_when, no first-class-kkustomize, no structuredget -o jsonexposed to later tasks.Proposed module
A new
!kubectlmodule implementing the standard two-trait module model:IsTask(src/tasks/common.rs:30) — deserialized struct,evaluate()does arg templating + validation.IsAction(src/tasks/common.rs:50) —dispatch()runs the subprocess, pattern-matchesQuery/Execute/Create/Modify.action: getshould default to-o jsonand expose the parsed object underresult(via the existingsave:machinery) so later tasks can branch on it.Manifest: inline OR bring-your-own file
The manifest source must support both:
Recommended design:
manifest:accepts a string; if it resolves to an existing file it is loaded as YAML, otherwise the string is treated as inline YAML. File lookup should reuse the same mechanism!copyuses forsrc(find_file_path,src/modules/files/copy.rs:~90) — resolved relative to the role/playbook/repo root. Anchoring to the repo root matters: it lets a playbook inplaybooks/foo/referencemanifest: deploy/manifests/x.yamlat the repo root rather than a CWD-relative path — which is exactly the "automation repository" root introduced in #46 (JET_REPO_ROOT), so this issue depends on / composes with #46. (Less-magic alternative if file-existence detection is rejected: two explicit keys —manifest:inline,manifest_file:path. Either resolves the BYO case.)Exactly one of
manifest/kustomizemay be set; specifying both is a validation error.Implementation anchors
src/registry/list.rs— import + enum variantKubectl+ the four match arms (get_module/get_name/get_with/evaluate) + thecategory()match. The!kubectlYAML tag is the enum variant lowercased via#[serde(rename_all = "lowercase")](list.rs:84-86), enforced byevery_module_tag_is_lowercase_snake(list.rs:391).FileAttributesInputInput/Evaluated-pair +deny_unknown_fields+template()constructor pattern (src/tasks/files.rs:27-41).manifest:andwait_for:are nested.find_file_pathas!copydoes (src/modules/files/copy.rs:~90), anchored to the repo root (DNS output lands in the playbook dir, not the repo root — introduce a first-class "automation repository" root #46).src/modules/commands/shell.rs:handle.remote.run_unsafe(src/handle/remote.rs:166),CheckRc,cmd_info(src/connection/command.rs:36),build_results_map(shell.rs:173-186),save:→update_variables(src/inventory/hosts.rs:205), andfailed_when/changed_when(shell.rs:129-151).with:/and:modifiers (condition/items/delegate_to/sudo/skip_if_exists) are applied by the FSM (src/playbooks/task_fsm.rs:316-410);sudoviasrc/handle/remote.rs:207.Idempotency / changed_when (the real value over
!shell)applyshould computechanged_whenfrom a server-side dry-run diff, not "always changed":kubectl apply --dry-run=server -o namevs the applied set.deleteshould bechanged_when: falsewhen the resource is already absent (kubectl getrc != 0).Caveat
CommandResult { cmd, out, rc }(src/connection/command.rs:23) captures combined stdout+stderr with no separate stderr (src/connection/local.rs:148). Forget -o jsonthat is fine (stdout), but flag if any flow needs stderr isolation — that may require extendingCommandResult.Tests (TDD)
proxmox_lxctests only the templating layer, no subprocess): assertaction: kustomize+kustomize: xyields["apply","-k","x"], etc.manifest+kustomizeset → error.changed_whentrue on first run / false on second;getreturns the object underresult.