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