github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/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 "testing" 13 14 tomb "gopkg.in/tomb.v1" 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 osMountSharedDirSupport = map[string]bool{ 24 "darwin": true, 25 "linux": true, 26 } 27 28 t1 = &structs.Task{ 29 Name: "web", 30 Driver: "exec", 31 Config: map[string]interface{}{ 32 "command": "/bin/date", 33 "args": "+%s", 34 }, 35 Resources: &structs.Resources{ 36 DiskMB: 1, 37 }, 38 } 39 40 t2 = &structs.Task{ 41 Name: "web2", 42 Driver: "exec", 43 Config: map[string]interface{}{ 44 "command": "/bin/date", 45 "args": "+%s", 46 }, 47 Resources: &structs.Resources{ 48 DiskMB: 1, 49 }, 50 } 51 ) 52 53 func testLogger() *log.Logger { 54 if testing.Verbose() { 55 return log.New(os.Stderr, "", log.LstdFlags) 56 } 57 return log.New(ioutil.Discard, "", log.LstdFlags) 58 } 59 60 // Test that AllocDir.Build builds just the alloc directory. 61 func TestAllocDir_BuildAlloc(t *testing.T) { 62 tmp, err := ioutil.TempDir("", "AllocDir") 63 if err != nil { 64 t.Fatalf("Couldn't create temp dir: %v", err) 65 } 66 defer os.RemoveAll(tmp) 67 68 d := NewAllocDir(testLogger(), tmp) 69 defer d.Destroy() 70 d.NewTaskDir(t1.Name) 71 d.NewTaskDir(t2.Name) 72 if err := d.Build(); err != nil { 73 t.Fatalf("Build() failed: %v", err) 74 } 75 76 // Check that the AllocDir and each of the task directories exist. 77 if _, err := os.Stat(d.AllocDir); os.IsNotExist(err) { 78 t.Fatalf("Build() didn't create AllocDir %v", d.AllocDir) 79 } 80 81 for _, task := range []*structs.Task{t1, t2} { 82 tDir, ok := d.TaskDirs[task.Name] 83 if !ok { 84 t.Fatalf("Task directory not found for %v", task.Name) 85 } 86 87 if stat, _ := os.Stat(tDir.Dir); stat != nil { 88 t.Fatalf("Build() created TaskDir %v", tDir.Dir) 89 } 90 91 if stat, _ := os.Stat(tDir.SecretsDir); stat != nil { 92 t.Fatalf("Build() created secret dir %v", tDir.Dir) 93 } 94 } 95 } 96 97 func TestAllocDir_MountSharedAlloc(t *testing.T) { 98 testutil.MountCompatible(t) 99 tmp, err := ioutil.TempDir("", "AllocDir") 100 if err != nil { 101 t.Fatalf("Couldn't create temp dir: %v", err) 102 } 103 defer os.RemoveAll(tmp) 104 105 d := NewAllocDir(testLogger(), tmp) 106 defer d.Destroy() 107 if err := d.Build(); err != nil { 108 t.Fatalf("Build() failed: %v", err) 109 } 110 111 // Build 2 task dirs 112 td1 := d.NewTaskDir(t1.Name) 113 if err := td1.Build(false, nil, cstructs.FSIsolationChroot); err != nil { 114 t.Fatalf("error build task=%q dir: %v", t1.Name, err) 115 } 116 td2 := d.NewTaskDir(t2.Name) 117 if err := td2.Build(false, nil, cstructs.FSIsolationChroot); err != nil { 118 t.Fatalf("error build task=%q dir: %v", t2.Name, err) 119 } 120 121 // Write a file to the shared dir. 122 contents := []byte("foo") 123 const filename = "bar" 124 if err := ioutil.WriteFile(filepath.Join(d.SharedDir, filename), contents, 0666); err != nil { 125 t.Fatalf("Couldn't write file to shared directory: %v", err) 126 } 127 128 // Check that the file exists in the task directories 129 for _, td := range []*TaskDir{td1, td2} { 130 taskFile := filepath.Join(td.SharedTaskDir, filename) 131 act, err := ioutil.ReadFile(taskFile) 132 if err != nil { 133 t.Errorf("Failed to read shared alloc file from task dir: %v", err) 134 continue 135 } 136 137 if !bytes.Equal(act, contents) { 138 t.Errorf("Incorrect data read from task dir: want %v; got %v", contents, act) 139 } 140 } 141 } 142 143 func TestAllocDir_Snapshot(t *testing.T) { 144 tmp, err := ioutil.TempDir("", "AllocDir") 145 if err != nil { 146 t.Fatalf("Couldn't create temp dir: %v", err) 147 } 148 defer os.RemoveAll(tmp) 149 150 d := NewAllocDir(testLogger(), tmp) 151 defer d.Destroy() 152 if err := d.Build(); err != nil { 153 t.Fatalf("Build() failed: %v", err) 154 } 155 156 // Build 2 task dirs 157 td1 := d.NewTaskDir(t1.Name) 158 if err := td1.Build(false, nil, cstructs.FSIsolationImage); err != nil { 159 t.Fatalf("error build task=%q dir: %v", t1.Name, err) 160 } 161 td2 := d.NewTaskDir(t2.Name) 162 if err := td2.Build(false, nil, cstructs.FSIsolationImage); err != nil { 163 t.Fatalf("error build task=%q dir: %v", t2.Name, err) 164 } 165 166 // Write a file to the shared dir. 167 exp := []byte{'f', 'o', 'o'} 168 file := "bar" 169 if err := ioutil.WriteFile(filepath.Join(d.SharedDir, "data", file), exp, 0666); err != nil { 170 t.Fatalf("Couldn't write file to shared directory: %v", err) 171 } 172 173 // Write a symlink to the shared dir 174 link := "qux" 175 if err := os.Symlink("foo", filepath.Join(d.SharedDir, "data", link)); err != nil { 176 t.Fatalf("Couldn't write symlink to shared directory: %v", err) 177 } 178 179 // Write a file to the task local 180 exp = []byte{'b', 'a', 'r'} 181 file1 := "lol" 182 if err := ioutil.WriteFile(filepath.Join(td1.LocalDir, file1), exp, 0666); err != nil { 183 t.Fatalf("couldn't write file to task local directory: %v", err) 184 } 185 186 // Write a symlink to the task local 187 link1 := "baz" 188 if err := os.Symlink("bar", filepath.Join(td1.LocalDir, link1)); err != nil { 189 t.Fatalf("couldn't write symlink to task local dirctory :%v", err) 190 } 191 192 var b bytes.Buffer 193 if err := d.Snapshot(&b); err != nil { 194 t.Fatalf("err: %v", err) 195 } 196 197 tr := tar.NewReader(&b) 198 var files []string 199 var links []string 200 for { 201 hdr, err := tr.Next() 202 if err != nil && err != io.EOF { 203 t.Fatalf("err: %v", err) 204 } 205 if err == io.EOF { 206 break 207 } 208 if hdr.Typeflag == tar.TypeReg { 209 files = append(files, hdr.FileInfo().Name()) 210 } else if hdr.Typeflag == tar.TypeSymlink { 211 links = append(links, hdr.FileInfo().Name()) 212 } 213 } 214 215 if len(files) != 2 { 216 t.Fatalf("bad files: %#v", files) 217 } 218 if len(links) != 2 { 219 t.Fatalf("bad links: %#v", links) 220 } 221 } 222 223 func TestAllocDir_Move(t *testing.T) { 224 tmp1, err := ioutil.TempDir("", "AllocDir") 225 if err != nil { 226 t.Fatalf("Couldn't create temp dir: %v", err) 227 } 228 defer os.RemoveAll(tmp1) 229 230 tmp2, err := ioutil.TempDir("", "AllocDir") 231 if err != nil { 232 t.Fatalf("Couldn't create temp dir: %v", err) 233 } 234 defer os.RemoveAll(tmp2) 235 236 // Create two alloc dirs 237 d1 := NewAllocDir(testLogger(), tmp1) 238 if err := d1.Build(); err != nil { 239 t.Fatalf("Build() failed: %v", err) 240 } 241 defer d1.Destroy() 242 243 d2 := NewAllocDir(testLogger(), tmp2) 244 if err := d2.Build(); err != nil { 245 t.Fatalf("Build() failed: %v", err) 246 } 247 defer d2.Destroy() 248 249 td1 := d1.NewTaskDir(t1.Name) 250 if err := td1.Build(false, nil, cstructs.FSIsolationImage); err != nil { 251 t.Fatalf("TaskDir.Build() faild: %v", err) 252 } 253 254 // Create but don't build second task dir to mimic alloc/task runner 255 // behavior (AllocDir.Move() is called pre-TaskDir.Build). 256 d2.NewTaskDir(t1.Name) 257 258 dataDir := filepath.Join(d1.SharedDir, SharedDataDir) 259 260 // Write a file to the shared dir. 261 exp1 := []byte("foo") 262 file1 := "bar" 263 if err := ioutil.WriteFile(filepath.Join(dataDir, file1), exp1, 0666); err != nil { 264 t.Fatalf("Couldn't write file to shared directory: %v", err) 265 } 266 267 // Write a file to the task local 268 exp2 := []byte("bar") 269 file2 := "lol" 270 if err := ioutil.WriteFile(filepath.Join(td1.LocalDir, file2), exp2, 0666); err != nil { 271 t.Fatalf("couldn't write to task local directory: %v", err) 272 } 273 274 // Move the d1 allocdir to d2 275 if err := d2.Move(d1, []*structs.Task{t1}); err != nil { 276 t.Fatalf("err: %v", err) 277 } 278 279 // Ensure the files in d1 are present in d2 280 fi, err := os.Stat(filepath.Join(d2.SharedDir, SharedDataDir, file1)) 281 if err != nil || fi == nil { 282 t.Fatalf("data dir was not moved") 283 } 284 285 fi, err = os.Stat(filepath.Join(d2.TaskDirs[t1.Name].LocalDir, file2)) 286 if err != nil || fi == nil { 287 t.Fatalf("task local dir was not moved") 288 } 289 } 290 291 func TestAllocDir_EscapeChecking(t *testing.T) { 292 tmp, err := ioutil.TempDir("", "AllocDir") 293 if err != nil { 294 t.Fatalf("Couldn't create temp dir: %v", err) 295 } 296 defer os.RemoveAll(tmp) 297 298 d := NewAllocDir(testLogger(), tmp) 299 if err := d.Build(); err != nil { 300 t.Fatalf("Build() failed: %v", err) 301 } 302 defer d.Destroy() 303 304 // Check that issuing calls that escape the alloc dir returns errors 305 // List 306 if _, err := d.List(".."); err == nil || !strings.Contains(err.Error(), "escapes") { 307 t.Fatalf("List of escaping path didn't error: %v", err) 308 } 309 310 // Stat 311 if _, err := d.Stat("../foo"); err == nil || !strings.Contains(err.Error(), "escapes") { 312 t.Fatalf("Stat of escaping path didn't error: %v", err) 313 } 314 315 // ReadAt 316 if _, err := d.ReadAt("../foo", 0); err == nil || !strings.Contains(err.Error(), "escapes") { 317 t.Fatalf("ReadAt of escaping path didn't error: %v", err) 318 } 319 320 // BlockUntilExists 321 tomb := tomb.Tomb{} 322 if _, err := d.BlockUntilExists("../foo", &tomb); 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("../foo", 0, &tomb); 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(testLogger(), 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, cstructs.FSIsolationImage); 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 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 }