refactor(driver-vm): replace OCI registry client with containerd shim#2313
refactor(driver-vm): replace OCI registry client with containerd shim#2313ericcurtin wants to merge 1 commit into
Conversation
|
There are only two implementations of the OCI protocol that have been widely battle-tested across the bazillion OCI registries in the world: https://github.com/containerd/containerd and everything that wraps these like docker, podman, skopeo, k8s, etc. The protocol is highly ossified, hard to test many of these incantations as they are in private data-centre's we cannot access. Using oci-client crate is a mistake. I propose a little bit of golang just for this transport component. containerd as a library gets the nod in this PR because it's a CNCF graduated project, it's the default runtime under most Kubernetes distributions (GKE, EKS, AKS), it sits inside Docker Engine, and its image-handling code paths (the remotes resolver and the newer transfer service) are exercised billions of times a day. containerd also supports Windows, Linux, macOS. Last I tried "containers/image" it isn't buildable or functioning on Windows. |
|
Thanks for expanding on the motivation here. I agree that registry compatibility At the same time, I do not see a concrete registry failure or required use case Before replacing the current implementation, however, I would prefer that we The current code combines several distinct responsibilities inside the VM
PR #2313 changes several of these responsibilities at once while they remain Conceptually, I would like the VM rootfs pipeline to look more like: The exact interface names are less important than keeping image acquisition and The existing We should also evaluate whether more of the acquisition and unpacking path can If we confirm compatibility gaps in the current Rust implementation and decide This separation is also relevant to PR #2312. I therefore suggest staging this work as follows:
My request is not to retain the existing OCI implementation indefinitely. It is |
b05d386 to
4025144
Compare
|
Agreed the boundary should exist eventually. I'll open a follow-up issue to extract ImageSource/ImageUnpacker once #2312 lands, so the interface is designed against two real implementations rather than speculatively. In the meantime, I've documented why umoci and the containerd archive unpacker coexist (host vs. guest trust boundary) so that's no longer an open question in this PR. |
The VM driver's registry pull and layer-unpack logic was a hand-rolled OCI client (auth, manifest parsing, per-layer download, digest verification) plus a from-scratch tar-layer merge that only understood plain files, dirs, symlinks, and OCI whiteouts. Replace it with containerd's own Go client libraries (core/remotes/docker, core/content/local, pkg/archive) for OCI-correct registry resolve/fetch/auth and layer application, including opaque dirs, xattrs, and device/fifo entries. No containerd daemon is involved: the new goshim/ Go module links those packages directly and builds into a small cgo shared library (libopenshell_containerd_shim), loaded at runtime via libloading the same way this crate already loads libkrun. - goshim/: exports ContainerdResolveDigest, ContainerdPullImage, and ContainerdUnpackLayout. - src/containerd_shim.rs: dynamic loader and safe Rust wrappers, mirroring ffi.rs's LibKrun pattern. - src/driver.rs: removed the manual registry client, layer-merge, and OCI layout writer (~1000 net lines); both pull paths now call the shim. - build.rs / embedded_runtime.rs: embed the shim like libkrun/libkrunfw/gvproxy. - tasks/scripts/vm/build-containerd-shim.sh: builds the shim natively or cross-compiled, wired into vm:setup and CI. - Dropped oci-client, flate2, sha2; added go to mise.toml/mise.lock. Registry auth env vars are unchanged. Per-layer pull progress is now a single event instead of one per layer, since the whole pull happens in one blocking call into the shim. Verified against a live registry (docker.io/library/busybox) through the built shared library via libloading, and cross-built for macOS with docker buildx/osxcross. Signed-off-by: Eric Curtin <eric.curtin@docker.com>
4025144 to
f80f1a0
Compare
Summary
The VM driver's registry pull and layer-unpack logic was a hand-rolled OCI
client (auth, manifest parsing, per-layer download, digest verification)
plus a from-scratch tar-layer merge that only understood plain files,
dirs, symlinks, and OCI whiteouts.
Replace it with containerd's own Go client libraries (
core/remotes/docker,core/content/local,pkg/archive) for OCI-correct registry resolve/fetch/authand layer application, including opaque dirs, xattrs, and device/fifo entries.
No containerd daemon is involved: the new
goshim/Go module links thosepackages directly and builds into a small cgo shared library
(
libopenshell_containerd_shim), loaded at runtime vialibloadingthe sameway this crate already loads
libkrun.Changes
goshim/: exportsContainerdResolveDigest,ContainerdPullImage, andContainerdUnpackLayout.src/containerd_shim.rs: dynamic loader and safe Rust wrappers, mirroringffi.rs'sLibKrunpattern.src/driver.rs: removed the manual registry client, layer-merge, and OCIlayout writer (~1000 net lines); both pull paths now call the shim.
build.rs/embedded_runtime.rs: embed the shim likelibkrun/libkrunfw/gvproxy.tasks/scripts/vm/build-containerd-shim.sh: builds the shim natively orcross-compiled, wired into
vm:setupand CI.oci-client,flate2,sha2; addedgotomise.toml/mise.lock.Registry auth env vars are unchanged. Per-layer pull progress is now a single
event instead of one per layer, since the whole pull happens in one blocking
call into the shim.
Testing
cargo fmt --all -- --check/cargo clippy --workspace --all-targets -- -D warningspasscargo test -p openshell-driver-vmpassesopenshell-driver-vmwith real runtime artifacts embeddedmarkdownlint-cli2passes on the changed READMEdocker.io/library/busybox) through the builtshared library via
libloading: resolve digest, pull into an OCI layout, unpacka layer, and error propagation for a nonexistent image. This caught and fixed a
real bug:
platforms.Onlydoesn't fail closed for cross-arch requests the wayplatforms.OnlyStrictdoes.docker buildx/osxcross, producing areal Mach-O arm64 binary with the shim cross-compiled and embedded
driver's own image-pull path is exercised by the verification above instead
Design notes (from review)
rootfs boots the guest, so no VM exists yet to unpack it inside of. Every other
(less trusted, user-requested) image still goes through the existing guest-side
umoci raw unpackpath, so archive extraction of untrusted registry content neverruns against host-owned storage. Documented in the README and in
pull_and_unpack_registry_image's doc comment.ImageSource/ImageUnpacker/VmRootfsMaterializerinterfaces before landing this, and to stage this behind docs(vm): clarify MicroVM driver use cases and limitations #2332, is a real
architectural question I want to answer in review rather than pre-empt here.
Known follow-ups
cargo-about/syftyet; this isthe first Go code in the repo.
from the shim if finer-grained CLI watch detail is needed later.
Checklist