| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package cipd | |
| 6 | |
| 7 import ( | |
| 8 "io/ioutil" | |
| 9 "os" | |
| 10 "path/filepath" | |
| 11 "testing" | |
| 12 | |
| 13 . "github.com/smartystreets/goconvey/convey" | |
| 14 ) | |
| 15 | |
| 16 func TestScanFileSystem(t *testing.T) { | |
| 17 Convey("Given a temp directory", t, func() { | |
| 18 tempDir, err := ioutil.TempDir("", "cipd_test") | |
| 19 So(err, ShouldBeNil) | |
| 20 Reset(func() { os.RemoveAll(tempDir) }) | |
| 21 | |
| 22 Convey("Scan empty dir works", func() { | |
| 23 files, err := ScanFileSystem(tempDir, tempDir, nil) | |
| 24 So(files, ShouldBeEmpty) | |
| 25 So(err, ShouldBeNil) | |
| 26 }) | |
| 27 | |
| 28 Convey("Discovering single file works", func() { | |
| 29 writeFile(tempDir, "single_file", "12345", 0666) | |
| 30 files, err := ScanFileSystem(tempDir, tempDir, nil) | |
| 31 So(len(files), ShouldEqual, 1) | |
| 32 So(err, ShouldBeNil) | |
| 33 | |
| 34 file := files[0] | |
| 35 So(file.Name(), ShouldEqual, "single_file") | |
| 36 So(file.Size(), ShouldEqual, uint64(5)) | |
| 37 So(file.Executable(), ShouldBeFalse) | |
| 38 | |
| 39 r, err := file.Open() | |
| 40 if r != nil { | |
| 41 defer r.Close() | |
| 42 } | |
| 43 So(err, ShouldBeNil) | |
| 44 buf, err := ioutil.ReadAll(r) | |
| 45 So(buf, ShouldResemble, []byte("12345")) | |
| 46 So(err, ShouldBeNil) | |
| 47 }) | |
| 48 | |
| 49 Convey("Discovering single executable file works", func() { | |
| 50 writeFile(tempDir, "single_file", "12345", 0766) | |
| 51 files, err := ScanFileSystem(tempDir, tempDir, nil) | |
| 52 So(len(files), ShouldEqual, 1) | |
| 53 So(err, ShouldBeNil) | |
| 54 file := files[0] | |
| 55 So(file.Executable(), ShouldBeTrue) | |
| 56 }) | |
| 57 | |
| 58 Convey("Relative symlink to outside of package cause error", fun
c() { | |
| 59 writeSymlink(tempDir, "a/b1/rel_symlink", filepath.FromS
lash("../../..")) | |
| 60 _, err := ScanFileSystem(tempDir, tempDir, nil) | |
| 61 So(err, ShouldNotBeNil) | |
| 62 }) | |
| 63 | |
| 64 Convey("Enumerating subdirectories", func() { | |
| 65 writeFile(tempDir, "a", "", 0666) | |
| 66 writeFile(tempDir, "b", "", 0666) | |
| 67 writeFile(tempDir, "1/a", "", 0666) | |
| 68 writeFile(tempDir, "1/b", "", 0666) | |
| 69 writeFile(tempDir, "1/2/a", "", 0666) | |
| 70 files, err := ScanFileSystem(tempDir, tempDir, nil) | |
| 71 So(err, ShouldBeNil) | |
| 72 names := []string{} | |
| 73 for _, f := range files { | |
| 74 names = append(names, f.Name()) | |
| 75 } | |
| 76 // Order matters. Slashes matters. | |
| 77 So(names, ShouldResemble, []string{ | |
| 78 "1/2/a", | |
| 79 "1/a", | |
| 80 "1/b", | |
| 81 "a", | |
| 82 "b", | |
| 83 }) | |
| 84 }) | |
| 85 | |
| 86 Convey("Empty subdirectories are skipped", func() { | |
| 87 mkDir(tempDir, "a") | |
| 88 mkDir(tempDir, "1/2/3") | |
| 89 mkDir(tempDir, "1/c") | |
| 90 writeFile(tempDir, "1/d/file", "1234", 0666) | |
| 91 files, err := ScanFileSystem(tempDir, tempDir, nil) | |
| 92 So(len(files), ShouldEqual, 1) | |
| 93 So(err, ShouldBeNil) | |
| 94 So(files[0].Name(), ShouldEqual, "1/d/file") | |
| 95 }) | |
| 96 | |
| 97 Convey("Non root start path works", func() { | |
| 98 writeFile(tempDir, "a", "", 0666) | |
| 99 writeFile(tempDir, "b", "", 0666) | |
| 100 writeFile(tempDir, "1/a", "", 0666) | |
| 101 writeFile(tempDir, "1/b", "", 0666) | |
| 102 writeFile(tempDir, "1/2/a", "", 0666) | |
| 103 files, err := ScanFileSystem(filepath.Join(tempDir, "1")
, tempDir, nil) | |
| 104 So(err, ShouldBeNil) | |
| 105 names := []string{} | |
| 106 for _, f := range files { | |
| 107 names = append(names, f.Name()) | |
| 108 } | |
| 109 // Order matters. Slashes matters. | |
| 110 So(names, ShouldResemble, []string{ | |
| 111 "1/2/a", | |
| 112 "1/a", | |
| 113 "1/b", | |
| 114 }) | |
| 115 }) | |
| 116 | |
| 117 Convey("Start path must be under root", func() { | |
| 118 _, err := ScanFileSystem(filepath.Dir(tempDir), tempDir,
nil) | |
| 119 So(err, ShouldNotBeNil) | |
| 120 }) | |
| 121 | |
| 122 Convey("Exclude filter works", func() { | |
| 123 writeFile(tempDir, "a", "", 0666) | |
| 124 writeFile(tempDir, "b", "", 0666) | |
| 125 writeFile(tempDir, "1/a", "", 0666) | |
| 126 writeFile(tempDir, "1/b", "", 0666) | |
| 127 writeFile(tempDir, "1/2/a", "", 0666) | |
| 128 | |
| 129 // Exclude "a" and entire "1/" directory. | |
| 130 excluderCalls := []string{} | |
| 131 excluder := func(abs string) bool { | |
| 132 excluderCalls = append(excluderCalls, abs) | |
| 133 if abs == filepath.Join(tempDir, "a") { | |
| 134 return true | |
| 135 } | |
| 136 if abs == filepath.Join(tempDir, "1") { | |
| 137 return true | |
| 138 } | |
| 139 return false | |
| 140 } | |
| 141 | |
| 142 files, err := ScanFileSystem(tempDir, tempDir, excluder) | |
| 143 So(err, ShouldBeNil) | |
| 144 So(len(files), ShouldEqual, 1) | |
| 145 So(files[0].Name(), ShouldEqual, "b") | |
| 146 | |
| 147 // "1/*" subdir should have been skipped completely. | |
| 148 So(excluderCalls, ShouldResemble, []string{ | |
| 149 filepath.Join(tempDir, "1"), | |
| 150 filepath.Join(tempDir, "a"), | |
| 151 filepath.Join(tempDir, "b"), | |
| 152 }) | |
| 153 }) | |
| 154 }) | |
| 155 } | |
| 156 | |
| 157 func TestWrapFile(t *testing.T) { | |
| 158 Convey("Given a temp directory", t, func() { | |
| 159 tempDir, err := ioutil.TempDir("", "cipd_test") | |
| 160 So(err, ShouldBeNil) | |
| 161 Reset(func() { os.RemoveAll(tempDir) }) | |
| 162 | |
| 163 Convey("WrapFile simple file works", func() { | |
| 164 writeFile(tempDir, "dir/a/b", "12345", 0666) | |
| 165 out, err := WrapFile(filepath.Join(tempDir, "dir", "a",
"b"), tempDir, nil) | |
| 166 So(err, ShouldBeNil) | |
| 167 So(out.Name(), ShouldEqual, "dir/a/b") | |
| 168 }) | |
| 169 | |
| 170 Convey("WrapFile executable file works", func() { | |
| 171 writeFile(tempDir, "single_file", "12345", 0766) | |
| 172 out, err := WrapFile(filepath.Join(tempDir, "single_file
"), tempDir, nil) | |
| 173 So(err, ShouldBeNil) | |
| 174 So(out.Executable(), ShouldBeTrue) | |
| 175 }) | |
| 176 | |
| 177 Convey("WrapFile directory fails", func() { | |
| 178 mkDir(tempDir, "dir") | |
| 179 _, err := WrapFile(filepath.Join(tempDir, "dir"), tempDi
r, nil) | |
| 180 So(err, ShouldNotBeNil) | |
| 181 }) | |
| 182 | |
| 183 Convey("WrapFile outside of root fails", func() { | |
| 184 mkDir(tempDir, "a") | |
| 185 writeFile(tempDir, "b", "body", 0666) | |
| 186 _, err := WrapFile(filepath.Join(tempDir, "b"), filepath
.Join(tempDir, "a"), nil) | |
| 187 So(err, ShouldNotBeNil) | |
| 188 }) | |
| 189 | |
| 190 Convey("WrapFile outside of root fails (tricky path)", func() { | |
| 191 mkDir(tempDir, "a") | |
| 192 // "abc" starts with "a", it tricks naive string.HasPref
ix subpath check. | |
| 193 writeFile(tempDir, "abc", "body", 0666) | |
| 194 _, err := WrapFile(filepath.Join(tempDir, "abc"), filepa
th.Join(tempDir, "a"), nil) | |
| 195 So(err, ShouldNotBeNil) | |
| 196 }) | |
| 197 | |
| 198 Convey("WrapFile rel symlink in root", func() { | |
| 199 writeSymlink(tempDir, "a/b/c", filepath.FromSlash("../..
/d")) | |
| 200 mkDir(tempDir, "d") | |
| 201 out, err := WrapFile(filepath.Join(tempDir, "a", "b", "c
"), tempDir, nil) | |
| 202 So(err, ShouldBeNil) | |
| 203 ensureSymlinkTarget(out, "../../d") | |
| 204 }) | |
| 205 | |
| 206 Convey("WrapFile rel symlink outside root", func() { | |
| 207 writeSymlink(tempDir, "a/b/c", filepath.FromSlash("../..
/../d")) | |
| 208 _, err := WrapFile(filepath.Join(tempDir, "a", "b", "c")
, tempDir, nil) | |
| 209 So(err, ShouldNotBeNil) | |
| 210 }) | |
| 211 | |
| 212 Convey("WrapFile abs symlink in root", func() { | |
| 213 writeSymlink(tempDir, "a/b/c", filepath.Join(tempDir, "a
", "d")) | |
| 214 out, err := WrapFile(filepath.Join(tempDir, "a", "b", "c
"), tempDir, nil) | |
| 215 So(err, ShouldBeNil) | |
| 216 ensureSymlinkTarget(out, "../d") | |
| 217 }) | |
| 218 | |
| 219 Convey("WrapFile abs symlink outside root", func() { | |
| 220 writeSymlink(tempDir, "a/b/c", filepath.Dir(tempDir)) | |
| 221 out, err := WrapFile(filepath.Join(tempDir, "a", "b", "c
"), tempDir, nil) | |
| 222 So(err, ShouldBeNil) | |
| 223 ensureSymlinkTarget(out, filepath.ToSlash(filepath.Dir(t
empDir))) | |
| 224 }) | |
| 225 }) | |
| 226 } | |
| 227 | |
| 228 func mkDir(root string, path string) { | |
| 229 abs := filepath.Join(root, filepath.FromSlash(path)) | |
| 230 err := os.MkdirAll(abs, 0777) | |
| 231 if err != nil { | |
| 232 panic("Failed to create a directory under temp directory") | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 func writeFile(root string, path string, data string, mode os.FileMode) { | |
| 237 abs := filepath.Join(root, filepath.FromSlash(path)) | |
| 238 os.MkdirAll(filepath.Dir(abs), 0777) | |
| 239 err := ioutil.WriteFile(abs, []byte(data), mode) | |
| 240 if err != nil { | |
| 241 panic("Failed to write a temp file") | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 func writeSymlink(root string, path string, target string) { | |
| 246 abs := filepath.Join(root, filepath.FromSlash(path)) | |
| 247 os.MkdirAll(filepath.Dir(abs), 0777) | |
| 248 err := os.Symlink(target, abs) | |
| 249 if err != nil { | |
| 250 panic("Failed to create symlink") | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 func ensureSymlinkTarget(file File, target string) { | |
| 255 So(file.Symlink(), ShouldBeTrue) | |
| 256 discoveredTarget, err := file.SymlinkTarget() | |
| 257 So(err, ShouldBeNil) | |
| 258 So(discoveredTarget, ShouldEqual, target) | |
| 259 } | |
| 260 | |
| 261 func TestFileSystemDestination(t *testing.T) { | |
| 262 Convey("Given a temp directory", t, func() { | |
| 263 tempDir, err := ioutil.TempDir("", "cipd_test") | |
| 264 destDir := filepath.Join(tempDir, "dest") | |
| 265 So(err, ShouldBeNil) | |
| 266 dest := NewFileSystemDestination(destDir) | |
| 267 Reset(func() { os.RemoveAll(tempDir) }) | |
| 268 | |
| 269 writeFileToDest := func(name string, executable bool, data strin
g) { | |
| 270 writer, err := dest.CreateFile(name, executable) | |
| 271 if writer != nil { | |
| 272 defer writer.Close() | |
| 273 } | |
| 274 So(err, ShouldBeNil) | |
| 275 _, err = writer.Write([]byte(data)) | |
| 276 So(err, ShouldBeNil) | |
| 277 } | |
| 278 | |
| 279 writeSymlinkToDest := func(name string, target string) { | |
| 280 err := dest.CreateSymlink(name, target) | |
| 281 So(err, ShouldBeNil) | |
| 282 } | |
| 283 | |
| 284 Convey("Empty success write works", func() { | |
| 285 So(dest.Begin(), ShouldBeNil) | |
| 286 So(dest.End(true), ShouldBeNil) | |
| 287 | |
| 288 // Should create a new directory. | |
| 289 stat, err := os.Stat(destDir) | |
| 290 So(err, ShouldBeNil) | |
| 291 So(stat.IsDir(), ShouldBeTrue) | |
| 292 | |
| 293 // And it should be empty. | |
| 294 files, err := ScanFileSystem(destDir, destDir, nil) | |
| 295 So(err, ShouldBeNil) | |
| 296 So(len(files), ShouldEqual, 0) | |
| 297 }) | |
| 298 | |
| 299 Convey("Empty failed write works", func() { | |
| 300 So(dest.Begin(), ShouldBeNil) | |
| 301 So(dest.End(false), ShouldBeNil) | |
| 302 | |
| 303 // Doesn't create a directory. | |
| 304 _, err := os.Stat(destDir) | |
| 305 So(os.IsNotExist(err), ShouldBeTrue) | |
| 306 }) | |
| 307 | |
| 308 Convey("Double begin or double end fails", func() { | |
| 309 So(dest.Begin(), ShouldBeNil) | |
| 310 So(dest.Begin(), ShouldNotBeNil) | |
| 311 So(dest.End(true), ShouldBeNil) | |
| 312 So(dest.End(true), ShouldNotBeNil) | |
| 313 }) | |
| 314 | |
| 315 Convey("CreateFile works only when destination is open", func()
{ | |
| 316 wr, err := dest.CreateFile("testing", true) | |
| 317 So(wr, ShouldBeNil) | |
| 318 So(err, ShouldNotBeNil) | |
| 319 }) | |
| 320 | |
| 321 Convey("CreateFile rejects invalid relative paths", func() { | |
| 322 So(dest.Begin(), ShouldBeNil) | |
| 323 defer dest.End(true) | |
| 324 | |
| 325 // Rel path that is still inside the package is ok. | |
| 326 wr, err := dest.CreateFile("a/b/c/../../../d", false) | |
| 327 So(err, ShouldBeNil) | |
| 328 wr.Close() | |
| 329 | |
| 330 // Rel path pointing outside is forbidden. | |
| 331 _, err = dest.CreateFile("a/b/c/../../../../d", false) | |
| 332 So(err, ShouldNotBeNil) | |
| 333 }) | |
| 334 | |
| 335 Convey("CreateSymlink rejects invalid relative paths", func() { | |
| 336 So(dest.Begin(), ShouldBeNil) | |
| 337 defer dest.End(true) | |
| 338 | |
| 339 // Rel symlink to a file inside the destination is OK. | |
| 340 So(dest.CreateSymlink("a/b/c", "../.."), ShouldBeNil) | |
| 341 // Rel symlink to a file outside -> error. | |
| 342 So(dest.CreateSymlink("a/b/c", "../../.."), ShouldNotBeN
il) | |
| 343 }) | |
| 344 | |
| 345 Convey("Committing bunch of files works", func() { | |
| 346 So(dest.Begin(), ShouldBeNil) | |
| 347 writeFileToDest("a", false, "a data") | |
| 348 writeFileToDest("exe", true, "exe data") | |
| 349 writeFileToDest("dir/c", false, "dir/c data") | |
| 350 writeFileToDest("dir/dir/d", false, "dir/dir/c data") | |
| 351 writeSymlinkToDest("abs_symlink", filepath.FromSlash(tem
pDir)) | |
| 352 writeSymlinkToDest("dir/dir/rel_symlink", "../../a") | |
| 353 So(dest.End(true), ShouldBeNil) | |
| 354 | |
| 355 // Ensure everything is there. | |
| 356 files, err := ScanFileSystem(destDir, destDir, nil) | |
| 357 So(err, ShouldBeNil) | |
| 358 names := []string{} | |
| 359 for _, f := range files { | |
| 360 names = append(names, f.Name()) | |
| 361 } | |
| 362 So(names, ShouldResemble, []string{ | |
| 363 "a", | |
| 364 "abs_symlink", | |
| 365 "dir/c", | |
| 366 "dir/dir/d", | |
| 367 "dir/dir/rel_symlink", | |
| 368 "exe", | |
| 369 }) | |
| 370 | |
| 371 // Ensure data is valid (check first file only). | |
| 372 r, err := files[0].Open() | |
| 373 if r != nil { | |
| 374 defer r.Close() | |
| 375 } | |
| 376 So(err, ShouldBeNil) | |
| 377 data, err := ioutil.ReadAll(r) | |
| 378 So(err, ShouldBeNil) | |
| 379 So(data, ShouldResemble, []byte("a data")) | |
| 380 | |
| 381 // Ensure file mode is valid. | |
| 382 So(files[5].Name(), ShouldEqual, "exe") | |
| 383 So(files[5].Executable(), ShouldBeTrue) | |
| 384 | |
| 385 // Ensure absolute symlink if valid. | |
| 386 So(files[1].Name(), ShouldEqual, "abs_symlink") | |
| 387 ensureSymlinkTarget(files[1], filepath.FromSlash(tempDir
)) | |
| 388 | |
| 389 // Ensure relative symlink is valid. | |
| 390 So(files[4].Name(), ShouldEqual, "dir/dir/rel_symlink") | |
| 391 ensureSymlinkTarget(files[4], "../../a") | |
| 392 | |
| 393 // Ensure no temp files left. | |
| 394 allFiles, err := ScanFileSystem(tempDir, tempDir, nil) | |
| 395 So(len(allFiles), ShouldEqual, len(files)) | |
| 396 }) | |
| 397 | |
| 398 Convey("Rolling back bunch of files works", func() { | |
| 399 So(dest.Begin(), ShouldBeNil) | |
| 400 writeFileToDest("a", false, "a data") | |
| 401 writeFileToDest("dir/c", false, "dir/c data") | |
| 402 writeSymlinkToDest("dir/d", "c") | |
| 403 So(dest.End(false), ShouldBeNil) | |
| 404 | |
| 405 // No dest directory. | |
| 406 _, err := os.Stat(destDir) | |
| 407 So(os.IsNotExist(err), ShouldBeTrue) | |
| 408 | |
| 409 // Ensure no temp files left. | |
| 410 allFiles, err := ScanFileSystem(tempDir, tempDir, nil) | |
| 411 So(len(allFiles), ShouldEqual, 0) | |
| 412 }) | |
| 413 | |
| 414 Convey("Overwriting a directory works", func() { | |
| 415 // Create dest directory manually with some stuff. | |
| 416 err := os.Mkdir(destDir, 0777) | |
| 417 So(err, ShouldBeNil) | |
| 418 err = ioutil.WriteFile(filepath.Join(destDir, "data"), [
]byte("data"), 0666) | |
| 419 So(err, ShouldBeNil) | |
| 420 | |
| 421 // Now deploy something to it. | |
| 422 So(dest.Begin(), ShouldBeNil) | |
| 423 writeFileToDest("a", false, "a data") | |
| 424 writeSymlinkToDest("b", "a") | |
| 425 So(dest.End(true), ShouldBeNil) | |
| 426 | |
| 427 // Overwritten. | |
| 428 files, err := ScanFileSystem(destDir, destDir, nil) | |
| 429 So(err, ShouldBeNil) | |
| 430 So(len(files), ShouldEqual, 2) | |
| 431 So(files[0].Name(), ShouldEqual, "a") | |
| 432 So(files[1].Name(), ShouldEqual, "b") | |
| 433 }) | |
| 434 | |
| 435 Convey("Not overwriting a directory works", func() { | |
| 436 // Create dest directory manually with some stuff. | |
| 437 err := os.Mkdir(destDir, 0777) | |
| 438 So(err, ShouldBeNil) | |
| 439 err = ioutil.WriteFile(filepath.Join(destDir, "data"), [
]byte("data"), 0666) | |
| 440 So(err, ShouldBeNil) | |
| 441 | |
| 442 // Now attempt deploy something to it, but roll back. | |
| 443 So(dest.Begin(), ShouldBeNil) | |
| 444 writeFileToDest("a", false, "a data") | |
| 445 writeSymlinkToDest("b", "a") | |
| 446 So(dest.End(false), ShouldBeNil) | |
| 447 | |
| 448 // Kept as is. | |
| 449 files, err := ScanFileSystem(destDir, destDir, nil) | |
| 450 So(err, ShouldBeNil) | |
| 451 So(len(files), ShouldEqual, 1) | |
| 452 So(files[0].Name(), ShouldEqual, "data") | |
| 453 }) | |
| 454 | |
| 455 Convey("Opening file twice fails", func() { | |
| 456 So(dest.Begin(), ShouldBeNil) | |
| 457 writeFileToDest("a", false, "a data") | |
| 458 w, err := dest.CreateFile("a", false) | |
| 459 So(w, ShouldBeNil) | |
| 460 So(err, ShouldNotBeNil) | |
| 461 So(dest.End(true), ShouldBeNil) | |
| 462 }) | |
| 463 | |
| 464 Convey("End with opened files fail", func() { | |
| 465 So(dest.Begin(), ShouldBeNil) | |
| 466 w, err := dest.CreateFile("a", false) | |
| 467 So(w, ShouldNotBeNil) | |
| 468 So(err, ShouldBeNil) | |
| 469 So(dest.End(true), ShouldNotBeNil) | |
| 470 w.Close() | |
| 471 So(dest.End(true), ShouldBeNil) | |
| 472 }) | |
| 473 }) | |
| 474 } | |
| OLD | NEW |