@@ -3,6 +3,7 @@ package agentcontainers
3
3
import (
4
4
"fmt"
5
5
"os"
6
+ "slices"
6
7
"strconv"
7
8
"strings"
8
9
"testing"
@@ -47,10 +48,13 @@ func TestIntegrationDocker(t *testing.T) {
47
48
// Pick a random port to expose for testing port bindings.
48
49
testRandPort := testutil .RandomPortNoListen (t )
49
50
ct , err := pool .RunWithOptions (& dockertest.RunOptions {
50
- Repository : "busybox" ,
51
- Tag : "latest" ,
52
- Cmd : []string {"sleep" , "infnity" },
53
- Labels : map [string ]string {"com.coder.test" : testLabelValue },
51
+ Repository : "busybox" ,
52
+ Tag : "latest" ,
53
+ Cmd : []string {"sleep" , "infnity" },
54
+ Labels : map [string ]string {
55
+ "com.coder.test" : testLabelValue ,
56
+ "devcontainer.metadata" : `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}` ,
57
+ },
54
58
Mounts : []string {testTempDir + ":" + testTempDir },
55
59
ExposedPorts : []string {fmt .Sprintf ("%d/tcp" , testRandPort )},
56
60
PortBindings : map [docker.Port ][]docker.PortBinding {
@@ -122,6 +126,12 @@ func TestIntegrationDocker(t *testing.T) {
122
126
matchHostnameOuput := func (line string ) bool {
123
127
return strings .Contains (strings .TrimSpace (line ), ct .Container .Config .Hostname )
124
128
}
129
+ matchEnvCmd := func (line string ) bool {
130
+ return strings .Contains (strings .TrimSpace (line ), "env" )
131
+ }
132
+ matchEnvOutput := func (line string ) bool {
133
+ return strings .Contains (line , "FOO=bar" ) || strings .Contains (line , "MULTILINE=foo" )
134
+ }
125
135
require .NoError (t , tr .ReadUntil (ctx , matchPrompt ), "failed to match prompt" )
126
136
t .Logf ("Matched prompt" )
127
137
_ , err = ptyCmd .InputWriter ().Write ([]byte ("hostname\r \n " ))
@@ -131,6 +141,13 @@ func TestIntegrationDocker(t *testing.T) {
131
141
t .Logf ("Matched hostname command" )
132
142
require .NoError (t , tr .ReadUntil (ctx , matchHostnameOuput ), "failed to match hostname output" )
133
143
t .Logf ("Matched hostname output" )
144
+ _ , err = ptyCmd .InputWriter ().Write ([]byte ("env\r \n " ))
145
+ require .NoError (t , err , "failed to write to pty" )
146
+ t .Logf ("Wrote env command" )
147
+ require .NoError (t , tr .ReadUntil (ctx , matchEnvCmd ), "failed to match env command" )
148
+ t .Logf ("Matched env command" )
149
+ require .NoError (t , tr .ReadUntil (ctx , matchEnvOutput ), "failed to match env output" )
150
+ t .Logf ("Matched env output" )
134
151
break
135
152
}
136
153
}
@@ -403,49 +420,56 @@ func TestConvertDockerVolume(t *testing.T) {
403
420
// CODER_TEST_USE_DOCKER=1 go test ./agent/agentcontainers -run TestDockerEnvInfoer
404
421
func TestDockerEnvInfoer (t * testing.T ) {
405
422
t .Parallel ()
406
- if ctud , ok := os .LookupEnv ("CODER_TEST_USE_DOCKER" ); ! ok || ctud != "1" {
407
- t .Skip ("Set CODER_TEST_USE_DOCKER=1 to run this test" )
408
- }
423
+ // if ctud, ok := os.LookupEnv("CODER_TEST_USE_DOCKER"); !ok || ctud != "1" {
424
+ // t.Skip("Set CODER_TEST_USE_DOCKER=1 to run this test")
425
+ // }
409
426
410
427
pool , err := dockertest .NewPool ("" )
411
428
require .NoError (t , err , "Could not connect to docker" )
412
429
// nolint:paralleltest // variable recapture no longer required
413
430
for idx , tt := range []struct {
414
431
image string
415
- env []string
432
+ labels map [string ]string
433
+ expectedEnv []string
416
434
containerUser string
417
435
expectedUsername string
418
436
expectedUserShell string
419
437
}{
420
438
{
421
- image : "busybox:latest" ,
422
- env : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
439
+ image : "busybox:latest" ,
440
+ labels : map [string ]string {`devcontainer.metadata` : `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}` },
441
+
442
+ expectedEnv : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
423
443
expectedUsername : "root" ,
424
444
expectedUserShell : "/bin/sh" ,
425
445
},
426
446
{
427
447
image : "busybox:latest" ,
428
- env : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
448
+ labels : map [string ]string {`devcontainer.metadata` : `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}` },
449
+ expectedEnv : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
429
450
containerUser : "root" ,
430
451
expectedUsername : "root" ,
431
452
expectedUserShell : "/bin/sh" ,
432
453
},
433
454
{
434
455
image : "codercom/enterprise-minimal:ubuntu" ,
435
- env : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
456
+ labels : map [string ]string {`devcontainer.metadata` : `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}` },
457
+ expectedEnv : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
436
458
expectedUsername : "coder" ,
437
459
expectedUserShell : "/bin/bash" ,
438
460
},
439
461
{
440
462
image : "codercom/enterprise-minimal:ubuntu" ,
441
- env : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
463
+ labels : map [string ]string {`devcontainer.metadata` : `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}` },
464
+ expectedEnv : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
442
465
containerUser : "coder" ,
443
466
expectedUsername : "coder" ,
444
467
expectedUserShell : "/bin/bash" ,
445
468
},
446
469
{
447
470
image : "codercom/enterprise-minimal:ubuntu" ,
448
- env : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
471
+ labels : map [string ]string {`devcontainer.metadata` : `{"remoteEnv": {"FOO": "bar", "MULTILINE": "foo\nbar\nbaz"}}` },
472
+ expectedEnv : []string {"FOO=bar" , "MULTILINE=foo\n bar\n baz" },
449
473
containerUser : "root" ,
450
474
expectedUsername : "root" ,
451
475
expectedUserShell : "/bin/bash" ,
@@ -462,7 +486,7 @@ func TestDockerEnvInfoer(t *testing.T) {
462
486
Repository : image ,
463
487
Tag : tag ,
464
488
Cmd : []string {"sleep" , "infinity" },
465
- Env : tt .env ,
489
+ Labels : tt .labels ,
466
490
}, func (config * docker.HostConfig ) {
467
491
config .AutoRemove = true
468
492
config .RestartPolicy = docker.RestartPolicy {Name : "no" }
@@ -490,9 +514,25 @@ func TestDockerEnvInfoer(t *testing.T) {
490
514
require .NoError (t , err , "Expected no error from UserShell()" )
491
515
require .Equal (t , tt .expectedUserShell , sh , "Expected user shell to match" )
492
516
493
- // TODO: environment from devcontainer labels.
494
- // environ := dei.Environ()
495
- // require.Subset(t, environ, tt.env, "Expected environment to match")
517
+ // We don't need to test the actual environment variables here.
518
+ environ := dei .Environ ()
519
+ require .NotEmpty (t , environ , "Expected environ to be non-empty" )
520
+
521
+ // Test that the environment variables are present in modified command
522
+ // output.
523
+ envCmd , envArgs := dei .ModifyCommand ("env" )
524
+ for _ , env := range tt .expectedEnv {
525
+ require .Subset (t , envArgs , []string {"--env" , env })
526
+ }
527
+ // Run the command in the container and check the output
528
+ // HACK: we remove the --tty argument because we're not running in a tty
529
+ envArgs = slices .DeleteFunc (envArgs , func (s string ) bool { return s == "--tty" })
530
+ stdout , stderr , err := run (ctx , agentexec .DefaultExecer , envCmd , envArgs ... )
531
+ require .Empty (t , stderr , "Expected no stderr output" )
532
+ require .NoError (t , err , "Expected no error from running command" )
533
+ for _ , env := range tt .expectedEnv {
534
+ require .Contains (t , stdout , env )
535
+ }
496
536
})
497
537
}
498
538
}
0 commit comments