github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/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 ) 20 21 var ( 22 osMountSharedDirSupport = map[string]bool{ 23 "darwin": true, 24 "linux": true, 25 } 26 27 t1 = &structs.Task{ 28 Name: "web", 29 Driver: "exec", 30 Config: map[string]interface{}{ 31 "command": "/bin/date", 32 "args": "+%s", 33 }, 34 Resources: &structs.Resources{ 35 DiskMB: 1, 36 }, 37 } 38 39 t2 = &structs.Task{ 40 Name: "web2", 41 Driver: "exec", 42 Config: map[string]interface{}{ 43 "command": "/bin/date", 44 "args": "+%s", 45 }, 46 Resources: &structs.Resources{ 47 DiskMB: 1, 48 }, 49 } 50 ) 51 52 func testLogger() *log.Logger { 53 return log.New(os.Stderr, "", 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 file to the task local 170 exp = []byte{'b', 'a', 'r'} 171 file1 := "lol" 172 if err := ioutil.WriteFile(filepath.Join(td1.LocalDir, file1), exp, 0666); err != nil { 173 t.Fatalf("couldn't write to task local directory: %v", err) 174 } 175 176 var b bytes.Buffer 177 if err := d.Snapshot(&b); err != nil { 178 t.Fatalf("err: %v", err) 179 } 180 181 tr := tar.NewReader(&b) 182 var files []string 183 for { 184 hdr, err := tr.Next() 185 if err != nil && err != io.EOF { 186 t.Fatalf("err: %v", err) 187 } 188 if err == io.EOF { 189 break 190 } 191 if hdr.Typeflag == tar.TypeReg { 192 files = append(files, hdr.FileInfo().Name()) 193 } 194 } 195 196 if len(files) != 2 { 197 t.Fatalf("bad files: %#v", files) 198 } 199 } 200 201 func TestAllocDir_Move(t *testing.T) { 202 tmp1, err := ioutil.TempDir("", "AllocDir") 203 if err != nil { 204 t.Fatalf("Couldn't create temp dir: %v", err) 205 } 206 defer os.RemoveAll(tmp1) 207 208 tmp2, err := ioutil.TempDir("", "AllocDir") 209 if err != nil { 210 t.Fatalf("Couldn't create temp dir: %v", err) 211 } 212 defer os.RemoveAll(tmp2) 213 214 // Create two alloc dirs 215 d1 := NewAllocDir(testLogger(), tmp1) 216 if err := d1.Build(); err != nil { 217 t.Fatalf("Build() failed: %v", err) 218 } 219 defer d1.Destroy() 220 221 d2 := NewAllocDir(testLogger(), tmp2) 222 if err := d2.Build(); err != nil { 223 t.Fatalf("Build() failed: %v", err) 224 } 225 defer d2.Destroy() 226 227 td1 := d1.NewTaskDir(t1.Name) 228 if err := td1.Build(false, nil, cstructs.FSIsolationImage); err != nil { 229 t.Fatalf("TaskDir.Build() faild: %v", err) 230 } 231 232 // Create but don't build second task dir to mimic alloc/task runner 233 // behavior (AllocDir.Move() is called pre-TaskDir.Build). 234 d2.NewTaskDir(t1.Name) 235 236 dataDir := filepath.Join(d1.SharedDir, SharedDataDir) 237 238 // Write a file to the shared dir. 239 exp1 := []byte("foo") 240 file1 := "bar" 241 if err := ioutil.WriteFile(filepath.Join(dataDir, file1), exp1, 0666); err != nil { 242 t.Fatalf("Couldn't write file to shared directory: %v", err) 243 } 244 245 // Write a file to the task local 246 exp2 := []byte("bar") 247 file2 := "lol" 248 if err := ioutil.WriteFile(filepath.Join(td1.LocalDir, file2), exp2, 0666); err != nil { 249 t.Fatalf("couldn't write to task local directory: %v", err) 250 } 251 252 // Move the d1 allocdir to d2 253 if err := d2.Move(d1, []*structs.Task{t1}); err != nil { 254 t.Fatalf("err: %v", err) 255 } 256 257 // Ensure the files in d1 are present in d2 258 fi, err := os.Stat(filepath.Join(d2.SharedDir, SharedDataDir, file1)) 259 if err != nil || fi == nil { 260 t.Fatalf("data dir was not moved") 261 } 262 263 fi, err = os.Stat(filepath.Join(d2.TaskDirs[t1.Name].LocalDir, file2)) 264 if err != nil || fi == nil { 265 t.Fatalf("task local dir was not moved") 266 } 267 } 268 269 func TestAllocDir_EscapeChecking(t *testing.T) { 270 tmp, err := ioutil.TempDir("", "AllocDir") 271 if err != nil { 272 t.Fatalf("Couldn't create temp dir: %v", err) 273 } 274 defer os.RemoveAll(tmp) 275 276 d := NewAllocDir(testLogger(), tmp) 277 if err := d.Build(); err != nil { 278 t.Fatalf("Build() failed: %v", err) 279 } 280 defer d.Destroy() 281 282 // Check that issuing calls that escape the alloc dir returns errors 283 // List 284 if _, err := d.List(".."); err == nil || !strings.Contains(err.Error(), "escapes") { 285 t.Fatalf("List of escaping path didn't error: %v", err) 286 } 287 288 // Stat 289 if _, err := d.Stat("../foo"); err == nil || !strings.Contains(err.Error(), "escapes") { 290 t.Fatalf("Stat of escaping path didn't error: %v", err) 291 } 292 293 // ReadAt 294 if _, err := d.ReadAt("../foo", 0); err == nil || !strings.Contains(err.Error(), "escapes") { 295 t.Fatalf("ReadAt of escaping path didn't error: %v", err) 296 } 297 298 // BlockUntilExists 299 tomb := tomb.Tomb{} 300 if _, err := d.BlockUntilExists("../foo", &tomb); err == nil || !strings.Contains(err.Error(), "escapes") { 301 t.Fatalf("BlockUntilExists of escaping path didn't error: %v", err) 302 } 303 304 // ChangeEvents 305 if _, err := d.ChangeEvents("../foo", 0, &tomb); err == nil || !strings.Contains(err.Error(), "escapes") { 306 t.Fatalf("ChangeEvents of escaping path didn't error: %v", err) 307 } 308 } 309 310 // Test that `nomad fs` can't read secrets 311 func TestAllocDir_ReadAt_SecretDir(t *testing.T) { 312 tmp, err := ioutil.TempDir("", "AllocDir") 313 if err != nil { 314 t.Fatalf("Couldn't create temp dir: %v", err) 315 } 316 defer os.RemoveAll(tmp) 317 318 d := NewAllocDir(testLogger(), tmp) 319 if err := d.Build(); err != nil { 320 t.Fatalf("Build() failed: %v", err) 321 } 322 defer d.Destroy() 323 324 td := d.NewTaskDir(t1.Name) 325 if err := td.Build(false, nil, cstructs.FSIsolationImage); err != nil { 326 t.Fatalf("TaskDir.Build() failed: %v", err) 327 } 328 329 // ReadAt of secret dir should fail 330 secret := filepath.Join(t1.Name, TaskSecrets, "test_file") 331 if _, err := d.ReadAt(secret, 0); err == nil || !strings.Contains(err.Error(), "secret file prohibited") { 332 t.Fatalf("ReadAt of secret file didn't error: %v", err) 333 } 334 } 335 336 func TestAllocDir_SplitPath(t *testing.T) { 337 dir, err := ioutil.TempDir("", "tmpdirtest") 338 if err != nil { 339 log.Fatal(err) 340 } 341 defer os.RemoveAll(dir) 342 343 dest := filepath.Join(dir, "/foo/bar/baz") 344 if err := os.MkdirAll(dest, os.ModePerm); err != nil { 345 t.Fatalf("err: %v", err) 346 } 347 348 info, err := splitPath(dest) 349 if err != nil { 350 t.Fatalf("err: %v", err) 351 } 352 if len(info) != 6 { 353 t.Fatalf("expected: %v, actual: %v", 6, len(info)) 354 } 355 } 356 357 func TestAllocDir_CreateDir(t *testing.T) { 358 dir, err := ioutil.TempDir("", "tmpdirtest") 359 if err != nil { 360 t.Fatalf("err: %v", err) 361 } 362 defer os.RemoveAll(dir) 363 364 // create a subdir and a file 365 subdir := filepath.Join(dir, "subdir") 366 if err := os.MkdirAll(subdir, 0760); err != nil { 367 t.Fatalf("err: %v", err) 368 } 369 subdirMode, err := os.Stat(subdir) 370 if err != nil { 371 t.Fatalf("err: %v", err) 372 } 373 374 // Create the above hierarchy under another destination 375 dir1, err := ioutil.TempDir("/tmp", "tempdirdest") 376 if err != nil { 377 t.Fatalf("err: %v", err) 378 } 379 380 if err := createDir(dir1, subdir); err != nil { 381 t.Fatalf("err: %v", err) 382 } 383 384 // Ensure that the subdir had the right perm 385 fi, err := os.Stat(filepath.Join(dir1, dir, "subdir")) 386 if err != nil { 387 t.Fatalf("err: %v", err) 388 } 389 if fi.Mode() != subdirMode.Mode() { 390 t.Fatalf("wrong file mode: %v, expected: %v", fi.Mode(), subdirMode.Mode()) 391 } 392 } 393 394 func TestPathFuncs(t *testing.T) { 395 dir, err := ioutil.TempDir("", "nomadtest-pathfuncs") 396 if err != nil { 397 t.Fatalf("error creating temp dir: %v", err) 398 } 399 defer os.RemoveAll(dir) 400 401 missingDir := filepath.Join(dir, "does-not-exist") 402 403 if !pathExists(dir) { 404 t.Errorf("%q exists", dir) 405 } 406 if pathExists(missingDir) { 407 t.Errorf("%q does not exist", missingDir) 408 } 409 410 if empty, err := pathEmpty(dir); err != nil || !empty { 411 t.Errorf("%q is empty and exists. empty=%v error=%v", dir, empty, err) 412 } 413 if empty, err := pathEmpty(missingDir); err == nil || empty { 414 t.Errorf("%q is missing. empty=%v error=%v", missingDir, empty, err) 415 } 416 417 filename := filepath.Join(dir, "just-some-file") 418 f, err := os.Create(filename) 419 if err != nil { 420 t.Fatalf("could not create %q: %v", filename, err) 421 } 422 f.Close() 423 424 if empty, err := pathEmpty(dir); err != nil || empty { 425 t.Errorf("%q is not empty. empty=%v error=%v", dir, empty, err) 426 } 427 }