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