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