github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/cmd/lhsm-plugin-posix/posix/posix_test.go (about) 1 // Copyright (c) 2018 DDN. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package posix_test 6 7 import ( 8 "bytes" 9 "os" 10 "path/filepath" 11 "testing" 12 13 "github.com/pborman/uuid" 14 "github.com/pkg/errors" 15 16 lustre "github.com/intel-hpdd/go-lustre" 17 "github.com/intel-hpdd/lemur/cmd/lhsm-plugin-posix/posix" 18 "github.com/intel-hpdd/lemur/dmplugin" 19 "github.com/intel-hpdd/lemur/internal/testhelpers" 20 "github.com/intel-hpdd/lemur/pkg/checksum" 21 "github.com/intel-hpdd/logging/debug" 22 ) 23 24 func testArchive(t *testing.T, mover *posix.Mover, path string, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction { 25 action := dmplugin.NewTestAction(t, path, offset, length, fileID, data) 26 if err := mover.Archive(action); err != nil { 27 t.Fatal(err) 28 } 29 return action 30 } 31 32 func testRemove(t *testing.T, mover *posix.Mover, fileID string, data []byte) *dmplugin.TestAction { 33 action := dmplugin.NewTestAction(t, "", 0, 0, fileID, data) 34 if err := mover.Remove(action); err != nil { 35 t.Fatal(err) 36 } 37 return action 38 } 39 40 func testRestore(t *testing.T, mover *posix.Mover, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction { 41 tfile, cleanFile := testhelpers.TempFile(t, 0) 42 defer cleanFile() 43 action := dmplugin.NewTestAction(t, tfile, offset, length, fileID, data) 44 if err := mover.Restore(action); err != nil { 45 t.Fatal(err) 46 } 47 return action 48 } 49 50 func testRestoreFail(t *testing.T, mover *posix.Mover, offset int64, length int64, fileID string, data []byte, outer error) *dmplugin.TestAction { 51 debug.Printf("restore %s", fileID) 52 tfile, cleanFile := testhelpers.TempFile(t, 0) 53 defer cleanFile() 54 action := dmplugin.NewTestAction(t, tfile, offset, length, fileID, data) 55 action.SetHash(data) 56 if err := mover.Restore(action); err == nil { 57 t.Fatalf("expected restore failure at: %s", outer) 58 } else { 59 t.Logf("got expected error: %v", err) 60 } 61 return action 62 } 63 64 func testDestinationFile(t *testing.T, mover *posix.Mover, fileID string) string { 65 return mover.Destination(fileID) 66 } 67 68 func defaultChecksum(cfg *posix.ArchiveConfig) *posix.ArchiveConfig { 69 cfg.Checksums = &posix.DefaultChecksums 70 return cfg 71 } 72 73 func TestPosixExtents(t *testing.T) { 74 WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) { 75 type extent struct { 76 id string 77 offset int64 78 length int64 79 } 80 var extents []extent 81 var maxExtent int64 = 1024 * 1024 82 var fileSize int64 = 4*1024*1024 + 42 83 tfile, cleanFile := testhelpers.TempFile(t, fileSize) 84 defer cleanFile() 85 86 st, err := os.Stat(tfile) 87 if err != nil { 88 t.Fatal(err) 89 } 90 actualSize := st.Size() 91 startSum, err := checksum.FileSha1Sum(tfile) 92 if err != nil { 93 t.Fatal(err) 94 } 95 96 for offset := int64(0); offset < actualSize; offset += maxExtent { 97 length := maxExtent 98 if offset+maxExtent > actualSize { 99 length = actualSize - offset 100 } 101 aa := dmplugin.NewTestAction(t, tfile, offset, length, "", nil) 102 if err := mover.Archive(aa); err != nil { 103 t.Fatal(err) 104 } 105 extents = append(extents, extent{aa.UUID(), offset, length}) 106 107 debug.Printf("%d/%d/%d: %s", offset, offset+length, actualSize, aa.UUID()) 108 } 109 110 // Zap the test file like it was released before restoring 111 // the data. 112 if err := os.Truncate(tfile, 0); err != nil { 113 t.Fatal(err) 114 } 115 116 for _, extent := range extents { 117 ra := dmplugin.NewTestAction(t, tfile, extent.offset, extent.length, extent.id, nil) 118 119 if err := mover.Restore(ra); err != nil { 120 t.Fatal(err) 121 } 122 } 123 124 endSum, err := checksum.FileSha1Sum(tfile) 125 if err != nil { 126 t.Fatal(err) 127 } 128 129 if !bytes.Equal(endSum, startSum) { 130 t.Fatalf("end sum (%x) != start sum (%x)", endSum, startSum) 131 } 132 }) 133 } 134 135 func TestPosixArchive(t *testing.T) { 136 WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) { 137 // trigger two updates (at current interval of 10MB 138 var length int64 = 20 * 1024 * 1024 139 tfile, cleanFile := testhelpers.TempFile(t, length) 140 defer cleanFile() 141 142 action := testArchive(t, mover, tfile, 0, length, "", nil) 143 144 // Need to introduce a delay to test new time based updates. 145 if action.Updates != 0 { 146 t.Fatalf("expected 0 updates, got %d", action.Updates) 147 } 148 149 testRestore(t, mover, 0, length, action.UUID(), nil) 150 }) 151 } 152 153 func TestPosixArchiveMaxSize(t *testing.T) { 154 WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) { 155 var length int64 = 1000000 156 tfile, cleanFile := testhelpers.TempFile(t, length) 157 defer cleanFile() 158 159 // we received MaxExtentLength from coordinator, so test this as well 160 action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil) 161 testRestore(t, mover, 0, lustre.MaxExtentLength, action.UUID(), nil) 162 }) 163 } 164 165 func TestPosixArchiveDefaultChecksum(t *testing.T) { 166 WithPosixMover(t, defaultChecksum, func(t *testing.T, mover *posix.Mover) { 167 var length int64 = 100 168 tfile, cleanFile := testhelpers.TempFile(t, length) 169 defer cleanFile() 170 171 action := testArchive(t, mover, tfile, 0, length, "", nil) 172 testRestore(t, mover, 0, length, action.UUID(), nil) 173 }) 174 } 175 176 func TestPosixArchiveDefaultChecksumCompress(t *testing.T) { 177 enableCompress := func(cfg *posix.ArchiveConfig) *posix.ArchiveConfig { 178 return cfg.Merge(&posix.ArchiveConfig{ 179 Compression: "on"}) 180 181 } 182 WithPosixMover(t, enableCompress, func(t *testing.T, mover *posix.Mover) { 183 var length int64 = 100 184 tfile, cleanFile := testhelpers.TempFile(t, length) 185 defer cleanFile() 186 187 action := testArchive(t, mover, tfile, 0, length, "", nil) 188 if filepath.Ext(action.UUID()) != ".gz" { 189 t.Fatal(errors.New("file not compressed")) 190 } 191 testRestore(t, mover, 0, length, action.UUID(), nil) 192 }) 193 } 194 195 func TestPosixArchiveRestoreBrokenFileID(t *testing.T) { 196 WithPosixMover(t, defaultChecksum, func(t *testing.T, mover *posix.Mover) { 197 var length int64 = 100 198 tfile, cleanFile := testhelpers.TempFile(t, length) 199 defer cleanFile() 200 201 action := testArchive(t, mover, tfile, 0, length, "", nil) 202 newID := uuid.New() 203 // Wrong UUID 204 action.SetUUID(newID) 205 testRestoreFail(t, mover, 0, length, action.UUID(), nil, errors.New("")) 206 207 // Missing FileID 208 action.SetUUID("") 209 testRestoreFail(t, mover, 0, length, action.UUID(), nil, errors.New("")) 210 211 // Garbage FildID 212 action.SetUUID("Not a FileID") 213 testRestoreFail(t, mover, 0, length, action.UUID(), nil, errors.New("")) 214 }) 215 } 216 217 func TestPosixArchiveRestoreError(t *testing.T) { 218 WithPosixMover(t, defaultChecksum, func(t *testing.T, mover *posix.Mover) { 219 var length int64 = 100 220 tfile, cleanFile := testhelpers.TempFile(t, length) 221 defer cleanFile() 222 223 // we received MaxExtentLength from coordinator, so test this as well 224 action := testArchive(t, mover, tfile, 0, length, "", nil) 225 226 failRestore := func(t *testing.T, mover *posix.Mover, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction { 227 tfile, cleanFile := testhelpers.TempFile(t, 0) 228 defer cleanFile() 229 os.Chmod(tfile, 0444) 230 action := dmplugin.NewTestAction(t, tfile+".oops", offset, length, fileID, data) 231 if err := mover.Restore(action); err != nil { 232 if !os.IsNotExist(errors.Cause(err)) { 233 t.Fatalf("Unexpected failure: %v", err) 234 } 235 } else { 236 fi, _ := os.Stat(tfile) 237 t.Fatalf("Expected ENOENT failure: %s mode:0%o", fi.Name(), fi.Mode()) 238 } 239 240 return action 241 } 242 243 failRestore(t, mover, 0, length, action.UUID(), nil) 244 }) 245 } 246 247 func TestPosixArchiveNoChecksum(t *testing.T) { 248 disableChecksum := func(cfg *posix.ArchiveConfig) *posix.ArchiveConfig { 249 return cfg.Merge(&posix.ArchiveConfig{ 250 Compression: "off", 251 Checksums: &posix.ChecksumConfig{Disabled: true}}) 252 253 } 254 WithPosixMover(t, disableChecksum, func(t *testing.T, mover *posix.Mover) { 255 var length int64 = 1000000 256 tfile, cleanFile := testhelpers.TempFile(t, length) 257 defer cleanFile() 258 259 action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil) 260 // we received MaxExtentLength from coordinator, so test this as well 261 262 testhelpers.CorruptFile(t, mover.Destination(action.UUID())) 263 264 // Successfully restore corrupt data 265 testRestore(t, mover, 0, lustre.MaxExtentLength, action.UUID(), nil) 266 }) 267 } 268 269 func combine(fnlist ...func(*posix.ArchiveConfig) *posix.ArchiveConfig) func(*posix.ArchiveConfig) *posix.ArchiveConfig { 270 return func(v *posix.ArchiveConfig) *posix.ArchiveConfig { 271 for _, fn := range fnlist { 272 v = fn(v) 273 } 274 return v 275 } 276 } 277 278 func TestPosixArchiveNoChecksumRestore(t *testing.T) { 279 updateConf := func(cfg *posix.ArchiveConfig) *posix.ArchiveConfig { 280 return cfg.Merge(&posix.ArchiveConfig{ 281 Compression: "off", 282 Checksums: &posix.ChecksumConfig{DisableCompareOnRestore: true}}) 283 } 284 285 WithPosixMover(t, updateConf, func(t *testing.T, mover *posix.Mover) { 286 var length int64 = 1000000 287 tfile, cleanFile := testhelpers.TempFile(t, length) 288 defer cleanFile() 289 290 action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil) 291 // we received MaxExtentLength from coordinator, so test this as well 292 293 testhelpers.CorruptFile(t, mover.Destination(action.UUID())) 294 // Successfully restore corrupt data 295 testRestore(t, mover, 0, lustre.MaxExtentLength, action.UUID(), nil) 296 }) 297 } 298 299 func TestPosixArchiveChecksumAfter(t *testing.T) { 300 WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) { 301 var length int64 = 1000000 302 tfile, cleanFile := testhelpers.TempFile(t, length) 303 defer cleanFile() 304 305 // we received MaxExtentLength from coordinator, so test this as well 306 action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil) 307 // Disable checksum generation but should still check existing checksums 308 mover.ChecksumConfig().Disabled = true 309 testhelpers.CorruptFile(t, testDestinationFile(t, mover, action.UUID())) 310 // Don't restore corrupt data 311 testRestoreFail(t, mover, 0, lustre.MaxExtentLength, action.UUID(), action.Hash(), errors.New("")) 312 }) 313 } 314 315 func TestPosixCorruptArchive(t *testing.T) { 316 WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) { 317 var length int64 = 1000000 318 tfile, cleanFile := testhelpers.TempFile(t, length) 319 defer cleanFile() 320 321 action := dmplugin.NewTestAction(t, tfile, 0, length, "", nil) 322 if err := mover.Archive(action); err != nil { 323 t.Fatal(err) 324 } 325 326 path := testDestinationFile(t, mover, action.UUID()) 327 328 testhelpers.CorruptFile(t, path) 329 330 // TODO check for specific CheckSum error 331 testRestoreFail(t, mover, 0, length, action.UUID(), action.Hash(), errors.New("")) 332 333 }) 334 } 335 336 func TestPosixRemove(t *testing.T) { 337 WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) { 338 var length int64 = 1000000 339 tfile, cleanFile := testhelpers.TempFile(t, length) 340 defer cleanFile() 341 342 action := testArchive(t, mover, tfile, 0, length, "", nil) 343 path := testDestinationFile(t, mover, action.UUID()) 344 345 if _, err := os.Stat(path); err != nil { 346 t.Fatalf("Destination file is missing: %v", err) 347 } 348 349 testRemove(t, mover, action.UUID(), nil) 350 351 _, err := os.Stat(path) 352 if !os.IsNotExist(err) { 353 t.Fatalf("Unexpected or missing error: %v", err) 354 } 355 356 testRestoreFail(t, mover, 0, length, action.UUID(), nil, errors.New("")) 357 }) 358 } 359 360 func WithPosixMover(t *testing.T, updateConfig func(*posix.ArchiveConfig) *posix.ArchiveConfig, 361 tester func(t *testing.T, mover *posix.Mover)) { 362 363 config := new(posix.ArchiveConfig) 364 config.Name = "posix-test" 365 366 defer testhelpers.ChdirTemp(t)() 367 archiveDir, cleanArchive := testhelpers.TempDir(t) 368 defer cleanArchive() 369 370 config.Root = archiveDir 371 if updateConfig != nil { 372 config = updateConfig(config) 373 } 374 375 mover, err := posix.NewMover(config) 376 if err != nil { 377 t.Fatal(err) 378 } 379 380 tester(t, mover) 381 }