1/* 2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage 3 * Copyright 2017-2020 MinIO, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package minio 19 20import ( 21 "bytes" 22 "context" 23 "log" 24 "net/http" 25 "os" 26 "strconv" 27 "testing" 28 "time" 29 30 "math/rand" 31 32 "github.com/minio/minio-go/v7/pkg/credentials" 33) 34 35const ( 36 serverEndpoint = "SERVER_ENDPOINT" 37 accessKey = "ACCESS_KEY" 38 secretKey = "SECRET_KEY" 39 enableSecurity = "ENABLE_HTTPS" 40) 41 42// Tests for Core GetObject() function. 43func TestGetObjectCore(t *testing.T) { 44 if testing.Short() { 45 t.Skip("skipping functional tests for the short runs") 46 } 47 48 // Seed random based on current time. 49 rand.Seed(time.Now().Unix()) 50 51 // Instantiate new minio core client object. 52 c, err := NewCore( 53 os.Getenv(serverEndpoint), 54 &Options{ 55 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""), 56 Secure: mustParseBool(os.Getenv(enableSecurity)), 57 }) 58 if err != nil { 59 t.Fatal("Error:", err) 60 } 61 62 // Enable tracing, write to stderr. 63 // c.TraceOn(os.Stderr) 64 65 // Set user agent. 66 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0") 67 68 // Generate a new random bucket name. 69 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test") 70 71 // Make a new bucket. 72 err = c.MakeBucket(context.Background(), bucketName, MakeBucketOptions{Region: "us-east-1"}) 73 if err != nil { 74 t.Fatal("Error:", err, bucketName) 75 } 76 77 // Generate data more than 32K 78 buf := bytes.Repeat([]byte("3"), rand.Intn(1<<20)+32*1024) 79 80 // Save the data 81 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") 82 _, err = c.Client.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), PutObjectOptions{ 83 ContentType: "binary/octet-stream", 84 }) 85 if err != nil { 86 t.Fatal("Error:", err, bucketName, objectName) 87 } 88 89 st, err := c.Client.StatObject(context.Background(), bucketName, objectName, StatObjectOptions{}) 90 if err != nil { 91 t.Fatal("Stat error:", err, bucketName, objectName) 92 } 93 if st.Size != int64(len(buf)) { 94 t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size) 95 } 96 97 offset := int64(2048) 98 99 // read directly 100 buf1 := make([]byte, 512) 101 buf2 := make([]byte, 512) 102 buf3 := make([]byte, st.Size) 103 buf4 := make([]byte, 1) 104 105 opts := GetObjectOptions{} 106 opts.SetRange(offset, offset+int64(len(buf1))-1) 107 reader, objectInfo, _, err := c.GetObject(context.Background(), bucketName, objectName, opts) 108 if err != nil { 109 t.Fatal(err) 110 } 111 m, err := readFull(reader, buf1) 112 reader.Close() 113 if err != nil { 114 t.Fatal(err) 115 } 116 117 if objectInfo.Size != int64(m) { 118 t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m) 119 } 120 if !bytes.Equal(buf1, buf[offset:offset+512]) { 121 t.Fatal("Error: Incorrect read between two GetObject from same offset.") 122 } 123 offset += 512 124 125 opts.SetRange(offset, offset+int64(len(buf2))-1) 126 reader, objectInfo, _, err = c.GetObject(context.Background(), bucketName, objectName, opts) 127 if err != nil { 128 t.Fatal(err) 129 } 130 131 m, err = readFull(reader, buf2) 132 reader.Close() 133 if err != nil { 134 t.Fatal(err) 135 } 136 137 if objectInfo.Size != int64(m) { 138 t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m) 139 } 140 if !bytes.Equal(buf2, buf[offset:offset+512]) { 141 t.Fatal("Error: Incorrect read between two GetObject from same offset.") 142 } 143 144 opts.SetRange(0, int64(len(buf3))) 145 reader, objectInfo, _, err = c.GetObject(context.Background(), bucketName, objectName, opts) 146 if err != nil { 147 t.Fatal(err) 148 } 149 150 m, err = readFull(reader, buf3) 151 if err != nil { 152 reader.Close() 153 t.Fatal(err) 154 } 155 reader.Close() 156 157 if objectInfo.Size != int64(m) { 158 t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m) 159 } 160 if !bytes.Equal(buf3, buf) { 161 t.Fatal("Error: Incorrect data read in GetObject, than what was previously upoaded.") 162 } 163 164 opts = GetObjectOptions{} 165 opts.SetMatchETag("etag") 166 _, _, _, err = c.GetObject(context.Background(), bucketName, objectName, opts) 167 if err == nil { 168 t.Fatal("Unexpected GetObject should fail with mismatching etags") 169 } 170 if errResp := ToErrorResponse(err); errResp.Code != "PreconditionFailed" { 171 t.Fatalf("Expected \"PreconditionFailed\" as code, got %s instead", errResp.Code) 172 } 173 174 opts = GetObjectOptions{} 175 opts.SetMatchETagExcept("etag") 176 reader, objectInfo, _, err = c.GetObject(context.Background(), bucketName, objectName, opts) 177 if err != nil { 178 t.Fatal(err) 179 } 180 181 m, err = readFull(reader, buf3) 182 reader.Close() 183 if err != nil { 184 t.Fatal(err) 185 } 186 187 if objectInfo.Size != int64(m) { 188 t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m) 189 } 190 if !bytes.Equal(buf3, buf) { 191 t.Fatal("Error: Incorrect data read in GetObject, than what was previously upoaded.") 192 } 193 194 opts = GetObjectOptions{} 195 opts.SetRange(0, 0) 196 reader, objectInfo, _, err = c.GetObject(context.Background(), bucketName, objectName, opts) 197 if err != nil { 198 t.Fatal(err) 199 } 200 201 m, err = readFull(reader, buf4) 202 reader.Close() 203 if err != nil { 204 t.Fatal(err) 205 } 206 207 if objectInfo.Size != int64(m) { 208 t.Fatalf("Error: GetObject read shorter bytes before reaching EOF, want %v, got %v\n", objectInfo.Size, m) 209 } 210 211 opts = GetObjectOptions{} 212 opts.SetRange(offset, offset+int64(len(buf2))-1) 213 contentLength := len(buf2) 214 var header http.Header 215 _, _, header, err = c.GetObject(context.Background(), bucketName, objectName, opts) 216 if err != nil { 217 t.Fatal(err) 218 } 219 220 contentLengthValue, err := strconv.Atoi(header.Get("Content-Length")) 221 if err != nil { 222 t.Fatal("Error: ", err) 223 } 224 if contentLength != contentLengthValue { 225 t.Fatalf("Error: Content Length in response header %v, not equal to set content length %v\n", contentLengthValue, contentLength) 226 } 227 228 err = c.RemoveObject(context.Background(), bucketName, objectName, RemoveObjectOptions{}) 229 if err != nil { 230 t.Fatal("Error: ", err) 231 } 232 err = c.RemoveBucket(context.Background(), bucketName) 233 if err != nil { 234 t.Fatal("Error:", err) 235 } 236} 237 238// Tests GetObject to return Content-Encoding properly set 239// and overrides any auto decoding. 240func TestGetObjectContentEncoding(t *testing.T) { 241 if testing.Short() { 242 t.Skip("skipping functional tests for the short runs") 243 } 244 245 // Seed random based on current time. 246 rand.Seed(time.Now().Unix()) 247 248 // Instantiate new minio core client object. 249 c, err := NewCore( 250 os.Getenv(serverEndpoint), 251 &Options{ 252 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""), 253 Secure: mustParseBool(os.Getenv(enableSecurity)), 254 }) 255 if err != nil { 256 t.Fatal("Error:", err) 257 } 258 259 // Enable tracing, write to stderr. 260 // c.TraceOn(os.Stderr) 261 262 // Set user agent. 263 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0") 264 265 // Generate a new random bucket name. 266 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test") 267 268 // Make a new bucket. 269 err = c.MakeBucket(context.Background(), bucketName, MakeBucketOptions{Region: "us-east-1"}) 270 if err != nil { 271 t.Fatal("Error:", err, bucketName) 272 } 273 274 // Generate data more than 32K 275 buf := bytes.Repeat([]byte("3"), rand.Intn(1<<20)+32*1024) 276 277 // Save the data 278 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") 279 _, err = c.Client.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), PutObjectOptions{ 280 ContentEncoding: "gzip", 281 }) 282 if err != nil { 283 t.Fatal("Error:", err, bucketName, objectName) 284 } 285 286 rwc, objInfo, _, err := c.GetObject(context.Background(), bucketName, objectName, GetObjectOptions{}) 287 if err != nil { 288 t.Fatalf("Error: %v", err) 289 } 290 rwc.Close() 291 if objInfo.Size != int64(len(buf)) { 292 t.Fatalf("Unexpected size of the object %v, expected %v", objInfo.Size, len(buf)) 293 } 294 value, ok := objInfo.Metadata["Content-Encoding"] 295 if !ok { 296 t.Fatalf("Expected Content-Encoding metadata to be set.") 297 } 298 if value[0] != "gzip" { 299 t.Fatalf("Unexpected content-encoding found, want gzip, got %v", value) 300 } 301 302 err = c.RemoveObject(context.Background(), bucketName, objectName, RemoveObjectOptions{}) 303 if err != nil { 304 t.Fatal("Error: ", err) 305 } 306 err = c.RemoveBucket(context.Background(), bucketName) 307 if err != nil { 308 t.Fatal("Error:", err) 309 } 310} 311 312// Tests get bucket policy core API. 313func TestGetBucketPolicy(t *testing.T) { 314 if testing.Short() { 315 t.Skip("skipping functional tests for short runs") 316 } 317 318 // Seed random based on current time. 319 rand.Seed(time.Now().Unix()) 320 321 // Instantiate new minio client object. 322 c, err := NewCore( 323 os.Getenv(serverEndpoint), 324 &Options{ 325 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""), 326 Secure: mustParseBool(os.Getenv(enableSecurity)), 327 }) 328 if err != nil { 329 t.Fatal("Error:", err) 330 } 331 332 // Enable to debug 333 // c.TraceOn(os.Stderr) 334 335 // Set user agent. 336 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0") 337 338 // Generate a new random bucket name. 339 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test") 340 341 // Make a new bucket. 342 err = c.MakeBucket(context.Background(), bucketName, MakeBucketOptions{Region: "us-east-1"}) 343 if err != nil { 344 t.Fatal("Error:", err, bucketName) 345 } 346 347 // Verify if bucket exits and you have access. 348 var exists bool 349 exists, err = c.BucketExists(context.Background(), bucketName) 350 if err != nil { 351 t.Fatal("Error:", err, bucketName) 352 } 353 if !exists { 354 t.Fatal("Error: could not find ", bucketName) 355 } 356 357 // Asserting the default bucket policy. 358 bucketPolicy, err := c.GetBucketPolicy(context.Background(), bucketName) 359 if err != nil { 360 errResp := ToErrorResponse(err) 361 if errResp.Code != "NoSuchBucketPolicy" { 362 t.Error("Error:", err, bucketName) 363 } 364 } 365 if bucketPolicy != "" { 366 t.Errorf("Bucket policy expected %#v, got %#v", "", bucketPolicy) 367 } 368 369 err = c.RemoveBucket(context.Background(), bucketName) 370 if err != nil { 371 t.Fatal("Error:", err) 372 } 373} 374 375// Tests Core CopyObject API implementation. 376func TestCoreCopyObject(t *testing.T) { 377 if testing.Short() { 378 t.Skip("skipping functional tests for short runs") 379 } 380 381 // Seed random based on current time. 382 rand.Seed(time.Now().Unix()) 383 384 // Instantiate new minio client object. 385 c, err := NewCore( 386 os.Getenv(serverEndpoint), 387 &Options{ 388 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""), 389 Secure: mustParseBool(os.Getenv(enableSecurity)), 390 }) 391 if err != nil { 392 t.Fatal("Error:", err) 393 } 394 395 // Enable tracing, write to stderr. 396 // c.TraceOn(os.Stderr) 397 398 // Set user agent. 399 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0") 400 401 // Generate a new random bucket name. 402 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test") 403 404 // Make a new bucket. 405 err = c.MakeBucket(context.Background(), bucketName, MakeBucketOptions{Region: "us-east-1"}) 406 if err != nil { 407 t.Fatal("Error:", err, bucketName) 408 } 409 410 buf := bytes.Repeat([]byte("a"), 32*1024) 411 412 // Save the data 413 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") 414 415 putopts := PutObjectOptions{ 416 UserMetadata: map[string]string{ 417 "Content-Type": "binary/octet-stream", 418 }, 419 } 420 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", putopts) 421 if err != nil { 422 t.Fatal("Error:", err, bucketName, objectName) 423 } 424 425 st, err := c.StatObject(context.Background(), bucketName, objectName, StatObjectOptions{}) 426 if err != nil { 427 t.Fatal("Error:", err, bucketName, objectName) 428 } 429 430 if st.Size != int64(len(buf)) { 431 t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size) 432 } 433 434 destBucketName := bucketName 435 destObjectName := objectName + "-dest" 436 437 cuploadInfo, err := c.CopyObject(context.Background(), bucketName, objectName, destBucketName, destObjectName, map[string]string{ 438 "X-Amz-Metadata-Directive": "REPLACE", 439 "Content-Type": "application/javascript", 440 }, CopySrcOptions{}, PutObjectOptions{}) 441 if err != nil { 442 t.Fatal("Error:", err, bucketName, objectName, destBucketName, destObjectName) 443 } 444 if cuploadInfo.ETag != uploadInfo.ETag { 445 t.Fatalf("Error: expected etag to be same as source object %s, but found different etag %s", uploadInfo.ETag, cuploadInfo.ETag) 446 } 447 448 // Attempt to read from destBucketName and object name. 449 r, err := c.Client.GetObject(context.Background(), destBucketName, destObjectName, GetObjectOptions{}) 450 if err != nil { 451 t.Fatal("Error:", err, bucketName, objectName) 452 } 453 454 st, err = r.Stat() 455 if err != nil { 456 t.Fatal("Error:", err, bucketName, objectName) 457 } 458 459 if st.Size != int64(len(buf)) { 460 t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n", 461 len(buf), st.Size) 462 } 463 464 if st.ContentType != "application/javascript" { 465 t.Fatalf("Error: Content types don't match, expected: application/javascript, found: %+v\n", st.ContentType) 466 } 467 468 if st.ETag != uploadInfo.ETag { 469 t.Fatalf("Error: expected etag to be same as source object %s, but found different etag :%s", uploadInfo.ETag, st.ETag) 470 } 471 472 if err := r.Close(); err != nil { 473 t.Fatal("Error:", err) 474 } 475 476 if err := r.Close(); err == nil { 477 t.Fatal("Error: object is already closed, should return error") 478 } 479 480 err = c.RemoveObject(context.Background(), bucketName, objectName, RemoveObjectOptions{}) 481 if err != nil { 482 t.Fatal("Error: ", err) 483 } 484 485 err = c.RemoveObject(context.Background(), destBucketName, destObjectName, RemoveObjectOptions{}) 486 if err != nil { 487 t.Fatal("Error: ", err) 488 } 489 490 err = c.RemoveBucket(context.Background(), bucketName) 491 if err != nil { 492 t.Fatal("Error:", err) 493 } 494 495 // Do not need to remove destBucketName its same as bucketName. 496} 497 498// Test Core CopyObjectPart implementation 499func TestCoreCopyObjectPart(t *testing.T) { 500 if testing.Short() { 501 t.Skip("skipping functional tests for short runs") 502 } 503 504 // Seed random based on current time. 505 rand.Seed(time.Now().Unix()) 506 507 // Instantiate new minio client object. 508 c, err := NewCore( 509 os.Getenv(serverEndpoint), 510 &Options{ 511 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""), 512 Secure: mustParseBool(os.Getenv(enableSecurity)), 513 }) 514 if err != nil { 515 t.Fatal("Error:", err) 516 } 517 518 // Enable tracing, write to stderr. 519 // c.TraceOn(os.Stderr) 520 521 // Set user agent. 522 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0") 523 524 // Generate a new random bucket name. 525 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test") 526 527 // Make a new bucket. 528 err = c.MakeBucket(context.Background(), bucketName, MakeBucketOptions{Region: "us-east-1"}) 529 if err != nil { 530 t.Fatal("Error:", err, bucketName) 531 } 532 533 // Make a buffer with 5MB of data 534 buf := bytes.Repeat([]byte("abcde"), 1024*1024) 535 metadata := map[string]string{ 536 "Content-Type": "binary/octet-stream", 537 } 538 putopts := PutObjectOptions{ 539 UserMetadata: metadata, 540 } 541 // Save the data 542 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") 543 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", putopts) 544 if err != nil { 545 t.Fatal("Error:", err, bucketName, objectName) 546 } 547 548 st, err := c.StatObject(context.Background(), bucketName, objectName, StatObjectOptions{}) 549 if err != nil { 550 t.Fatal("Error:", err, bucketName, objectName) 551 } 552 553 if st.Size != int64(len(buf)) { 554 t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size) 555 } 556 557 destBucketName := bucketName 558 destObjectName := objectName + "-dest" 559 560 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, PutObjectOptions{}) 561 if err != nil { 562 t.Fatal("Error:", err, bucketName, objectName) 563 } 564 565 // Content of the destination object will be two copies of 566 // `objectName` concatenated, followed by first byte of 567 // `objectName`. 568 569 // First of three parts 570 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, nil) 571 if err != nil { 572 t.Fatal("Error:", err, destBucketName, destObjectName) 573 } 574 575 // Second of three parts 576 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, nil) 577 if err != nil { 578 t.Fatal("Error:", err, destBucketName, destObjectName) 579 } 580 581 // Last of three parts 582 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, nil) 583 if err != nil { 584 t.Fatal("Error:", err, destBucketName, destObjectName) 585 } 586 587 // Complete the multipart upload 588 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []CompletePart{fstPart, sndPart, lstPart}, PutObjectOptions{}) 589 if err != nil { 590 t.Fatal("Error:", err, destBucketName, destObjectName) 591 } 592 593 // Stat the object and check its length matches 594 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, StatObjectOptions{}) 595 if err != nil { 596 t.Fatal("Error:", err, destBucketName, destObjectName) 597 } 598 599 if objInfo.Size != (5*1024*1024)*2+1 { 600 t.Fatal("Destination object has incorrect size!") 601 } 602 603 // Now we read the data back 604 getOpts := GetObjectOptions{} 605 getOpts.SetRange(0, 5*1024*1024-1) 606 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts) 607 if err != nil { 608 t.Fatal("Error:", err, destBucketName, destObjectName) 609 } 610 getBuf := make([]byte, 5*1024*1024) 611 _, err = readFull(r, getBuf) 612 if err != nil { 613 t.Fatal("Error:", err, destBucketName, destObjectName) 614 } 615 if !bytes.Equal(getBuf, buf) { 616 t.Fatal("Got unexpected data in first 5MB") 617 } 618 619 getOpts.SetRange(5*1024*1024, 0) 620 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts) 621 if err != nil { 622 t.Fatal("Error:", err, destBucketName, destObjectName) 623 } 624 getBuf = make([]byte, 5*1024*1024+1) 625 _, err = readFull(r, getBuf) 626 if err != nil { 627 t.Fatal("Error:", err, destBucketName, destObjectName) 628 } 629 if !bytes.Equal(getBuf[:5*1024*1024], buf) { 630 t.Fatal("Got unexpected data in second 5MB") 631 } 632 if getBuf[5*1024*1024] != buf[0] { 633 t.Fatal("Got unexpected data in last byte of copied object!") 634 } 635 636 if err := c.RemoveObject(context.Background(), destBucketName, destObjectName, RemoveObjectOptions{}); err != nil { 637 t.Fatal("Error: ", err) 638 } 639 640 if err := c.RemoveObject(context.Background(), bucketName, objectName, RemoveObjectOptions{}); err != nil { 641 t.Fatal("Error: ", err) 642 } 643 644 if err := c.RemoveBucket(context.Background(), bucketName); err != nil { 645 t.Fatal("Error: ", err) 646 } 647 648 // Do not need to remove destBucketName its same as bucketName. 649} 650 651// Test Core PutObject. 652func TestCorePutObject(t *testing.T) { 653 if testing.Short() { 654 t.Skip("skipping functional tests for short runs") 655 } 656 657 // Seed random based on current time. 658 rand.Seed(time.Now().Unix()) 659 660 // Instantiate new minio client object. 661 c, err := NewCore( 662 os.Getenv(serverEndpoint), 663 &Options{ 664 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""), 665 Secure: mustParseBool(os.Getenv(enableSecurity)), 666 }) 667 if err != nil { 668 t.Fatal("Error:", err) 669 } 670 671 // Enable tracing, write to stderr. 672 // c.TraceOn(os.Stderr) 673 674 // Set user agent. 675 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0") 676 677 // Generate a new random bucket name. 678 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test") 679 680 // Make a new bucket. 681 err = c.MakeBucket(context.Background(), bucketName, MakeBucketOptions{Region: "us-east-1"}) 682 if err != nil { 683 t.Fatal("Error:", err, bucketName) 684 } 685 686 buf := bytes.Repeat([]byte("a"), 32*1024) 687 688 // Save the data 689 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") 690 // Object content type 691 objectContentType := "binary/octet-stream" 692 metadata := make(map[string]string) 693 metadata["Content-Type"] = objectContentType 694 putopts := PutObjectOptions{ 695 UserMetadata: metadata, 696 } 697 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "1B2M2Y8AsgTpgAmY7PhCfg==", "", putopts) 698 if err == nil { 699 t.Fatal("Error expected: error, got: nil(success)") 700 } 701 702 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", putopts) 703 if err != nil { 704 t.Fatal("Error:", err, bucketName, objectName) 705 } 706 707 // Read the data back 708 r, err := c.Client.GetObject(context.Background(), bucketName, objectName, GetObjectOptions{}) 709 if err != nil { 710 t.Fatal("Error:", err, bucketName, objectName) 711 } 712 713 st, err := r.Stat() 714 if err != nil { 715 t.Fatal("Error:", err, bucketName, objectName) 716 } 717 718 if st.Size != int64(len(buf)) { 719 t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n", 720 len(buf), st.Size) 721 } 722 723 if st.ContentType != objectContentType { 724 t.Fatalf("Error: Content types don't match, expected: %+v, found: %+v\n", objectContentType, st.ContentType) 725 } 726 727 if err := r.Close(); err != nil { 728 t.Fatal("Error:", err) 729 } 730 731 if err := r.Close(); err == nil { 732 t.Fatal("Error: object is already closed, should return error") 733 } 734 735 err = c.RemoveObject(context.Background(), bucketName, objectName, RemoveObjectOptions{}) 736 if err != nil { 737 t.Fatal("Error: ", err) 738 } 739 740 err = c.RemoveBucket(context.Background(), bucketName) 741 if err != nil { 742 t.Fatal("Error:", err) 743 } 744} 745 746func TestCoreGetObjectMetadata(t *testing.T) { 747 if testing.Short() { 748 t.Skip("skipping functional tests for the short runs") 749 } 750 751 core, err := NewCore( 752 os.Getenv(serverEndpoint), 753 &Options{ 754 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""), 755 Secure: mustParseBool(os.Getenv(enableSecurity)), 756 }) 757 if err != nil { 758 t.Fatal(err) 759 } 760 761 // Generate a new random bucket name. 762 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test") 763 764 // Make a new bucket. 765 err = core.MakeBucket(context.Background(), bucketName, MakeBucketOptions{Region: "us-east-1"}) 766 if err != nil { 767 t.Fatal("Error:", err, bucketName) 768 } 769 770 metadata := map[string]string{ 771 "X-Amz-Meta-Key-1": "Val-1", 772 } 773 putopts := PutObjectOptions{ 774 UserMetadata: metadata, 775 } 776 777 _, err = core.PutObject(context.Background(), bucketName, "my-objectname", 778 bytes.NewReader([]byte("hello")), 5, "", "", putopts) 779 if err != nil { 780 log.Fatalln(err) 781 } 782 783 reader, objInfo, _, err := core.GetObject(context.Background(), bucketName, "my-objectname", GetObjectOptions{}) 784 if err != nil { 785 log.Fatalln(err) 786 } 787 defer reader.Close() 788 789 if objInfo.Metadata.Get("X-Amz-Meta-Key-1") != "Val-1" { 790 log.Fatalln("Expected metadata to be available but wasn't") 791 } 792 793 err = core.RemoveObject(context.Background(), bucketName, "my-objectname", RemoveObjectOptions{}) 794 if err != nil { 795 t.Fatal("Error: ", err) 796 } 797 err = core.RemoveBucket(context.Background(), bucketName) 798 if err != nil { 799 t.Fatal("Error:", err) 800 } 801} 802