github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/suite_test.go (about) 1 // Copyright (c) 2015-2024 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 "crypto/md5" 23 "crypto/tls" 24 "encoding/json" 25 "fmt" 26 "io" 27 "log" 28 "math/rand" 29 "net/http" 30 "os" 31 "os/exec" 32 "runtime" 33 "runtime/debug" 34 "strconv" 35 "strings" 36 "testing" 37 "time" 38 39 "github.com/google/uuid" 40 "github.com/minio/mc/pkg/disk" 41 ) 42 43 // RUN: go test -v ./... -run Test_FullSuite 44 func Test_FullSuite(t *testing.T) { 45 if os.Getenv("MC_TEST_RUN_FULL_SUITE") != "true" { 46 return 47 } 48 49 defer func() { 50 r := recover() 51 if r != nil { 52 log.Println(r, string(debug.Stack())) 53 } 54 55 postRunCleanup(t) 56 }() 57 58 preflightCheck(t) 59 // initializeTestSuite builds the mc client and creates local files which are used for testing 60 initializeTestSuite(t) 61 62 // Tests within this function depend on one another 63 testsThatDependOnOneAnother(t) 64 65 // Alias tests 66 AddALIASWithError(t) 67 68 // Basic admin user tests 69 AdminUserFunctionalTest(t) 70 71 // Share upload/download 72 ShareURLUploadTest(t) 73 ShareURLDownloadTest(t) 74 75 // TODO .. for some reason the connection is randomly 76 // reset when running curl. 77 // ShareURLUploadErrorTests(t) 78 79 // Bucket Error Tests 80 CreateBucketUsingInvalidSymbols(t) 81 RemoveBucketWithNameTooLong(t) 82 RemoveBucketThatDoesNotExist(t) 83 84 // MC_TEST_ENABLE_HTTPS=true 85 // needs to be set in order to run these tests 86 if protocol == "https://" { 87 PutObjectWithSSEC(t) 88 PutObjectWithSSECPartialPrefixMatch(t) 89 PutObjectWithSSECMultipart(t) 90 PutObjectWithSSECInvalidKeys(t) 91 GetObjectWithSSEC(t) 92 GetObjectWithSSECWithoutKey(t) 93 CatObjectWithSSEC(t) 94 CatObjectWithSSECWithoutKey(t) 95 CopyObjectWithSSECToNewBucketWithNewKey(t) 96 MirrorTempDirectoryUsingSSEC(t) 97 RemoveObjectWithSSEC(t) 98 } else { 99 PutObjectErrorWithSSECOverHTTP(t) 100 } 101 102 // MC_TEST_KMS_KEY=[KEY_NAME] 103 // needs to be set in order to run these tests 104 if sseKMSKeyName != "" { 105 VerifyKMSKey(t) 106 PutObjectWithSSEKMS(t) 107 PutObjectWithSSEKMSPartialPrefixMatch(t) 108 PutObjectWithSSEKMSMultipart(t) 109 PutObjectWithSSEKMSInvalidKeys(t) 110 GetObjectWithSSEKMS(t) 111 CatObjectWithSSEKMS(t) 112 CopyObjectWithSSEKMSToNewBucket(t) 113 MirrorTempDirectoryUsingSSEKMS(t) 114 RemoveObjectWithSSEKMS(t) 115 116 // Error tests 117 CopyObjectWithSSEKMSWithOverLappingKeys(t) 118 } 119 120 // MC_TEST_ENABLE_SSE_S3=true 121 // needs to be set to in order to run these tests. 122 if sseS3Enabled { 123 PutObjectWithSSES3(t) 124 PutObjectWithSSES3PartialPrefixMatch(t) 125 PutObjectWithSSES3Multipart(t) 126 GetObjectWithSSES3(t) 127 CatObjectWithSSES3(t) 128 CopyObjectWithSSES3ToNewBucket(t) 129 MirrorTempDirectoryUsingSSES3(t) 130 } 131 132 if protocol == "https://" && sseKMSKeyName != "" { 133 CopyObjectWithSSEKMSToNewBucketWithSSEC(t) 134 } 135 136 // (DEPRECATED CLI PARAMETERS) 137 if includeDeprecatedMethods { 138 fmt.Println("No deprecated methods implemented") 139 } 140 } 141 142 func testsThatDependOnOneAnother(t *testing.T) { 143 CreateFileBundle() 144 // uploadAllFiles uploads all files in FileMap to MainTestBucket 145 uploadAllFiles(t) 146 // LSObjects saves the output of LS inside *testFile in FileMap 147 LSObjects(t) 148 // StatObjecsts saves the output of Stat inside *testFile in FileMap 149 StatObjects(t) 150 // ValidateFileMetaDataPostUpload validates the output of LS and Stat 151 ValidateFileMetaData(t) 152 153 // DU tests 154 DUBucket(t) 155 156 // Std in/out .. pipe/cat 157 CatObjectToStdIn(t) 158 CatObjectFromStdin(t) 159 160 // Preserve attributes 161 PutObjectPreserveAttributes(t) 162 163 // Mirror 164 MirrorTempDirectoryStorageClassReducedRedundancy(t) 165 MirrorTempDirectory(t) 166 167 // General object tests 168 FindObjects(t) 169 FindObjectsUsingName(t) 170 FindObjectsUsingNameAndFilteringForTxtType(t) 171 FindObjectsLargerThan64Mebibytes(t) 172 FindObjectsSmallerThan64Mebibytes(t) 173 FindObjectsOlderThan1d(t) 174 FindObjectsNewerThen1d(t) 175 GetObjectsAndCompareMD5(t) 176 } 177 178 type TestUser struct { 179 Username string 180 Password string 181 } 182 183 var ( 184 oneMBSlice [1048576]byte // 1x Mebibyte 185 defaultAlias = "mintest" 186 fileMap = make(map[string]*testFile) 187 randomLargeString = "lksdjfljsdklfjklsdjfklksjdf;lsjdk;fjks;djflsdlfkjskldjfklkljsdfljsldkfjklsjdfkljsdklfjklsdjflksjdlfjsdjflsjdflsldfjlsjdflksjdflkjslkdjflksfdj" 188 jsonFlag = "--json" 189 insecureFlag = "--insecure" 190 jsonOutput = true 191 printRawOut = false 192 skipBuild = false 193 mcCmd = ".././mc" 194 preCmdParameters = make([]string, 0) 195 buildPath = "../." 196 metaPrefix = "X-Amz-Meta-" 197 includeDeprecatedMethods = false 198 199 serverEndpoint = "127.0.0.1:9000" 200 acessKey = "minioadmin" 201 secretKey = "minioadmin" 202 protocol = "http://" 203 skipInsecure = true 204 tempDir = "" 205 mainTestBucket string 206 sseTestBucket string 207 bucketList = make([]string, 0) 208 userList = make(map[string]TestUser, 0) 209 210 // KMS 211 sseBaseEncodedKey = "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDA" 212 invalidSSEBaseEncodedKey = "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5" 213 sseBaseEncodedKey2 = "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5YWE" 214 sseKMSKeyName = "" 215 sseInvalidKmsKeyName = "" 216 sseS3Enabled = false 217 218 curlPath = "/usb/bin/curl" 219 HTTPClient *http.Client 220 failIndicator = "!! FAIL !! _______________________ !! FAIL !! _______________________ !! FAIL !!" 221 ) 222 223 func openFileAndGetMd5Sum(path string) (md5s string, err error) { 224 f, err := os.Open(path) 225 if err != nil { 226 return "", err 227 } 228 defer f.Close() 229 fb, err := io.ReadAll(f) 230 if err != nil { 231 return "", err 232 } 233 md5s = GetMD5Sum(fb) 234 return 235 } 236 237 func GetMBSizeInBytes(MB int) int64 { 238 return int64(MB * len(oneMBSlice)) 239 } 240 241 func initializeTestSuite(t *testing.T) { 242 shouldSkipBuild := os.Getenv("MC_TEST_SKIP_BUILD") 243 skipBuild, _ = strconv.ParseBool(shouldSkipBuild) 244 fmt.Println("SKIP BUILD:", skipBuild) 245 if !skipBuild { 246 err := BuildCLI() 247 if err != nil { 248 os.Exit(1) 249 } 250 } 251 envBuildPath := os.Getenv("MC_TEST_BUILD_PATH") 252 if envBuildPath != "" { 253 buildPath = envBuildPath 254 } 255 256 envALIAS := os.Getenv("MC_TEST_ALIAS") 257 if envALIAS != "" { 258 defaultAlias = envALIAS 259 } 260 261 envSecretKey := os.Getenv("MC_TEST_SECRET_KEY") 262 if envSecretKey != "" { 263 secretKey = envSecretKey 264 } 265 266 envAccessKey := os.Getenv("MC_TEST_ACCESS_KEY") 267 if envAccessKey != "" { 268 acessKey = envAccessKey 269 } 270 271 envServerEndpoint := os.Getenv("MC_TEST_SERVER_ENDPOINT") 272 if envServerEndpoint != "" { 273 serverEndpoint = envServerEndpoint 274 } 275 276 envIncludeDeprecated := os.Getenv("MC_TEST_INCLUDE_DEPRECATED") 277 includeDeprecatedMethods, _ = strconv.ParseBool(envIncludeDeprecated) 278 279 envKmsKey := os.Getenv("MC_TEST_KMS_KEY") 280 if envKmsKey != "" { 281 sseKMSKeyName = envKmsKey 282 } 283 284 envSSES3Enabled := os.Getenv("MC_TEST_ENABLE_SSE_S3") 285 if envSSES3Enabled != "" { 286 sseS3Enabled, _ = strconv.ParseBool(envSSES3Enabled) 287 } 288 289 envSkipInsecure := os.Getenv("MC_TEST_SKIP_INSECURE") 290 if envSkipInsecure != "" { 291 skipInsecure, _ = strconv.ParseBool(envSkipInsecure) 292 } 293 294 envEnableHTTP := os.Getenv("MC_TEST_ENABLE_HTTPS") 295 EnableHTTPS, _ := strconv.ParseBool(envEnableHTTP) 296 if EnableHTTPS { 297 protocol = "https://" 298 } 299 300 envCMD := os.Getenv("MC_TEST_BINARY_PATH") 301 if envCMD != "" { 302 mcCmd = envCMD 303 } 304 305 var err error 306 tempDir, err = os.MkdirTemp("", "test-") 307 if err != nil { 308 log.Println(err) 309 os.Exit(1) 310 } 311 312 for i := 0; i < len(oneMBSlice); i++ { 313 oneMBSlice[i] = byte(rand.Intn(250)) 314 } 315 316 for i := 0; i < 10; i++ { 317 tmpNameMap["aaa"+strconv.Itoa(i)] = false 318 } 319 for i := 0; i < 10; i++ { 320 tmpNameMap["bbb"+strconv.Itoa(i)] = false 321 } 322 for i := 0; i < 10; i++ { 323 tmpNameMap["ccc"+strconv.Itoa(i)] = false 324 } 325 for i := 0; i < 10; i++ { 326 tmpNameMap["ddd"+strconv.Itoa(i)] = false 327 } 328 329 HTTPClient = &http.Client{ 330 Transport: &http.Transport{ 331 TLSClientConfig: &tls.Config{InsecureSkipVerify: skipInsecure}, 332 }, 333 } 334 335 if jsonOutput { 336 preCmdParameters = append(preCmdParameters, jsonFlag) 337 } 338 339 if skipInsecure { 340 preCmdParameters = append(preCmdParameters, insecureFlag) 341 } 342 343 CreateTestUsers() 344 345 _, err = RunMC( 346 "alias", 347 "set", 348 defaultAlias, 349 protocol+serverEndpoint, 350 acessKey, 351 secretKey, 352 ) 353 fatalIfError(err, t) 354 355 out, err := RunMC("--version") 356 fatalIfError(err, t) 357 fmt.Println(out) 358 359 preRunCleanup() 360 361 mainTestBucket = CreateBucket(t) 362 sseTestBucket = CreateBucket(t) 363 } 364 365 func preflightCheck(t *testing.T) { 366 out, err := exec.Command("which", "curl").Output() 367 fatalIfError(err, t) 368 if len(out) == 0 { 369 fatalMsgOnly("No curl found, output from 'which curl': "+string(out), t) 370 } 371 curlPath = string(out) 372 } 373 374 func CreateTestUsers() { 375 userList["user1"] = TestUser{ 376 Username: "user1", 377 Password: "user1-password", 378 } 379 userList["user2"] = TestUser{ 380 Username: "user2", 381 Password: "user2-password", 382 } 383 userList["user3"] = TestUser{ 384 Username: "user3", 385 Password: "user3-password", 386 } 387 } 388 389 func CreateFileBundle() { 390 createFile(newTestFile{ 391 tag: "0M", 392 prefix: "", 393 extension: ".jpg", 394 storageClass: "", 395 sizeInMBS: 0, 396 tags: map[string]string{"name": "0M"}, 397 // uploadShouldFail: false, 398 addToGlobalFileMap: true, 399 }) 400 createFile(newTestFile{ 401 tag: "1M", 402 prefix: "", 403 extension: ".txt", 404 storageClass: "REDUCED_REDUNDANCY", 405 sizeInMBS: 1, 406 metaData: map[string]string{"name": "1M"}, 407 tags: map[string]string{"tag1": "1M-tag"}, 408 // uploadShouldFail: false, 409 addToGlobalFileMap: true, 410 }) 411 createFile(newTestFile{ 412 tag: "2M", 413 prefix: "LVL1", 414 extension: ".jpg", 415 storageClass: "REDUCED_REDUNDANCY", 416 sizeInMBS: 2, 417 metaData: map[string]string{"name": "2M"}, 418 // uploadShouldFail: false, 419 addToGlobalFileMap: true, 420 }) 421 createFile(newTestFile{ 422 tag: "3M", 423 prefix: "LVL1/LVL2", 424 extension: ".png", 425 storageClass: "", 426 sizeInMBS: 3, 427 metaData: map[string]string{"name": "3M"}, 428 // uploadShouldFail: false, 429 addToGlobalFileMap: true, 430 }) 431 createFile(newTestFile{ 432 tag: "65M", 433 prefix: "LVL1/LVL2/LVL3", 434 extension: ".exe", 435 storageClass: "", 436 sizeInMBS: 65, 437 metaData: map[string]string{"name": "65M", "tag1": "value1"}, 438 // uploadShouldFail: false, 439 addToGlobalFileMap: true, 440 }) 441 } 442 443 var tmpNameMap = make(map[string]bool) 444 445 func GetRandomName() string { 446 for i := range tmpNameMap { 447 if tmpNameMap[i] == false { 448 tmpNameMap[i] = true 449 return i 450 } 451 } 452 return uuid.NewString() 453 } 454 455 func CreateBucket(t *testing.T) (bucketPath string) { 456 bucketName := "test-" + GetRandomName() 457 bucketPath = defaultAlias + "/" + bucketName 458 out, err := RunMC("mb", bucketPath) 459 if err != nil { 460 t.Fatalf("Unable to create bucket (%s) err: %s", bucketPath, out) 461 return 462 } 463 bucketList = append(bucketList, bucketPath) 464 out, err = RunMC("stat", defaultAlias+"/"+bucketName) 465 if err != nil { 466 t.Fatalf("Unable to ls stat (%s) err: %s", defaultAlias+"/"+bucketName, out) 467 return 468 } 469 if !strings.Contains(out, bucketName) { 470 t.Fatalf("stat output does not contain bucket name (%s)", bucketName) 471 } 472 return 473 } 474 475 func AddALIASWithError(t *testing.T) { 476 out, err := RunMC( 477 "alias", 478 "set", 479 defaultAlias, 480 protocol+serverEndpoint, 481 acessKey, 482 "random-invalid-secret-that-will-not-work", 483 ) 484 fatalIfNoErrorWMsg(err, out, t) 485 } 486 487 func AdminUserFunctionalTest(t *testing.T) { 488 user1Bucket := CreateBucket(t) 489 490 user1File := createFile(newTestFile{ 491 addToGlobalFileMap: false, 492 tag: "user1", 493 sizeInMBS: 1, 494 }) 495 496 out, err := RunMC( 497 "admin", 498 "user", 499 "add", 500 defaultAlias, 501 userList["user1"].Username, 502 userList["user1"].Password, 503 ) 504 fatalIfErrorWMsg(err, out, t) 505 506 out, err = RunMC( 507 "admin", 508 "user", 509 "list", 510 defaultAlias, 511 ) 512 fatalIfErrorWMsg(err, out, t) 513 userOutput, err := parseUserMessageListOutput(out) 514 fatalIfErrorWMsg(err, out, t) 515 516 user1found := false 517 for i := range userOutput { 518 if userOutput[i].AccessKey == userList["user1"].Username { 519 user1found = true 520 } 521 } 522 523 if !user1found { 524 fatalMsgOnly(fmt.Sprintf("did not find user %s when running admin user list --json", userList["user1"].Username), t) 525 } 526 527 out, err = RunMC( 528 "admin", 529 "policy", 530 "attach", 531 defaultAlias, 532 "readwrite", 533 "--user="+userList["user1"].Username, 534 ) 535 fatalIfErrorWMsg(err, out, t) 536 537 out, err = RunMC( 538 "alias", 539 "set", 540 userList["user1"].Username, 541 protocol+serverEndpoint, 542 userList["user1"].Username, 543 userList["user1"].Password, 544 ) 545 fatalIfErrorWMsg(err, out, t) 546 547 out, err = RunMC( 548 "cp", 549 user1File.diskFile.Name(), 550 user1Bucket+"/"+user1File.fileNameWithoutPath, 551 ) 552 fatalIfErrorWMsg(err, out, t) 553 } 554 555 func ShareURLUploadErrorTests(t *testing.T) { 556 shareURLErrorBucket := CreateBucket(t) 557 558 file := createFile(newTestFile{ 559 addToGlobalFileMap: false, 560 tag: "presigned-error", 561 sizeInMBS: 1, 562 }) 563 564 out, err := RunMC( 565 "share", 566 "upload", 567 shareURLErrorBucket+"/"+file.fileNameWithoutPath, 568 ) 569 fatalIfErrorWMsg(err, out, t) 570 571 shareMsg, err := parseShareMessageFromJSONOutput(out) 572 fatalIfErrorWMsg(err, out, t) 573 574 finalURL := strings.Replace(shareMsg.ShareURL, "<FILE>", file.diskFile.Name(), -1) 575 splitCommand := strings.Split(finalURL, " ") 576 577 if skipInsecure { 578 splitCommand = append(splitCommand, "--insecure") 579 } 580 581 bucketOnly := strings.Replace(shareURLErrorBucket, defaultAlias+"/", "", -1) 582 583 // Modify base url bucket path 584 newCmd := make([]string, len(splitCommand)) 585 copy(newCmd, splitCommand) 586 newCmd[1] = strings.Replace(newCmd[1], bucketOnly, "fake-bucket-name", -1) 587 out, _ = RunCommand(newCmd[0], newCmd[1:]...) 588 curlFatalIfNoErrorTag(out, t) 589 590 // Modify -F key=X 591 newCmd = make([]string, len(splitCommand)) 592 copy(newCmd, splitCommand) 593 for i := range newCmd { 594 if strings.HasPrefix(newCmd[i], "key=") { 595 newCmd[i] = "key=fake-object-name" 596 break 597 } 598 } 599 out, _ = RunCommand(newCmd[0], newCmd[1:]...) 600 curlFatalIfNoErrorTag(out, t) 601 } 602 603 func ShareURLUploadTest(t *testing.T) { 604 ShareURLTestBucket := CreateBucket(t) 605 606 file := createFile(newTestFile{ 607 addToGlobalFileMap: true, 608 tag: "presigned-upload", 609 sizeInMBS: 1, 610 }) 611 612 out, err := RunMC( 613 "share", 614 "upload", 615 ShareURLTestBucket+"/"+file.fileNameWithoutPath, 616 ) 617 fatalIfErrorWMsg(err, out, t) 618 619 shareMsg, err := parseShareMessageFromJSONOutput(out) 620 fatalIfErrorWMsg(err, out, t) 621 622 finalURL := strings.Replace(shareMsg.ShareURL, "<FILE>", file.diskFile.Name(), -1) 623 splitCommand := strings.Split(finalURL, " ") 624 625 if skipInsecure { 626 splitCommand = append(splitCommand, "--insecure") 627 } 628 629 _, err = exec.Command(splitCommand[0], splitCommand[1:]...).CombinedOutput() 630 fatalIfErrorWMsg(err, out, t) 631 632 out, err = RunMC( 633 "stat", 634 ShareURLTestBucket+"/"+file.fileNameWithoutPath, 635 ) 636 fatalIfErrorWMsg(err, out, t) 637 638 statMsg, err := parseStatSingleObjectJSONOutput(out) 639 fatalIfError(err, t) 640 641 if statMsg.ETag != file.md5Sum { 642 fatalMsgOnly(fmt.Sprintf("expecting md5sum (%s) but got md5sum (%s)", file.md5Sum, file.md5Sum), t) 643 } 644 } 645 646 func ShareURLDownloadTest(t *testing.T) { 647 ShareURLTestBucket := CreateBucket(t) 648 file := createFile(newTestFile{ 649 addToGlobalFileMap: true, 650 tag: "presigned-download", 651 sizeInMBS: 1, 652 }) 653 654 out, err := RunMC( 655 "cp", 656 file.diskFile.Name(), 657 ShareURLTestBucket+"/"+file.fileNameWithoutPath, 658 ) 659 fatalIfErrorWMsg(err, out, t) 660 661 out, err = RunMC( 662 "share", 663 "download", 664 ShareURLTestBucket+"/"+file.fileNameWithoutPath, 665 ) 666 fatalIfErrorWMsg(err, out, t) 667 668 shareMsg, err := parseShareMessageFromJSONOutput(out) 669 fatalIfErrorWMsg(err, out, t) 670 671 resp, err := HTTPClient.Get(shareMsg.ShareURL) 672 fatalIfError(err, t) 673 674 downloadedFile, err := io.ReadAll(resp.Body) 675 fatalIfError(err, t) 676 677 md5sum := GetMD5Sum(downloadedFile) 678 if md5sum != file.md5Sum { 679 fatalMsgOnly( 680 fmt.Sprintf("expecting md5sum (%s) but got md5sum (%s)", file.md5Sum, md5sum), 681 t, 682 ) 683 } 684 } 685 686 func PutObjectPreserveAttributes(t *testing.T) { 687 AttrTestBucket := CreateBucket(t) 688 file := fileMap["1M"] 689 out, err := RunMC( 690 "cp", 691 "-a", 692 file.diskFile.Name(), 693 AttrTestBucket+"/"+file.fileNameWithoutPath, 694 ) 695 fatalIfErrorWMsg(err, out, t) 696 697 out, err = RunMC( 698 "stat", 699 AttrTestBucket+"/"+file.fileNameWithPrefix, 700 ) 701 fatalIfError(err, t) 702 703 stats, err := parseStatSingleObjectJSONOutput(out) 704 fatalIfError(err, t) 705 706 attr, err := disk.GetFileSystemAttrs(file.diskFile.Name()) 707 fatalIfError(err, t) 708 if attr != stats.Metadata["X-Amz-Meta-Mc-Attrs"] { 709 fatalMsgOnly(fmt.Sprintf("expecting file attributes (%s) but got file attributes (%s)", attr, stats.Metadata["X-Amz-Meta-Mc-Attrs"]), t) 710 } 711 } 712 713 func MirrorTempDirectoryStorageClassReducedRedundancy(t *testing.T) { 714 MirrorBucket := CreateBucket(t) 715 out, err := RunMC( 716 "mirror", 717 "--storage-class", "REDUCED_REDUNDANCY", 718 tempDir, 719 MirrorBucket, 720 ) 721 fatalIfErrorWMsg(err, out, t) 722 723 out, err = RunMC("ls", "-r", MirrorBucket) 724 fatalIfError(err, t) 725 726 fileList, err := parseLSJSONOutput(out) 727 fatalIfError(err, t) 728 729 for i, f := range fileMap { 730 fileFound := false 731 732 for _, o := range fileList { 733 if o.Key == f.fileNameWithoutPath { 734 fileMap[i].MinioLS = o 735 fileFound = true 736 } 737 } 738 739 if !fileFound { 740 t.Fatalf("File was not uploaded: %s", f.fileNameWithPrefix) 741 } 742 } 743 } 744 745 func MirrorTempDirectory(t *testing.T) { 746 MirrorBucket := CreateBucket(t) 747 748 out, err := RunMC( 749 "mirror", 750 tempDir, 751 MirrorBucket, 752 ) 753 fatalIfErrorWMsg(err, out, t) 754 755 out, err = RunMC("ls", "-r", MirrorBucket) 756 fatalIfError(err, t) 757 758 fileList, err := parseLSJSONOutput(out) 759 fatalIfError(err, t) 760 761 for i, f := range fileMap { 762 fileFound := false 763 764 for _, o := range fileList { 765 if o.Key == f.fileNameWithoutPath { 766 fileMap[i].MinioLS = o 767 fileFound = true 768 } 769 } 770 771 if !fileFound { 772 t.Fatalf("File was not uploaded: %s", f.fileNameWithPrefix) 773 } 774 } 775 } 776 777 func CatObjectFromStdin(t *testing.T) { 778 objectName := "pipe-test-object" 779 CatEchoBucket := CreateBucket(t) 780 781 file := fileMap["1M"] 782 783 cmdCAT := exec.Command( 784 "cat", 785 file.diskFile.Name(), 786 ) 787 788 p := []string{ 789 "pipe", 790 CatEchoBucket + "/" + objectName, 791 } 792 if skipInsecure { 793 p = append(p, "--insecure") 794 } 795 796 cmdMC := exec.Command(mcCmd, p...) 797 798 r, w := io.Pipe() 799 defer r.Close() 800 defer w.Close() 801 802 cmdCAT.Stdout = w 803 cmdMC.Stdin = r 804 805 err := cmdMC.Start() 806 fatalIfError(err, t) 807 err = cmdCAT.Start() 808 fatalIfError(err, t) 809 810 err = cmdCAT.Wait() 811 fatalIfError(err, t) 812 w.Close() 813 err = cmdMC.Wait() 814 fatalIfError(err, t) 815 r.Close() 816 817 outB, err := RunMC( 818 "cat", 819 CatEchoBucket+"/"+objectName, 820 ) 821 fatalIfErrorWMsg(err, outB, t) 822 823 md5SumCat := GetMD5Sum([]byte(outB)) 824 if file.md5Sum != md5SumCat { 825 fatalMsgOnly( 826 fmt.Sprintf("expecting md5sum (%s) but got md5sum (%s)", file.md5Sum, md5SumCat), 827 t, 828 ) 829 } 830 } 831 832 func CatObjectToStdIn(t *testing.T) { 833 file := fileMap["1M"] 834 out, err := RunMC( 835 "cat", 836 mainTestBucket+"/"+file.fileNameWithoutPath, 837 ) 838 fatalIfErrorWMsg(err, out, t) 839 md5Sum := GetMD5Sum([]byte(out)) 840 if md5Sum != file.md5Sum { 841 fatalMsgOnly( 842 fmt.Sprintf("expecting md5sum (%s) but got md5sum (%s)", file.md5Sum, md5Sum), 843 t, 844 ) 845 } 846 } 847 848 func VerifyKMSKey(t *testing.T) { 849 out, err := RunMC( 850 "admin", 851 "kms", 852 "key", 853 "list", 854 defaultAlias, 855 ) 856 fatalIfError(err, t) 857 keyMsg := new(kmsKeysMsg) 858 err = json.Unmarshal([]byte(out), keyMsg) 859 fatalIfError(err, t) 860 sseInvalidKmsKeyName = uuid.NewString() 861 found := false 862 invalidKeyFound := false 863 for _, v := range keyMsg.Keys { 864 if v == sseKMSKeyName { 865 found = true 866 break 867 } 868 if v == sseInvalidKmsKeyName { 869 invalidKeyFound = true 870 } 871 } 872 if !found { 873 fatalMsgOnly(fmt.Sprintf("expected to find kms key %s but got these keys: %v", sseKMSKeyName, keyMsg.Keys), t) 874 } 875 if invalidKeyFound { 876 fatalMsgOnly("tried to create invalid uuid kms key but for some reason it overlapped with an already existing key", t) 877 } 878 } 879 880 func PutObjectWithSSEKMSPartialPrefixMatch(t *testing.T) { 881 file := createFile(newTestFile{ 882 addToGlobalFileMap: false, 883 tag: "encput-kms-prefix-test", 884 sizeInMBS: 1, 885 }) 886 887 out, err := RunMC( 888 "cp", 889 "--enc-kms", 890 sseTestBucket+"/"+file.fileNameWithoutPath+"="+sseKMSKeyName, 891 file.diskFile.Name(), 892 sseTestBucket, 893 ) 894 fatalIfErrorWMsg(err, out, t) 895 } 896 897 func PutObjectWithSSEKMS(t *testing.T) { 898 file := createFile(newTestFile{ 899 addToGlobalFileMap: false, 900 tag: "encput-kms", 901 sizeInMBS: 1, 902 }) 903 904 out, err := RunMC( 905 "cp", 906 "--enc-kms", 907 sseTestBucket+"="+sseKMSKeyName, 908 file.diskFile.Name(), 909 sseTestBucket+"/"+file.fileNameWithoutPath, 910 ) 911 fatalIfErrorWMsg(err, out, t) 912 } 913 914 func PutObjectWithSSEKMSMultipart(t *testing.T) { 915 file := createFile(newTestFile{ 916 addToGlobalFileMap: false, 917 tag: "encmultiput-kms", 918 sizeInMBS: 68, 919 }) 920 921 out, err := RunMC( 922 "cp", 923 "--enc-kms", 924 sseTestBucket+"="+sseKMSKeyName, 925 file.diskFile.Name(), 926 sseTestBucket+"/"+file.fileNameWithoutPath, 927 ) 928 fatalIfErrorWMsg(err, out, t) 929 } 930 931 func PutObjectWithSSEKMSInvalidKeys(t *testing.T) { 932 file := createFile(newTestFile{ 933 addToGlobalFileMap: false, 934 tag: "encerror-kms", 935 sizeInMBS: 1, 936 }) 937 938 out, err := RunMC( 939 "cp", 940 "--enc-kms="+sseTestBucket+"="+sseInvalidKmsKeyName, 941 file.diskFile.Name(), 942 sseTestBucket+"/"+file.fileNameWithoutPath, 943 ) 944 fatalIfNoErrorWMsg(err, out, t) 945 } 946 947 func GetObjectWithSSES3(t *testing.T) { 948 file := createFile(newTestFile{ 949 addToGlobalFileMap: false, 950 tag: "encget-s3", 951 sizeInMBS: 1, 952 }) 953 954 out, err := RunMC( 955 "cp", 956 "--enc-s3="+sseTestBucket, 957 file.diskFile.Name(), 958 sseTestBucket+"/"+file.fileNameWithoutPath, 959 ) 960 fatalIfErrorWMsg(err, out, t) 961 962 out, err = RunMC( 963 "cp", 964 sseTestBucket+"/"+file.fileNameWithoutPath, 965 file.diskFile.Name()+".download", 966 ) 967 fatalIfErrorWMsg(err, out, t) 968 969 md5s, err := openFileAndGetMd5Sum(file.diskFile.Name() + ".download") 970 fatalIfError(err, t) 971 if md5s != file.md5Sum { 972 fatalMsgOnly(fmt.Sprintf("expecting md5sum (%s) but got sum (%s)", file.md5Sum, md5s), t) 973 } 974 } 975 976 func CatObjectWithSSES3(t *testing.T) { 977 file := createFile(newTestFile{ 978 addToGlobalFileMap: false, 979 tag: "enccat-s3", 980 sizeInMBS: 1, 981 }) 982 983 out, err := RunMC( 984 "cp", 985 "--enc-s3="+sseTestBucket, 986 file.diskFile.Name(), 987 sseTestBucket+"/"+file.fileNameWithoutPath, 988 ) 989 fatalIfErrorWMsg(err, out, t) 990 991 out, err = RunMC( 992 "cat", 993 sseTestBucket+"/"+file.fileNameWithoutPath, 994 ) 995 fatalIfErrorWMsg(err, out, t) 996 catMD5Sum := GetMD5Sum([]byte(out)) 997 998 if catMD5Sum != file.md5Sum { 999 fatalMsgOnly(fmt.Sprintf( 1000 "expected md5sum %s but we got %s", 1001 file.md5Sum, 1002 catMD5Sum, 1003 ), t) 1004 } 1005 1006 if int64(len(out)) != file.diskStat.Size() { 1007 fatalMsgOnly(fmt.Sprintf( 1008 "file size is %d but we got %d", 1009 file.diskStat.Size(), 1010 len(out), 1011 ), t) 1012 } 1013 1014 fatalIfErrorWMsg( 1015 err, 1016 "cat length: "+strconv.Itoa(len(out))+" -- file length:"+strconv.Itoa(int(file.diskStat.Size())), 1017 t, 1018 ) 1019 } 1020 1021 func CopyObjectWithSSES3ToNewBucket(t *testing.T) { 1022 file := createFile(newTestFile{ 1023 addToGlobalFileMap: false, 1024 tag: "encbucketcopy-s3", 1025 sizeInMBS: 1, 1026 }) 1027 1028 out, err := RunMC( 1029 "cp", 1030 "--enc-s3="+sseTestBucket, 1031 file.diskFile.Name(), 1032 sseTestBucket+"/"+file.fileNameWithoutPath, 1033 ) 1034 fatalIfErrorWMsg(err, out, t) 1035 1036 TargetSSEBucket := CreateBucket(t) 1037 1038 out, err = RunMC( 1039 "cp", 1040 "--enc-s3="+TargetSSEBucket, 1041 sseTestBucket+"/"+file.fileNameWithoutPath, 1042 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1043 ) 1044 fatalIfErrorWMsg(err, out, t) 1045 1046 out, err = RunMC( 1047 "cp", 1048 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1049 file.diskFile.Name()+".download", 1050 ) 1051 fatalIfErrorWMsg(err, out, t) 1052 1053 md5s, err := openFileAndGetMd5Sum(file.diskFile.Name() + ".download") 1054 fatalIfError(err, t) 1055 if md5s != file.md5Sum { 1056 fatalMsgOnly(fmt.Sprintf("expecting md5sum (%s) but got sum (%s)", file.md5Sum, md5s), t) 1057 } 1058 } 1059 1060 func MirrorTempDirectoryUsingSSES3(t *testing.T) { 1061 MirrorBucket := CreateBucket(t) 1062 1063 subDir := "encmirror-s3" 1064 1065 f1 := createFile(newTestFile{ 1066 addToGlobalFileMap: false, 1067 subDir: subDir, 1068 tag: "encmirror1-s3", 1069 sizeInMBS: 1, 1070 }) 1071 1072 f2 := createFile(newTestFile{ 1073 addToGlobalFileMap: false, 1074 subDir: subDir, 1075 tag: "encmirror2-s3", 1076 sizeInMBS: 2, 1077 }) 1078 1079 f3 := createFile(newTestFile{ 1080 addToGlobalFileMap: false, 1081 subDir: subDir, 1082 tag: "encmirror3-s3", 1083 sizeInMBS: 4, 1084 }) 1085 1086 files := append([]*testFile{}, f1, f2, f3) 1087 1088 out, err := RunMC( 1089 "mirror", 1090 "--enc-s3="+MirrorBucket, 1091 tempDir+string(os.PathSeparator)+subDir, 1092 MirrorBucket, 1093 ) 1094 fatalIfErrorWMsg(err, out, t) 1095 1096 out, err = RunMC("ls", "-r", MirrorBucket) 1097 fatalIfError(err, t) 1098 1099 fileList, err := parseLSJSONOutput(out) 1100 fatalIfError(err, t) 1101 1102 for i, f := range files { 1103 fileFound := false 1104 1105 for _, o := range fileList { 1106 if o.Key == f.fileNameWithoutPath { 1107 files[i].MinioLS = o 1108 fileFound = true 1109 } 1110 } 1111 1112 if !fileFound { 1113 fatalMsgOnly(fmt.Sprintf( 1114 "File was not uploaded: %s", 1115 f.fileNameWithPrefix, 1116 ), t) 1117 } 1118 1119 out, err := RunMC("stat", MirrorBucket+"/"+files[i].MinioLS.Key) 1120 fatalIfError(err, t) 1121 stat, err := parseStatSingleObjectJSONOutput(out) 1122 fatalIfError(err, t) 1123 files[i].MinioStat = stat 1124 1125 foundKmsTag := false 1126 for ii := range stat.Metadata { 1127 if ii == amzObjectSSE { 1128 foundKmsTag = true 1129 break 1130 } 1131 } 1132 1133 if !foundKmsTag { 1134 fmt.Println(stat) 1135 fatalMsgOnly(amzObjectSSEKMSKeyID+" not found for object "+files[i].MinioLS.Key, t) 1136 } 1137 1138 } 1139 } 1140 1141 func PutObjectWithSSES3PartialPrefixMatch(t *testing.T) { 1142 file := createFile(newTestFile{ 1143 addToGlobalFileMap: false, 1144 tag: "encput-s3-prefix-test", 1145 sizeInMBS: 1, 1146 }) 1147 1148 out, err := RunMC( 1149 "cp", 1150 "--enc-s3="+sseTestBucket+"/"+file.fileNameWithoutPath, 1151 file.diskFile.Name(), 1152 sseTestBucket, 1153 ) 1154 fatalIfErrorWMsg(err, out, t) 1155 } 1156 1157 func PutObjectWithSSES3(t *testing.T) { 1158 file := createFile(newTestFile{ 1159 addToGlobalFileMap: false, 1160 tag: "encput-s3", 1161 sizeInMBS: 1, 1162 }) 1163 1164 out, err := RunMC( 1165 "cp", 1166 "--enc-s3="+sseTestBucket, 1167 file.diskFile.Name(), 1168 sseTestBucket+"/"+file.fileNameWithoutPath, 1169 ) 1170 fatalIfErrorWMsg(err, out, t) 1171 } 1172 1173 func PutObjectWithSSES3Multipart(t *testing.T) { 1174 file := createFile(newTestFile{ 1175 addToGlobalFileMap: false, 1176 tag: "encmultiput-s3", 1177 sizeInMBS: 68, 1178 }) 1179 1180 out, err := RunMC( 1181 "cp", 1182 "--enc-s3="+sseTestBucket, 1183 file.diskFile.Name(), 1184 sseTestBucket+"/"+file.fileNameWithoutPath, 1185 ) 1186 fatalIfErrorWMsg(err, out, t) 1187 } 1188 1189 func GetObjectWithSSEKMS(t *testing.T) { 1190 file := createFile(newTestFile{ 1191 addToGlobalFileMap: false, 1192 tag: "encget-kms", 1193 sizeInMBS: 1, 1194 }) 1195 1196 out, err := RunMC( 1197 "cp", 1198 "--enc-kms="+sseTestBucket+"="+sseKMSKeyName, 1199 file.diskFile.Name(), 1200 sseTestBucket+"/"+file.fileNameWithoutPath, 1201 ) 1202 fatalIfErrorWMsg(err, out, t) 1203 1204 out, err = RunMC( 1205 "cp", 1206 sseTestBucket+"/"+file.fileNameWithoutPath, 1207 file.diskFile.Name()+".download", 1208 ) 1209 fatalIfErrorWMsg(err, out, t) 1210 1211 md5s, err := openFileAndGetMd5Sum(file.diskFile.Name() + ".download") 1212 fatalIfError(err, t) 1213 if md5s != file.md5Sum { 1214 fatalMsgOnly(fmt.Sprintf("expecting md5sum (%s) but got sum (%s)", file.md5Sum, md5s), t) 1215 } 1216 } 1217 1218 func PutObjectWithSSECMultipart(t *testing.T) { 1219 file := createFile(newTestFile{ 1220 addToGlobalFileMap: false, 1221 tag: "encmultiput", 1222 sizeInMBS: 68, 1223 }) 1224 1225 out, err := RunMC( 1226 "cp", 1227 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1228 file.diskFile.Name(), 1229 sseTestBucket+"/"+file.fileNameWithoutPath, 1230 ) 1231 fatalIfErrorWMsg(err, out, t) 1232 } 1233 1234 func CatObjectWithSSEKMS(t *testing.T) { 1235 file := createFile(newTestFile{ 1236 addToGlobalFileMap: false, 1237 tag: "enccat-kms", 1238 sizeInMBS: 1, 1239 }) 1240 1241 out, err := RunMC( 1242 "cp", 1243 "--enc-kms="+sseTestBucket+"="+sseKMSKeyName, 1244 file.diskFile.Name(), 1245 sseTestBucket+"/"+file.fileNameWithoutPath, 1246 ) 1247 fatalIfErrorWMsg(err, out, t) 1248 1249 out, err = RunMC( 1250 "cat", 1251 sseTestBucket+"/"+file.fileNameWithoutPath, 1252 ) 1253 fatalIfErrorWMsg(err, out, t) 1254 catMD5Sum := GetMD5Sum([]byte(out)) 1255 1256 if catMD5Sum != file.md5Sum { 1257 fatalMsgOnly(fmt.Sprintf( 1258 "expected md5sum %s but we got %s", 1259 file.md5Sum, 1260 catMD5Sum, 1261 ), t) 1262 } 1263 1264 if int64(len(out)) != file.diskStat.Size() { 1265 fatalMsgOnly(fmt.Sprintf( 1266 "file size is %d but we got %d", 1267 file.diskStat.Size(), 1268 len(out), 1269 ), t) 1270 } 1271 1272 fatalIfErrorWMsg( 1273 err, 1274 "cat length: "+strconv.Itoa(len(out))+" -- file length:"+strconv.Itoa(int(file.diskStat.Size())), 1275 t, 1276 ) 1277 } 1278 1279 func PutObjectWithSSECPartialPrefixMatch(t *testing.T) { 1280 file := createFile(newTestFile{ 1281 addToGlobalFileMap: false, 1282 tag: "encput-prefix-test", 1283 sizeInMBS: 1, 1284 }) 1285 1286 out, err := RunMC( 1287 "cp", 1288 "--enc-c="+sseTestBucket+"/"+file.fileNameWithoutPath+"="+sseBaseEncodedKey, 1289 file.diskFile.Name(), 1290 sseTestBucket, 1291 ) 1292 fatalIfErrorWMsg(err, out, t) 1293 } 1294 1295 func PutObjectWithSSEC(t *testing.T) { 1296 file := createFile(newTestFile{ 1297 addToGlobalFileMap: false, 1298 tag: "encput", 1299 sizeInMBS: 1, 1300 }) 1301 1302 out, err := RunMC( 1303 "cp", 1304 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1305 file.diskFile.Name(), 1306 sseTestBucket+"/"+file.fileNameWithoutPath, 1307 ) 1308 fatalIfErrorWMsg(err, out, t) 1309 } 1310 1311 func PutObjectErrorWithSSECOverHTTP(t *testing.T) { 1312 file := createFile(newTestFile{ 1313 addToGlobalFileMap: false, 1314 tag: "encput-http", 1315 sizeInMBS: 1, 1316 }) 1317 1318 out, err := RunMC( 1319 "cp", 1320 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1321 file.diskFile.Name(), 1322 sseTestBucket+"/"+file.fileNameWithoutPath, 1323 ) 1324 fatalIfNoErrorWMsg(err, out, t) 1325 } 1326 1327 func PutObjectWithSSECInvalidKeys(t *testing.T) { 1328 file := createFile(newTestFile{ 1329 addToGlobalFileMap: false, 1330 tag: "encerror-dep", 1331 sizeInMBS: 1, 1332 }) 1333 1334 out, err := RunMC( 1335 "cp", 1336 "--enc-c="+sseTestBucket+"="+invalidSSEBaseEncodedKey, 1337 file.diskFile.Name(), 1338 sseTestBucket+"/"+file.fileNameWithoutPath, 1339 ) 1340 fatalIfNoErrorWMsg(err, out, t) 1341 } 1342 1343 func GetObjectWithSSEC(t *testing.T) { 1344 file := createFile(newTestFile{ 1345 addToGlobalFileMap: false, 1346 tag: "encget", 1347 sizeInMBS: 1, 1348 }) 1349 1350 out, err := RunMC( 1351 "cp", 1352 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1353 file.diskFile.Name(), 1354 sseTestBucket+"/"+file.fileNameWithoutPath, 1355 ) 1356 fatalIfErrorWMsg(err, out, t) 1357 1358 out, err = RunMC( 1359 "cp", 1360 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1361 sseTestBucket+"/"+file.fileNameWithoutPath, 1362 file.diskFile.Name()+".download", 1363 ) 1364 fatalIfErrorWMsg(err, out, t) 1365 1366 md5s, err := openFileAndGetMd5Sum(file.diskFile.Name() + ".download") 1367 fatalIfError(err, t) 1368 if md5s != file.md5Sum { 1369 fatalMsgOnly(fmt.Sprintf("expecting md5sum (%s) but got sum (%s)", file.md5Sum, md5s), t) 1370 } 1371 } 1372 1373 func GetObjectWithSSECWithoutKey(t *testing.T) { 1374 file := createFile(newTestFile{ 1375 addToGlobalFileMap: false, 1376 tag: "encerror", 1377 sizeInMBS: 1, 1378 }) 1379 1380 out, err := RunMC( 1381 "cp", 1382 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1383 file.diskFile.Name(), 1384 sseTestBucket+"/"+file.fileNameWithoutPath, 1385 ) 1386 fatalIfErrorWMsg(err, out, t) 1387 1388 out, err = RunMC( 1389 "cp", 1390 sseTestBucket+"/"+file.fileNameWithoutPath, 1391 file.diskFile.Name()+"-get", 1392 ) 1393 fatalIfNoErrorWMsg(err, out, t) 1394 } 1395 1396 func CatObjectWithSSEC(t *testing.T) { 1397 file := createFile(newTestFile{ 1398 addToGlobalFileMap: false, 1399 tag: "enccat", 1400 sizeInMBS: 1, 1401 }) 1402 1403 out, err := RunMC( 1404 "cp", 1405 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1406 file.diskFile.Name(), 1407 sseTestBucket+"/"+file.fileNameWithoutPath, 1408 ) 1409 fatalIfErrorWMsg(err, out, t) 1410 1411 out, err = RunMC( 1412 "cat", 1413 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1414 sseTestBucket+"/"+file.fileNameWithoutPath, 1415 ) 1416 fatalIfErrorWMsg(err, out, t) 1417 catMD5Sum := GetMD5Sum([]byte(out)) 1418 1419 if catMD5Sum != file.md5Sum { 1420 fatalMsgOnly(fmt.Sprintf( 1421 "expected md5sum %s but we got %s", 1422 file.md5Sum, 1423 catMD5Sum, 1424 ), t) 1425 } 1426 1427 if int64(len(out)) != file.diskStat.Size() { 1428 fatalMsgOnly(fmt.Sprintf( 1429 "file size is %d but we got %d", 1430 file.diskStat.Size(), 1431 len(out), 1432 ), t) 1433 } 1434 1435 fatalIfErrorWMsg( 1436 err, 1437 "cat length: "+strconv.Itoa(len(out))+" -- file length:"+strconv.Itoa(int(file.diskStat.Size())), 1438 t, 1439 ) 1440 } 1441 1442 func CopyObjectWithSSEKMSWithOverLappingKeys(t *testing.T) { 1443 file := createFile(newTestFile{ 1444 addToGlobalFileMap: false, 1445 tag: "encbucketcopy-kms", 1446 sizeInMBS: 1, 1447 }) 1448 1449 out, err := RunMC( 1450 "cp", 1451 "--enc-kms="+sseTestBucket+"="+sseKMSKeyName, 1452 file.diskFile.Name(), 1453 sseTestBucket+"/"+file.fileNameWithoutPath, 1454 ) 1455 fatalIfErrorWMsg(err, out, t) 1456 1457 TargetSSEBucket := CreateBucket(t) 1458 1459 out, err = RunMC( 1460 "cp", 1461 "--enc-kms="+TargetSSEBucket+"="+sseKMSKeyName, 1462 "--enc-kms="+TargetSSEBucket+"="+sseKMSKeyName, 1463 sseTestBucket+"/"+file.fileNameWithoutPath, 1464 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1465 ) 1466 fatalIfNoErrorWMsg(err, out, t) 1467 } 1468 1469 func CopyObjectWithSSEKMSToNewBucket(t *testing.T) { 1470 file := createFile(newTestFile{ 1471 addToGlobalFileMap: false, 1472 tag: "encbucketcopy-kms", 1473 sizeInMBS: 1, 1474 }) 1475 1476 out, err := RunMC( 1477 "cp", 1478 "--enc-kms="+sseTestBucket+"="+sseKMSKeyName, 1479 file.diskFile.Name(), 1480 sseTestBucket+"/"+file.fileNameWithoutPath, 1481 ) 1482 fatalIfErrorWMsg(err, out, t) 1483 1484 TargetSSEBucket := CreateBucket(t) 1485 1486 out, err = RunMC( 1487 "cp", 1488 "--enc-kms="+TargetSSEBucket+"="+sseKMSKeyName, 1489 "--enc-kms="+sseTestBucket+"="+sseKMSKeyName, 1490 sseTestBucket+"/"+file.fileNameWithoutPath, 1491 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1492 ) 1493 fatalIfErrorWMsg(err, out, t) 1494 1495 out, err = RunMC( 1496 "cp", 1497 "--enc-kms="+TargetSSEBucket+"="+sseKMSKeyName, 1498 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1499 file.diskFile.Name()+".download", 1500 ) 1501 fatalIfErrorWMsg(err, out, t) 1502 1503 md5s, err := openFileAndGetMd5Sum(file.diskFile.Name() + ".download") 1504 fatalIfError(err, t) 1505 if md5s != file.md5Sum { 1506 fatalMsgOnly(fmt.Sprintf("expecting md5sum (%s) but got sum (%s)", file.md5Sum, md5s), t) 1507 } 1508 } 1509 1510 func CopyObjectWithSSEKMSToNewBucketWithSSEC(t *testing.T) { 1511 file := createFile(newTestFile{ 1512 addToGlobalFileMap: false, 1513 tag: "encbucketcopy-kms-c", 1514 sizeInMBS: 1, 1515 }) 1516 1517 out, err := RunMC( 1518 "cp", 1519 "--enc-kms="+sseTestBucket+"="+sseKMSKeyName, 1520 file.diskFile.Name(), 1521 sseTestBucket+"/"+file.fileNameWithoutPath, 1522 ) 1523 fatalIfErrorWMsg(err, out, t) 1524 1525 TargetSSEBucket := CreateBucket(t) 1526 1527 out, err = RunMC( 1528 "cp", 1529 "--enc-c="+TargetSSEBucket+"="+sseBaseEncodedKey, 1530 sseTestBucket+"/"+file.fileNameWithoutPath, 1531 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1532 ) 1533 fatalIfErrorWMsg(err, out, t) 1534 1535 out, err = RunMC( 1536 "cp", 1537 "--enc-c="+TargetSSEBucket+"="+sseBaseEncodedKey, 1538 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1539 file.diskFile.Name()+".download", 1540 ) 1541 fatalIfErrorWMsg(err, out, t) 1542 1543 md5s, err := openFileAndGetMd5Sum(file.diskFile.Name() + ".download") 1544 fatalIfError(err, t) 1545 if md5s != file.md5Sum { 1546 fatalMsgOnly(fmt.Sprintf("expecting md5sum (%s) but got sum (%s)", file.md5Sum, md5s), t) 1547 } 1548 } 1549 1550 func MirrorTempDirectoryUsingSSEKMS(t *testing.T) { 1551 MirrorBucket := CreateBucket(t) 1552 1553 subDir := "encmirror-kms" 1554 1555 f1 := createFile(newTestFile{ 1556 addToGlobalFileMap: false, 1557 subDir: subDir, 1558 tag: "encmirror1-kms", 1559 sizeInMBS: 1, 1560 }) 1561 1562 f2 := createFile(newTestFile{ 1563 addToGlobalFileMap: false, 1564 subDir: subDir, 1565 tag: "encmirror2-kms", 1566 sizeInMBS: 2, 1567 }) 1568 1569 f3 := createFile(newTestFile{ 1570 addToGlobalFileMap: false, 1571 subDir: subDir, 1572 tag: "encmirror3-kms", 1573 sizeInMBS: 4, 1574 }) 1575 1576 files := append([]*testFile{}, f1, f2, f3) 1577 1578 out, err := RunMC( 1579 "mirror", 1580 "--enc-kms="+MirrorBucket+"="+sseKMSKeyName, 1581 tempDir+string(os.PathSeparator)+subDir, 1582 MirrorBucket, 1583 ) 1584 fatalIfErrorWMsg(err, out, t) 1585 1586 out, err = RunMC("ls", "-r", MirrorBucket) 1587 fatalIfError(err, t) 1588 1589 fileList, err := parseLSJSONOutput(out) 1590 fatalIfError(err, t) 1591 1592 for i, f := range files { 1593 fileFound := false 1594 1595 for _, o := range fileList { 1596 if o.Key == f.fileNameWithoutPath { 1597 files[i].MinioLS = o 1598 fileFound = true 1599 } 1600 } 1601 1602 if !fileFound { 1603 fatalMsgOnly(fmt.Sprintf( 1604 "File was not uploaded: %s", 1605 f.fileNameWithPrefix, 1606 ), t) 1607 } 1608 1609 out, err := RunMC("stat", MirrorBucket+"/"+files[i].MinioLS.Key) 1610 fatalIfError(err, t) 1611 stat, err := parseStatSingleObjectJSONOutput(out) 1612 fatalIfError(err, t) 1613 files[i].MinioStat = stat 1614 1615 foundKmsTag := false 1616 for ii, v := range stat.Metadata { 1617 if ii == amzObjectSSEKMSKeyID { 1618 foundKmsTag = true 1619 if !strings.HasSuffix(v, sseKMSKeyName) { 1620 fatalMsgOnly("invalid KMS key for object "+files[i].MinioLS.Key, t) 1621 break 1622 } 1623 } 1624 } 1625 1626 if !foundKmsTag { 1627 fatalMsgOnly(amzObjectSSEKMSKeyID+" not found for object "+files[i].MinioLS.Key, t) 1628 } 1629 1630 } 1631 } 1632 1633 func RemoveObjectWithSSEKMS(t *testing.T) { 1634 file := createFile(newTestFile{ 1635 addToGlobalFileMap: false, 1636 tag: "encrm-kms", 1637 sizeInMBS: 1, 1638 }) 1639 1640 out, err := RunMC( 1641 "cp", 1642 "--enc-kms="+sseTestBucket+"="+sseKMSKeyName, 1643 file.diskFile.Name(), 1644 sseTestBucket+"/"+file.fileNameWithoutPath, 1645 ) 1646 fatalIfErrorWMsg(err, out, t) 1647 1648 out, err = RunMC( 1649 "rm", 1650 sseTestBucket+"/"+file.fileNameWithoutPath, 1651 ) 1652 fatalIfErrorWMsg(err, out, t) 1653 1654 out, err = RunMC( 1655 "stat", 1656 sseTestBucket+"/"+file.fileNameWithoutPath, 1657 ) 1658 fatalIfNoErrorWMsg(err, out, t) 1659 } 1660 1661 func CatObjectWithSSECWithoutKey(t *testing.T) { 1662 file := createFile(newTestFile{ 1663 addToGlobalFileMap: false, 1664 tag: "encerror", 1665 sizeInMBS: 1, 1666 }) 1667 1668 out, err := RunMC( 1669 "cp", 1670 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1671 file.diskFile.Name(), 1672 sseTestBucket+"/"+file.fileNameWithoutPath, 1673 ) 1674 fatalIfErrorWMsg(err, out, t) 1675 1676 out, err = RunMC( 1677 "cat", 1678 sseTestBucket+"/"+file.fileNameWithoutPath, 1679 file.diskFile.Name()+"-cat", 1680 ) 1681 fatalIfNoErrorWMsg(err, out, t) 1682 } 1683 1684 func RemoveObjectWithSSEC(t *testing.T) { 1685 file := createFile(newTestFile{ 1686 addToGlobalFileMap: false, 1687 tag: "encrm", 1688 sizeInMBS: 1, 1689 }) 1690 1691 out, err := RunMC( 1692 "cp", 1693 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1694 file.diskFile.Name(), 1695 sseTestBucket+"/"+file.fileNameWithoutPath, 1696 ) 1697 fatalIfErrorWMsg(err, out, t) 1698 1699 out, err = RunMC( 1700 "rm", 1701 sseTestBucket+"/"+file.fileNameWithoutPath, 1702 ) 1703 fatalIfErrorWMsg(err, out, t) 1704 1705 out, err = RunMC( 1706 "stat", 1707 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1708 sseTestBucket+"/"+file.fileNameWithoutPath, 1709 ) 1710 fatalIfNoErrorWMsg(err, out, t) 1711 } 1712 1713 func MirrorTempDirectoryUsingSSEC(t *testing.T) { 1714 MirrorBucket := CreateBucket(t) 1715 1716 subDir := "encmirror" 1717 1718 f1 := createFile(newTestFile{ 1719 addToGlobalFileMap: false, 1720 subDir: subDir, 1721 tag: "encmirror1", 1722 sizeInMBS: 1, 1723 }) 1724 1725 f2 := createFile(newTestFile{ 1726 addToGlobalFileMap: false, 1727 subDir: subDir, 1728 tag: "encmirror2", 1729 sizeInMBS: 2, 1730 }) 1731 1732 f3 := createFile(newTestFile{ 1733 addToGlobalFileMap: false, 1734 subDir: subDir, 1735 tag: "encmirror3", 1736 sizeInMBS: 4, 1737 }) 1738 1739 files := append([]*testFile{}, f1, f2, f3) 1740 1741 out, err := RunMC( 1742 "mirror", 1743 "--enc-c="+MirrorBucket+"="+sseBaseEncodedKey, 1744 tempDir+string(os.PathSeparator)+subDir, 1745 MirrorBucket, 1746 ) 1747 fatalIfErrorWMsg(err, out, t) 1748 1749 out, err = RunMC("ls", "-r", MirrorBucket) 1750 fatalIfError(err, t) 1751 1752 fileList, err := parseLSJSONOutput(out) 1753 fatalIfError(err, t) 1754 1755 for i, f := range files { 1756 fileFound := false 1757 1758 for _, o := range fileList { 1759 if o.Key == f.fileNameWithoutPath { 1760 files[i].MinioLS = o 1761 fileFound = true 1762 } 1763 } 1764 1765 if !fileFound { 1766 fatalMsgOnly(fmt.Sprintf( 1767 "File was not uploaded: %s", 1768 f.fileNameWithPrefix, 1769 ), t) 1770 } 1771 1772 out, err := RunMC( 1773 "stat", 1774 "--enc-c="+MirrorBucket+"="+sseBaseEncodedKey, 1775 MirrorBucket+"/"+files[i].MinioLS.Key, 1776 ) 1777 fatalIfError(err, t) 1778 _, err = parseStatSingleObjectJSONOutput(out) 1779 fatalIfError(err, t) 1780 1781 } 1782 } 1783 1784 func CopyObjectWithSSECToNewBucketWithNewKey(t *testing.T) { 1785 file := createFile(newTestFile{ 1786 addToGlobalFileMap: false, 1787 tag: "encbucketcopy", 1788 sizeInMBS: 1, 1789 }) 1790 1791 out, err := RunMC( 1792 "cp", 1793 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1794 file.diskFile.Name(), 1795 sseTestBucket+"/"+file.fileNameWithoutPath, 1796 ) 1797 fatalIfErrorWMsg(err, out, t) 1798 1799 TargetSSEBucket := CreateBucket(t) 1800 1801 out, err = RunMC( 1802 "cp", 1803 "--enc-c="+TargetSSEBucket+"="+sseBaseEncodedKey2, 1804 "--enc-c="+sseTestBucket+"="+sseBaseEncodedKey, 1805 sseTestBucket+"/"+file.fileNameWithoutPath, 1806 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1807 ) 1808 fatalIfErrorWMsg(err, out, t) 1809 1810 out, err = RunMC( 1811 "cp", 1812 "--enc-c="+TargetSSEBucket+"="+sseBaseEncodedKey2, 1813 TargetSSEBucket+"/"+file.fileNameWithoutPath, 1814 file.diskFile.Name()+".download", 1815 ) 1816 fatalIfErrorWMsg(err, out, t) 1817 1818 md5s, err := openFileAndGetMd5Sum(file.diskFile.Name() + ".download") 1819 fatalIfError(err, t) 1820 if md5s != file.md5Sum { 1821 fatalMsgOnly(fmt.Sprintf("expecting md5sum (%s) but got sum (%s)", file.md5Sum, md5s), t) 1822 } 1823 } 1824 1825 func uploadAllFiles(t *testing.T) { 1826 for _, v := range fileMap { 1827 parameters := make([]string, 0) 1828 parameters = append(parameters, "cp") 1829 1830 if v.storageClass != "" { 1831 parameters = append(parameters, "--storage-class", v.storageClass) 1832 } 1833 1834 if len(v.metaData) > 0 { 1835 parameters = append(parameters, "--attr") 1836 meta := "" 1837 for i, v := range v.metaData { 1838 meta += i + "=" + v + ";" 1839 } 1840 meta = strings.TrimSuffix(meta, ";") 1841 parameters = append(parameters, meta) 1842 } 1843 if len(v.tags) > 0 { 1844 parameters = append(parameters, "--tags") 1845 tags := "" 1846 for i, v := range v.tags { 1847 tags += i + "=" + v + ";" 1848 } 1849 tags = strings.TrimSuffix(tags, ";") 1850 parameters = append(parameters, tags) 1851 } 1852 1853 parameters = append(parameters, v.diskFile.Name()) 1854 1855 if v.prefix != "" { 1856 parameters = append( 1857 parameters, 1858 mainTestBucket+"/"+v.fileNameWithPrefix, 1859 ) 1860 } else { 1861 parameters = append( 1862 parameters, 1863 mainTestBucket+"/"+v.fileNameWithoutPath, 1864 ) 1865 } 1866 1867 _, err := RunMC(parameters...) 1868 if err != nil { 1869 t.Fatal(err) 1870 } 1871 } 1872 } 1873 1874 func OD(t *testing.T) { 1875 LocalBucketPath := CreateBucket(t) 1876 1877 file := fileMap["65M"] 1878 out, err := RunMC( 1879 "od", 1880 "if="+file.diskFile.Name(), 1881 "of="+LocalBucketPath+"/od/"+file.fileNameWithoutPath, 1882 "parts=10", 1883 ) 1884 1885 fatalIfError(err, t) 1886 odMsg, err := parseSingleODMessageJSONOutput(out) 1887 fatalIfError(err, t) 1888 1889 if odMsg.TotalSize != file.diskStat.Size() { 1890 t.Fatalf( 1891 "Expected (%d) bytes to be uploaded but only uploaded (%d) bytes", 1892 odMsg.TotalSize, 1893 file.diskStat.Size(), 1894 ) 1895 } 1896 1897 if odMsg.Parts != 10 { 1898 t.Fatalf( 1899 "Expected upload parts to be (10) but they were (%d)", 1900 odMsg.Parts, 1901 ) 1902 } 1903 1904 if odMsg.Type != "FStoS3" { 1905 t.Fatalf( 1906 "Expected type to be (FStoS3) but got (%s)", 1907 odMsg.Type, 1908 ) 1909 } 1910 1911 if odMsg.PartSize != uint64(file.diskStat.Size())/10 { 1912 t.Fatalf( 1913 "Expected part size to be (%d) but got (%d)", 1914 file.diskStat.Size()/10, 1915 odMsg.PartSize, 1916 ) 1917 } 1918 1919 out, err = RunMC( 1920 "od", 1921 "of="+file.diskFile.Name(), 1922 "if="+LocalBucketPath+"/od/"+file.fileNameWithoutPath, 1923 "parts=10", 1924 ) 1925 1926 fatalIfError(err, t) 1927 fmt.Println(out) 1928 odMsg, err = parseSingleODMessageJSONOutput(out) 1929 fatalIfError(err, t) 1930 1931 if odMsg.TotalSize != file.diskStat.Size() { 1932 t.Fatalf( 1933 "Expected (%d) bytes to be uploaded but only uploaded (%d) bytes", 1934 odMsg.TotalSize, 1935 file.diskStat.Size(), 1936 ) 1937 } 1938 1939 if odMsg.Parts != 10 { 1940 t.Fatalf( 1941 "Expected upload parts to be (10) but they were (%d)", 1942 odMsg.Parts, 1943 ) 1944 } 1945 1946 if odMsg.Type != "S3toFS" { 1947 t.Fatalf( 1948 "Expected type to be (FStoS3) but got (%s)", 1949 odMsg.Type, 1950 ) 1951 } 1952 1953 if odMsg.PartSize != uint64(file.diskStat.Size())/10 { 1954 t.Fatalf( 1955 "Expected part size to be (%d) but got (%d)", 1956 file.diskStat.Size()/10, 1957 odMsg.PartSize, 1958 ) 1959 } 1960 } 1961 1962 func MvFromDiskToMinio(t *testing.T) { 1963 LocalBucketPath := CreateBucket(t) 1964 1965 file := createFile(newTestFile{ 1966 addToGlobalFileMap: false, 1967 tag: "10Move", 1968 prefix: "", 1969 extension: ".txt", 1970 storageClass: "", 1971 sizeInMBS: 1, 1972 metaData: map[string]string{"name": "10Move"}, 1973 tags: map[string]string{"tag1": "10Move-tag"}, 1974 }) 1975 1976 out, err := RunMC( 1977 "mv", 1978 file.diskFile.Name(), 1979 LocalBucketPath+"/"+file.fileNameWithoutPath, 1980 ) 1981 1982 fatalIfError(err, t) 1983 splitReturn := bytes.Split([]byte(out), []byte{10}) 1984 1985 mvMSG, err := parseSingleCPMessageJSONOutput(string(splitReturn[0])) 1986 fatalIfError(err, t) 1987 1988 if mvMSG.TotalCount != 1 { 1989 t.Fatalf("Expected count to be 1 but got (%d)", mvMSG.TotalCount) 1990 } 1991 1992 if mvMSG.Size != file.diskStat.Size() { 1993 t.Fatalf( 1994 "Expected size to be (%d) but got (%d)", 1995 file.diskStat.Size(), 1996 mvMSG.Size, 1997 ) 1998 } 1999 2000 if mvMSG.Status != "success" { 2001 t.Fatalf( 2002 "Expected status to be (success) but got (%s)", 2003 mvMSG.Status, 2004 ) 2005 } 2006 2007 statMSG, err := parseSingleAccountStatJSONOutput(string(splitReturn[1])) 2008 fatalIfError(err, t) 2009 2010 if statMSG.Transferred != file.diskStat.Size() { 2011 t.Fatalf( 2012 "Expected transfeered to be (%d) but got (%d)", 2013 file.diskStat.Size(), 2014 statMSG.Transferred, 2015 ) 2016 } 2017 2018 if statMSG.Total != file.diskStat.Size() { 2019 t.Fatalf( 2020 "Expected total to be (%d) but got (%d)", 2021 file.diskStat.Size(), 2022 statMSG.Total, 2023 ) 2024 } 2025 2026 if statMSG.Status != "success" { 2027 t.Fatalf( 2028 "Expected status to be (success) but got (%s)", 2029 statMSG.Status, 2030 ) 2031 } 2032 } 2033 2034 func DUBucket(t *testing.T) { 2035 var totalFileSize int64 2036 for _, v := range fileMap { 2037 totalFileSize += v.MinioStat.Size 2038 } 2039 2040 out, err := RunMC("du", mainTestBucket) 2041 fatalIfError(err, t) 2042 2043 duList, err := parseDUJSONOutput(out) 2044 fatalIfError(err, t) 2045 if len(duList) != 1 { 2046 fatalMsgOnly("Expected 1 result to be returned", t) 2047 } 2048 if duList[0].Size != totalFileSize { 2049 fatalMsgOnly( 2050 fmt.Sprintf("total size to be %d but got %d", totalFileSize, duList[0].Size), 2051 t, 2052 ) 2053 } 2054 } 2055 2056 func LSObjects(t *testing.T) { 2057 out, err := RunMC("ls", "-r", mainTestBucket) 2058 fatalIfError(err, t) 2059 2060 fileList, err := parseLSJSONOutput(out) 2061 fatalIfError(err, t) 2062 2063 for i, f := range fileMap { 2064 fileFound := false 2065 2066 for _, o := range fileList { 2067 if o.Key == f.fileNameWithPrefix { 2068 fileMap[i].MinioLS = o 2069 fileFound = true 2070 } 2071 } 2072 2073 if !fileFound { 2074 t.Fatalf("File was not uploaded: %s", f.fileNameWithPrefix) 2075 } 2076 } 2077 } 2078 2079 func StatObjects(t *testing.T) { 2080 for i, v := range fileMap { 2081 2082 out, err := RunMC( 2083 "stat", 2084 mainTestBucket+"/"+v.fileNameWithPrefix, 2085 ) 2086 fatalIfError(err, t) 2087 2088 fileMap[i].MinioStat, err = parseStatSingleObjectJSONOutput(out) 2089 fatalIfError(err, t) 2090 2091 if fileMap[i].MinioStat.Key == "" { 2092 t.Fatalf("Unable to stat Minio object (%s)", v.fileNameWithPrefix) 2093 } 2094 2095 } 2096 } 2097 2098 func ValidateFileMetaData(t *testing.T) { 2099 for _, f := range fileMap { 2100 validateFileLSInfo(t, f) 2101 validateObjectMetaData(t, f) 2102 // validateContentType(t, f) 2103 } 2104 } 2105 2106 func FindObjects(t *testing.T) { 2107 out, err := RunMC("find", mainTestBucket) 2108 fatalIfError(err, t) 2109 2110 findList, err := parseFindJSONOutput(out) 2111 fatalIfError(err, t) 2112 2113 for _, v := range fileMap { 2114 2115 found := false 2116 for _, vv := range findList { 2117 if strings.HasSuffix(vv.Key, v.MinioLS.Key) { 2118 found = true 2119 } 2120 } 2121 2122 if !found { 2123 t.Fatalf("File (%s) not found by 'find' command", v.MinioLS.Key) 2124 } 2125 } 2126 } 2127 2128 func FindObjectsUsingName(t *testing.T) { 2129 for _, v := range fileMap { 2130 2131 out, err := RunMC( 2132 "find", 2133 mainTestBucket, 2134 "--name", 2135 v.fileNameWithoutPath, 2136 ) 2137 2138 fatalIfError(err, t) 2139 info, err := parseFindSingleObjectJSONOutput(out) 2140 fatalIfError(err, t) 2141 if !strings.HasSuffix(info.Key, v.MinioLS.Key) { 2142 t.Fatalf("Invalid key (%s) when searching for (%s)", info.Key, v.MinioLS.Key) 2143 } 2144 2145 } 2146 } 2147 2148 func FindObjectsUsingNameAndFilteringForTxtType(t *testing.T) { 2149 out, err := RunMC( 2150 "find", 2151 mainTestBucket, 2152 "--name", 2153 "*.txt", 2154 ) 2155 fatalIfError(err, t) 2156 2157 findList, err := parseFindJSONOutput(out) 2158 fatalIfError(err, t) 2159 2160 for _, v := range fileMap { 2161 if v.extension != ".txt" { 2162 continue 2163 } 2164 2165 found := false 2166 for _, vv := range findList { 2167 if strings.HasSuffix(vv.Key, v.MinioLS.Key) { 2168 found = true 2169 } 2170 } 2171 2172 if !found { 2173 t.Fatalf("File (%s) not found by 'find' command", v.MinioLS.Key) 2174 } 2175 } 2176 } 2177 2178 func FindObjectsSmallerThan64Mebibytes(t *testing.T) { 2179 out, err := RunMC( 2180 "find", 2181 mainTestBucket, 2182 "--smaller", 2183 "64MB", 2184 ) 2185 fatalIfError(err, t) 2186 2187 findList, err := parseFindJSONOutput(out) 2188 fatalIfError(err, t) 2189 2190 for _, v := range fileMap { 2191 if v.diskStat.Size() > GetMBSizeInBytes(64) { 2192 continue 2193 } 2194 2195 found := false 2196 for _, vv := range findList { 2197 if strings.HasSuffix(vv.Key, v.MinioLS.Key) { 2198 found = true 2199 } 2200 } 2201 2202 if !found { 2203 t.Fatalf("File (%s) not found by 'find' command", v.MinioLS.Key) 2204 } 2205 } 2206 } 2207 2208 func FindObjectsLargerThan64Mebibytes(t *testing.T) { 2209 out, err := RunMC( 2210 "find", 2211 mainTestBucket, 2212 "--larger", 2213 "64MB", 2214 ) 2215 fatalIfError(err, t) 2216 2217 findList, err := parseFindJSONOutput(out) 2218 fatalIfError(err, t) 2219 2220 for _, v := range fileMap { 2221 if v.diskStat.Size() < GetMBSizeInBytes(64) { 2222 continue 2223 } 2224 2225 found := false 2226 for _, vv := range findList { 2227 if strings.HasSuffix(vv.Key, v.MinioLS.Key) { 2228 found = true 2229 } 2230 } 2231 2232 if !found { 2233 t.Fatalf("File (%s) not found by 'find' command", v.MinioLS.Key) 2234 } 2235 } 2236 } 2237 2238 func FindObjectsOlderThan1d(t *testing.T) { 2239 out, err := RunMC( 2240 "find", 2241 mainTestBucket, 2242 "--older-than", 2243 "1d", 2244 ) 2245 fatalIfError(err, t) 2246 2247 findList, err := parseFindJSONOutput(out) 2248 fatalIfError(err, t) 2249 2250 if len(findList) > 0 { 2251 t.Fatalf("We should not have found any files which are older then 1 day") 2252 } 2253 } 2254 2255 func FindObjectsNewerThen1d(t *testing.T) { 2256 out, err := RunMC( 2257 "find", 2258 mainTestBucket, 2259 "--newer-than", 2260 "1d", 2261 ) 2262 fatalIfError(err, t) 2263 2264 findList, err := parseFindJSONOutput(out) 2265 fatalIfError(err, t) 2266 2267 for _, v := range fileMap { 2268 2269 found := false 2270 for _, vv := range findList { 2271 if strings.HasSuffix(vv.Key, v.MinioLS.Key) { 2272 found = true 2273 } 2274 } 2275 2276 if !found { 2277 t.Fatalf("File (%s) not found by 'find' command", v.MinioLS.Key) 2278 } 2279 } 2280 } 2281 2282 func GetObjectsAndCompareMD5(t *testing.T) { 2283 for _, v := range fileMap { 2284 2285 // make sure old downloads are not in our way 2286 _ = os.Remove(tempDir + "/" + v.fileNameWithoutPath + ".downloaded") 2287 2288 _, err := RunMC( 2289 "cp", 2290 mainTestBucket+"/"+v.fileNameWithPrefix, 2291 tempDir+"/"+v.fileNameWithoutPath+".downloaded", 2292 ) 2293 fatalIfError(err, t) 2294 2295 downloadedFile, err := os.Open( 2296 tempDir + "/" + v.fileNameWithoutPath + ".downloaded", 2297 ) 2298 fatalIfError(err, t) 2299 2300 fileBytes, err := io.ReadAll(downloadedFile) 2301 fatalIfError(err, t) 2302 md5sum := GetMD5Sum(fileBytes) 2303 2304 if v.md5Sum != md5sum { 2305 t.Fatalf( 2306 "The downloaded file md5sum is wrong: original-md5(%s) downloaded-md5(%s)", 2307 v.md5Sum, 2308 md5sum, 2309 ) 2310 } 2311 } 2312 } 2313 2314 func CreateBucketUsingInvalidSymbols(t *testing.T) { 2315 bucketNameMap := make(map[string]string) 2316 bucketNameMap["name-too-big"] = randomLargeString 2317 bucketNameMap["!"] = "symbol!" 2318 bucketNameMap["@"] = "symbol@" 2319 bucketNameMap["#"] = "symbol#" 2320 bucketNameMap["$"] = "symbol$" 2321 bucketNameMap["%"] = "symbol%" 2322 bucketNameMap["^"] = "symbol^" 2323 bucketNameMap["&"] = "symbol&" 2324 bucketNameMap["*"] = "symbol*" 2325 bucketNameMap["("] = "symbol(" 2326 bucketNameMap[")"] = "symbol)" 2327 bucketNameMap["{"] = "symbol{" 2328 bucketNameMap["}"] = "symbol}" 2329 bucketNameMap["["] = "symbol[" 2330 bucketNameMap["]"] = "symbol]" 2331 2332 for _, v := range bucketNameMap { 2333 _, err := RunMC("mb", defaultAlias+"/"+v) 2334 if err == nil { 2335 t.Fatalf("We should not have been able to create a bucket with the name: %s", v) 2336 } 2337 } 2338 } 2339 2340 func RemoveBucketThatDoesNotExist(t *testing.T) { 2341 randomID := uuid.NewString() 2342 out, _ := RunMC( 2343 "rb", 2344 defaultAlias+"/"+randomID, 2345 ) 2346 errMSG, _ := parseSingleErrorMessageJSONOutput(out) 2347 validateErrorMSGValues( 2348 t, 2349 errMSG, 2350 "error", 2351 "Unable to validate", 2352 "does not exist", 2353 ) 2354 } 2355 2356 func RemoveBucketWithNameTooLong(t *testing.T) { 2357 randomID := uuid.NewString() 2358 out, _ := RunMC( 2359 "rb", 2360 defaultAlias+"/"+randomID+randomID, 2361 ) 2362 errMSG, _ := parseSingleErrorMessageJSONOutput(out) 2363 validateErrorMSGValues( 2364 t, 2365 errMSG, 2366 "error", 2367 "Unable to validate", 2368 "Bucket name cannot be longer than 63 characters", 2369 ) 2370 } 2371 2372 func UploadToUnknownBucket(t *testing.T) { 2373 randomBucketID := uuid.NewString() 2374 parameters := append( 2375 []string{}, 2376 "cp", 2377 fileMap["1M"].diskFile.Name(), 2378 defaultAlias+"/"+randomBucketID+"-test-should-not-exist"+"/"+fileMap["1M"].fileNameWithoutPath, 2379 ) 2380 2381 _, err := RunMC(parameters...) 2382 if err == nil { 2383 t.Fatalf("We should not have been able to upload to bucket: %s", randomBucketID) 2384 } 2385 } 2386 2387 func preRunCleanup() { 2388 for i := range tmpNameMap { 2389 _, _ = RunMC("rb", "--force", "--dangerous", defaultAlias+"/test-"+i) 2390 } 2391 } 2392 2393 func postRunCleanup(t *testing.T) { 2394 var err error 2395 var berr error 2396 var out string 2397 2398 err = os.RemoveAll(tempDir) 2399 if err != nil { 2400 fmt.Println(err) 2401 } 2402 2403 for _, v := range bucketList { 2404 out, berr = RunMC("rb", "--force", "--dangerous", v) 2405 if berr != nil { 2406 fmt.Printf("Unable to remove bucket (%s) err: %s // out: %s", v, berr, out) 2407 } 2408 } 2409 2410 for _, v := range userList { 2411 _, _ = RunMC( 2412 "admin", 2413 "user", 2414 "remove", 2415 defaultAlias, 2416 v.Username, 2417 ) 2418 } 2419 2420 fatalIfError(berr, t) 2421 fatalIfError(err, t) 2422 } 2423 2424 func validateFileLSInfo(t *testing.T, file *testFile) { 2425 if file.diskStat.Size() != int64(file.MinioLS.Size) { 2426 t.Fatalf( 2427 "File and minio object are not the same size - Object (%d) vs File (%d)", 2428 file.MinioLS.Size, 2429 file.diskStat.Size(), 2430 ) 2431 } 2432 // if file.md5Sum != file.findOutput.Etag { 2433 // t.Fatalf("File and file.findOutput do not have the same md5Sum - Object (%s) vs File (%s)", file.findOutput.Etag, file.md5Sum) 2434 // } 2435 if file.storageClass != "" { 2436 if file.storageClass != file.MinioLS.StorageClass { 2437 t.Fatalf( 2438 "File and minio object do not have the same storage class - Object (%s) vs File (%s)", 2439 file.MinioLS.StorageClass, 2440 file.storageClass, 2441 ) 2442 } 2443 } else { 2444 if file.MinioLS.StorageClass != "STANDARD" { 2445 t.Fatalf( 2446 "Minio object was expected to have storage class (STANDARD) but it was (%s)", 2447 file.MinioLS.StorageClass, 2448 ) 2449 } 2450 } 2451 } 2452 2453 func validateObjectMetaData(t *testing.T, file *testFile) { 2454 for i, v := range file.metaData { 2455 found := false 2456 2457 for ii, vv := range file.MinioStat.Metadata { 2458 if metaPrefix+strings.Title(i) == ii { 2459 found = true 2460 if v != vv { 2461 fmt.Println("------------------------") 2462 fmt.Println("META CHECK") 2463 fmt.Println(file.MinioStat.Metadata) 2464 fmt.Println(file.metaData) 2465 fmt.Println("------------------------") 2466 t.Fatalf("Meta values are not the same v1(%s) v2(%s)", v, vv) 2467 } 2468 } 2469 } 2470 2471 if !found { 2472 fmt.Println("------------------------") 2473 fmt.Println("META CHECK") 2474 fmt.Println(file.MinioStat.Metadata) 2475 fmt.Println(file.metaData) 2476 fmt.Println("------------------------") 2477 t.Fatalf("Meta tag(%s) not found", i) 2478 } 2479 2480 } 2481 } 2482 2483 // func validateContentType(t *testing.T, file *testFile) { 2484 // value, ok := file.MinioStat.Metadata["Content-Type"] 2485 // if !ok { 2486 // t.Fatalf("File (%s) did not have a content type", file.fileNameWithPrefix) 2487 // return 2488 // } 2489 // 2490 // contentType := mime.TypeByExtension(file.extension) 2491 // if contentType != value { 2492 // log.Println(file) 2493 // log.Println(file.MinioLS) 2494 // log.Println(file.extension) 2495 // log.Println(file.MinioStat) 2496 // t.Fatalf("Content types on file (%s) do not match, extension(%s) File(%s) MinIO object(%s)", file.fileNameWithPrefix, file.extension, contentType, file.MinioStat.Metadata["Content-Type"]) 2497 // } 2498 // } 2499 2500 func GetSource(skip int) (out string) { 2501 pc := make([]uintptr, 3) // at least 1 entry needed 2502 runtime.Callers(skip, pc) 2503 f := runtime.FuncForPC(pc[0]) 2504 file, line := f.FileLine(pc[0]) 2505 sn := strings.Split(f.Name(), ".") 2506 var name string 2507 if sn[len(sn)-1] == "func1" { 2508 name = sn[len(sn)-2] 2509 } else { 2510 name = sn[len(sn)-1] 2511 } 2512 out = file + ":" + fmt.Sprint(line) + ":" + name 2513 return 2514 } 2515 2516 func GetMD5Sum(data []byte) string { 2517 md5Writer := md5.New() 2518 md5Writer.Write(data) 2519 return fmt.Sprintf("%x", md5Writer.Sum(nil)) 2520 } 2521 2522 func curlFatalIfNoErrorTag(msg string, t *testing.T) { 2523 if !strings.Contains(msg, "<Error>") { 2524 fmt.Println(failIndicator) 2525 fmt.Println(msg) 2526 t.Fatal(msg) 2527 } 2528 } 2529 2530 func fatalMsgOnly(msg string, t *testing.T) { 2531 fmt.Println(failIndicator) 2532 t.Fatal(msg) 2533 } 2534 2535 func fatalIfNoErrorWMsg(err error, msg string, t *testing.T) { 2536 if err == nil { 2537 fmt.Println(failIndicator) 2538 fmt.Println(msg) 2539 t.Fatal(err) 2540 } 2541 } 2542 2543 func fatalIfErrorWMsg(err error, msg string, t *testing.T) { 2544 if err != nil { 2545 fmt.Println(failIndicator) 2546 fmt.Println(msg) 2547 t.Fatal(err) 2548 } 2549 } 2550 2551 func fatalIfError(err error, t *testing.T) { 2552 if err != nil { 2553 fmt.Println(failIndicator) 2554 t.Fatal(err) 2555 } 2556 } 2557 2558 func parseFindJSONOutput(out string) (findList []*findMessage, err error) { 2559 findList = make([]*findMessage, 0) 2560 splitList := bytes.Split([]byte(out), []byte{10}) 2561 2562 for _, v := range splitList { 2563 if len(v) < 1 { 2564 continue 2565 } 2566 line := new(findMessage) 2567 err = json.Unmarshal(v, line) 2568 if err != nil { 2569 return 2570 } 2571 findList = append(findList, line) 2572 } 2573 2574 if printRawOut { 2575 fmt.Println("FIND LIST ------------------------------") 2576 for _, v := range findList { 2577 fmt.Println(v) 2578 } 2579 fmt.Println(" ------------------------------") 2580 } 2581 return 2582 } 2583 2584 func parseDUJSONOutput(out string) (duList []duMessage, err error) { 2585 duList = make([]duMessage, 0) 2586 splitList := bytes.Split([]byte(out), []byte{10}) 2587 2588 for _, v := range splitList { 2589 if len(v) < 1 { 2590 continue 2591 } 2592 line := duMessage{} 2593 err = json.Unmarshal(v, &line) 2594 if err != nil { 2595 return 2596 } 2597 duList = append(duList, line) 2598 } 2599 2600 if printRawOut { 2601 fmt.Println("DU LIST ------------------------------") 2602 for _, v := range duList { 2603 fmt.Println(v) 2604 } 2605 fmt.Println(" ------------------------------") 2606 } 2607 return 2608 } 2609 2610 func parseLSJSONOutput(out string) (lsList []contentMessage, err error) { 2611 lsList = make([]contentMessage, 0) 2612 splitList := bytes.Split([]byte(out), []byte{10}) 2613 2614 for _, v := range splitList { 2615 if len(v) < 1 { 2616 continue 2617 } 2618 line := contentMessage{} 2619 err = json.Unmarshal(v, &line) 2620 if err != nil { 2621 return 2622 } 2623 lsList = append(lsList, line) 2624 } 2625 2626 if printRawOut { 2627 fmt.Println("LS LIST ------------------------------") 2628 for _, v := range lsList { 2629 fmt.Println(v) 2630 } 2631 fmt.Println(" ------------------------------") 2632 } 2633 return 2634 } 2635 2636 func parseFindSingleObjectJSONOutput(out string) (findInfo contentMessage, err error) { 2637 err = json.Unmarshal([]byte(out), &findInfo) 2638 if err != nil { 2639 return 2640 } 2641 2642 if printRawOut { 2643 fmt.Println("FIND SINGLE OBJECT ------------------------------") 2644 fmt.Println(findInfo) 2645 fmt.Println(" ------------------------------") 2646 } 2647 return 2648 } 2649 2650 func parseStatSingleObjectJSONOutput(out string) (stat statMessage, err error) { 2651 err = json.Unmarshal([]byte(out), &stat) 2652 if err != nil { 2653 return 2654 } 2655 2656 if printRawOut { 2657 fmt.Println("STAT ------------------------------") 2658 fmt.Println(stat) 2659 fmt.Println(" ------------------------------") 2660 } 2661 return 2662 } 2663 2664 // We have to wrap the error output because the console 2665 // printing mechanism for json marshals into an anonymous 2666 // object before printing, see cmd/error.go line 70 2667 type errorMessageWrapper struct { 2668 Error errorMessage `json:"error"` 2669 Status string `json:"status"` 2670 } 2671 2672 func validateErrorMSGValues( 2673 t *testing.T, 2674 errMSG errorMessageWrapper, 2675 TypeToValidate string, 2676 MessageToValidate string, 2677 CauseToValidate string, 2678 ) { 2679 if TypeToValidate != "" { 2680 if !strings.Contains(errMSG.Error.Type, TypeToValidate) { 2681 t.Fatalf( 2682 "Expected error.Error.Type to contain (%s) - but got (%s)", 2683 TypeToValidate, 2684 errMSG.Error.Type, 2685 ) 2686 } 2687 } 2688 if MessageToValidate != "" { 2689 if !strings.Contains(errMSG.Error.Message, MessageToValidate) { 2690 t.Fatalf( 2691 "Expected error.Error.Message to contain (%s) - but got (%s)", 2692 MessageToValidate, 2693 errMSG.Error.Message, 2694 ) 2695 } 2696 } 2697 if CauseToValidate != "" { 2698 if !strings.Contains(errMSG.Error.Cause.Message, CauseToValidate) { 2699 t.Fatalf( 2700 "Expected error.Error.Cause.Message to contain (%s) - but got (%s)", 2701 CauseToValidate, 2702 errMSG.Error.Cause.Message, 2703 ) 2704 } 2705 } 2706 } 2707 2708 func parseUserMessageListOutput(out string) (users []*userMessage, err error) { 2709 users = make([]*userMessage, 0) 2710 splitList := bytes.Split([]byte(out), []byte{10}) 2711 for _, v := range splitList { 2712 if len(v) < 1 { 2713 continue 2714 } 2715 msg := new(userMessage) 2716 err = json.Unmarshal(v, msg) 2717 if err != nil { 2718 return 2719 } 2720 users = append(users, msg) 2721 } 2722 2723 if printRawOut { 2724 fmt.Println("USER LIST ------------------------------") 2725 for _, v := range users { 2726 fmt.Println(v) 2727 } 2728 fmt.Println(" ------------------------------") 2729 } 2730 2731 return 2732 } 2733 2734 func parseShareMessageFromJSONOutput(out string) (share *shareMessage, err error) { 2735 share = new(shareMessage) 2736 err = json.Unmarshal([]byte(out), share) 2737 return 2738 } 2739 2740 func parseSingleErrorMessageJSONOutput(out string) (errMSG errorMessageWrapper, err error) { 2741 err = json.Unmarshal([]byte(out), &errMSG) 2742 if err != nil { 2743 return 2744 } 2745 2746 fmt.Println("ERROR ------------------------------") 2747 fmt.Println(errMSG) 2748 fmt.Println(" ------------------------------") 2749 return 2750 } 2751 2752 func parseSingleODMessageJSONOutput(out string) (odMSG odMessage, err error) { 2753 err = json.Unmarshal([]byte(out), &odMSG) 2754 if err != nil { 2755 return 2756 } 2757 2758 return 2759 } 2760 2761 func parseSingleAccountStatJSONOutput(out string) (stat accountStat, err error) { 2762 err = json.Unmarshal([]byte(out), &stat) 2763 if err != nil { 2764 return 2765 } 2766 2767 return 2768 } 2769 2770 func parseSingleCPMessageJSONOutput(out string) (cpMSG copyMessage, err error) { 2771 err = json.Unmarshal([]byte(out), &cpMSG) 2772 if err != nil { 2773 return 2774 } 2775 2776 return 2777 } 2778 2779 type newTestFile struct { 2780 tag string // The tag used to identify the file inside the FileMap. This tag is also used in the objects name. 2781 prefix string // Prefix for the object name ( not including the object name itself) 2782 extension string 2783 storageClass string 2784 sizeInMBS int 2785 // uploadShouldFail bool 2786 metaData map[string]string 2787 tags map[string]string 2788 2789 addToGlobalFileMap bool 2790 // sub directory path to place the file in 2791 // tempDir+/+subDir 2792 subDir string 2793 } 2794 2795 type testFile struct { 2796 newTestFile 2797 2798 // File on disk 2799 diskFile *os.File 2800 // File info on disk 2801 diskStat os.FileInfo 2802 // md5sum at the time of creation 2803 md5Sum string 2804 // File name without full path 2805 fileNameWithoutPath string 2806 // File name with assigned prefix 2807 fileNameWithPrefix string 2808 2809 // These field are not automatically populated unless 2810 // the file is created at the initialization phase of 2811 // the test suite: testsThatDependOnOneAnother() 2812 // Minio mc stat output 2813 MinioStat statMessage 2814 // Minio mc ls output 2815 MinioLS contentMessage 2816 } 2817 2818 func (f *testFile) String() (out string) { 2819 out = fmt.Sprintf( 2820 "Size: %d || Name: %s || md5Sum: %s", 2821 f.diskStat.Size(), 2822 f.fileNameWithoutPath, 2823 f.md5Sum, 2824 ) 2825 return 2826 } 2827 2828 func createFile(nf newTestFile) (newTestFile *testFile) { 2829 var newFile *os.File 2830 var err error 2831 if nf.subDir != "" { 2832 2833 err = os.MkdirAll( 2834 tempDir+string(os.PathSeparator)+nf.subDir, 2835 0o755) 2836 if err != nil { 2837 log.Println("Could not make additional dir:", err) 2838 os.Exit(1) 2839 } 2840 2841 newFile, err = os.CreateTemp( 2842 tempDir+string(os.PathSeparator)+nf.subDir, 2843 nf.tag+"-*"+nf.extension, 2844 ) 2845 2846 } else { 2847 newFile, err = os.CreateTemp(tempDir, nf.tag+"-*"+nf.extension) 2848 } 2849 2850 if err != nil { 2851 log.Println("Could not make file:", err) 2852 os.Exit(1) 2853 } 2854 2855 md5Writer := md5.New() 2856 for i := 0; i < nf.sizeInMBS; i++ { 2857 n, err := newFile.Write(oneMBSlice[:]) 2858 mn, merr := md5Writer.Write(oneMBSlice[:]) 2859 if err != nil || merr != nil { 2860 log.Println(err) 2861 log.Println(merr) 2862 return nil 2863 } 2864 if n != len(oneMBSlice) { 2865 log.Println("Did not write 1MB to file") 2866 return nil 2867 } 2868 if mn != len(oneMBSlice) { 2869 log.Println("Did not write 1MB to md5sum writer") 2870 return nil 2871 } 2872 } 2873 splitName := strings.Split(newFile.Name(), string(os.PathSeparator)) 2874 fileNameWithoutPath := splitName[len(splitName)-1] 2875 md5sum := fmt.Sprintf("%x", md5Writer.Sum(nil)) 2876 stats, err := newFile.Stat() 2877 if err != nil { 2878 return nil 2879 } 2880 newTestFile = &testFile{ 2881 md5Sum: md5sum, 2882 fileNameWithoutPath: fileNameWithoutPath, 2883 diskFile: newFile, 2884 diskStat: stats, 2885 } 2886 2887 newTestFile.tag = nf.tag 2888 newTestFile.metaData = nf.metaData 2889 newTestFile.storageClass = nf.storageClass 2890 newTestFile.sizeInMBS = nf.sizeInMBS 2891 newTestFile.tags = nf.tags 2892 newTestFile.prefix = nf.prefix 2893 newTestFile.extension = nf.extension 2894 2895 if nf.prefix != "" { 2896 newTestFile.fileNameWithPrefix = nf.prefix + "/" + fileNameWithoutPath 2897 } else { 2898 newTestFile.fileNameWithPrefix = fileNameWithoutPath 2899 } 2900 if nf.addToGlobalFileMap { 2901 fileMap[nf.tag] = newTestFile 2902 } 2903 return newTestFile 2904 } 2905 2906 func BuildCLI() error { 2907 wd, _ := os.Getwd() 2908 fmt.Println("WORKING DIR:", wd) 2909 fmt.Println("go build -o", mcCmd, buildPath) 2910 os.Remove(mcCmd) 2911 out, err := exec.Command("go", "build", "-o", mcCmd, buildPath).CombinedOutput() 2912 if err != nil { 2913 log.Println("BUILD OUT:", out) 2914 log.Println(err) 2915 panic(err) 2916 } 2917 err = os.Chmod(mcCmd, 0o777) 2918 if err != nil { 2919 panic(err) 2920 } 2921 return nil 2922 } 2923 2924 func RunMC(parameters ...string) (out string, err error) { 2925 var outBytes []byte 2926 var outErr error 2927 2928 fmt.Println("") 2929 fmt.Println(time.Now().Format("2006-01-02T15:04:05.000"), "||", GetSource(3)) 2930 fmt.Println(mcCmd, strings.Join(preCmdParameters, " "), strings.Join(parameters, " ")) 2931 2932 outBytes, outErr = exec.Command(mcCmd, append(preCmdParameters, parameters...)...).CombinedOutput() 2933 if printRawOut { 2934 fmt.Println(string(outBytes)) 2935 } 2936 out = string(outBytes) 2937 err = outErr 2938 return 2939 } 2940 2941 func RunCommand(cmd string, parameters ...string) (out string, err error) { 2942 fmt.Println("") 2943 fmt.Println(time.Now().Format("2006-01-02T15:04:05.000"), "||", GetSource(3)) 2944 fmt.Println(cmd, strings.Join(parameters, " ")) 2945 var outBytes []byte 2946 var outErr error 2947 2948 outBytes, outErr = exec.Command(cmd, parameters...).CombinedOutput() 2949 if printRawOut { 2950 fmt.Println(string(outBytes)) 2951 } 2952 out = string(outBytes) 2953 err = outErr 2954 return 2955 }