github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/object_api_suite_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "bytes" 22 "context" 23 "io" 24 "math/rand" 25 "strconv" 26 "testing" 27 28 "github.com/dustin/go-humanize" 29 "github.com/minio/minio/internal/kms" 30 ) 31 32 // Return pointer to testOneByteReadEOF{} 33 func newTestReaderEOF(data []byte) io.Reader { 34 return &testOneByteReadEOF{false, data} 35 } 36 37 // OneByteReadEOF - implements io.Reader which returns 1 byte along with io.EOF error. 38 type testOneByteReadEOF struct { 39 eof bool 40 data []byte 41 } 42 43 func (r *testOneByteReadEOF) Read(p []byte) (n int, err error) { 44 if r.eof { 45 return 0, io.EOF 46 } 47 n = copy(p, r.data) 48 r.eof = true 49 return n, io.EOF 50 } 51 52 // Return pointer to testOneByteReadNoEOF{} 53 func newTestReaderNoEOF(data []byte) io.Reader { 54 return &testOneByteReadNoEOF{false, data} 55 } 56 57 // testOneByteReadNoEOF - implements io.Reader which returns 1 byte and nil error, but 58 // returns io.EOF on the next Read(). 59 type testOneByteReadNoEOF struct { 60 eof bool 61 data []byte 62 } 63 64 func (r *testOneByteReadNoEOF) Read(p []byte) (n int, err error) { 65 if r.eof { 66 return 0, io.EOF 67 } 68 n = copy(p, r.data) 69 r.eof = true 70 return n, nil 71 } 72 73 // Wrapper for calling testMakeBucket for both Erasure and FS. 74 func TestMakeBucket(t *testing.T) { 75 ExecObjectLayerTest(t, testMakeBucket) 76 } 77 78 // Tests validate bucket creation. 79 func testMakeBucket(obj ObjectLayer, instanceType string, t TestErrHandler) { 80 err := obj.MakeBucket(context.Background(), "bucket-unknown", MakeBucketOptions{}) 81 if err != nil { 82 t.Fatalf("%s: <ERROR> %s", instanceType, err) 83 } 84 } 85 86 // Wrapper for calling testMultipartObjectCreation for both Erasure and FS. 87 func TestMultipartObjectCreation(t *testing.T) { 88 ExecExtendedObjectLayerTest(t, testMultipartObjectCreation) 89 } 90 91 // Tests validate creation of part files during Multipart operation. 92 func testMultipartObjectCreation(obj ObjectLayer, instanceType string, t TestErrHandler) { 93 var opts ObjectOptions 94 err := obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 95 if err != nil { 96 t.Fatalf("%s: <ERROR> %s", instanceType, err) 97 } 98 res, err := obj.NewMultipartUpload(context.Background(), "bucket", "key", opts) 99 if err != nil { 100 t.Fatalf("%s: <ERROR> %s", instanceType, err) 101 } 102 uploadID := res.UploadID 103 104 // Create a byte array of 5MiB. 105 data := bytes.Repeat([]byte("0123456789abcdef"), 5*humanize.MiByte/16) 106 completedParts := CompleteMultipartUpload{} 107 for i := 1; i <= 10; i++ { 108 expectedETaghex := getMD5Hash(data) 109 110 var calcPartInfo PartInfo 111 calcPartInfo, err = obj.PutObjectPart(context.Background(), "bucket", "key", uploadID, i, mustGetPutObjReader(t, bytes.NewBuffer(data), int64(len(data)), expectedETaghex, ""), opts) 112 if err != nil { 113 t.Errorf("%s: <ERROR> %s", instanceType, err) 114 } 115 if calcPartInfo.ETag != expectedETaghex { 116 t.Errorf("MD5 Mismatch") 117 } 118 completedParts.Parts = append(completedParts.Parts, CompletePart{ 119 PartNumber: i, 120 ETag: calcPartInfo.ETag, 121 }) 122 } 123 objInfo, err := obj.CompleteMultipartUpload(context.Background(), "bucket", "key", uploadID, completedParts.Parts, ObjectOptions{}) 124 if err != nil { 125 t.Fatalf("%s: <ERROR> %s", instanceType, err) 126 } 127 if objInfo.ETag != "7d364cb728ce42a74a96d22949beefb2-10" { 128 t.Errorf("Md5 mismtch") 129 } 130 } 131 132 // Wrapper for calling testMultipartObjectAbort for both Erasure and FS. 133 func TestMultipartObjectAbort(t *testing.T) { 134 ExecObjectLayerTest(t, testMultipartObjectAbort) 135 } 136 137 // Tests validate abortion of Multipart operation. 138 func testMultipartObjectAbort(obj ObjectLayer, instanceType string, t TestErrHandler) { 139 var opts ObjectOptions 140 err := obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 141 if err != nil { 142 t.Fatalf("%s: <ERROR> %s", instanceType, err) 143 } 144 res, err := obj.NewMultipartUpload(context.Background(), "bucket", "key", opts) 145 if err != nil { 146 t.Fatalf("%s: <ERROR> %s", instanceType, err) 147 } 148 uploadID := res.UploadID 149 150 parts := make(map[int]string) 151 metadata := make(map[string]string) 152 for i := 1; i <= 10; i++ { 153 randomPerm := rand.Perm(10) 154 randomString := "" 155 for _, num := range randomPerm { 156 randomString += strconv.Itoa(num) 157 } 158 159 expectedETaghex := getMD5Hash([]byte(randomString)) 160 161 metadata["md5"] = expectedETaghex 162 var calcPartInfo PartInfo 163 calcPartInfo, err = obj.PutObjectPart(context.Background(), "bucket", "key", uploadID, i, mustGetPutObjReader(t, bytes.NewBufferString(randomString), int64(len(randomString)), expectedETaghex, ""), opts) 164 if err != nil { 165 t.Fatalf("%s: <ERROR> %s", instanceType, err) 166 } 167 if calcPartInfo.ETag != expectedETaghex { 168 t.Errorf("Md5 Mismatch") 169 } 170 parts[i] = expectedETaghex 171 } 172 err = obj.AbortMultipartUpload(context.Background(), "bucket", "key", uploadID, ObjectOptions{}) 173 if err != nil { 174 t.Fatalf("%s: <ERROR> %s", instanceType, err) 175 } 176 } 177 178 // Wrapper for calling testMultipleObjectCreation for both Erasure and FS. 179 func TestMultipleObjectCreation(t *testing.T) { 180 ExecExtendedObjectLayerTest(t, testMultipleObjectCreation) 181 } 182 183 // Tests validate object creation. 184 func testMultipleObjectCreation(obj ObjectLayer, instanceType string, t TestErrHandler) { 185 objects := make(map[string][]byte) 186 var opts ObjectOptions 187 err := obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 188 if err != nil { 189 t.Fatalf("%s: <ERROR> %s", instanceType, err) 190 } 191 for i := 0; i < 10; i++ { 192 randomPerm := rand.Perm(100) 193 randomString := "" 194 for _, num := range randomPerm { 195 randomString += strconv.Itoa(num) 196 } 197 198 expectedETaghex := getMD5Hash([]byte(randomString)) 199 200 key := "obj" + strconv.Itoa(i) 201 objects[key] = []byte(randomString) 202 metadata := make(map[string]string) 203 metadata["etag"] = expectedETaghex 204 var objInfo ObjectInfo 205 objInfo, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(randomString), int64(len(randomString)), metadata["etag"], ""), ObjectOptions{UserDefined: metadata}) 206 if err != nil { 207 t.Fatalf("%s: <ERROR> %s", instanceType, err) 208 } 209 if objInfo.ETag != expectedETaghex { 210 t.Errorf("Md5 Mismatch") 211 } 212 } 213 214 for key, value := range objects { 215 var byteBuffer bytes.Buffer 216 err = GetObject(context.Background(), obj, "bucket", key, 0, int64(len(value)), &byteBuffer, "", opts) 217 if err != nil { 218 t.Fatalf("%s: <ERROR> %s", instanceType, err) 219 } 220 if !bytes.Equal(byteBuffer.Bytes(), value) { 221 t.Errorf("%s: Mismatch of GetObject data with the expected one.", instanceType) 222 } 223 224 objInfo, err := obj.GetObjectInfo(context.Background(), "bucket", key, opts) 225 if err != nil { 226 t.Fatalf("%s: <ERROR> %s", instanceType, err) 227 } 228 if objInfo.Size != int64(len(value)) { 229 t.Errorf("%s: Size mismatch of the GetObject data.", instanceType) 230 } 231 232 } 233 } 234 235 // Wrapper for calling TestPaging for both Erasure and FS. 236 func TestPaging(t *testing.T) { 237 ExecObjectLayerTest(t, testPaging) 238 } 239 240 // Tests validate creation of objects and the order of listing using various filters for ListObjects operation. 241 func testPaging(obj ObjectLayer, instanceType string, t TestErrHandler) { 242 obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 243 result, err := obj.ListObjects(context.Background(), "bucket", "", "", "", 0) 244 if err != nil { 245 t.Fatalf("%s: <ERROR> %s", instanceType, err) 246 } 247 if len(result.Objects) != 0 { 248 t.Errorf("%s: Number of objects in the result different from expected value.", instanceType) 249 } 250 if result.IsTruncated { 251 t.Errorf("%s: Expected IsTruncated to be `false`, but instead found it to be `%v`", instanceType, result.IsTruncated) 252 } 253 254 uploadContent := "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed." 255 var opts ObjectOptions 256 // check before paging occurs. 257 for i := 0; i < 5; i++ { 258 key := "obj" + strconv.Itoa(i) 259 _, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts) 260 if err != nil { 261 t.Fatalf("%s: <ERROR> %s", instanceType, err) 262 } 263 264 result, err = obj.ListObjects(context.Background(), "bucket", "", "", "", 5) 265 if err != nil { 266 t.Fatalf("%s: <ERROR> %s", instanceType, err) 267 } 268 if len(result.Objects) != i+1 { 269 t.Errorf("%s: Expected length of objects to be %d, instead found to be %d", instanceType, len(result.Objects), i+1) 270 } 271 if result.IsTruncated { 272 t.Errorf("%s: Expected IsTruncated to be `false`, but instead found it to be `%v`", instanceType, result.IsTruncated) 273 } 274 } 275 276 // check after paging occurs pages work. 277 for i := 6; i <= 10; i++ { 278 key := "obj" + strconv.Itoa(i) 279 _, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts) 280 if err != nil { 281 t.Fatalf("%s: <ERROR> %s", instanceType, err) 282 } 283 result, err = obj.ListObjects(context.Background(), "bucket", "obj", "", "", 5) 284 if err != nil { 285 t.Fatalf("%s: <ERROR> %s", instanceType, err) 286 } 287 if len(result.Objects) != 5 { 288 t.Errorf("%s: Expected length of objects to be %d, instead found to be %d", instanceType, 5, len(result.Objects)) 289 } 290 if !result.IsTruncated { 291 t.Errorf("%s: Expected IsTruncated to be `true`, but instead found it to be `%v`", instanceType, result.IsTruncated) 292 } 293 } 294 // check paging with prefix at end returns less objects. 295 { 296 _, err = obj.PutObject(context.Background(), "bucket", "newPrefix", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts) 297 if err != nil { 298 t.Fatalf("%s: <ERROR> %s", instanceType, err) 299 } 300 _, err = obj.PutObject(context.Background(), "bucket", "newPrefix2", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts) 301 if err != nil { 302 t.Fatalf("%s: <ERROR> %s", instanceType, err) 303 } 304 result, err = obj.ListObjects(context.Background(), "bucket", "new", "", "", 5) 305 if err != nil { 306 t.Fatalf("%s: <ERROR> %s", instanceType, err) 307 } 308 if len(result.Objects) != 2 { 309 t.Errorf("%s: Expected length of objects to be %d, instead found to be %d", instanceType, 2, len(result.Objects)) 310 } 311 } 312 313 // check ordering of pages. 314 { 315 result, err = obj.ListObjects(context.Background(), "bucket", "", "", "", 1000) 316 if err != nil { 317 t.Fatalf("%s: <ERROR> %s", instanceType, err) 318 } 319 if result.Objects[0].Name != "newPrefix" { 320 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[0].Name) 321 } 322 if result.Objects[1].Name != "newPrefix2" { 323 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[1].Name) 324 } 325 if result.Objects[2].Name != "obj0" { 326 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[2].Name) 327 } 328 if result.Objects[3].Name != "obj1" { 329 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[3].Name) 330 } 331 if result.Objects[4].Name != "obj10" { 332 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[4].Name) 333 } 334 } 335 336 // check delimited results with delimiter and prefix. 337 { 338 _, err = obj.PutObject(context.Background(), "bucket", "this/is/delimited", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts) 339 if err != nil { 340 t.Fatalf("%s: <ERROR> %s", instanceType, err) 341 } 342 _, err = obj.PutObject(context.Background(), "bucket", "this/is/also/a/delimited/file", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts) 343 if err != nil { 344 t.Fatalf("%s: <ERROR> %s", instanceType, err) 345 } 346 result, err = obj.ListObjects(context.Background(), "bucket", "this/is/", "", SlashSeparator, 10) 347 if err != nil { 348 t.Fatalf("%s: <ERROR> %s", instanceType, err) 349 } 350 if len(result.Objects) != 1 { 351 t.Errorf("%s: Expected the number of objects in the result to be %d, but instead found %d", instanceType, 1, len(result.Objects)) 352 } 353 if result.Prefixes[0] != "this/is/also/" { 354 t.Errorf("%s: Expected prefix to be `%s`, but instead found `%s`", instanceType, "this/is/also/", result.Prefixes[0]) 355 } 356 } 357 358 // check delimited results with delimiter without prefix. 359 { 360 result, err = obj.ListObjects(context.Background(), "bucket", "", "", SlashSeparator, 1000) 361 if err != nil { 362 t.Fatalf("%s: <ERROR> %s", instanceType, err) 363 } 364 365 if result.Objects[0].Name != "newPrefix" { 366 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[0].Name) 367 } 368 if result.Objects[1].Name != "newPrefix2" { 369 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[1].Name) 370 } 371 if result.Objects[2].Name != "obj0" { 372 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[2].Name) 373 } 374 if result.Objects[3].Name != "obj1" { 375 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[3].Name) 376 } 377 if result.Objects[4].Name != "obj10" { 378 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[4].Name) 379 } 380 if result.Prefixes[0] != "this/" { 381 t.Errorf("%s: Expected the prefix to be `%s`, but instead found `%s`", instanceType, "this/", result.Prefixes[0]) 382 } 383 } 384 385 // check results with Marker. 386 { 387 388 result, err = obj.ListObjects(context.Background(), "bucket", "", "newPrefix", "", 3) 389 if err != nil { 390 t.Fatalf("%s: <ERROR> %s", instanceType, err) 391 } 392 if result.Objects[0].Name != "newPrefix2" { 393 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix2", result.Objects[0].Name) 394 } 395 if result.Objects[1].Name != "obj0" { 396 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj0", result.Objects[1].Name) 397 } 398 if result.Objects[2].Name != "obj1" { 399 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj1", result.Objects[2].Name) 400 } 401 } 402 // check ordering of results with prefix. 403 { 404 result, err = obj.ListObjects(context.Background(), "bucket", "obj", "", "", 1000) 405 if err != nil { 406 t.Fatalf("%s: <ERROR> %s", instanceType, err) 407 } 408 if result.Objects[0].Name != "obj0" { 409 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj0", result.Objects[0].Name) 410 } 411 if result.Objects[1].Name != "obj1" { 412 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj1", result.Objects[1].Name) 413 } 414 if result.Objects[2].Name != "obj10" { 415 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj10", result.Objects[2].Name) 416 } 417 if result.Objects[3].Name != "obj2" { 418 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj2", result.Objects[3].Name) 419 } 420 if result.Objects[4].Name != "obj3" { 421 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "obj3", result.Objects[4].Name) 422 } 423 } 424 // check ordering of results with prefix and no paging. 425 { 426 result, err = obj.ListObjects(context.Background(), "bucket", "new", "", "", 5) 427 if err != nil { 428 t.Fatalf("%s: <ERROR> %s", instanceType, err) 429 } 430 if result.Objects[0].Name != "newPrefix" { 431 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix", result.Objects[0].Name) 432 } 433 if result.Objects[1].Name != "newPrefix2" { 434 t.Errorf("%s: Expected the object name to be `%s`, but instead found `%s`", instanceType, "newPrefix2", result.Objects[0].Name) 435 } 436 } 437 } 438 439 // Wrapper for calling testObjectOverwriteWorks for both Erasure and FS. 440 func TestObjectOverwriteWorks(t *testing.T) { 441 ExecObjectLayerTest(t, testObjectOverwriteWorks) 442 } 443 444 // Tests validate overwriting of an existing object. 445 func testObjectOverwriteWorks(obj ObjectLayer, instanceType string, t TestErrHandler) { 446 err := obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 447 if err != nil { 448 t.Fatalf("%s: <ERROR> %s", instanceType, err) 449 } 450 451 var opts ObjectOptions 452 uploadContent := "The list of parts was not in ascending order. The parts list must be specified in order by part number." 453 length := int64(len(uploadContent)) 454 _, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), opts) 455 if err != nil { 456 t.Fatalf("%s: <ERROR> %s", instanceType, err) 457 } 458 459 uploadContent = "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed." 460 length = int64(len(uploadContent)) 461 _, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), opts) 462 if err != nil { 463 t.Fatalf("%s: <ERROR> %s", instanceType, err) 464 } 465 466 var bytesBuffer bytes.Buffer 467 err = GetObject(context.Background(), obj, "bucket", "object", 0, length, &bytesBuffer, "", opts) 468 if err != nil { 469 t.Fatalf("%s: <ERROR> %s", instanceType, err) 470 } 471 if bytesBuffer.String() != "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed." { 472 t.Errorf("%s: Invalid upload ID error mismatch.", instanceType) 473 } 474 } 475 476 // Wrapper for calling testNonExistentBucketOperations for both Erasure and FS. 477 func TestNonExistentBucketOperations(t *testing.T) { 478 ExecObjectLayerTest(t, testNonExistentBucketOperations) 479 } 480 481 // Tests validate that bucket operation on non-existent bucket fails. 482 func testNonExistentBucketOperations(obj ObjectLayer, instanceType string, t TestErrHandler) { 483 var opts ObjectOptions 484 _, err := obj.PutObject(context.Background(), "bucket1", "object", mustGetPutObjReader(t, bytes.NewBufferString("one"), int64(len("one")), "", ""), opts) 485 if err == nil { 486 t.Fatal("Expected error but found nil") 487 } 488 if err.Error() != "Bucket not found: bucket1" { 489 t.Errorf("%s: Expected the error msg to be `%s`, but instead found `%s`", instanceType, "Bucket not found: bucket1", err.Error()) 490 } 491 } 492 493 // Wrapper for calling testBucketRecreateFails for both Erasure and FS. 494 func TestBucketRecreateFails(t *testing.T) { 495 ExecObjectLayerTest(t, testBucketRecreateFails) 496 } 497 498 // Tests validate that recreation of the bucket fails. 499 func testBucketRecreateFails(obj ObjectLayer, instanceType string, t TestErrHandler) { 500 err := obj.MakeBucket(context.Background(), "string", MakeBucketOptions{}) 501 if err != nil { 502 t.Fatalf("%s: <ERROR> %s", instanceType, err) 503 } 504 err = obj.MakeBucket(context.Background(), "string", MakeBucketOptions{}) 505 if err == nil { 506 t.Fatalf("%s: Expected error but found nil.", instanceType) 507 } 508 509 if err.Error() != "Bucket exists: string" { 510 t.Errorf("%s: Expected the error message to be `%s`, but instead found `%s`", instanceType, "Bucket exists: string", err.Error()) 511 } 512 } 513 514 func enableCompression(t *testing.T, encrypt bool) { 515 // Enable compression and exec... 516 globalCompressConfigMu.Lock() 517 globalCompressConfig.Enabled = true 518 globalCompressConfig.MimeTypes = nil 519 globalCompressConfig.Extensions = nil 520 globalCompressConfig.AllowEncrypted = encrypt 521 globalCompressConfigMu.Unlock() 522 if encrypt { 523 globalAutoEncryption = encrypt 524 var err error 525 GlobalKMS, err = kms.Parse("my-minio-key:5lF+0pJM0OWwlQrvK2S/I7W9mO4a6rJJI7wzj7v09cw=") 526 if err != nil { 527 t.Fatal(err) 528 } 529 } 530 } 531 532 func enableEncryption(t *testing.T) { 533 // Exec with default settings... 534 globalCompressConfigMu.Lock() 535 globalCompressConfig.Enabled = false 536 globalCompressConfigMu.Unlock() 537 538 globalAutoEncryption = true 539 var err error 540 GlobalKMS, err = kms.Parse("my-minio-key:5lF+0pJM0OWwlQrvK2S/I7W9mO4a6rJJI7wzj7v09cw=") 541 if err != nil { 542 t.Fatal(err) 543 } 544 } 545 546 func resetCompressEncryption() { 547 // Reset... 548 globalCompressConfigMu.Lock() 549 globalCompressConfig.Enabled = false 550 globalCompressConfig.AllowEncrypted = false 551 globalCompressConfigMu.Unlock() 552 globalAutoEncryption = false 553 GlobalKMS = nil 554 } 555 556 func execExtended(t *testing.T, fn func(t *testing.T)) { 557 // Exec with default settings... 558 resetCompressEncryption() 559 t.Run("default", func(t *testing.T) { 560 fn(t) 561 }) 562 563 if testing.Short() { 564 return 565 } 566 567 t.Run("compressed", func(t *testing.T) { 568 resetCompressEncryption() 569 enableCompression(t, false) 570 fn(t) 571 }) 572 573 t.Run("encrypted", func(t *testing.T) { 574 resetCompressEncryption() 575 enableEncryption(t) 576 fn(t) 577 }) 578 579 t.Run("compressed+encrypted", func(t *testing.T) { 580 resetCompressEncryption() 581 enableCompression(t, true) 582 fn(t) 583 }) 584 } 585 586 // ExecExtendedObjectLayerTest will execute the tests with combinations of encrypted & compressed. 587 // This can be used to test functionality when reading and writing data. 588 func ExecExtendedObjectLayerTest(t *testing.T, objTest objTestType) { 589 execExtended(t, func(t *testing.T) { 590 ExecObjectLayerTest(t, objTest) 591 }) 592 } 593 594 // Wrapper for calling testPutObject for both Erasure and FS. 595 func TestPutObject(t *testing.T) { 596 ExecExtendedObjectLayerTest(t, testPutObject) 597 } 598 599 // Tests validate PutObject without prefix. 600 func testPutObject(obj ObjectLayer, instanceType string, t TestErrHandler) { 601 content := []byte("testcontent") 602 length := int64(len(content)) 603 readerEOF := newTestReaderEOF(content) 604 readerNoEOF := newTestReaderNoEOF(content) 605 err := obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 606 if err != nil { 607 t.Fatalf("%s: <ERROR> %s", instanceType, err) 608 } 609 610 var bytesBuffer1 bytes.Buffer 611 var opts ObjectOptions 612 _, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, readerEOF, length, "", ""), opts) 613 if err != nil { 614 t.Fatalf("%s: <ERROR> %s", instanceType, err) 615 } 616 err = GetObject(context.Background(), obj, "bucket", "object", 0, length, &bytesBuffer1, "", opts) 617 if err != nil { 618 t.Fatalf("%s: <ERROR> %s", instanceType, err) 619 } 620 if len(bytesBuffer1.Bytes()) != len(content) { 621 t.Errorf("%s: Expected content length to be `%d`, but instead found `%d`", instanceType, len(content), len(bytesBuffer1.Bytes())) 622 } 623 624 var bytesBuffer2 bytes.Buffer 625 _, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, readerNoEOF, length, "", ""), opts) 626 if err != nil { 627 t.Fatalf("%s: <ERROR> %s", instanceType, err) 628 } 629 err = GetObject(context.Background(), obj, "bucket", "object", 0, length, &bytesBuffer2, "", opts) 630 if err != nil { 631 t.Fatalf("%s: <ERROR> %s", instanceType, err) 632 } 633 if len(bytesBuffer2.Bytes()) != len(content) { 634 t.Errorf("%s: Expected content length to be `%d`, but instead found `%d`", instanceType, len(content), len(bytesBuffer2.Bytes())) 635 } 636 } 637 638 // Wrapper for calling testPutObjectInSubdir for both Erasure and FS. 639 func TestPutObjectInSubdir(t *testing.T) { 640 ExecExtendedObjectLayerTest(t, testPutObjectInSubdir) 641 } 642 643 // Tests validate PutObject with subdirectory prefix. 644 func testPutObjectInSubdir(obj ObjectLayer, instanceType string, t TestErrHandler) { 645 err := obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 646 if err != nil { 647 t.Fatalf("%s: <ERROR> %s", instanceType, err) 648 } 649 650 var opts ObjectOptions 651 uploadContent := `The specified multipart upload does not exist. The upload ID might be invalid, or the multipart 652 upload might have been aborted or completed.` 653 length := int64(len(uploadContent)) 654 _, err = obj.PutObject(context.Background(), "bucket", "dir1/dir2/object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), opts) 655 if err != nil { 656 t.Fatalf("%s: <ERROR> %s", instanceType, err) 657 } 658 659 var bytesBuffer bytes.Buffer 660 err = GetObject(context.Background(), obj, "bucket", "dir1/dir2/object", 0, length, &bytesBuffer, "", opts) 661 if err != nil { 662 t.Fatalf("%s: <ERROR> %s", instanceType, err) 663 } 664 if len(bytesBuffer.Bytes()) != len(uploadContent) { 665 t.Errorf("%s: Expected length of downloaded data to be `%d`, but instead found `%d`", 666 instanceType, len(uploadContent), len(bytesBuffer.Bytes())) 667 } 668 } 669 670 // Wrapper for calling testListBuckets for both Erasure and FS. 671 func TestListBuckets(t *testing.T) { 672 ExecObjectLayerTest(t, testListBuckets) 673 } 674 675 // Tests validate ListBuckets. 676 func testListBuckets(obj ObjectLayer, instanceType string, t TestErrHandler) { 677 // test empty list. 678 buckets, err := obj.ListBuckets(context.Background(), BucketOptions{}) 679 if err != nil { 680 t.Fatalf("%s: <ERROR> %s", instanceType, err) 681 } 682 if len(buckets) != 0 { 683 t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 0, len(buckets)) 684 } 685 686 // add one and test exists. 687 err = obj.MakeBucket(context.Background(), "bucket1", MakeBucketOptions{}) 688 if err != nil { 689 t.Fatalf("%s: <ERROR> %s", instanceType, err) 690 } 691 692 buckets, err = obj.ListBuckets(context.Background(), BucketOptions{}) 693 if err != nil { 694 t.Fatalf("%s: <ERROR> %s", instanceType, err) 695 } 696 if len(buckets) != 1 { 697 t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 1, len(buckets)) 698 } 699 700 // add two and test exists. 701 err = obj.MakeBucket(context.Background(), "bucket2", MakeBucketOptions{}) 702 if err != nil { 703 t.Fatalf("%s: <ERROR> %s", instanceType, err) 704 } 705 706 buckets, err = obj.ListBuckets(context.Background(), BucketOptions{}) 707 if err != nil { 708 t.Fatalf("%s: <ERROR> %s", instanceType, err) 709 } 710 if len(buckets) != 2 { 711 t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 2, len(buckets)) 712 } 713 714 // add three and test exists + prefix. 715 err = obj.MakeBucket(context.Background(), "bucket22", MakeBucketOptions{}) 716 if err != nil { 717 t.Fatalf("%s: <ERROR> %s", instanceType, err) 718 } 719 720 buckets, err = obj.ListBuckets(context.Background(), BucketOptions{}) 721 if err != nil { 722 t.Fatalf("%s: <ERROR> %s", instanceType, err) 723 } 724 if len(buckets) != 3 { 725 t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 3, len(buckets)) 726 } 727 } 728 729 // Wrapper for calling testListBucketsOrder for both Erasure and FS. 730 func TestListBucketsOrder(t *testing.T) { 731 ExecObjectLayerTest(t, testListBucketsOrder) 732 } 733 734 // Tests validate the order of result of ListBuckets. 735 func testListBucketsOrder(obj ObjectLayer, instanceType string, t TestErrHandler) { 736 // if implementation contains a map, order of map keys will vary. 737 // this ensures they return in the same order each time. 738 // add one and test exists. 739 err := obj.MakeBucket(context.Background(), "bucket1", MakeBucketOptions{}) 740 if err != nil { 741 t.Fatalf("%s: <ERROR> %s", instanceType, err) 742 } 743 err = obj.MakeBucket(context.Background(), "bucket2", MakeBucketOptions{}) 744 if err != nil { 745 t.Fatalf("%s: <ERROR> %s", instanceType, err) 746 } 747 buckets, err := obj.ListBuckets(context.Background(), BucketOptions{}) 748 if err != nil { 749 t.Fatalf("%s: <ERROR> %s", instanceType, err) 750 } 751 if len(buckets) != 2 { 752 t.Errorf("%s: Expected number of bucket to be `%d`, but instead found `%d`", instanceType, 2, len(buckets)) 753 } 754 755 if buckets[0].Name != "bucket1" { 756 t.Errorf("%s: Expected bucket name to be `%s`, but instead found `%s`", instanceType, "bucket1", buckets[0].Name) 757 } 758 if buckets[1].Name != "bucket2" { 759 t.Errorf("%s: Expected bucket name to be `%s`, but instead found `%s`", instanceType, "bucket2", buckets[1].Name) 760 } 761 } 762 763 // Wrapper for calling testListObjectsTestsForNonExistentBucket for both Erasure and FS. 764 func TestListObjectsTestsForNonExistentBucket(t *testing.T) { 765 ExecObjectLayerTest(t, testListObjectsTestsForNonExistentBucket) 766 } 767 768 // Tests validate that ListObjects operation on a non-existent bucket fails as expected. 769 func testListObjectsTestsForNonExistentBucket(obj ObjectLayer, instanceType string, t TestErrHandler) { 770 result, err := obj.ListObjects(context.Background(), "bucket", "", "", "", 1000) 771 if err == nil { 772 t.Fatalf("%s: Expected error but found nil.", instanceType) 773 } 774 if len(result.Objects) != 0 { 775 t.Fatalf("%s: Expected number of objects in the result to be `%d`, but instead found `%d`", instanceType, 0, len(result.Objects)) 776 } 777 if result.IsTruncated { 778 t.Fatalf("%s: Expected IsTruncated to be `false`, but instead found it to be `%v`", instanceType, result.IsTruncated) 779 } 780 if err.Error() != "Bucket not found: bucket" { 781 t.Errorf("%s: Expected the error msg to be `%s`, but instead found `%s`", instanceType, "Bucket not found: bucket", err.Error()) 782 } 783 } 784 785 // Wrapper for calling testNonExistentObjectInBucket for both Erasure and FS. 786 func TestNonExistentObjectInBucket(t *testing.T) { 787 ExecObjectLayerTest(t, testNonExistentObjectInBucket) 788 } 789 790 // Tests validate that GetObject fails on a non-existent bucket as expected. 791 func testNonExistentObjectInBucket(obj ObjectLayer, instanceType string, t TestErrHandler) { 792 err := obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 793 if err != nil { 794 t.Fatalf("%s: <ERROR> %s", instanceType, err) 795 } 796 797 _, err = obj.GetObjectInfo(context.Background(), "bucket", "dir1", ObjectOptions{}) 798 if err == nil { 799 t.Fatalf("%s: Expected error but found nil", instanceType) 800 } 801 if isErrObjectNotFound(err) { 802 if err.Error() != "Object not found: bucket/dir1" { 803 t.Errorf("%s: Expected the Error message to be `%s`, but instead found `%s`", instanceType, "Object not found: bucket/dir1", err.Error()) 804 } 805 } else { 806 if err.Error() != "fails" { 807 t.Errorf("%s: Expected the Error message to be `%s`, but instead found it to be `%s`", instanceType, "fails", err.Error()) 808 } 809 } 810 } 811 812 // Wrapper for calling testGetDirectoryReturnsObjectNotFound for both Erasure and FS. 813 func TestGetDirectoryReturnsObjectNotFound(t *testing.T) { 814 ExecObjectLayerTest(t, testGetDirectoryReturnsObjectNotFound) 815 } 816 817 // Tests validate that GetObject on an existing directory fails as expected. 818 func testGetDirectoryReturnsObjectNotFound(obj ObjectLayer, instanceType string, t TestErrHandler) { 819 bucketName := "bucket" 820 err := obj.MakeBucket(context.Background(), bucketName, MakeBucketOptions{}) 821 if err != nil { 822 t.Fatalf("%s: <ERROR> %s", instanceType, err) 823 } 824 content := "One or more of the specified parts could not be found. The part might not have been uploaded, or the specified entity tag might not have matched the part's entity tag." 825 length := int64(len(content)) 826 var opts ObjectOptions 827 _, err = obj.PutObject(context.Background(), bucketName, "dir1/dir3/object", mustGetPutObjReader(t, bytes.NewBufferString(content), length, "", ""), opts) 828 if err != nil { 829 t.Fatalf("%s: <ERROR> %s", instanceType, err) 830 } 831 832 testCases := []struct { 833 dir string 834 err error 835 }{ 836 { 837 dir: "dir1/", 838 err: ObjectNotFound{Bucket: bucketName, Object: "dir1/"}, 839 }, 840 { 841 dir: "dir1/dir3/", 842 err: ObjectNotFound{Bucket: bucketName, Object: "dir1/dir3/"}, 843 }, 844 } 845 846 for i, testCase := range testCases { 847 _, expectedErr := obj.GetObjectInfo(context.Background(), bucketName, testCase.dir, opts) 848 if expectedErr != nil && expectedErr.Error() != testCase.err.Error() { 849 t.Errorf("Test %d, %s: Expected error %s, got %s", i+1, instanceType, testCase.err, expectedErr) 850 } 851 } 852 } 853 854 // Wrapper for calling testContentType for both Erasure and FS. 855 func TestContentType(t *testing.T) { 856 ExecObjectLayerTest(t, testContentType) 857 } 858 859 // Test content-type. 860 func testContentType(obj ObjectLayer, instanceType string, t TestErrHandler) { 861 err := obj.MakeBucket(context.Background(), "bucket", MakeBucketOptions{}) 862 if err != nil { 863 t.Fatalf("%s: <ERROR> %s", instanceType, err) 864 } 865 var opts ObjectOptions 866 uploadContent := "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed." 867 // Test empty. 868 _, err = obj.PutObject(context.Background(), "bucket", "minio.png", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), opts) 869 if err != nil { 870 t.Fatalf("%s: <ERROR> %s", instanceType, err) 871 } 872 objInfo, err := obj.GetObjectInfo(context.Background(), "bucket", "minio.png", opts) 873 if err != nil { 874 t.Fatalf("%s: <ERROR> %s", instanceType, err) 875 } 876 877 if objInfo.ContentType != "image/png" { 878 t.Errorf("%s: Expected Content type to be `%s`, but instead found `%s`", instanceType, "image/png", objInfo.ContentType) 879 } 880 }