github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/allocdir/alloc_dir_test.go (about) 1 package allocdir 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "context" 7 "io" 8 "io/ioutil" 9 "log" 10 "os" 11 "path/filepath" 12 "runtime" 13 "strings" 14 "syscall" 15 "testing" 16 17 "github.com/hashicorp/nomad/helper/testlog" 18 "github.com/hashicorp/nomad/nomad/structs" 19 "github.com/stretchr/testify/require" 20 ) 21 22 var ( 23 t1 = &structs.Task{ 24 Name: "web", 25 Driver: "exec", 26 Config: map[string]interface{}{ 27 "command": "/bin/date", 28 "args": "+%s", 29 }, 30 Resources: &structs.Resources{ 31 DiskMB: 1, 32 }, 33 } 34 35 t2 = &structs.Task{ 36 Name: "web2", 37 Driver: "exec", 38 Config: map[string]interface{}{ 39 "command": "/bin/date", 40 "args": "+%s", 41 }, 42 Resources: &structs.Resources{ 43 DiskMB: 1, 44 }, 45 } 46 ) 47 48 // Test that AllocDir.Build builds just the alloc directory. 49 func TestAllocDir_BuildAlloc(t *testing.T) { 50 tmp, err := ioutil.TempDir("", "AllocDir") 51 if err != nil { 52 t.Fatalf("Couldn't create temp dir: %v", err) 53 } 54 defer os.RemoveAll(tmp) 55 56 d := NewAllocDir(testlog.HCLogger(t), tmp) 57 defer d.Destroy() 58 d.NewTaskDir(t1.Name) 59 d.NewTaskDir(t2.Name) 60 if err := d.Build(); err != nil { 61 t.Fatalf("Build() failed: %v", err) 62 } 63 64 // Check that the AllocDir and each of the task directories exist. 65 if _, err := os.Stat(d.AllocDir); os.IsNotExist(err) { 66 t.Fatalf("Build() didn't create AllocDir %v", d.AllocDir) 67 } 68 69 for _, task := range []*structs.Task{t1, t2} { 70 tDir, ok := d.TaskDirs[task.Name] 71 if !ok { 72 t.Fatalf("Task directory not found for %v", task.Name) 73 } 74 75 if stat, _ := os.Stat(tDir.Dir); stat != nil { 76 t.Fatalf("Build() created TaskDir %v", tDir.Dir) 77 } 78 79 if stat, _ := os.Stat(tDir.SecretsDir); stat != nil { 80 t.Fatalf("Build() created secret dir %v", tDir.Dir) 81 } 82 } 83 } 84 85 // HACK: This function is copy/pasted from client.testutil to prevent a test 86 // import cycle, due to testutil transitively importing allocdir. This 87 // should be fixed after DriverManager is implemented. 88 func MountCompatible(t *testing.T) { 89 if runtime.GOOS == "windows" { 90 t.Skip("Windows does not support mount") 91 } 92 93 if syscall.Geteuid() != 0 { 94 t.Skip("Must be root to run test") 95 } 96 } 97 98 func TestAllocDir_MountSharedAlloc(t *testing.T) { 99 MountCompatible(t) 100 tmp, err := ioutil.TempDir("", "AllocDir") 101 if err != nil { 102 t.Fatalf("Couldn't create temp dir: %v", err) 103 } 104 defer os.RemoveAll(tmp) 105 106 d := NewAllocDir(testlog.HCLogger(t), tmp) 107 defer d.Destroy() 108 if err := d.Build(); err != nil { 109 t.Fatalf("Build() failed: %v", err) 110 } 111 112 // Build 2 task dirs 113 td1 := d.NewTaskDir(t1.Name) 114 if err := td1.Build(true, nil); err != nil { 115 t.Fatalf("error build task=%q dir: %v", t1.Name, err) 116 } 117 td2 := d.NewTaskDir(t2.Name) 118 if err := td2.Build(true, nil); err != nil { 119 t.Fatalf("error build task=%q dir: %v", t2.Name, err) 120 } 121 122 // Write a file to the shared dir. 123 contents := []byte("foo") 124 const filename = "bar" 125 if err := ioutil.WriteFile(filepath.Join(d.SharedDir, filename), contents, 0666); err != nil { 126 t.Fatalf("Couldn't write file to shared directory: %v", err) 127 } 128 129 // Check that the file exists in the task directories 130 for _, td := range []*TaskDir{td1, td2} { 131 taskFile := filepath.Join(td.SharedTaskDir, filename) 132 act, err := ioutil.ReadFile(taskFile) 133 if err != nil { 134 t.Errorf("Failed to read shared alloc file from task dir: %v", err) 135 continue 136 } 137 138 if !bytes.Equal(act, contents) { 139 t.Errorf("Incorrect data read from task dir: want %v; got %v", contents, act) 140 } 141 } 142 } 143 144 func TestAllocDir_Snapshot(t *testing.T) { 145 tmp, err := ioutil.TempDir("", "AllocDir") 146 if err != nil { 147 t.Fatalf("Couldn't create temp dir: %v", err) 148 } 149 defer os.RemoveAll(tmp) 150 151 d := NewAllocDir(testlog.HCLogger(t), tmp) 152 defer d.Destroy() 153 if err := d.Build(); err != nil { 154 t.Fatalf("Build() failed: %v", err) 155 } 156 157 // Build 2 task dirs 158 td1 := d.NewTaskDir(t1.Name) 159 if err := td1.Build(false, nil); err != nil { 160 t.Fatalf("error build task=%q dir: %v", t1.Name, err) 161 } 162 td2 := d.NewTaskDir(t2.Name) 163 if err := td2.Build(false, nil); err != nil { 164 t.Fatalf("error build task=%q dir: %v", t2.Name, err) 165 } 166 167 // Write a file to the shared dir. 168 exp := []byte{'f', 'o', 'o'} 169 file := "bar" 170 if err := ioutil.WriteFile(filepath.Join(d.SharedDir, "data", file), exp, 0666); err != nil { 171 t.Fatalf("Couldn't write file to shared directory: %v", err) 172 } 173 174 // Write a symlink to the shared dir 175 link := "qux" 176 if err := os.Symlink("foo", filepath.Join(d.SharedDir, "data", link)); err != nil { 177 t.Fatalf("Couldn't write symlink to shared directory: %v", err) 178 } 179 180 // Write a file to the task local 181 exp = []byte{'b', 'a', 'r'} 182 file1 := "lol" 183 if err := ioutil.WriteFile(filepath.Join(td1.LocalDir, file1), exp, 0666); err != nil { 184 t.Fatalf("couldn't write file to task local directory: %v", err) 185 } 186 187 // Write a symlink to the task local 188 link1 := "baz" 189 if err := os.Symlink("bar", filepath.Join(td1.LocalDir, link1)); err != nil { 190 t.Fatalf("couldn't write symlink to task local directory :%v", err) 191 } 192 193 var b bytes.Buffer 194 if err := d.Snapshot(&b); err != nil { 195 t.Fatalf("err: %v", err) 196 } 197 198 tr := tar.NewReader(&b) 199 var files []string 200 var links []string 201 for { 202 hdr, err := tr.Next() 203 if err != nil && err != io.EOF { 204 t.Fatalf("err: %v", err) 205 } 206 if err == io.EOF { 207 break 208 } 209 if hdr.Typeflag == tar.TypeReg { 210 files = append(files, hdr.FileInfo().Name()) 211 } else if hdr.Typeflag == tar.TypeSymlink { 212 links = append(links, hdr.FileInfo().Name()) 213 } 214 } 215 216 if len(files) != 2 { 217 t.Fatalf("bad files: %#v", files) 218 } 219 if len(links) != 2 { 220 t.Fatalf("bad links: %#v", links) 221 } 222 } 223 224 func TestAllocDir_Move(t *testing.T) { 225 tmp1, err := ioutil.TempDir("", "AllocDir") 226 if err != nil { 227 t.Fatalf("Couldn't create temp dir: %v", err) 228 } 229 defer os.RemoveAll(tmp1) 230 231 tmp2, err := ioutil.TempDir("", "AllocDir") 232 if err != nil { 233 t.Fatalf("Couldn't create temp dir: %v", err) 234 } 235 defer os.RemoveAll(tmp2) 236 237 // Create two alloc dirs 238 d1 := NewAllocDir(testlog.HCLogger(t), tmp1) 239 if err := d1.Build(); err != nil { 240 t.Fatalf("Build() failed: %v", err) 241 } 242 defer d1.Destroy() 243 244 d2 := NewAllocDir(testlog.HCLogger(t), tmp2) 245 if err := d2.Build(); err != nil { 246 t.Fatalf("Build() failed: %v", err) 247 } 248 defer d2.Destroy() 249 250 td1 := d1.NewTaskDir(t1.Name) 251 if err := td1.Build(false, nil); err != nil { 252 t.Fatalf("TaskDir.Build() faild: %v", err) 253 } 254 255 // Create but don't build second task dir to mimic alloc/task runner 256 // behavior (AllocDir.Move() is called pre-TaskDir.Build). 257 d2.NewTaskDir(t1.Name) 258 259 dataDir := filepath.Join(d1.SharedDir, SharedDataDir) 260 261 // Write a file to the shared dir. 262 exp1 := []byte("foo") 263 file1 := "bar" 264 if err := ioutil.WriteFile(filepath.Join(dataDir, file1), exp1, 0666); err != nil { 265 t.Fatalf("Couldn't write file to shared directory: %v", err) 266 } 267 268 // Write a file to the task local 269 exp2 := []byte("bar") 270 file2 := "lol" 271 if err := ioutil.WriteFile(filepath.Join(td1.LocalDir, file2), exp2, 0666); err != nil { 272 t.Fatalf("couldn't write to task local directory: %v", err) 273 } 274 275 // Move the d1 allocdir to d2 276 if err := d2.Move(d1, []*structs.Task{t1}); err != nil { 277 t.Fatalf("err: %v", err) 278 } 279 280 // Ensure the files in d1 are present in d2 281 fi, err := os.Stat(filepath.Join(d2.SharedDir, SharedDataDir, file1)) 282 if err != nil || fi == nil { 283 t.Fatalf("data dir was not moved") 284 } 285 286 fi, err = os.Stat(filepath.Join(d2.TaskDirs[t1.Name].LocalDir, file2)) 287 if err != nil || fi == nil { 288 t.Fatalf("task local dir was not moved") 289 } 290 } 291 292 func TestAllocDir_EscapeChecking(t *testing.T) { 293 tmp, err := ioutil.TempDir("", "AllocDir") 294 if err != nil { 295 t.Fatalf("Couldn't create temp dir: %v", err) 296 } 297 defer os.RemoveAll(tmp) 298 299 d := NewAllocDir(testlog.HCLogger(t), tmp) 300 if err := d.Build(); err != nil { 301 t.Fatalf("Build() failed: %v", err) 302 } 303 defer d.Destroy() 304 305 // Check that issuing calls that escape the alloc dir returns errors 306 // List 307 if _, err := d.List(".."); err == nil || !strings.Contains(err.Error(), "escapes") { 308 t.Fatalf("List of escaping path didn't error: %v", err) 309 } 310 311 // Stat 312 if _, err := d.Stat("../foo"); err == nil || !strings.Contains(err.Error(), "escapes") { 313 t.Fatalf("Stat of escaping path didn't error: %v", err) 314 } 315 316 // ReadAt 317 if _, err := d.ReadAt("../foo", 0); err == nil || !strings.Contains(err.Error(), "escapes") { 318 t.Fatalf("ReadAt of escaping path didn't error: %v", err) 319 } 320 321 // BlockUntilExists 322 if _, err := d.BlockUntilExists(context.Background(), "../foo"); err == nil || !strings.Contains(err.Error(), "escapes") { 323 t.Fatalf("BlockUntilExists of escaping path didn't error: %v", err) 324 } 325 326 // ChangeEvents 327 if _, err := d.ChangeEvents(context.Background(), "../foo", 0); err == nil || !strings.Contains(err.Error(), "escapes") { 328 t.Fatalf("ChangeEvents of escaping path didn't error: %v", err) 329 } 330 } 331 332 // Test that `nomad fs` can't read secrets 333 func TestAllocDir_ReadAt_SecretDir(t *testing.T) { 334 tmp, err := ioutil.TempDir("", "AllocDir") 335 if err != nil { 336 t.Fatalf("Couldn't create temp dir: %v", err) 337 } 338 defer os.RemoveAll(tmp) 339 340 d := NewAllocDir(testlog.HCLogger(t), tmp) 341 if err := d.Build(); err != nil { 342 t.Fatalf("Build() failed: %v", err) 343 } 344 defer d.Destroy() 345 346 td := d.NewTaskDir(t1.Name) 347 if err := td.Build(false, nil); err != nil { 348 t.Fatalf("TaskDir.Build() failed: %v", err) 349 } 350 351 // ReadAt of secret dir should fail 352 secret := filepath.Join(t1.Name, TaskSecrets, "test_file") 353 if _, err := d.ReadAt(secret, 0); err == nil || !strings.Contains(err.Error(), "secret file prohibited") { 354 t.Fatalf("ReadAt of secret file didn't error: %v", err) 355 } 356 } 357 358 func TestAllocDir_SplitPath(t *testing.T) { 359 dir, err := ioutil.TempDir("", "tmpdirtest") 360 if err != nil { 361 log.Fatal(err) 362 } 363 defer os.RemoveAll(dir) 364 365 dest := filepath.Join(dir, "/foo/bar/baz") 366 if err := os.MkdirAll(dest, os.ModePerm); err != nil { 367 t.Fatalf("err: %v", err) 368 } 369 370 info, err := splitPath(dest) 371 if err != nil { 372 t.Fatalf("err: %v", err) 373 } 374 // Testing that is 6 or more rather than 6 because on osx, the temp dir is 375 // randomized. 376 if len(info) < 6 { 377 t.Fatalf("expected more than: %v, actual: %v", 6, len(info)) 378 } 379 } 380 381 func TestAllocDir_CreateDir(t *testing.T) { 382 if syscall.Geteuid() != 0 { 383 t.Skip("Must be root to run test") 384 } 385 386 dir, err := ioutil.TempDir("", "tmpdirtest") 387 if err != nil { 388 t.Fatalf("err: %v", err) 389 } 390 defer os.RemoveAll(dir) 391 392 // create a subdir and a file 393 subdir := filepath.Join(dir, "subdir") 394 if err := os.MkdirAll(subdir, 0760); err != nil { 395 t.Fatalf("err: %v", err) 396 } 397 subdirMode, err := os.Stat(subdir) 398 if err != nil { 399 t.Fatalf("err: %v", err) 400 } 401 402 // Create the above hierarchy under another destination 403 dir1, err := ioutil.TempDir("/tmp", "tempdirdest") 404 if err != nil { 405 t.Fatalf("err: %v", err) 406 } 407 408 if err := createDir(dir1, subdir); err != nil { 409 t.Fatalf("err: %v", err) 410 } 411 412 // Ensure that the subdir had the right perm 413 fi, err := os.Stat(filepath.Join(dir1, dir, "subdir")) 414 if err != nil { 415 t.Fatalf("err: %v", err) 416 } 417 if fi.Mode() != subdirMode.Mode() { 418 t.Fatalf("wrong file mode: %v, expected: %v", fi.Mode(), subdirMode.Mode()) 419 } 420 } 421 422 // TestAllocDir_Copy asserts that AllocDir.Copy does a deep copy of itself and 423 // all TaskDirs. 424 func TestAllocDir_Copy(t *testing.T) { 425 a := NewAllocDir(testlog.HCLogger(t), "foo") 426 a.NewTaskDir("bar") 427 a.NewTaskDir("baz") 428 429 b := a.Copy() 430 431 // Clear the logger 432 require.Equal(t, a, b) 433 434 // Make sure TaskDirs map is copied 435 a.NewTaskDir("new") 436 if b.TaskDirs["new"] != nil { 437 t.Errorf("TaskDirs map shared between copied") 438 } 439 } 440 441 func TestPathFuncs(t *testing.T) { 442 dir, err := ioutil.TempDir("", "nomadtest-pathfuncs") 443 if err != nil { 444 t.Fatalf("error creating temp dir: %v", err) 445 } 446 defer os.RemoveAll(dir) 447 448 missingDir := filepath.Join(dir, "does-not-exist") 449 450 if !pathExists(dir) { 451 t.Errorf("%q exists", dir) 452 } 453 if pathExists(missingDir) { 454 t.Errorf("%q does not exist", missingDir) 455 } 456 457 if empty, err := pathEmpty(dir); err != nil || !empty { 458 t.Errorf("%q is empty and exists. empty=%v error=%v", dir, empty, err) 459 } 460 if empty, err := pathEmpty(missingDir); err == nil || empty { 461 t.Errorf("%q is missing. empty=%v error=%v", missingDir, empty, err) 462 } 463 464 filename := filepath.Join(dir, "just-some-file") 465 f, err := os.Create(filename) 466 if err != nil { 467 t.Fatalf("could not create %q: %v", filename, err) 468 } 469 f.Close() 470 471 if empty, err := pathEmpty(dir); err != nil || empty { 472 t.Errorf("%q is not empty. empty=%v error=%v", dir, empty, err) 473 } 474 } 475 476 func TestAllocDir_DetectContentType(t *testing.T) { 477 require := require.New(t) 478 inputPath := "input/" 479 var testFiles []string 480 err := filepath.Walk(inputPath, func(path string, info os.FileInfo, err error) error { 481 if !info.IsDir() { 482 testFiles = append(testFiles, path) 483 } 484 return err 485 }) 486 require.Nil(err) 487 488 expectedEncodings := map[string]string{ 489 "input/happy.gif": "image/gif", 490 "input/image.png": "image/png", 491 "input/nomad.jpg": "image/jpeg", 492 "input/test.bin": "application/octet-stream", 493 "input/test.json": "application/json", 494 "input/test.txt": "text/plain; charset=utf-8", 495 "input/test.go": "text/plain; charset=utf-8", 496 } 497 for _, file := range testFiles { 498 fileInfo, err := os.Stat(file) 499 require.Nil(err) 500 res := detectContentType(fileInfo, file) 501 require.Equal(expectedEncodings[file], res, "unexpected output for %v", file) 502 } 503 }