44package getter
55
66import (
7+ "archive/tar"
78 "fmt"
89 "net/http"
910 "net/http/cgi"
@@ -16,12 +17,15 @@ import (
1617 "time"
1718
1819 "github.com/hashicorp/nomad/client/config"
20+ "github.com/hashicorp/nomad/client/interfaces"
1921 "github.com/hashicorp/nomad/client/testutil"
2022 "github.com/hashicorp/nomad/helper/testlog"
2123 "github.com/hashicorp/nomad/nomad/structs"
2224 "github.com/shoenig/test/must"
2325)
2426
27+ const testFileContent = "test-file-content"
28+
2529func artifactConfig (timeout time.Duration ) * config.ArtifactConfig {
2630 return & config.ArtifactConfig {
2731 HTTPReadTimeout : timeout ,
@@ -119,13 +123,20 @@ func TestSandbox_Get_inspection(t *testing.T) {
119123 testutil .RequireRoot (t )
120124 logger := testlog .HCLogger (t )
121125
122- // Create a temporary directory directly so the repos
123- // don't end up being found improperly
124- tdir , err := os .MkdirTemp ("" , "nomad-test" )
125- must .NoError (t , err , must .Sprint ("failed to create top level local repo directory" ))
126+ sandboxSetup := func () (string , * Sandbox , interfaces.EnvReplacer ) {
127+ testutil .RequireRoot (t )
128+ logger := testlog .HCLogger (t )
129+ ac := artifactConfig (10 * time .Second )
130+ sbox := New (ac , logger )
131+ _ , taskDir := SetupDir (t )
132+ env := noopTaskEnv (taskDir )
133+ sbox .ac .DisableFilesystemIsolation = true
134+
135+ return taskDir , sbox , env
136+ }
126137
127138 t .Run ("symlink escaped sandbox" , func (t * testing.T ) {
128- dir , err := os .MkdirTemp (tdir , "fake-repo" )
139+ dir , err := os .MkdirTemp (t . TempDir () , "fake-repo" )
129140 must .NoError (t , err , must .Sprint ("failed to create local repo directory" ))
130141 must .NoError (t , os .Symlink ("/" , filepath .Join (dir , "bad-file" )), must .Sprint ("could not create symlink in local repo" ))
131142 srv := makeAndServeGitRepo (t , dir )
@@ -162,7 +173,7 @@ func TestSandbox_Get_inspection(t *testing.T) {
162173 })
163174
164175 t .Run ("symlink within sandbox" , func (t * testing.T ) {
165- dir , err := os .MkdirTemp (tdir , "fake-repo" )
176+ dir , err := os .MkdirTemp (t . TempDir () , "fake-repo" )
166177 must .NoError (t , err , must .Sprint ("failed to create local repo" ))
167178 // create a file to link to
168179 f , err := os .Create (filepath .Join (dir , "test-file" ))
@@ -193,6 +204,195 @@ func TestSandbox_Get_inspection(t *testing.T) {
193204 err = sbox .Get (env , artifact , "nobody" )
194205 must .NoError (t , err )
195206 })
207+
208+ t .Run ("ignores existing symlinks" , func (t * testing.T ) {
209+ taskDir , sbox , env := sandboxSetup ()
210+ src , _ := servTestFile (t , "test-file" )
211+ must .NoError (t , os .Symlink ("/" , filepath .Join (taskDir , "bad-file" )))
212+
213+ artifact := & structs.TaskArtifact {
214+ GetterSource : src ,
215+ RelativeDest : "local/downloads" ,
216+ }
217+
218+ err := sbox .Get (env , artifact , "nobody" )
219+ must .NoError (t , err )
220+
221+ _ , err = os .Stat (filepath .Join (taskDir , "local" , "downloads" , "test-file" ))
222+ must .NoError (t , err )
223+ })
224+
225+ t .Run ("properly chowns destination" , func (t * testing.T ) {
226+ taskDir , sbox , env := sandboxSetup ()
227+ src , _ := servTestFile (t , "test-file" )
228+
229+ artifact := & structs.TaskArtifact {
230+ GetterSource : src ,
231+ RelativeDest : "local/downloads" ,
232+ Chown : true ,
233+ }
234+
235+ err := sbox .Get (env , artifact , "nobody" )
236+ must .NoError (t , err )
237+
238+ info , err := os .Stat (filepath .Join (taskDir , "local" , "downloads" ))
239+ must .NoError (t , err )
240+
241+ uid := info .Sys ().(* syscall.Stat_t ).Uid
242+ must .Eq (t , 65534 , uid ) // nobody's conventional uid
243+
244+ info , err = os .Stat (filepath .Join (taskDir , "local" , "downloads" , "test-file" ))
245+ must .NoError (t , err )
246+
247+ uid = info .Sys ().(* syscall.Stat_t ).Uid
248+ must .Eq (t , 65534 , uid ) // nobody's conventional uid
249+ })
250+
251+ t .Run ("when destination file exists" , func (t * testing.T ) {
252+ taskDir , sbox , env := sandboxSetup ()
253+ src , _ := servTestFile (t , "test-file" )
254+
255+ testFile := filepath .Join (taskDir , "local" , "downloads" , "test-file" )
256+ must .NoError (t , os .MkdirAll (filepath .Dir (testFile ), 0755 ))
257+ f , err := os .OpenFile (testFile , os .O_CREATE , 0644 )
258+ must .NoError (t , err )
259+ f .Write ([]byte ("testing" ))
260+ f .Close ()
261+ originalInfo , err := os .Stat (testFile )
262+ must .NoError (t , err )
263+
264+ artifact := & structs.TaskArtifact {
265+ GetterSource : src ,
266+ RelativeDest : "local/downloads" ,
267+ Chown : true ,
268+ }
269+
270+ err = sbox .Get (env , artifact , "nobody" )
271+ must .NoError (t , err )
272+
273+ newInfo , err := os .Stat (testFile )
274+ must .NoError (t , err )
275+
276+ must .False (t , os .SameFile (originalInfo , newInfo ))
277+ })
278+
279+ t .Run ("when destination directory exists" , func (t * testing.T ) {
280+ taskDir , sbox , env := sandboxSetup ()
281+ src , _ := servTestFile (t , "test-file" )
282+
283+ testFile := filepath .Join (taskDir , "local" , "downloads" , "testfile.txt" )
284+ must .NoError (t , os .MkdirAll (filepath .Dir (testFile ), 0755 ))
285+ f , err := os .OpenFile (testFile , os .O_CREATE , 0644 )
286+ must .NoError (t , err )
287+ f .Write ([]byte ("testing" ))
288+ f .Close ()
289+
290+ artifact := & structs.TaskArtifact {
291+ GetterSource : src ,
292+ RelativeDest : "local/downloads" ,
293+ Chown : true ,
294+ }
295+
296+ err = sbox .Get (env , artifact , "nobody" )
297+ must .NoError (t , err )
298+
299+ // check that new file exists
300+ _ , err = os .Stat (filepath .Join (taskDir , "local" , "downloads" , "test-file" ))
301+ must .NoError (t , err )
302+
303+ // check that existing file still exists
304+ _ , err = os .Stat (testFile )
305+ must .NoError (t , err )
306+ })
307+
308+ t .Run ("when unpacking file to an existing directory" , func (t * testing.T ) {
309+ taskDir , sbox , env := sandboxSetup ()
310+
311+ tarFiles := []string {
312+ "test.file" ,
313+ "nested/test.file" ,
314+ "other/test.file" ,
315+ }
316+ src , _ := servTarFile (t , tarFiles ... )
317+
318+ testFile := filepath .Join (taskDir , "local" , "downloads" , "other" , "testfile.txt" )
319+ must .NoError (t , os .MkdirAll (filepath .Dir (testFile ), 0755 ))
320+ f , err := os .Create (testFile )
321+ must .NoError (t , err )
322+ f .Write ([]byte ("testing" ))
323+ f .Close ()
324+
325+ artifact := & structs.TaskArtifact {
326+ GetterSource : src ,
327+ RelativeDest : "local/downloads" ,
328+ Chown : true ,
329+ }
330+
331+ err = sbox .Get (env , artifact , "nobody" )
332+ must .NoError (t , err )
333+
334+ // check that all unpacked files exist
335+ for _ , tarFile := range tarFiles {
336+ _ , err := os .Stat (filepath .Join (taskDir , "local" , "downloads" , tarFile ))
337+ must .NoError (t , err )
338+ }
339+
340+ // check existing file remains
341+ _ , err = os .Stat (testFile )
342+ must .NoError (t , err )
343+ })
344+ }
345+
346+ func servTestFile (t * testing.T , filename string ) (string , * httptest.Server ) {
347+ t .Helper ()
348+
349+ dir , err := os .MkdirTemp (t .TempDir (), "file" )
350+ must .NoError (t , err )
351+ f , err := os .Create (filepath .Join (dir , filename ))
352+ must .NoError (t , err )
353+ defer f .Close ()
354+ f .Write ([]byte (testFileContent ))
355+
356+ s := servDir (t , dir )
357+ return fmt .Sprintf ("%s/%s" , s .URL , filename ), s
358+ }
359+
360+ func servTarFile (t * testing.T , paths ... string ) (string , * httptest.Server ) {
361+ t .Helper ()
362+
363+ dir , err := os .MkdirTemp (t .TempDir (), "tar" )
364+ f , err := os .Create (filepath .Join (dir , "test-compressed.tar" ))
365+ must .NoError (t , err )
366+ defer f .Close ()
367+
368+ w := tar .NewWriter (f )
369+ defer w .Close ()
370+ for _ , path := range paths {
371+ err := w .WriteHeader (& tar.Header {
372+ Name : path ,
373+ Mode : 0644 ,
374+ Size : int64 (len (testFileContent )),
375+ })
376+ must .NoError (t , err )
377+ bytes , err := w .Write ([]byte (testFileContent ))
378+ must .NoError (t , err )
379+ must .Eq (t , len (testFileContent ), bytes )
380+ }
381+
382+ s := servDir (t , dir )
383+ return fmt .Sprintf ("%s/test-compressed.tar" , s .URL ), s
384+ }
385+
386+ func servDir (t * testing.T , dir string ) * httptest.Server {
387+ t .Helper ()
388+
389+ fs := os .DirFS (dir )
390+ s := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
391+ http .ServeFileFS (w , r , fs , r .URL .Path )
392+ }))
393+ t .Cleanup (s .Close )
394+
395+ return s
196396}
197397
198398func makeAndServeGitRepo (t * testing.T , repoPath string ) * httptest.Server {
0 commit comments