Support publishing additional container ports in thv run#3892
Support publishing additional container ports in thv run#3892Sanskarzz wants to merge 11 commits intostacklok:mainfrom
Conversation
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3892 +/- ##
==========================================
- Coverage 68.10% 67.98% -0.13%
==========================================
Files 461 461
Lines 46700 46760 +60
==========================================
- Hits 31804 31788 -16
- Misses 12131 12177 +46
- Partials 2765 2795 +30 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
jerm-dro
left a comment
There was a problem hiding this comment.
Mostly LGTM to me. Can you manually verify this fixes the user's reported issue by running this locally? A series of screenshots or video will do.
pkg/networking/port_test.go
Outdated
| if tt.wantError { | ||
| if err == nil { | ||
| t.Errorf("ParsePortSpec(%s) expected error but got nil", tt.portSpec) | ||
| } | ||
| return | ||
| } | ||
|
|
||
| if err != nil { | ||
| t.Errorf("ParsePortSpec(%s) unexpected error: %v", tt.portSpec, err) | ||
| return | ||
| } | ||
|
|
||
| if tt.expectedHostPort != "" && hostPort != tt.expectedHostPort { | ||
| t.Errorf("ParsePortSpec(%s) hostPort = %s, want %s", tt.portSpec, hostPort, tt.expectedHostPort) | ||
| } | ||
|
|
||
| if tt.expectedHostPort == "" && hostPort == "" { | ||
| t.Errorf("ParsePortSpec(%s) hostPort is empty, want random port", tt.portSpec) | ||
| } | ||
|
|
||
| if containerPort != tt.expectedContainer { | ||
| t.Errorf("ParsePortSpec(%s) containerPort = %d, want %d", tt.portSpec, containerPort, tt.expectedContainer) | ||
| } |
There was a problem hiding this comment.
nit: can you rewrite these assertions to use the require package (e.g.require.Equal or require.True)?
This will make the tests less nested and easier to read.
There was a problem hiding this comment.
I have rewritten the nested if statements into cleaner require assertions in pkg/networking/port_test.go as requested.
| hostPortStr := bindings[0].HostPort | ||
| if hostPortStr == "" || hostPortStr == "0" { | ||
| hostPort = networking.FindAvailable() | ||
| if hostPort == 0 { | ||
| return nil, 0, fmt.Errorf("could not find an available port") | ||
| } | ||
| bindings[0].HostPort = fmt.Sprintf("%d", hostPort) | ||
| portBindings[key] = bindings | ||
| } else { | ||
| var err error | ||
| hostPort, err = strconv.Atoi(hostPortStr) | ||
| if err != nil { | ||
| return nil, 0, fmt.Errorf("failed to convert host port %s to int: %w", hostPortStr, err) | ||
| } | ||
| } |
There was a problem hiding this comment.
Is this the fix for:
It also fixes a bug in the Docker runtime client where explicitly requested host ports were being overwritten by random ports.
?
If so, can you find a way to add a regression test and some unit tests here? The generatePortBindings function seems like it should be amenable to unit testing without any refactoring.
There was a problem hiding this comment.
Added two new regression tests for generatePortBindings to client_helpers_test.go:
TestGeneratePortBindings_NonAuxiliaryKeepsExplicitHostPort: ensures explicit host ports (e.g., "9090") are kept instead of replaced with a random port.TestGeneratePortBindings_NonAuxiliaryAssignsRandomPortForZero: ensures passing "0" correctly assigns a random available port.
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>
It looks like I need to create one or two MCP servers that are exposed on multiple ports. One of them is @jerm-dro, if you have a similar MCP server in your inventory that exposes multiple ports, could you please share it? |
jerm-dro
left a comment
There was a problem hiding this comment.
Thanks for including the screenshots of the manual validation 😃
|
|
||
| require.Contains(t, out, "8080/tcp") | ||
| require.Len(t, out["8080/tcp"], 1) | ||
| assert.Equal(t, fmt.Sprintf("%d", hostPort), out["8080/tcp"][0].HostPort) |
There was a problem hiding this comment.
nit, feel free to ignore: could also assert non-zero
There was a problem hiding this comment.
I added assert.NotEqual(t, "0", out["8080/tcp"][0].HostPort) to the test in client_helpers_test.go.
Signed-off-by: Sanskarzz <sanskar.gur@gmail.com>

Fix: #3812
Summary
This PR adds support for the
--publish / -pflag to thethv runcommand, enabling users to expose arbitrary container ports to the host, similar todocker run -p. It also fixes a bug in the Docker runtime client where explicitly requested host ports were being overwritten by random ports.Key Changes
--publishflag tothv run.networking.ParsePortSpecfor robust port specification parsing.runtime.Setupto handle multiple port bindings efficiently.pkg/container/docker/client.goto respect existingHostPortassignments ingeneratePortBindings.