Skip to content

Commit 5189101

Browse files
committed
fix(agent/agentcontainers): fix incorrectly parsed port
1 parent 9a2f5c4 commit 5189101

File tree

2 files changed

+57
-30
lines changed

2 files changed

+57
-30
lines changed

agent/agentcontainers/containers_dockercli.go

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -319,22 +319,23 @@ func runDockerInspect(ctx context.Context, execer agentexec.Execer, ids ...strin
319319
// To avoid a direct dependency on the Docker API, we use the docker CLI
320320
// to fetch information about containers.
321321
type dockerInspect struct {
322-
ID string `json:"Id"`
323-
Created time.Time `json:"Created"`
324-
Config dockerInspectConfig `json:"Config"`
325-
HostConfig dockerInspectHostConfig `json:"HostConfig"`
326-
Name string `json:"Name"`
327-
Mounts []dockerInspectMount `json:"Mounts"`
328-
State dockerInspectState `json:"State"`
322+
ID string `json:"Id"`
323+
Created time.Time `json:"Created"`
324+
Config dockerInspectConfig `json:"Config"`
325+
Name string `json:"Name"`
326+
Mounts []dockerInspectMount `json:"Mounts"`
327+
State dockerInspectState `json:"State"`
328+
NetworkSettings dockerInspectNetworkSettings `json:"NetworkSettings"`
329329
}
330330

331331
type dockerInspectConfig struct {
332332
Image string `json:"Image"`
333333
Labels map[string]string `json:"Labels"`
334334
}
335335

336-
type dockerInspectHostConfig struct {
337-
PortBindings map[string]any `json:"PortBindings"`
336+
type dockerInspectPort struct {
337+
HostIP string `json:"HostIp"`
338+
HostPort string `json:"HostPort"`
338339
}
339340

340341
type dockerInspectMount struct {
@@ -349,6 +350,10 @@ type dockerInspectState struct {
349350
Error string `json:"Error"`
350351
}
351352

353+
type dockerInspectNetworkSettings struct {
354+
Ports map[string][]dockerInspectPort `json:"Ports"`
355+
}
356+
352357
func (dis dockerInspectState) String() string {
353358
if dis.Running {
354359
return "running"
@@ -388,20 +393,41 @@ func convertDockerInspect(raw string) ([]codersdk.WorkspaceAgentDevcontainer, []
388393
Volumes: make(map[string]string, len(in.Mounts)),
389394
}
390395

391-
if in.HostConfig.PortBindings == nil {
392-
in.HostConfig.PortBindings = make(map[string]any)
396+
if in.NetworkSettings.Ports == nil {
397+
in.NetworkSettings.Ports = make(map[string][]dockerInspectPort)
393398
}
394-
portKeys := maps.Keys(in.HostConfig.PortBindings)
399+
portKeys := maps.Keys(in.NetworkSettings.Ports)
395400
// Sort the ports for deterministic output.
396401
sort.Strings(portKeys)
397-
for _, p := range portKeys {
398-
if port, network, err := convertDockerPort(p); err != nil {
399-
warns = append(warns, err.Error())
400-
} else {
402+
// Each port binding may have multiple entries mapped to the same interface.
403+
// Keep track of the ports we've already seen.
404+
seen := make(map[int]struct{}, len(in.NetworkSettings.Ports))
405+
for _, pk := range portKeys {
406+
for _, p := range in.NetworkSettings.Ports[pk] {
407+
_, network, err := convertDockerPort(pk)
408+
if err != nil {
409+
warns = append(warns, fmt.Sprintf("convert docker port: %s", err.Error()))
410+
// Default network to "tcp" if we can't parse it.
411+
network = "tcp"
412+
}
413+
hp, err := strconv.Atoi(p.HostPort)
414+
if err != nil {
415+
warns = append(warns, fmt.Sprintf("convert docker port: %s", err.Error()))
416+
continue
417+
}
418+
if hp > 65535 || hp < 0 { // invalid port
419+
warns = append(warns, fmt.Sprintf("convert docker port: invalid host port %d", hp))
420+
continue
421+
}
422+
if _, ok := seen[hp]; ok {
423+
// We've already seen this port, so skip it.
424+
continue
425+
}
401426
out.Ports = append(out.Ports, codersdk.WorkspaceAgentListeningPort{
402427
Network: network,
403-
Port: port,
428+
Port: uint16(hp),
404429
})
430+
seen[hp] = struct{}{}
405431
}
406432
}
407433

agent/agentcontainers/containers_internal_test.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ func TestConvertDockerPort(t *testing.T) {
357357
expectError: "invalid port",
358358
},
359359
} {
360-
tc := tc // not needed anymore but makes the linter happy
360+
//nolint: paralleltest // variable recapture no longer required
361361
t.Run(tc.name, func(t *testing.T) {
362362
t.Parallel()
363363
actualPort, actualNetwork, actualErr := convertDockerPort(tc.in)
@@ -511,7 +511,7 @@ func TestConvertDockerInspect(t *testing.T) {
511511
Ports: []codersdk.WorkspaceAgentListeningPort{
512512
{
513513
Network: "tcp",
514-
Port: 23456,
514+
Port: 12345,
515515
},
516516
},
517517
Volumes: map[string]string{},
@@ -548,10 +548,10 @@ func TestConvertDockerInspect(t *testing.T) {
548548
"devcontainer.config_file": "/home/coder/src/coder/coder/agent/agentcontainers/testdata/devcontainer_simple.json",
549549
"devcontainer.metadata": "[]",
550550
},
551-
Running: true,
552-
Status: "running",
553-
Ports: []codersdk.WorkspaceAgentListeningPort{},
554-
Volumes: map[string]string{},
551+
Running: true,
552+
Status: "running",
553+
Ports: []codersdk.WorkspaceAgentListeningPort{},
554+
Volumes: map[string]string{},
555555
},
556556
},
557557
},
@@ -567,10 +567,10 @@ func TestConvertDockerInspect(t *testing.T) {
567567
"devcontainer.config_file": "/home/coder/src/coder/coder/agent/agentcontainers/testdata/devcontainer_forwardport.json",
568568
"devcontainer.metadata": "[]",
569569
},
570-
Running: true,
571-
Status: "running",
572-
Ports: []codersdk.WorkspaceAgentListeningPort{},
573-
Volumes: map[string]string{},
570+
Running: true,
571+
Status: "running",
572+
Ports: []codersdk.WorkspaceAgentListeningPort{},
573+
Volumes: map[string]string{},
574574
},
575575
},
576576
},
@@ -586,12 +586,13 @@ func TestConvertDockerInspect(t *testing.T) {
586586
"devcontainer.config_file": "/home/coder/src/coder/coder/agent/agentcontainers/testdata/devcontainer_appport.json",
587587
"devcontainer.metadata": "[]",
588588
},
589-
Running: true,
590-
Status: "running",
589+
Running: true,
590+
Status: "running",
591591
Ports: []codersdk.WorkspaceAgentListeningPort{
592592
{
593593
Network: "tcp",
594-
Port: 8080,
594+
// Container port 8080 is mapped to 32768 on the host.
595+
Port: 32768,
595596
},
596597
},
597598
Volumes: map[string]string{},

0 commit comments

Comments
 (0)