4
4
"fmt"
5
5
"os"
6
6
"slices"
7
+ "sort"
7
8
"strconv"
8
9
"strings"
9
10
"testing"
@@ -46,7 +47,7 @@ func TestIntegrationDocker(t *testing.T) {
46
47
// Create a temporary directory to validate that we surface mounts correctly.
47
48
testTempDir := t .TempDir ()
48
49
// Pick a random port to expose for testing port bindings.
49
- testRandPort := testutil .RandomPortNoListen (t )
50
+ testRandHostPort , testRandContainerPort := testutil . RandomPortNoListen ( t ), testutil .RandomPortNoListen (t )
50
51
ct , err := pool .RunWithOptions (& dockertest.RunOptions {
51
52
Repository : "busybox" ,
52
53
Tag : "latest" ,
@@ -56,12 +57,12 @@ func TestIntegrationDocker(t *testing.T) {
56
57
"devcontainer.metadata" : `[{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}]` ,
57
58
},
58
59
Mounts : []string {testTempDir + ":" + testTempDir },
59
- ExposedPorts : []string {fmt .Sprintf ("%d/tcp" , testRandPort )},
60
+ ExposedPorts : []string {fmt .Sprintf ("%d/tcp" , testRandContainerPort )},
60
61
PortBindings : map [docker.Port ][]docker.PortBinding {
61
- docker .Port (fmt .Sprintf ("%d/tcp" , testRandPort )): {
62
+ docker .Port (fmt .Sprintf ("%d/tcp" , testRandContainerPort )): {
62
63
{
63
64
HostIP : "0.0.0.0" ,
64
- HostPort : strconv .FormatInt (int64 (testRandPort ), 10 ),
65
+ HostPort : strconv .FormatInt (int64 (testRandHostPort ), 10 ),
65
66
},
66
67
},
67
68
},
@@ -99,7 +100,7 @@ func TestIntegrationDocker(t *testing.T) {
99
100
assert .True (t , foundContainer .Running )
100
101
assert .Equal (t , "running" , foundContainer .Status )
101
102
if assert .Len (t , foundContainer .Ports , 1 ) {
102
- assert .Equal (t , testRandPort , foundContainer .Ports [0 ].Port )
103
+ assert .Equal (t , testRandHostPort , foundContainer .Ports [0 ].Port )
103
104
assert .Equal (t , "tcp" , foundContainer .Ports [0 ].Network )
104
105
}
105
106
if assert .Len (t , foundContainer .Volumes , 1 ) {
@@ -306,67 +307,162 @@ func TestContainersHandler(t *testing.T) {
306
307
})
307
308
}
308
309
309
- func TestConvertDockerPort (t * testing.T ) {
310
+ // TestDockerPortBinding tests the port binding handling in convertDockerInspect
311
+ func TestDockerPortBinding (t * testing.T ) {
310
312
t .Parallel ()
311
313
312
- for _ , tc := range []struct {
314
+ testCases := []struct {
313
315
name string
314
- in string
315
- expectPort uint16
316
- expectNetwork string
317
- expectError string
316
+ networkPorts map [string ][]dockerPortBinding
317
+ expectedPorts []codersdk.WorkspaceAgentListeningPort
318
+ expectedWarns int
318
319
}{
319
320
{
320
- name : "empty port " ,
321
- in : "" ,
322
- expectError : "invalid port" ,
321
+ name : "nil " ,
322
+ networkPorts : nil ,
323
+ expectedPorts : []codersdk. WorkspaceAgentListeningPort {} ,
323
324
},
324
325
{
325
- name : "valid tcp port" ,
326
- in : "8080/tcp" ,
327
- expectPort : 8080 ,
328
- expectNetwork : "tcp" ,
326
+ name : "simple port binding" ,
327
+ networkPorts : map [string ][]dockerPortBinding {
328
+ "8080/tcp" : {
329
+ {HostIP : "0.0.0.0" , HostPort : "9090" },
330
+ },
331
+ },
332
+ expectedPorts : []codersdk.WorkspaceAgentListeningPort {
333
+ {Network : "tcp" , Port : 9090 },
334
+ },
329
335
},
330
336
{
331
- name : "valid udp port" ,
332
- in : "8080/udp" ,
333
- expectPort : 8080 ,
334
- expectNetwork : "udp" ,
337
+ name : "multiple port bindings" ,
338
+ networkPorts : map [string ][]dockerPortBinding {
339
+ "8080/tcp" : {
340
+ {HostIP : "0.0.0.0" , HostPort : "9090" },
341
+ },
342
+ "8081/tcp" : {
343
+ {HostIP : "0.0.0.0" , HostPort : "9091" },
344
+ },
345
+ },
346
+ expectedPorts : []codersdk.WorkspaceAgentListeningPort {
347
+ {Network : "tcp" , Port : 9090 },
348
+ {Network : "tcp" , Port : 9091 },
349
+ },
335
350
},
336
351
{
337
- name : "valid port no network" ,
338
- in : "8080" ,
339
- expectPort : 8080 ,
340
- expectNetwork : "tcp" ,
352
+ name : "duplicate host ports on different interfaces" ,
353
+ networkPorts : map [string ][]dockerPortBinding {
354
+ "8080/tcp" : {
355
+ {HostIP : "0.0.0.0" , HostPort : "9090" },
356
+ {HostIP : "127.0.0.1" , HostPort : "9090" },
357
+ },
358
+ },
359
+ expectedPorts : []codersdk.WorkspaceAgentListeningPort {
360
+ {Network : "tcp" , Port : 9090 },
361
+ },
341
362
},
342
363
{
343
- name : "invalid port" ,
344
- in : "invalid/tcp" ,
345
- expectError : "invalid port" ,
364
+ name : "udp protocol" ,
365
+ networkPorts : map [string ][]dockerPortBinding {
366
+ "8080/udp" : {
367
+ {HostIP : "0.0.0.0" , HostPort : "9090" },
368
+ },
369
+ },
370
+ expectedPorts : []codersdk.WorkspaceAgentListeningPort {
371
+ {Network : "udp" , Port : 9090 },
372
+ },
346
373
},
347
374
{
348
- name : "invalid port no network" ,
349
- in : "invalid" ,
350
- expectError : "invalid port" ,
375
+ name : "no protocol defaults to tcp" ,
376
+ networkPorts : map [string ][]dockerPortBinding {
377
+ "8080" : {
378
+ {HostIP : "0.0.0.0" , HostPort : "9090" },
379
+ },
380
+ },
381
+ expectedPorts : []codersdk.WorkspaceAgentListeningPort {
382
+ {Network : "tcp" , Port : 9090 },
383
+ },
351
384
},
352
385
{
353
- name : "multiple network" ,
354
- in : "8080/tcp/udp" ,
355
- expectError : "invalid port" ,
386
+ name : "no bindings should not create ports" ,
387
+ networkPorts : map [string ][]dockerPortBinding {
388
+ "8080/tcp" : {},
389
+ },
390
+ expectedPorts : []codersdk.WorkspaceAgentListeningPort {},
356
391
},
357
- } {
358
- tc := tc // not needed anymore but makes the linter happy
392
+ {
393
+ name : "invalid host port" ,
394
+ networkPorts : map [string ][]dockerPortBinding {
395
+ "8080/tcp" : {
396
+ {HostIP : "0.0.0.0" , HostPort : "invalid" },
397
+ },
398
+ },
399
+ expectedPorts : []codersdk.WorkspaceAgentListeningPort {},
400
+ expectedWarns : 1 ,
401
+ },
402
+ {
403
+ name : "mix of valid and invalid ports" ,
404
+ networkPorts : map [string ][]dockerPortBinding {
405
+ "8080/tcp" : {
406
+ {HostIP : "0.0.0.0" , HostPort : "9090" },
407
+ },
408
+ "8081/tcp" : {
409
+ {HostIP : "0.0.0.0" , HostPort : "invalid" },
410
+ },
411
+ },
412
+ expectedPorts : []codersdk.WorkspaceAgentListeningPort {
413
+ {Network : "tcp" , Port : 9090 },
414
+ },
415
+ expectedWarns : 1 ,
416
+ },
417
+ }
418
+
419
+ for _ , tc := range testCases {
420
+ tc := tc
359
421
t .Run (tc .name , func (t * testing.T ) {
360
422
t .Parallel ()
361
- actualPort , actualNetwork , actualErr := convertDockerPort (tc .in )
362
- if tc .expectError != "" {
363
- assert .Zero (t , actualPort , "expected no port" )
364
- assert .Empty (t , actualNetwork , "expected no network" )
365
- assert .ErrorContains (t , actualErr , tc .expectError )
366
- } else {
367
- assert .NoError (t , actualErr , "expected no error" )
368
- assert .Equal (t , tc .expectPort , actualPort , "expected port to match" )
369
- assert .Equal (t , tc .expectNetwork , actualNetwork , "expected network to match" )
423
+
424
+ // Create a sample docker inspection result
425
+ dockerData := dockerInspect {
426
+ ID : "test-container" ,
427
+ Created : time .Now (),
428
+ Config : dockerInspectConfig {
429
+ Image : "test-image" ,
430
+ Labels : map [string ]string {"test" : "value" },
431
+ },
432
+ Name : "test-container" ,
433
+ State : dockerInspectState {Running : true },
434
+ NetworkSettings : dockerNetworkSettings {
435
+ Ports : tc .networkPorts ,
436
+ },
437
+ }
438
+
439
+ // Process the docker data
440
+ container , warns := convertDockerInspect (dockerData )
441
+
442
+ // Verify the ports
443
+ assert .Len (t , container .Ports , len (tc .expectedPorts ), "wrong number of ports" )
444
+ assert .Len (t , warns , tc .expectedWarns , "wrong number of warnings" )
445
+
446
+ // Sort ports for consistent comparison (order may vary)
447
+ sort .Slice (container .Ports , func (i , j int ) bool {
448
+ if container .Ports [i ].Network == container .Ports [j ].Network {
449
+ return container .Ports [i ].Port < container .Ports [j ].Port
450
+ }
451
+ return container .Ports [i ].Network < container .Ports [j ].Network
452
+ })
453
+ sort .Slice (tc .expectedPorts , func (i , j int ) bool {
454
+ if tc .expectedPorts [i ].Network == tc .expectedPorts [j ].Network {
455
+ return tc .expectedPorts [i ].Port < tc .expectedPorts [j ].Port
456
+ }
457
+ return tc .expectedPorts [i ].Network < tc .expectedPorts [j ].Network
458
+ })
459
+
460
+ // Compare ports
461
+ for i , expected := range tc .expectedPorts {
462
+ if i < len (container .Ports ) {
463
+ assert .Equal (t , expected .Network , container .Ports [i ].Network , "network mismatch" )
464
+ assert .Equal (t , expected .Port , container .Ports [i ].Port , "port mismatch" )
465
+ }
370
466
}
371
467
})
372
468
}
0 commit comments