Remove the Buildx image-resolver dependency and add prerequisite refusal E2E
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 45/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- docker, go
- 領域
- cli, devops, infrastructure
調査の方向性
まず internal/app/preflight.go、internal/engine/preflight.go、internal/engine/bootstrap.go、internal/app/buildx.go の前提条件チェックを読み、次に internal/engine/plan.go と既存のサーバープローブテストを確認します。Part 1 は、bootstrap、preflight、doctor、deploy が同じ前提条件を適用し、e2e/server_probe_test.go が前提条件の 1 つが欠けているケースをカバーした時点で完了です。Part 2 では、Buildx の依存関係を削除する前に、記載された registry と credential の動作を解決する必要があります。
索引モデルが issue の本文から書いたものです。
説明
Current scope (v2026.10.1-alpha)
The inconsistent prerequisite gates in Part 1 were fixed by #146: bootstrap, read-only preflight, and deploy preflight share the runtime/Compose/Buildx prerequisite set and typed host_prerequisite_unmet refusals. ob doctor remains local-only.
The remaining work is:
- Decide and implement a replacement for server-side Buildx digest resolution, preserving the target's registry network access and declared authentication contract. A workstation-only resolver is a behavior change, not a drop-in replacement.
- Remove the Buildx prerequisite only once the replacement has an executable, tested contract.
- Exercise missing/incompatible prerequisite refusals against the real-server fixture, not only through unit fakes and happy-path server tests.
Current internal/engine/plan.go still shells out to docker buildx imagetools inspect; successful release verification does not resolve Part 2.
Original proposal and historical context
Summary
Onebox deliberately does not install host dependencies. internal/engine/bootstrap.go:79 states the policy:
Docker is an explicit host prerequisite, never an implicit network installer. The authored hook runs first so an operator may deliberately provision a pinned runtime inside the lock, fence, and journal boundary.
That policy is right and is not what this issue disputes. site/src/content/docs/start/install.mdx:188 documents it correctly, names all three prerequisites, and shows the bootstrap-hook escape hatch for operators who want a pinned installer to run inside the safety boundary.
The defect is in the enforcement of that contract, not the contract itself. Onebox asserts the prerequisite set in four places, each checks a different subset, and no two agree. An operator can pass the command whose entire purpose is to answer "could this deploy?" and still fail on a missing prerequisite.
Separately, one of the three prerequisites is a dependency Onebox does not need to have.
Checked against main at ff40790 (v2026.8.21).
Part 1 — the four gates disagree
| gate | daemon | compose plugin | buildx --format |
disk headroom | paths / collisions |
|---|---|---|---|---|---|
ob bootstrap — internal/engine/bootstrap.go:82 |
yes | no | no | no | no |
ob preflight — internal/app/preflight.go:76 |
yes | no | yes | no | yes |
deploy StepPreflight — internal/engine/preflight.go:19 |
yes | yes | no | yes | partial |
plan, lazily on first unpinned image — internal/engine/plan.go:246 |
— | — | yes | — | — |
Two consequences, both reachable today:
ob bootstrap reports success on a host that cannot deploy. It checks docker version -f '{{.Server.Version}}' and nothing else. A host with a working daemon and no Compose plugin, or with a Buildx that does not honour --format, completes bootstrap and fails two commands later.
ob preflight reports success on a host that cannot deploy. This is the worse one. Its own help text says it exists to report "a missing container runtime, a missing or incompatible Docker Buildx image resolver, a base path this account cannot write, a derived name already held by something Onebox does not own, or a missing ingress network" (cmd/ob/preflight.go:20), and it promises "every problem is reported at once rather than the first one." It never runs docker compose version. That check lives only in internal/engine/preflight.go:37, which runs as a deploy step. So the read-only command built to be the pre-deploy gate cannot detect a prerequisite the deploy gate refuses on.
e2e/ covers neither case: the server suite bootstraps a Lima guest that has all three, so no test asserts what a partially-provisioned host does.
No version floor anywhere
There is no minimum version for the daemon, the Compose plugin, or Buildx — not in internal/app/buildx.go, internal/engine/preflight.go, internal/engine/bootstrap.go, or the install documentation. The Buildx probe is a capability probe against --help output and reports only imagetools inspect --format available.
That was a deliberate and good choice for the failure it was written against (#114): Ubuntu's docker-buildx 0.30.1 accepts --format, ignores it, and exits 0, so a version string would not have caught it either. But the capability probe and a version floor answer different questions, and the effective requirement — Buildx new enough to return a digest from imagetools inspect --format, confirmed working at upstream v0.33.0 and broken at Ubuntu's 0.30.1 — is currently written down nowhere an operator can read before provisioning.
Proposed fix
One exported prerequisite check with one result type, called by ob bootstrap, ob preflight, ob doctor, and the deploy StepPreflight. Every caller asserts the same set; callers differ only in how they report, not in what they check.
ob bootstraprefuses a host missing any prerequisite, after the bootstrap hook has had its chance, rather than accepting a host that cannot deploy.ob preflightgains the Compose check, so its "every problem at once" promise holds.- Each check reports the observed version alongside the capability result, so
ob doctoroutput is useful in a bug report. - Document the minimum versions in
install.mdxbeside the existing prerequisite paragraph, keeping the capability probe as the authority when a client lies about what it supports. e2e/server_probe_test.gogains a case that removes a prerequisite from the guest and asserts each gate refuses with the same code.
Part 2 — Buildx is a dependency Onebox does not need
Onebox uses Buildx for exactly one operation. internal/engine/plan.go:256:
docker buildx imagetools inspect IMAGE --format '{{json .Manifest.Digest}}'
It is never used to build. Production does not build images (internal/engine/plan.go:233), so a build: context is a local-development affordance only. The single production use is reading a manifest digest to bind a tag to an immutable reference — one registry HTTP GET, expressed as a shell-out to a CLI whose output format is not a stable contract.
Three recorded defects share that root cause, all from the real-server work in #44:
- Ubuntu's
docker.iopackage installs a working daemon and no Buildx, so a correctly-followed "install Docker" instruction yields a host that cannot plan. docker-buildx 0.30.1accepts--format, ignores it, and exits 0, producingregistry inspection returned exit 0 without a valid sha256 digest— a client defect reported as a registry error.internal/app/buildx.goexists solely to detect a CLI lying about its own capabilities.imagetools inspectwalks every child manifest in an index — sixteen requests forbusybox:1.37— so a few deploys exhaust Docker Hub's anonymous quota. A registry client reads the index manifest alone.
Resolving the digest in-process with an OCI registry client removes all three. go.mod already carries github.com/distribution/reference, and internal/imageref already parses references, so the reference-handling half exists.
The objection this has to answer
e.T.Run is the SSH transport. Digest resolution currently happens on the server, using the server's network path to the registry and the credentials written by docker login during bootstrap (internal/engine/bootstrap.go:99). Moving it in-process moves it to the workstation running ob, which is a real behaviour change:
- A registry reachable only from the server's network — a VPN-scoped or internally-routed private registry — would stop resolving.
- Credentials would come from the workstation's Docker config rather than the server's, so
registries:withpassword_envwould need to authenticate the in-process client directly.
Neither is hypothetical, and the second interacts with how registries: is specified today. Options, roughly in order of preference:
- Resolve in-process from the workstation, authenticating with the already-declared
registries:credentials rather than a scraped credential store. Keep a documented fallback for the server-only-reachable-registry case. - Resolve in-process, but over the existing SSH transport as a tunnelled connection, preserving the server's network path while dropping the CLI dependency.
- Keep resolution on the server, and ship a small static helper rather than depending on the operator's Buildx.
Option 1 is the smallest and matches how ob already treats the registry as something the project declares. Option 2 preserves current behaviour exactly at more cost. Either way, Buildx leaves the prerequisite list and internal/app/buildx.go is deleted.
Non-goals
- Installing Docker, Compose, or anything else. The policy in
bootstrap.go:79stands, and this issue depends on it: the tool can only be strict about prerequisites because it is honest about not providing them. - Replacing the Compose plugin. That is orchestration, and reimplementing it is a rewrite, not a dependency removal.
- Managing the daemon's lifecycle, version, or upgrades.
- Building images in production.
Sequencing
Part 1 is a self-contained defect with no dependency and should land first; it is worth fixing even if Part 2 is rejected. Part 2 shrinks the set Part 1 enforces, so doing it second means deleting a check rather than reworking one.
Open question
Whether a reference bootstrap hook belongs in the repository — a pinned, idempotent, per-distro hooks/bootstrap-docker.sh that an operator opts into by naming it in hooks.bootstrap.run. It would give the ergonomics of an installer with none of the implicit-curl | sh problem the current policy exists to avoid, since the operator still chooses to run it. The argument against is that it is the first step onto the config-management ground bootstrap.go:15 names as a non-goal.
- 主要言語
- Go
- スター
- 4
- フォーク
- 0
- 平均マージ
- 2時間 46分
- マージ済み PR(30日)
- 38
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
labstack/onebox のほかの issue
-
bug
難易度 1/5 1時間未満 初心者へのやさしさ 80/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 3/5 1〜2日 初心者へのやさしさ 65/100
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 55/100
メンテナーはふだん 1 日以内に返信
labstack/onebox の issue をすべて見る
似ている issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
open-telemetry/opentelemetry-go-compile-instrumentation#1467 ·
メンテナーはふだん 3 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
modelcontextprotocol/go-sdk#1367 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
Python 3.15 support対応中かも @amnesiaof が今日担当しました。 オープンL: python L: python:uv
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
dependabot/dependabot-core#16524 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
duplication
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
openvibely/openvibely#1443 ·
メンテナーはふだん 2 日以内に返信