9
9
"testing"
10
10
"time"
11
11
12
+ "github.com/stretchr/testify/assert"
12
13
"github.com/stretchr/testify/require"
13
14
14
15
"github.com/coder/envbox/integration/integrationtest"
@@ -41,8 +42,7 @@ func TestDocker_Nvidia(t *testing.T) {
41
42
)
42
43
43
44
// Assert that we can run nvidia-smi in the inner container.
44
- _ , err := execContainerCmd (ctx , t , ctID , "docker" , "exec" , "workspace_cvm" , "nvidia-smi" )
45
- require .NoError (t , err , "failed to run nvidia-smi in the inner container" )
45
+ assertInnerNvidiaSMI (ctx , t , ctID )
46
46
})
47
47
48
48
t .Run ("Redhat" , func (t * testing.T ) {
@@ -52,16 +52,21 @@ func TestDocker_Nvidia(t *testing.T) {
52
52
53
53
// Start the envbox container.
54
54
ctID := startEnvboxCmd (ctx , t , integrationtest .RedhatImage , "root" ,
55
- "-v" , "/usr/lib/x86_64-linux-gnu:/var/coder/usr/lib64 " ,
55
+ "-v" , "/usr/lib/x86_64-linux-gnu:/var/coder/usr/lib " ,
56
56
"--env" , "CODER_ADD_GPU=true" ,
57
- "--env" , "CODER_USR_LIB_DIR=/var/coder/usr/lib64 " ,
57
+ "--env" , "CODER_USR_LIB_DIR=/var/coder/usr/lib " ,
58
58
"--runtime=nvidia" ,
59
59
"--gpus=all" ,
60
60
)
61
61
62
62
// Assert that we can run nvidia-smi in the inner container.
63
- _ , err := execContainerCmd (ctx , t , ctID , "docker" , "exec" , "workspace_cvm" , "nvidia-smi" )
64
- require .NoError (t , err , "failed to run nvidia-smi in the inner container" )
63
+ assertInnerNvidiaSMI (ctx , t , ctID )
64
+
65
+ // Make sure dnf still works.
66
+ out , err := execContainerCmd (ctx , t , ctID , "docker" , "exec" , "workspace_cvm" , "dnf" , "list" )
67
+ if ! assert .NoError (t , err , "failed to run dnf in the inner container" ) {
68
+ t .Logf ("dnf output:\n %s" , strings .TrimSpace (out ))
69
+ }
65
70
})
66
71
67
72
t .Run ("InnerUsrLibDirOverride" , func (t * testing.T ) {
@@ -79,11 +84,35 @@ func TestDocker_Nvidia(t *testing.T) {
79
84
"--gpus=all" ,
80
85
)
81
86
82
- // Assert that the libraries end up in the expected location in the inner container.
83
- out , err := execContainerCmd (ctx , t , ctID , "docker" , "exec" , "workspace_cvm" , "ls" , "-l" , "/usr/lib/coder" )
87
+ // Assert that the libraries end up in the expected location in the inner
88
+ // container.
89
+ out , err := execContainerCmd (ctx , t , ctID , "docker" , "exec" , "workspace_cvm" , "ls" , "-1" , "/usr/lib/coder" )
84
90
require .NoError (t , err , "inner usr lib dir override failed" )
85
91
require .Regexp (t , `(?i)(libgl|nvidia|vulkan|cuda)` , out )
86
92
})
93
+
94
+ t .Run ("EmptyHostUsrLibDir" , func (t * testing.T ) {
95
+ t .Parallel ()
96
+ ctx , cancel := context .WithCancel (context .Background ())
97
+ t .Cleanup (cancel )
98
+ emptyUsrLibDir := t .TempDir ()
99
+
100
+ // Start the envbox container.
101
+ ctID := startEnvboxCmd (ctx , t , integrationtest .UbuntuImage , "root" ,
102
+ "-v" , emptyUsrLibDir + ":/var/coder/usr/lib" ,
103
+ "--env" , "CODER_ADD_GPU=true" ,
104
+ "--env" , "CODER_USR_LIB_DIR=/var/coder/usr/lib" ,
105
+ "--runtime=nvidia" ,
106
+ "--gpus=all" ,
107
+ )
108
+
109
+ ofs := outerFiles (ctx , t , ctID , "/usr/lib/x86_64-linux-gnu/libnv*" )
110
+ // Assert invariant: the outer container has the files we expect.
111
+ require .NotEmpty (t , ofs , "failed to list outer container files" )
112
+ // Assert that expected files are available in the inner container.
113
+ assertInnerFiles (ctx , t , ctID , "/usr/lib/x86_64-linux-gnu/libnv*" , ofs ... )
114
+ assertInnerNvidiaSMI (ctx , t , ctID )
115
+ })
87
116
}
88
117
89
118
// dockerRuntimes returns the list of container runtimes available on the host.
@@ -101,6 +130,49 @@ func dockerRuntimes(t *testing.T) []string {
101
130
return strings .Split (raw , "\n " )
102
131
}
103
132
133
+ // outerFiles returns the list of files in the outer container matching the
134
+ // given pattern. It does this by running `ls -1` in the outer container.
135
+ func outerFiles (ctx context.Context , t * testing.T , containerID , pattern string ) []string {
136
+ t .Helper ()
137
+ // We need to use /bin/sh -c to avoid the shell interpreting the glob.
138
+ out , err := execContainerCmd (ctx , t , containerID , "/bin/sh" , "-c" , "ls -1 " + pattern )
139
+ require .NoError (t , err , "failed to list outer container files" )
140
+ files := strings .Split (strings .TrimSpace (out ), "\n " )
141
+ slices .Sort (files )
142
+ return files
143
+ }
144
+
145
+ // assertInnerFiles checks that all the files matching the given pattern exist in the
146
+ // inner container.
147
+ func assertInnerFiles (ctx context.Context , t * testing.T , containerID , pattern string , expected ... string ) {
148
+ t .Helper ()
149
+
150
+ // Get the list of files in the inner container.
151
+ // We need to use /bin/sh -c to avoid the shell interpreting the glob.
152
+ out , err := execContainerCmd (ctx , t , containerID , "docker" , "exec" , "workspace_cvm" , "/bin/sh" , "-c" , "ls -1 " + pattern )
153
+ require .NoError (t , err , "failed to list inner container files" )
154
+ innerFiles := strings .Split (strings .TrimSpace (out ), "\n " )
155
+
156
+ // Check that the expected files exist in the inner container.
157
+ missingFiles := make ([]string , 0 )
158
+ for _ , expectedFile := range expected {
159
+ if ! slices .Contains (innerFiles , expectedFile ) {
160
+ missingFiles = append (missingFiles , expectedFile )
161
+ }
162
+ }
163
+ require .Empty (t , missingFiles , "missing files in inner container: %s" , strings .Join (missingFiles , ", " ))
164
+ }
165
+
166
+ // assertInnerNvidiaSMI checks that nvidia-smi runs successfully in the inner
167
+ // container.
168
+ func assertInnerNvidiaSMI (ctx context.Context , t * testing.T , containerID string ) {
169
+ t .Helper ()
170
+ // Assert that we can run nvidia-smi in the inner container.
171
+ out , err := execContainerCmd (ctx , t , containerID , "docker" , "exec" , "workspace_cvm" , "nvidia-smi" )
172
+ require .NoError (t , err , "failed to run nvidia-smi in the inner container" )
173
+ require .Contains (t , out , "NVIDIA-SMI" , "nvidia-smi output does not contain NVIDIA-SMI" )
174
+ }
175
+
104
176
// startEnvboxCmd starts the envbox container with the given arguments.
105
177
// Ideally we would use ory/dockertest for this, but it doesn't support
106
178
// specifying the runtime. We have alternatively used the docker client library,
0 commit comments