feat(net): auto-remap EXPOSE host port on conflict#614
Open
G4614 wants to merge 3 commits into
Open
Conversation
When an image declares EXPOSE ports (e.g., redis:6379, docker:dind:2375), boxlite previously bound them 1:1 to the host. If the desired host port was already in use, gvproxy_create would fail and the box could not start — even though the user never explicitly requested that host port. resolve_expose_host_port() now probes the desired host port; on collision it falls back to an OS-allocated ephemeral host port. Guest still listens on the EXPOSE port; only the host binding moves. User-provided -p mappings are never remapped — they continue to fail fast via the gvproxy initErr path (covered by gvproxy_port_conflict regression). ResolvedPortMapping carries the final binding (host_port, guest_port, protocol, source) and is persisted on BoxState.port_mappings, exposed via inspect/list/REST so users can discover the resolved host port for detached boxes. Ported from upstream PR boxlite-ai#568 (only the EXPOSE auto-resolve subset; the --privileged/dind machinery remains in boxlite-ai#568 for separate consideration). Drive-by: tests/image_registries.rs needed `.clone()` after boxlite-ai#604 added Drop to PerTestBoxHome (one-char fix). Regression: expose_auto_remap_falls_back_when_desired_port_busy. Two-side verified — fails without this change ("listen tcp 0.0.0.0:6379: bind: address already in use"), passes with it (inspect shows host:<ephemeral> → guest:6379 source=auto_remap).
Locks in the scope boundary that auto-remap only touches implicit image EXPOSE ports, never explicit user `-p HOST:GUEST` (the user picked that host port deliberately). alpine has no EXPOSE, so the only host-side mapping is the explicit `-p`; assert inspect tags it Source=user with the chosen host port preserved. Two-sided: routing user `-p` through resolve_expose_host_port flips a free port to Source=auto_expose → the assertion fails; the unchanged path keeps Source=user. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap the `as u16` cast and the long eprintln! line — fixes the "Check Rust formatting" CI check on the prior commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Auto-remap an image
EXPOSEport to an OS-allocated ephemeral host port when its desired host port is busy (instead of failing fast),explicit user
-pmappings are never remapped.Test plan
Unit (
resolve_expose_host_port):_uses_desired_when_free(free → 1:1AutoExpose) and_falls_back_when_busy(busy → ephemeralAutoRemap).Integration
expose_auto_remap— pre-bind0.0.0.0:6379, thenboxlite run -d redis:alpine(image EXPOSEs 6379). Verified two-sided (resolve_expose_host_portstubbed to never remap vs applied):6379(1:1, collides with the pre-bound port)≠ 6379)Sourceauto_exposeauto_remapassert host_port != 6379atexpose_auto_remap.rs:155Guest keeps listening on 6379; only the host side moves.
Integration
user_published_port_keeps_user_source(scope guard) —boxlite run -d -p <free>:<guest> alpine(no EXPOSE, so the explicit-pis the only mapping). Verified two-sided (user-prouted throughresolve_expose_host_portvs not):-pmapping)-prouted through remap)Sourceauto_exposeuserassert Source == "user"atexpose_auto_remap.rs:273