github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/allocwatcher/alloc_watcher_unix_test.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package allocwatcher 5 6 import ( 7 "archive/tar" 8 "bytes" 9 "context" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "path/filepath" 15 "syscall" 16 "testing" 17 18 "github.com/hashicorp/nomad/ci" 19 ctestutil "github.com/hashicorp/nomad/client/testutil" 20 "github.com/hashicorp/nomad/helper/testlog" 21 ) 22 23 // TestPrevAlloc_StreamAllocDir_Ok asserts that streaming a tar to an alloc dir 24 // works. 25 func TestPrevAlloc_StreamAllocDir_Ok(t *testing.T) { 26 ci.Parallel(t) 27 ctestutil.RequireRoot(t) 28 29 dir := t.TempDir() 30 31 // Create foo/ 32 fooDir := filepath.Join(dir, "foo") 33 if err := os.Mkdir(fooDir, 0777); err != nil { 34 t.Fatalf("err: %v", err) 35 } 36 37 // Change ownership of foo/ to test #3702 (any non-root user is fine) 38 const uid, gid = 1, 1 39 if err := os.Chown(fooDir, uid, gid); err != nil { 40 t.Fatalf("err : %v", err) 41 } 42 43 dirInfo, err := os.Stat(fooDir) 44 if err != nil { 45 t.Fatalf("err: %v", err) 46 } 47 48 // Create foo/bar 49 f, err := os.Create(filepath.Join(fooDir, "bar")) 50 if err != nil { 51 t.Fatalf("err: %v", err) 52 } 53 if _, err := f.WriteString("123"); err != nil { 54 t.Fatalf("err: %v", err) 55 } 56 if err := f.Chmod(0644); err != nil { 57 t.Fatalf("err: %v", err) 58 } 59 fInfo, err := f.Stat() 60 if err != nil { 61 t.Fatalf("err: %v", err) 62 } 63 f.Close() 64 65 // Create foo/baz -> bar symlink 66 if err := os.Symlink("bar", filepath.Join(dir, "foo", "baz")); err != nil { 67 t.Fatalf("err: %v", err) 68 } 69 linkInfo, err := os.Lstat(filepath.Join(dir, "foo", "baz")) 70 if err != nil { 71 t.Fatalf("err: %v", err) 72 } 73 74 buf := new(bytes.Buffer) 75 tw := tar.NewWriter(buf) 76 77 walkFn := func(path string, fileInfo os.FileInfo, err error) error { 78 // filepath.Walk passes in an error 79 if err != nil { 80 return fmt.Errorf("error from filepath.Walk(): %s", err) 81 } 82 // Include the path of the file name relative to the alloc dir 83 // so that we can put the files in the right directories 84 link := "" 85 if fileInfo.Mode()&os.ModeSymlink != 0 { 86 target, err := os.Readlink(path) 87 if err != nil { 88 return fmt.Errorf("error reading symlink: %v", err) 89 } 90 link = target 91 } 92 hdr, err := tar.FileInfoHeader(fileInfo, link) 93 if err != nil { 94 return fmt.Errorf("error creating file header: %v", err) 95 } 96 hdr.Name = fileInfo.Name() 97 tw.WriteHeader(hdr) 98 99 // If it's a directory or symlink we just write the header into the tar 100 if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0) { 101 return nil 102 } 103 104 // Write the file into the archive 105 file, err := os.Open(path) 106 if err != nil { 107 return err 108 } 109 defer file.Close() 110 111 if _, err := io.Copy(tw, file); err != nil { 112 return err 113 } 114 115 return nil 116 } 117 118 if err := filepath.Walk(dir, walkFn); err != nil { 119 t.Fatalf("err: %v", err) 120 } 121 tw.Close() 122 123 dir1 := t.TempDir() 124 125 rc := ioutil.NopCloser(buf) 126 prevAlloc := &remotePrevAlloc{logger: testlog.HCLogger(t)} 127 if err := prevAlloc.streamAllocDir(context.Background(), rc, dir1); err != nil { 128 t.Fatalf("err: %v", err) 129 } 130 131 // Ensure foo is present 132 fi, err := os.Stat(filepath.Join(dir1, "foo")) 133 if err != nil { 134 t.Fatalf("err: %v", err) 135 } 136 if fi.Mode() != dirInfo.Mode() { 137 t.Fatalf("mode: %v", fi.Mode()) 138 } 139 stat := fi.Sys().(*syscall.Stat_t) 140 if stat.Uid != uid || stat.Gid != gid { 141 t.Fatalf("foo/ has incorrect ownership: expected %d:%d found %d:%d", 142 uid, gid, stat.Uid, stat.Gid) 143 } 144 145 fi1, err := os.Stat(filepath.Join(dir1, "bar")) 146 if err != nil { 147 t.Fatalf("err: %v", err) 148 } 149 if fi1.Mode() != fInfo.Mode() { 150 t.Fatalf("mode: %v", fi1.Mode()) 151 } 152 153 fi2, err := os.Lstat(filepath.Join(dir1, "baz")) 154 if err != nil { 155 t.Fatalf("err: %v", err) 156 } 157 if fi2.Mode() != linkInfo.Mode() { 158 t.Fatalf("mode: %v", fi2.Mode()) 159 } 160 }