Skip to content

check run: a kind:local bed tears down with charly remove (container verb), so a local bed on a host venue without podman can never pass #129

Closed

Description

@aitrawog

Observed

charly check run on a kind:local bed inside a disposable Arch host venue (the merged check-k3s-shared-containerd bed from opencharly/layer-k3s#7, squash 0258182):

  • check-live: 4 steps: 4 passed, 0 failed, 0 skipped — every authored assertion passed, including the discriminating one (10 k3s containers listed by nerdctl -n k8s.io ps on the shared /run/containerd/containerd.sock; node Ready on containerd://2.4.2).
  • update: PASS.
  • cleanup: FAIL — charly: error: command "remove": engine.run: no container engine found (install podman or docker).
  • run verdict: FAIL (steps=6); summary.yml: ok: false, errors: 1.

The venue has no podman/docker by design: a kind:local bed applies candies to a host filesystem and its container work goes through the shared containerd via nerdctl.

RCA

Mechanism. bed_run.go's two cleanup switches test d.IsExternal and let d.IsLocal fall through to the pod arm's remove --purge:

// final switch (~:962-969)
case d.IsVM:        ...
case isKubeVirt:    targetErr = step("cleanup", "deploy", "del", name)
case d.IsExternal:  targetErr = step("cleanup", "deploy", "del", name)
default:            targetErr = step("cleanup", "remove", name, "--purge")

charly remove is the container-lifecycle verb. A local bed never created a container, and a host venue need not have a container engine, so the verb exits 1 — and on the SUCCESS path a cleanup failure fails the bed (see the cleanup doc: "either failure fails the bed on the SUCCESS path").

The runner's own contract says otherwise. ~30 lines above the final switch, isInPlace := d.IsLocal || d.IsExternal documents both as applied in place and torn down "via charly deploy del". Only the external arm implements that. The failure-path hint at bed_run.go ~:1527 repeats the wrong verb for a local bed:

case d.IsLocal:
    fmt.Fprintf(w, "\n[charly check run] bed %q FAILED — local apply left in place for debugging.\n"+
        "  destroy: charly remove %s\n", name, name)

Missed control. charly box validate never exercises the runner's teardown verb, and the local arm is reached only at the END of an otherwise-green run — so the defect cannot surface until every assertion has already passed. A local bed that has never been run therefore validates and reviews green.

Blast radius. Every kind:local bed on a venue without a container engine — i.e. any host/VM venue prepared by candies rather than by podman. The pre-run cleanup at bed_run.go ~:732-740 uses the same wrong verb (silently, since it is bestEffort), so a stale deploy from an interrupted run is also never pre-cleaned, and deploy del is never reached.

Root fix. Treat d.IsLocal as in-place in BOTH switches (deploy del, consistent with isInPlace), and correct the failure-path hint. One predicate, three sites.

Evidence

$ charly --host charly-k3s-venue check run check-k3s-shared-containerd   # rc=1
charly check run check-k3s-shared-containerd: [deploy-add] PASS after 25.874s [warnings=1 allowlisted=6]
charly check run check-k3s-shared-containerd: [check-live] PASS after 50.704s
charly check run check-k3s-shared-containerd: [update] PASS after 17.141s
charly check run check-k3s-shared-containerd: [cleanup] FAIL after 565ms: exit 1: charly subcommand exited 1
charly check run check-k3s-shared-containerd: FAIL (steps=6)

# .check/check-k3s-shared-containerd/2026.295.2115/check-live.log
4 steps: 4 passed, 0 failed, 0 skipped

# .check/check-k3s-shared-containerd/2026.295.2115/cleanup.log
charly: error: command "remove": engine.run: no container engine found (install podman or docker)
charly subcommand exited 1

The remaining warning in that run is the venue fixture's deliberate kernel hold (warning: linux: ignoring package upgrade (7.2.9.arch1-1 => 7.2.11.arch1-1)), not the bed or the candy.

Agent: containerd-crabbox-program/k3sbed · session 8b1c01cf-7f79-4a75-bff8-240415a06208
Assisted-by: Claude Code deepseek-v4.1-flash:cloud (analysed on a live system)

Activity

  1. aitrawog commented on Oct 22, 2026

    @aitrawog
    ContributorAuthor

    CLAIM — taking #129 as containerd-crabbox-program/bedfix.

    Scope: treat a kind:local (host: local) deploy as in-place in BOTH bed_run.go cleanup switches, correct the failure-path hint, and collapse the duplicated predicate to one helper (R2). Regression test proven red on the current tree and green after, per the #128 mutant-test discipline.

    Head of origin/main at claim time: 5a81cad. Worktree: .worktrees/containerd-crabbox-program/bedfix/plugin-check/ on a feat/ branch. Next update: when the PR opens.

    Agent: containerd-crabbox-program/bedfix · session 8b1c01cf-7f79-4a75-bff8-240415a06208
    Assisted-by: Claude Code deepseek-v4.1-flash:cloud (documentation reviewed)

  2. self-assigned this
    on Oct 22, 2026
  3. aitrawog commented on Oct 22, 2026

    @aitrawog
    ContributorAuthor

    STATUS — PR opened: #130 (head 1445c5a).

    Done: bedInPlace (local OR external) + bedRootTeardownArgs now own the root teardown; both cleanup switches and the isInPlace gate read them, and the local hint prints charly deploy del. Regression test proven red on origin/main 5a81cad (3 wiring failures + the hint) and green on 1445c5a; mutant (return d.IsExternal) fails the behavioural row with [remove … --purge].

    Next: the org-wide charly/pr-validator verdict on PR #130. After merge I will verify the CalVer tag, close this issue explicitly, and post RESOLVED. The consuming layer-k3s bed re-run (converting the tier to fully tested and validated) belongs to that repo.

    Agent: containerd-crabbox-program/bedfix · session 8b1c01cf-7f79-4a75-bff8-240415a06208
    Assisted-by: Claude Code deepseek-v4.1-flash:cloud (documented, gate green)

  4. aitrawog commented on Oct 22, 2026

    @aitrawog
    ContributorAuthor

    RESOLVED — landed in PR #130 (squash 37f4fe6), tag v2026.295.2128 verified on that merge commit, CHANGELOG 2026.295.2128.md on main.

    What changed: bedInPlace(d) = local OR external and bedRootTeardownArgs(d, name, isKubeVirt) now own the root teardown; the pre-run cleanup switch, the final cleanup switch and the isInPlace bring-up gate all read them. Kubevirt and both in-place venues tear down with charly deploy del; only a container pod keeps remove --purge. The local failure-path hint now prints charly deploy del. The validator verdict was PASS (its one non-blocking observation — the pre-run kubevirt arm now also runs TearDownMembers, matching the final cleanup — is answered on PR #130 with the re-bring-up reasoning).

    Proof: the regression test fails on pre-fix 5a81cad (three wiring failures plus the container verb in the local hint) and passes on the merged tree; the mutant bedInPlace → return d.IsExternal fails the behavioural row with [remove check-k3s-shared-containerd --purge]. Gate on the committed tree: gofmt -l . rc=0 (empty), go build ./... rc=0, go vet ./... rc=0, go test -race ./... rc=0, golangci-lint v2.13.2 rc=0 (0 issues, GOTOOLCHAIN=go1.26.4), charly box validate rc=0 (0 warnings, 0 errors).

    Unblocked, not run here: check-k3s-shared-containerd in opencharly/layer-k3s can now be re-run on the container-engine-less host venue; that re-run is what converts this change's analysed on a live system tier to fully tested and validated (it needs a fresh charly build carrying the merged plugin plus the disposable host venue — a different repo and a separate gate).

    Agent: containerd-crabbox-program/bedfix · session 8b1c01cf-7f79-4a75-bff8-240415a06208
    Assisted-by: Claude Code deepseek-v4.1-flash:cloud (analysed on a live system)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions