github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/cmd/lhsm-plugin-s3/s3_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 main 6 7 import ( 8 "bytes" 9 "os" 10 "testing" 11 "time" 12 13 lustre "github.com/intel-hpdd/go-lustre" 14 "github.com/intel-hpdd/lemur/dmplugin" 15 "github.com/intel-hpdd/lemur/internal/testhelpers" 16 "github.com/intel-hpdd/lemur/pkg/checksum" 17 "github.com/intel-hpdd/logging/debug" 18 ) 19 20 func testArchive(t *testing.T, mover *Mover, path string, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction { 21 action := dmplugin.NewTestAction(t, path, offset, length, fileID, data) 22 if err := mover.Archive(action); err != nil { 23 t.Fatal(err) 24 } 25 return action 26 } 27 28 func testRemove(t *testing.T, mover *Mover, fileID string, data []byte) *dmplugin.TestAction { 29 action := dmplugin.NewTestAction(t, "", 0, 0, fileID, data) 30 if err := mover.Remove(action); err != nil { 31 t.Fatal(err) 32 } 33 return action 34 } 35 36 func testRestore(t *testing.T, mover *Mover, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction { 37 tfile, cleanFile := testhelpers.TempFile(t, 0) 38 defer cleanFile() 39 action := dmplugin.NewTestAction(t, tfile, offset, length, fileID, data) 40 if err := mover.Restore(action); err != nil { 41 t.Fatal(err) 42 } 43 return action 44 } 45 46 func testRestoreFail(t *testing.T, mover *Mover, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction { 47 tfile, cleanFile := testhelpers.TempFile(t, 0) 48 defer cleanFile() 49 action := dmplugin.NewTestAction(t, tfile, offset, length, fileID, data) 50 if err := mover.Restore(action); err == nil { 51 t.Fatal("Expected restore to fail") 52 } 53 return action 54 } 55 56 /* 57 func testDestinationFile(t *testing.T, mover *Mover, buf []byte) string { 58 fileID, err := posix.ParseFileID(buf) 59 if err != nil { 60 t.Fatal(err) 61 } 62 63 return mover.Destination(fileID.UUID) 64 } 65 */ 66 67 func TestS3Extents(t *testing.T) { 68 WithS3Mover(t, nil, func(t *testing.T, mover *Mover) { 69 type extent struct { 70 id string 71 offset int64 72 length int64 73 } 74 var extents []extent 75 var maxExtent int64 = 1024 * 1024 76 var fileSize int64 = 4*1024*1024 + 42 77 tfile, cleanFile := testhelpers.TempFile(t, fileSize) 78 defer cleanFile() 79 80 st, err := os.Stat(tfile) 81 if err != nil { 82 t.Fatal(err) 83 } 84 actualSize := st.Size() 85 startSum, err := checksum.FileSha1Sum(tfile) 86 if err != nil { 87 t.Fatal(err) 88 } 89 debug.Printf("%s actual size: %d", tfile, actualSize) 90 91 for offset := int64(0); offset < actualSize; offset += maxExtent { 92 length := maxExtent 93 if offset+maxExtent > actualSize { 94 length = actualSize - offset 95 } 96 aa := dmplugin.NewTestAction(t, tfile, offset, length, "", nil) 97 if err := mover.Archive(aa); err != nil { 98 t.Fatal(err) 99 } 100 extents = append(extents, extent{aa.UUID(), offset, length}) 101 102 debug.Printf("ARCHIVE %d/%d/%d: %s", offset, offset+length, actualSize, aa.UUID()) 103 } 104 105 // Zap the test file like it was released before restoring 106 // the data. 107 if err := os.Truncate(tfile, 0); err != nil { 108 t.Fatal(err) 109 } 110 for _, extent := range extents { 111 ra := dmplugin.NewTestAction(t, tfile, extent.offset, extent.length, extent.id, nil) 112 113 if err := mover.Restore(ra); err != nil { 114 t.Fatal(err) 115 } 116 117 debug.Printf("RESTORE %d/%d/%d: %s", extent.offset, extent.offset+extent.length, actualSize, ra.UUID()) 118 } 119 120 endSum, err := checksum.FileSha1Sum(tfile) 121 if err != nil { 122 t.Fatal(err) 123 } 124 125 if !bytes.Equal(endSum, startSum) { 126 t.Fatalf("end sum (%x) != start sum (%x)", endSum, startSum) 127 } 128 }) 129 } 130 131 func TestS3Archive(t *testing.T) { 132 // Quick hack to let us test against radosgw; a better solution 133 // would be to figure out some kind of fallback. 134 embiggenUploadPartSize := func(cfg *archiveConfig) *archiveConfig { 135 cfg.UploadPartSize = 128 * 1024 * 1024 136 return cfg 137 } 138 if os.Getenv("LHSM_TEST_RADOSGW") == "" { 139 embiggenUploadPartSize = nil 140 } 141 142 WithS3Mover(t, embiggenUploadPartSize, func(t *testing.T, mover *Mover) { 143 // trigger two updates (at current interval of 10MB 144 var length int64 = 20 * 1024 * 1024 145 tfile, cleanFile := testhelpers.TempFile(t, length) 146 defer cleanFile() 147 148 start := time.Now() 149 action := testArchive(t, mover, tfile, 0, length, "", nil) 150 151 // TODO: parameterize the update interval 152 expectedUpdates := int((time.Since(start) / time.Second) / 10) 153 154 if action.Updates != expectedUpdates { 155 t.Errorf("expected %d updates, got %d", expectedUpdates, action.Updates) 156 } 157 158 start = time.Now() 159 restore := testRestore(t, mover, 0, length, action.UUID(), nil) 160 // TODO: parameterize the update interval 161 duration := time.Since(start) 162 expectedUpdates = int((duration / time.Second) / 10) 163 if restore.Updates != expectedUpdates { 164 t.Errorf("expected %d updates, got %d, duration: %v", expectedUpdates, restore.Updates, duration) 165 } 166 testRemove(t, mover, action.UUID(), nil) 167 }) 168 } 169 170 func TestS3ArchiveMaxSize(t *testing.T) { 171 WithS3Mover(t, nil, func(t *testing.T, mover *Mover) { 172 var length int64 = 1000000 173 tfile, cleanFile := testhelpers.TempFile(t, length) 174 defer cleanFile() 175 176 // we received MaxExtentLength from coordinator, so test this as well 177 action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil) 178 testRestore(t, mover, 0, lustre.MaxExtentLength, action.UUID(), nil) 179 testRemove(t, mover, action.UUID(), nil) 180 }) 181 } 182 183 /* 184 func TestS3ArchiveNoChecksum(t *testing.T) { 185 disableChecksum := func(cfg *MoverConfig) *MoverConfig { 186 cfg.Checksums.Disabled = true 187 return cfg 188 } 189 190 WithS3Mover(t, disableChecksum, func(t *testing.T, mover *Mover) { 191 var length int64 = 1000000 192 tfile, cleanFile := testTempFile(t, length) 193 defer cleanFile() 194 195 action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, nil, nil) 196 // we received MaxExtentLength from coordinator, so test this as well 197 198 fileID, err := posix.ParseFileID(action.FileID()) 199 if err != nil { 200 t.Fatal(err) 201 } 202 203 testCorruptFile(t, mover.Destination(fileID.UUID)) 204 205 // Successfully restore corrupt data 206 testRestore(t, mover, 0, lustre.MaxExtentLength, action.FileID(), nil) 207 }) 208 } 209 210 func TestS3ArchiveNoChecksumRestore(t *testing.T) { 211 disableChecksum := func(cfg *MoverConfig) *MoverConfig { 212 cfg.Checksums.DisableCompareOnRestore = true 213 return cfg 214 } 215 216 WithS3Mover(t, disableChecksum, func(t *testing.T, mover *Mover) { 217 var length int64 = 1000000 218 tfile, cleanFile := testTempFile(t, length) 219 defer cleanFile() 220 221 action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, nil, nil) 222 // we received MaxExtentLength from coordinator, so test this as well 223 224 fileID, err := posix.ParseFileID(action.FileID()) 225 if err != nil { 226 t.Fatal(err) 227 } 228 229 testCorruptFile(t, mover.Destination(fileID.UUID)) 230 // Successfully restore corrupt data 231 testRestore(t, mover, 0, lustre.MaxExtentLength, action.FileID(), nil) 232 }) 233 } 234 235 func TestS3ArchiveChecksumAfter(t *testing.T) { 236 WithS3Mover(t, nil, func(t *testing.T, mover *Mover) { 237 var length int64 = 1000000 238 tfile, cleanFile := testTempFile(t, length) 239 defer cleanFile() 240 241 // we received MaxExtentLength from coordinator, so test this as well 242 action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, nil, nil) 243 // Disable checksum generation but should still check existing checksums 244 mover.ChecksumConfig().Disabled = true 245 testCorruptFile(t, testDestinationFile(t, mover, action.FileID())) 246 // Don't restore corrupt data 247 testRestoreFail(t, mover, 0, lustre.MaxExtentLength, action.FileID(), nil) 248 }) 249 } 250 */ 251 252 /* 253 func TestS3CorruptArchive(t *testing.T) { 254 WithS3Mover(t, nil, func(t *testing.T, mover *Mover) { 255 var length int64 = 1000000 256 tfile, cleanFile := testTempFile(t, length) 257 defer cleanFile() 258 259 action := dmplugin.NewTestAction(t, tfile, 0, length, nil, nil) 260 if err := mover.Archive(action); err != nil { 261 t.Fatal(err) 262 } 263 264 path := testDestinationFile(t, mover, action.FileID()) 265 266 testCorruptFile(t, path) 267 268 // TODO check for specific CheckSum error 269 testRestoreFail(t, mover, 0, length, action.FileID(), nil) 270 271 }) 272 } 273 */ 274 func TestS3Remove(t *testing.T) { 275 WithS3Mover(t, nil, func(t *testing.T, mover *Mover) { 276 var length int64 = 1000000 277 tfile, cleanFile := testhelpers.TempFile(t, length) 278 defer cleanFile() 279 280 action := testArchive(t, mover, tfile, 0, length, "", nil) 281 282 testRemove(t, mover, action.UUID(), nil) 283 testRestoreFail(t, mover, 0, length, action.UUID(), nil) 284 }) 285 } 286 287 func WithS3Mover(t *testing.T, updateConfig func(*archiveConfig) *archiveConfig, 288 tester func(t *testing.T, mover *Mover)) { 289 bucketVar := "LHSM_TEST_BUCKET" 290 // Default region to us-east-1 291 region := os.Getenv("AWS_REGION") 292 if region == "" { 293 region = "us-east-1" 294 } 295 296 bucket := os.Getenv(bucketVar) 297 if bucket == "" { 298 t.Skipf("Set %q in environment to test S3 mover.", bucketVar) 299 } 300 301 config := &archiveConfig{ 302 Name: "test-s3", 303 Region: region, 304 Bucket: bucket, 305 Prefix: "ptest", 306 Endpoint: os.Getenv("AWS_S3_ENDPOINT"), 307 } 308 309 if updateConfig != nil { 310 config = updateConfig(config) 311 } 312 313 defer testhelpers.ChdirTemp(t)() 314 mover := S3Mover(config, s3Svc(config), 1) 315 316 tester(t, mover) 317 }