aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/minio/minio-go/v7/functional_tests.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/minio/minio-go/v7/functional_tests.go')
-rw-r--r--vendor/github.com/minio/minio-go/v7/functional_tests.go13004
1 files changed, 13004 insertions, 0 deletions
diff --git a/vendor/github.com/minio/minio-go/v7/functional_tests.go b/vendor/github.com/minio/minio-go/v7/functional_tests.go
new file mode 100644
index 0000000..f951cd0
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/v7/functional_tests.go
@@ -0,0 +1,13004 @@
1//go:build mint
2// +build mint
3
4/*
5 * MinIO Go Library for Amazon S3 Compatible Cloud Storage
6 * Copyright 2015-2020 MinIO, Inc.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package main
22
23import (
24 "archive/zip"
25 "bytes"
26 "context"
27 "crypto/sha1"
28 "encoding/base64"
29 "errors"
30 "fmt"
31 "hash"
32 "hash/crc32"
33 "io"
34 "math/rand"
35 "mime/multipart"
36 "net/http"
37 "net/url"
38 "os"
39 "path"
40 "path/filepath"
41 "reflect"
42 "runtime"
43 "sort"
44 "strconv"
45 "strings"
46 "sync"
47 "time"
48
49 "github.com/dustin/go-humanize"
50 jsoniter "github.com/json-iterator/go"
51 "github.com/minio/sha256-simd"
52 log "github.com/sirupsen/logrus"
53
54 "github.com/minio/minio-go/v7"
55 "github.com/minio/minio-go/v7/pkg/credentials"
56 "github.com/minio/minio-go/v7/pkg/encrypt"
57 "github.com/minio/minio-go/v7/pkg/notification"
58 "github.com/minio/minio-go/v7/pkg/tags"
59)
60
61const letterBytes = "abcdefghijklmnopqrstuvwxyz01234569"
62const (
63 letterIdxBits = 6 // 6 bits to represent a letter index
64 letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
65 letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
66)
67
68const (
69 serverEndpoint = "SERVER_ENDPOINT"
70 accessKey = "ACCESS_KEY"
71 secretKey = "SECRET_KEY"
72 enableHTTPS = "ENABLE_HTTPS"
73 enableKMS = "ENABLE_KMS"
74)
75
76type mintJSONFormatter struct{}
77
78func (f *mintJSONFormatter) Format(entry *log.Entry) ([]byte, error) {
79 data := make(log.Fields, len(entry.Data))
80 for k, v := range entry.Data {
81 switch v := v.(type) {
82 case error:
83 // Otherwise errors are ignored by `encoding/json`
84 // https://github.com/sirupsen/logrus/issues/137
85 data[k] = v.Error()
86 default:
87 data[k] = v
88 }
89 }
90 json := jsoniter.ConfigCompatibleWithStandardLibrary
91 serialized, err := json.Marshal(data)
92 if err != nil {
93 return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
94 }
95 return append(serialized, '\n'), nil
96}
97
98var readFull = func(r io.Reader, buf []byte) (n int, err error) {
99 // ReadFull reads exactly len(buf) bytes from r into buf.
100 // It returns the number of bytes copied and an error if
101 // fewer bytes were read. The error is EOF only if no bytes
102 // were read. If an EOF happens after reading some but not
103 // all the bytes, ReadFull returns ErrUnexpectedEOF.
104 // On return, n == len(buf) if and only if err == nil.
105 // If r returns an error having read at least len(buf) bytes,
106 // the error is dropped.
107 for n < len(buf) && err == nil {
108 var nn int
109 nn, err = r.Read(buf[n:])
110 // Some spurious io.Reader's return
111 // io.ErrUnexpectedEOF when nn == 0
112 // this behavior is undocumented
113 // so we are on purpose not using io.ReadFull
114 // implementation because this can lead
115 // to custom handling, to avoid that
116 // we simply modify the original io.ReadFull
117 // implementation to avoid this issue.
118 // io.ErrUnexpectedEOF with nn == 0 really
119 // means that io.EOF
120 if err == io.ErrUnexpectedEOF && nn == 0 {
121 err = io.EOF
122 }
123 n += nn
124 }
125 if n >= len(buf) {
126 err = nil
127 } else if n > 0 && err == io.EOF {
128 err = io.ErrUnexpectedEOF
129 }
130 return
131}
132
133func cleanEmptyEntries(fields log.Fields) log.Fields {
134 cleanFields := log.Fields{}
135 for k, v := range fields {
136 if v != "" {
137 cleanFields[k] = v
138 }
139 }
140 return cleanFields
141}
142
143// log successful test runs
144func successLogger(testName, function string, args map[string]interface{}, startTime time.Time) *log.Entry {
145 // calculate the test case duration
146 duration := time.Since(startTime)
147 // log with the fields as per mint
148 fields := log.Fields{"name": "minio-go: " + testName, "function": function, "args": args, "duration": duration.Nanoseconds() / 1000000, "status": "PASS"}
149 return log.WithFields(cleanEmptyEntries(fields))
150}
151
152// As few of the features are not available in Gateway(s) currently, Check if err value is NotImplemented,
153// and log as NA in that case and continue execution. Otherwise log as failure and return
154func logError(testName, function string, args map[string]interface{}, startTime time.Time, alert, message string, err error) {
155 // If server returns NotImplemented we assume it is gateway mode and hence log it as info and move on to next tests
156 // Special case for ComposeObject API as it is implemented on client side and adds specific error details like `Error in upload-part-copy` in
157 // addition to NotImplemented error returned from server
158 if isErrNotImplemented(err) {
159 ignoredLog(testName, function, args, startTime, message).Info()
160 } else if isRunOnFail() {
161 failureLog(testName, function, args, startTime, alert, message, err).Error()
162 } else {
163 failureLog(testName, function, args, startTime, alert, message, err).Fatal()
164 }
165}
166
167// log failed test runs
168func failureLog(testName, function string, args map[string]interface{}, startTime time.Time, alert, message string, err error) *log.Entry {
169 // calculate the test case duration
170 duration := time.Since(startTime)
171 var fields log.Fields
172 // log with the fields as per mint
173 if err != nil {
174 fields = log.Fields{
175 "name": "minio-go: " + testName, "function": function, "args": args,
176 "duration": duration.Nanoseconds() / 1000000, "status": "FAIL", "alert": alert, "message": message, "error": err,
177 }
178 } else {
179 fields = log.Fields{
180 "name": "minio-go: " + testName, "function": function, "args": args,
181 "duration": duration.Nanoseconds() / 1000000, "status": "FAIL", "alert": alert, "message": message,
182 }
183 }
184 return log.WithFields(cleanEmptyEntries(fields))
185}
186
187// log not applicable test runs
188func ignoredLog(testName, function string, args map[string]interface{}, startTime time.Time, alert string) *log.Entry {
189 // calculate the test case duration
190 duration := time.Since(startTime)
191 // log with the fields as per mint
192 fields := log.Fields{
193 "name": "minio-go: " + testName, "function": function, "args": args,
194 "duration": duration.Nanoseconds() / 1000000, "status": "NA", "alert": strings.Split(alert, " ")[0] + " is NotImplemented",
195 }
196 return log.WithFields(cleanEmptyEntries(fields))
197}
198
199// Delete objects in given bucket, recursively
200func cleanupBucket(bucketName string, c *minio.Client) error {
201 // Create a done channel to control 'ListObjectsV2' go routine.
202 doneCh := make(chan struct{})
203 // Exit cleanly upon return.
204 defer close(doneCh)
205 // Iterate over all objects in the bucket via listObjectsV2 and delete
206 for objCh := range c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{Recursive: true}) {
207 if objCh.Err != nil {
208 return objCh.Err
209 }
210 if objCh.Key != "" {
211 err := c.RemoveObject(context.Background(), bucketName, objCh.Key, minio.RemoveObjectOptions{})
212 if err != nil {
213 return err
214 }
215 }
216 }
217 for objPartInfo := range c.ListIncompleteUploads(context.Background(), bucketName, "", true) {
218 if objPartInfo.Err != nil {
219 return objPartInfo.Err
220 }
221 if objPartInfo.Key != "" {
222 err := c.RemoveIncompleteUpload(context.Background(), bucketName, objPartInfo.Key)
223 if err != nil {
224 return err
225 }
226 }
227 }
228 // objects are already deleted, clear the buckets now
229 err := c.RemoveBucket(context.Background(), bucketName)
230 if err != nil {
231 return err
232 }
233 return err
234}
235
236func cleanupVersionedBucket(bucketName string, c *minio.Client) error {
237 doneCh := make(chan struct{})
238 defer close(doneCh)
239 for obj := range c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true}) {
240 if obj.Err != nil {
241 return obj.Err
242 }
243 if obj.Key != "" {
244 err := c.RemoveObject(context.Background(), bucketName, obj.Key,
245 minio.RemoveObjectOptions{VersionID: obj.VersionID, GovernanceBypass: true})
246 if err != nil {
247 return err
248 }
249 }
250 }
251 for objPartInfo := range c.ListIncompleteUploads(context.Background(), bucketName, "", true) {
252 if objPartInfo.Err != nil {
253 return objPartInfo.Err
254 }
255 if objPartInfo.Key != "" {
256 err := c.RemoveIncompleteUpload(context.Background(), bucketName, objPartInfo.Key)
257 if err != nil {
258 return err
259 }
260 }
261 }
262 // objects are already deleted, clear the buckets now
263 err := c.RemoveBucket(context.Background(), bucketName)
264 if err != nil {
265 for obj := range c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true}) {
266 log.Println("found", obj.Key, obj.VersionID)
267 }
268 return err
269 }
270 return err
271}
272
273func isErrNotImplemented(err error) bool {
274 return minio.ToErrorResponse(err).Code == "NotImplemented"
275}
276
277func isRunOnFail() bool {
278 return os.Getenv("RUN_ON_FAIL") == "1"
279}
280
281func init() {
282 // If server endpoint is not set, all tests default to
283 // using https://play.min.io
284 if os.Getenv(serverEndpoint) == "" {
285 os.Setenv(serverEndpoint, "play.min.io")
286 os.Setenv(accessKey, "Q3AM3UQ867SPQQA43P2F")
287 os.Setenv(secretKey, "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
288 os.Setenv(enableHTTPS, "1")
289 }
290}
291
292var mintDataDir = os.Getenv("MINT_DATA_DIR")
293
294func getMintDataDirFilePath(filename string) (fp string) {
295 if mintDataDir == "" {
296 return
297 }
298 return filepath.Join(mintDataDir, filename)
299}
300
301func newRandomReader(seed, size int64) io.Reader {
302 return io.LimitReader(rand.New(rand.NewSource(seed)), size)
303}
304
305func mustCrcReader(r io.Reader) uint32 {
306 crc := crc32.NewIEEE()
307 _, err := io.Copy(crc, r)
308 if err != nil {
309 panic(err)
310 }
311 return crc.Sum32()
312}
313
314func crcMatches(r io.Reader, want uint32) error {
315 crc := crc32.NewIEEE()
316 _, err := io.Copy(crc, r)
317 if err != nil {
318 panic(err)
319 }
320 got := crc.Sum32()
321 if got != want {
322 return fmt.Errorf("crc mismatch, want %x, got %x", want, got)
323 }
324 return nil
325}
326
327func crcMatchesName(r io.Reader, name string) error {
328 want := dataFileCRC32[name]
329 crc := crc32.NewIEEE()
330 _, err := io.Copy(crc, r)
331 if err != nil {
332 panic(err)
333 }
334 got := crc.Sum32()
335 if got != want {
336 return fmt.Errorf("crc mismatch, want %x, got %x", want, got)
337 }
338 return nil
339}
340
341// read data from file if it exists or optionally create a buffer of particular size
342func getDataReader(fileName string) io.ReadCloser {
343 if mintDataDir == "" {
344 size := int64(dataFileMap[fileName])
345 if _, ok := dataFileCRC32[fileName]; !ok {
346 dataFileCRC32[fileName] = mustCrcReader(newRandomReader(size, size))
347 }
348 return io.NopCloser(newRandomReader(size, size))
349 }
350 reader, _ := os.Open(getMintDataDirFilePath(fileName))
351 if _, ok := dataFileCRC32[fileName]; !ok {
352 dataFileCRC32[fileName] = mustCrcReader(reader)
353 reader.Close()
354 reader, _ = os.Open(getMintDataDirFilePath(fileName))
355 }
356 return reader
357}
358
359// randString generates random names and prepends them with a known prefix.
360func randString(n int, src rand.Source, prefix string) string {
361 b := make([]byte, n)
362 // A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
363 for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
364 if remain == 0 {
365 cache, remain = src.Int63(), letterIdxMax
366 }
367 if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
368 b[i] = letterBytes[idx]
369 i--
370 }
371 cache >>= letterIdxBits
372 remain--
373 }
374 return prefix + string(b[0:30-len(prefix)])
375}
376
377var dataFileMap = map[string]int{
378 "datafile-0-b": 0,
379 "datafile-1-b": 1,
380 "datafile-1-kB": 1 * humanize.KiByte,
381 "datafile-10-kB": 10 * humanize.KiByte,
382 "datafile-33-kB": 33 * humanize.KiByte,
383 "datafile-100-kB": 100 * humanize.KiByte,
384 "datafile-1.03-MB": 1056 * humanize.KiByte,
385 "datafile-1-MB": 1 * humanize.MiByte,
386 "datafile-5-MB": 5 * humanize.MiByte,
387 "datafile-6-MB": 6 * humanize.MiByte,
388 "datafile-11-MB": 11 * humanize.MiByte,
389 "datafile-65-MB": 65 * humanize.MiByte,
390 "datafile-129-MB": 129 * humanize.MiByte,
391}
392
393var dataFileCRC32 = map[string]uint32{}
394
395func isFullMode() bool {
396 return os.Getenv("MINT_MODE") == "full"
397}
398
399func getFuncName() string {
400 return getFuncNameLoc(2)
401}
402
403func getFuncNameLoc(caller int) string {
404 pc, _, _, _ := runtime.Caller(caller)
405 return strings.TrimPrefix(runtime.FuncForPC(pc).Name(), "main.")
406}
407
408// Tests bucket re-create errors.
409func testMakeBucketError() {
410 region := "eu-central-1"
411
412 // initialize logging params
413 startTime := time.Now()
414 testName := getFuncName()
415 function := "MakeBucket(bucketName, region)"
416 // initialize logging params
417 args := map[string]interface{}{
418 "bucketName": "",
419 "region": region,
420 }
421
422 // Seed random based on current time.
423 rand.Seed(time.Now().Unix())
424
425 // Instantiate new minio client object.
426 c, err := minio.New(os.Getenv(serverEndpoint),
427 &minio.Options{
428 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
429 Secure: mustParseBool(os.Getenv(enableHTTPS)),
430 })
431 if err != nil {
432 logError(testName, function, args, startTime, "", "MinIO client creation failed", err)
433 return
434 }
435
436 // Enable tracing, write to stderr.
437 // c.TraceOn(os.Stderr)
438
439 // Set user agent.
440 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
441
442 // Generate a new random bucket name.
443 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
444 args["bucketName"] = bucketName
445
446 // Make a new bucket in 'eu-central-1'.
447 if err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: region}); err != nil {
448 logError(testName, function, args, startTime, "", "MakeBucket Failed", err)
449 return
450 }
451 defer cleanupBucket(bucketName, c)
452
453 if err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: region}); err == nil {
454 logError(testName, function, args, startTime, "", "Bucket already exists", err)
455 return
456 }
457 // Verify valid error response from server.
458 if minio.ToErrorResponse(err).Code != "BucketAlreadyExists" &&
459 minio.ToErrorResponse(err).Code != "BucketAlreadyOwnedByYou" {
460 logError(testName, function, args, startTime, "", "Invalid error returned by server", err)
461 return
462 }
463
464 successLogger(testName, function, args, startTime).Info()
465}
466
467func testMetadataSizeLimit() {
468 startTime := time.Now()
469 testName := getFuncName()
470 function := "PutObject(bucketName, objectName, reader, objectSize, opts)"
471 args := map[string]interface{}{
472 "bucketName": "",
473 "objectName": "",
474 "opts.UserMetadata": "",
475 }
476 rand.Seed(startTime.Unix())
477
478 // Instantiate new minio client object.
479 c, err := minio.New(os.Getenv(serverEndpoint),
480 &minio.Options{
481 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
482 Secure: mustParseBool(os.Getenv(enableHTTPS)),
483 })
484 if err != nil {
485 logError(testName, function, args, startTime, "", "MinIO client creation failed", err)
486 return
487 }
488 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
489
490 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
491 args["bucketName"] = bucketName
492
493 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
494 args["objectName"] = objectName
495
496 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
497 if err != nil {
498 logError(testName, function, args, startTime, "", "Make bucket failed", err)
499 return
500 }
501
502 defer cleanupBucket(bucketName, c)
503
504 const HeaderSizeLimit = 8 * 1024
505 const UserMetadataLimit = 2 * 1024
506
507 // Meta-data greater than the 2 KB limit of AWS - PUT calls with this meta-data should fail
508 metadata := make(map[string]string)
509 metadata["X-Amz-Meta-Mint-Test"] = string(bytes.Repeat([]byte("m"), 1+UserMetadataLimit-len("X-Amz-Meta-Mint-Test")))
510 args["metadata"] = fmt.Sprint(metadata)
511
512 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(nil), 0, minio.PutObjectOptions{UserMetadata: metadata})
513 if err == nil {
514 logError(testName, function, args, startTime, "", "Created object with user-defined metadata exceeding metadata size limits", nil)
515 return
516 }
517
518 // Meta-data (headers) greater than the 8 KB limit of AWS - PUT calls with this meta-data should fail
519 metadata = make(map[string]string)
520 metadata["X-Amz-Mint-Test"] = string(bytes.Repeat([]byte("m"), 1+HeaderSizeLimit-len("X-Amz-Mint-Test")))
521 args["metadata"] = fmt.Sprint(metadata)
522 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(nil), 0, minio.PutObjectOptions{UserMetadata: metadata})
523 if err == nil {
524 logError(testName, function, args, startTime, "", "Created object with headers exceeding header size limits", nil)
525 return
526 }
527
528 successLogger(testName, function, args, startTime).Info()
529}
530
531// Tests various bucket supported formats.
532func testMakeBucketRegions() {
533 region := "eu-central-1"
534 // initialize logging params
535 startTime := time.Now()
536 testName := getFuncName()
537 function := "MakeBucket(bucketName, region)"
538 // initialize logging params
539 args := map[string]interface{}{
540 "bucketName": "",
541 "region": region,
542 }
543
544 // Seed random based on current time.
545 rand.Seed(time.Now().Unix())
546
547 // Instantiate new minio client object.
548 c, err := minio.New(os.Getenv(serverEndpoint),
549 &minio.Options{
550 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
551 Secure: mustParseBool(os.Getenv(enableHTTPS)),
552 })
553 if err != nil {
554 logError(testName, function, args, startTime, "", "MinIO client creation failed", err)
555 return
556 }
557
558 // Enable tracing, write to stderr.
559 // c.TraceOn(os.Stderr)
560
561 // Set user agent.
562 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
563
564 // Generate a new random bucket name.
565 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
566 args["bucketName"] = bucketName
567
568 // Make a new bucket in 'eu-central-1'.
569 if err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: region}); err != nil {
570 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
571 return
572 }
573
574 // Delete all objects and buckets
575 if err = cleanupBucket(bucketName, c); err != nil {
576 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
577 return
578 }
579
580 // Make a new bucket with '.' in its name, in 'us-west-2'. This
581 // request is internally staged into a path style instead of
582 // virtual host style.
583 region = "us-west-2"
584 args["region"] = region
585 if err = c.MakeBucket(context.Background(), bucketName+".withperiod", minio.MakeBucketOptions{Region: region}); err != nil {
586 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
587 return
588 }
589
590 // Delete all objects and buckets
591 if err = cleanupBucket(bucketName+".withperiod", c); err != nil {
592 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
593 return
594 }
595 successLogger(testName, function, args, startTime).Info()
596}
597
598// Test PutObject using a large data to trigger multipart readat
599func testPutObjectReadAt() {
600 // initialize logging params
601 startTime := time.Now()
602 testName := getFuncName()
603 function := "PutObject(bucketName, objectName, reader, opts)"
604 args := map[string]interface{}{
605 "bucketName": "",
606 "objectName": "",
607 "opts": "objectContentType",
608 }
609
610 // Seed random based on current time.
611 rand.Seed(time.Now().Unix())
612
613 // Instantiate new minio client object.
614 c, err := minio.New(os.Getenv(serverEndpoint),
615 &minio.Options{
616 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
617 Secure: mustParseBool(os.Getenv(enableHTTPS)),
618 })
619 if err != nil {
620 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
621 return
622 }
623
624 // Enable tracing, write to stderr.
625 // c.TraceOn(os.Stderr)
626
627 // Set user agent.
628 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
629
630 // Generate a new random bucket name.
631 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
632 args["bucketName"] = bucketName
633
634 // Make a new bucket.
635 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
636 if err != nil {
637 logError(testName, function, args, startTime, "", "Make bucket failed", err)
638 return
639 }
640
641 defer cleanupBucket(bucketName, c)
642
643 bufSize := dataFileMap["datafile-129-MB"]
644 reader := getDataReader("datafile-129-MB")
645 defer reader.Close()
646
647 // Save the data
648 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
649 args["objectName"] = objectName
650
651 // Object content type
652 objectContentType := "binary/octet-stream"
653 args["objectContentType"] = objectContentType
654
655 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: objectContentType})
656 if err != nil {
657 logError(testName, function, args, startTime, "", "PutObject failed", err)
658 return
659 }
660
661 // Read the data back
662 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
663 if err != nil {
664 logError(testName, function, args, startTime, "", "Get Object failed", err)
665 return
666 }
667
668 st, err := r.Stat()
669 if err != nil {
670 logError(testName, function, args, startTime, "", "Stat Object failed", err)
671 return
672 }
673 if st.Size != int64(bufSize) {
674 logError(testName, function, args, startTime, "", fmt.Sprintf("Number of bytes in stat does not match, expected %d got %d", bufSize, st.Size), err)
675 return
676 }
677 if st.ContentType != objectContentType && st.ContentType != "application/octet-stream" {
678 logError(testName, function, args, startTime, "", "Content types don't match", err)
679 return
680 }
681 if err := crcMatchesName(r, "datafile-129-MB"); err != nil {
682 logError(testName, function, args, startTime, "", "data CRC check failed", err)
683 return
684 }
685 if err := r.Close(); err != nil {
686 logError(testName, function, args, startTime, "", "Object Close failed", err)
687 return
688 }
689 if err := r.Close(); err == nil {
690 logError(testName, function, args, startTime, "", "Object is already closed, didn't return error on Close", err)
691 return
692 }
693
694 successLogger(testName, function, args, startTime).Info()
695}
696
697func testListObjectVersions() {
698 // initialize logging params
699 startTime := time.Now()
700 testName := getFuncName()
701 function := "ListObjectVersions(bucketName, prefix, recursive)"
702 args := map[string]interface{}{
703 "bucketName": "",
704 "prefix": "",
705 "recursive": "",
706 }
707
708 // Seed random based on current time.
709 rand.Seed(time.Now().Unix())
710
711 // Instantiate new minio client object.
712 c, err := minio.New(os.Getenv(serverEndpoint),
713 &minio.Options{
714 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
715 Secure: mustParseBool(os.Getenv(enableHTTPS)),
716 })
717 if err != nil {
718 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
719 return
720 }
721
722 // Enable tracing, write to stderr.
723 // c.TraceOn(os.Stderr)
724
725 // Set user agent.
726 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
727
728 // Generate a new random bucket name.
729 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
730 args["bucketName"] = bucketName
731
732 // Make a new bucket.
733 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
734 if err != nil {
735 logError(testName, function, args, startTime, "", "Make bucket failed", err)
736 return
737 }
738
739 err = c.EnableVersioning(context.Background(), bucketName)
740 if err != nil {
741 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
742 return
743 }
744
745 // Save the data
746 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
747 args["objectName"] = objectName
748
749 bufSize := dataFileMap["datafile-10-kB"]
750 reader := getDataReader("datafile-10-kB")
751
752 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{})
753 if err != nil {
754 logError(testName, function, args, startTime, "", "PutObject failed", err)
755 return
756 }
757 reader.Close()
758
759 bufSize = dataFileMap["datafile-1-b"]
760 reader = getDataReader("datafile-1-b")
761 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{})
762 if err != nil {
763 logError(testName, function, args, startTime, "", "PutObject failed", err)
764 return
765 }
766 reader.Close()
767
768 err = c.RemoveObject(context.Background(), bucketName, objectName, minio.RemoveObjectOptions{})
769 if err != nil {
770 logError(testName, function, args, startTime, "", "Unexpected object deletion", err)
771 return
772 }
773
774 var deleteMarkers, versions int
775
776 objectsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
777 for info := range objectsInfo {
778 if info.Err != nil {
779 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
780 return
781 }
782 if info.Key != objectName {
783 logError(testName, function, args, startTime, "", "Unexpected object name in listing objects", nil)
784 return
785 }
786 if info.VersionID == "" {
787 logError(testName, function, args, startTime, "", "Unexpected version id in listing objects", nil)
788 return
789 }
790 if info.IsDeleteMarker {
791 deleteMarkers++
792 if !info.IsLatest {
793 logError(testName, function, args, startTime, "", "Unexpected IsLatest field in listing objects", nil)
794 return
795 }
796 } else {
797 versions++
798 }
799 }
800
801 if deleteMarkers != 1 {
802 logError(testName, function, args, startTime, "", "Unexpected number of DeleteMarker elements in listing objects", nil)
803 return
804 }
805
806 if versions != 2 {
807 logError(testName, function, args, startTime, "", "Unexpected number of Version elements in listing objects", nil)
808 return
809 }
810
811 // Delete all objects and their versions as long as the bucket itself
812 if err = cleanupVersionedBucket(bucketName, c); err != nil {
813 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
814 return
815 }
816
817 successLogger(testName, function, args, startTime).Info()
818}
819
820func testStatObjectWithVersioning() {
821 // initialize logging params
822 startTime := time.Now()
823 testName := getFuncName()
824 function := "StatObject"
825 args := map[string]interface{}{}
826
827 // Seed random based on current time.
828 rand.Seed(time.Now().Unix())
829
830 // Instantiate new minio client object.
831 c, err := minio.New(os.Getenv(serverEndpoint),
832 &minio.Options{
833 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
834 Secure: mustParseBool(os.Getenv(enableHTTPS)),
835 })
836 if err != nil {
837 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
838 return
839 }
840
841 // Enable tracing, write to stderr.
842 // c.TraceOn(os.Stderr)
843
844 // Set user agent.
845 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
846
847 // Generate a new random bucket name.
848 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
849 args["bucketName"] = bucketName
850
851 // Make a new bucket.
852 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
853 if err != nil {
854 logError(testName, function, args, startTime, "", "Make bucket failed", err)
855 return
856 }
857
858 err = c.EnableVersioning(context.Background(), bucketName)
859 if err != nil {
860 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
861 return
862 }
863
864 // Save the data
865 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
866 args["objectName"] = objectName
867
868 bufSize := dataFileMap["datafile-10-kB"]
869 reader := getDataReader("datafile-10-kB")
870
871 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{})
872 if err != nil {
873 logError(testName, function, args, startTime, "", "PutObject failed", err)
874 return
875 }
876 reader.Close()
877
878 bufSize = dataFileMap["datafile-1-b"]
879 reader = getDataReader("datafile-1-b")
880 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{})
881 if err != nil {
882 logError(testName, function, args, startTime, "", "PutObject failed", err)
883 return
884 }
885 reader.Close()
886
887 objectsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
888
889 var results []minio.ObjectInfo
890 for info := range objectsInfo {
891 if info.Err != nil {
892 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
893 return
894 }
895 results = append(results, info)
896 }
897
898 if len(results) != 2 {
899 logError(testName, function, args, startTime, "", "Unexpected number of Version elements in listing objects", nil)
900 return
901 }
902
903 for i := 0; i < len(results); i++ {
904 opts := minio.StatObjectOptions{VersionID: results[i].VersionID}
905 statInfo, err := c.StatObject(context.Background(), bucketName, objectName, opts)
906 if err != nil {
907 logError(testName, function, args, startTime, "", "error during HEAD object", err)
908 return
909 }
910 if statInfo.VersionID == "" || statInfo.VersionID != results[i].VersionID {
911 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected version id", err)
912 return
913 }
914 if statInfo.ETag != results[i].ETag {
915 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected ETag", err)
916 return
917 }
918 if statInfo.LastModified.Unix() != results[i].LastModified.Unix() {
919 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected Last-Modified", err)
920 return
921 }
922 if statInfo.Size != results[i].Size {
923 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected Content-Length", err)
924 return
925 }
926 }
927
928 // Delete all objects and their versions as long as the bucket itself
929 if err = cleanupVersionedBucket(bucketName, c); err != nil {
930 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
931 return
932 }
933
934 successLogger(testName, function, args, startTime).Info()
935}
936
937func testGetObjectWithVersioning() {
938 // initialize logging params
939 startTime := time.Now()
940 testName := getFuncName()
941 function := "GetObject()"
942 args := map[string]interface{}{}
943
944 // Seed random based on current time.
945 rand.Seed(time.Now().Unix())
946
947 // Instantiate new minio client object.
948 c, err := minio.New(os.Getenv(serverEndpoint),
949 &minio.Options{
950 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
951 Secure: mustParseBool(os.Getenv(enableHTTPS)),
952 })
953 if err != nil {
954 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
955 return
956 }
957
958 // Enable tracing, write to stderr.
959 // c.TraceOn(os.Stderr)
960
961 // Set user agent.
962 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
963
964 // Generate a new random bucket name.
965 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
966 args["bucketName"] = bucketName
967
968 // Make a new bucket.
969 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
970 if err != nil {
971 logError(testName, function, args, startTime, "", "Make bucket failed", err)
972 return
973 }
974
975 err = c.EnableVersioning(context.Background(), bucketName)
976 if err != nil {
977 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
978 return
979 }
980
981 // Save the data
982 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
983 args["objectName"] = objectName
984
985 // Save the contents of datafiles to check with GetObject() reader output later
986 var buffers [][]byte
987 testFiles := []string{"datafile-1-b", "datafile-10-kB"}
988
989 for _, testFile := range testFiles {
990 r := getDataReader(testFile)
991 buf, err := io.ReadAll(r)
992 if err != nil {
993 logError(testName, function, args, startTime, "", "unexpected failure", err)
994 return
995 }
996 r.Close()
997 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{})
998 if err != nil {
999 logError(testName, function, args, startTime, "", "PutObject failed", err)
1000 return
1001 }
1002 buffers = append(buffers, buf)
1003 }
1004
1005 objectsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1006
1007 var results []minio.ObjectInfo
1008 for info := range objectsInfo {
1009 if info.Err != nil {
1010 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1011 return
1012 }
1013 results = append(results, info)
1014 }
1015
1016 if len(results) != 2 {
1017 logError(testName, function, args, startTime, "", "Unexpected number of Version elements in listing objects", nil)
1018 return
1019 }
1020
1021 sort.SliceStable(results, func(i, j int) bool {
1022 return results[i].Size < results[j].Size
1023 })
1024
1025 sort.SliceStable(buffers, func(i, j int) bool {
1026 return len(buffers[i]) < len(buffers[j])
1027 })
1028
1029 for i := 0; i < len(results); i++ {
1030 opts := minio.GetObjectOptions{VersionID: results[i].VersionID}
1031 reader, err := c.GetObject(context.Background(), bucketName, objectName, opts)
1032 if err != nil {
1033 logError(testName, function, args, startTime, "", "error during GET object", err)
1034 return
1035 }
1036 statInfo, err := reader.Stat()
1037 if err != nil {
1038 logError(testName, function, args, startTime, "", "error during calling reader.Stat()", err)
1039 return
1040 }
1041 if statInfo.ETag != results[i].ETag {
1042 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected ETag", err)
1043 return
1044 }
1045 if statInfo.LastModified.Unix() != results[i].LastModified.Unix() {
1046 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected Last-Modified", err)
1047 return
1048 }
1049 if statInfo.Size != results[i].Size {
1050 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected Content-Length", err)
1051 return
1052 }
1053
1054 tmpBuffer := bytes.NewBuffer([]byte{})
1055 _, err = io.Copy(tmpBuffer, reader)
1056 if err != nil {
1057 logError(testName, function, args, startTime, "", "unexpected io.Copy()", err)
1058 return
1059 }
1060
1061 if !bytes.Equal(tmpBuffer.Bytes(), buffers[i]) {
1062 logError(testName, function, args, startTime, "", "unexpected content of GetObject()", err)
1063 return
1064 }
1065 }
1066
1067 // Delete all objects and their versions as long as the bucket itself
1068 if err = cleanupVersionedBucket(bucketName, c); err != nil {
1069 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
1070 return
1071 }
1072
1073 successLogger(testName, function, args, startTime).Info()
1074}
1075
1076func testPutObjectWithVersioning() {
1077 // initialize logging params
1078 startTime := time.Now()
1079 testName := getFuncName()
1080 function := "GetObject()"
1081 args := map[string]interface{}{}
1082
1083 // Seed random based on current time.
1084 rand.Seed(time.Now().Unix())
1085
1086 // Instantiate new minio client object.
1087 c, err := minio.New(os.Getenv(serverEndpoint),
1088 &minio.Options{
1089 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
1090 Secure: mustParseBool(os.Getenv(enableHTTPS)),
1091 })
1092 if err != nil {
1093 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
1094 return
1095 }
1096
1097 // Enable tracing, write to stderr.
1098 // c.TraceOn(os.Stderr)
1099
1100 // Set user agent.
1101 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
1102
1103 // Generate a new random bucket name.
1104 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
1105 args["bucketName"] = bucketName
1106
1107 // Make a new bucket.
1108 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
1109 if err != nil {
1110 logError(testName, function, args, startTime, "", "Make bucket failed", err)
1111 return
1112 }
1113
1114 err = c.EnableVersioning(context.Background(), bucketName)
1115 if err != nil {
1116 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
1117 return
1118 }
1119
1120 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
1121 args["objectName"] = objectName
1122
1123 const n = 10
1124 // Read input...
1125
1126 // Save the data concurrently.
1127 var wg sync.WaitGroup
1128 wg.Add(n)
1129 buffers := make([][]byte, n)
1130 var errs [n]error
1131 for i := 0; i < n; i++ {
1132 r := newRandomReader(int64((1<<20)*i+i), int64(i))
1133 buf, err := io.ReadAll(r)
1134 if err != nil {
1135 logError(testName, function, args, startTime, "", "unexpected failure", err)
1136 return
1137 }
1138 buffers[i] = buf
1139
1140 go func(i int) {
1141 defer wg.Done()
1142 _, errs[i] = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{PartSize: 5 << 20})
1143 }(i)
1144 }
1145 wg.Wait()
1146 for _, err := range errs {
1147 if err != nil {
1148 logError(testName, function, args, startTime, "", "PutObject failed", err)
1149 return
1150 }
1151 }
1152
1153 objectsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1154 var results []minio.ObjectInfo
1155 for info := range objectsInfo {
1156 if info.Err != nil {
1157 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1158 return
1159 }
1160 results = append(results, info)
1161 }
1162
1163 if len(results) != n {
1164 logError(testName, function, args, startTime, "", "Unexpected number of Version elements in listing objects", nil)
1165 return
1166 }
1167
1168 sort.Slice(results, func(i, j int) bool {
1169 return results[i].Size < results[j].Size
1170 })
1171
1172 sort.Slice(buffers, func(i, j int) bool {
1173 return len(buffers[i]) < len(buffers[j])
1174 })
1175
1176 for i := 0; i < len(results); i++ {
1177 opts := minio.GetObjectOptions{VersionID: results[i].VersionID}
1178 reader, err := c.GetObject(context.Background(), bucketName, objectName, opts)
1179 if err != nil {
1180 logError(testName, function, args, startTime, "", "error during GET object", err)
1181 return
1182 }
1183 statInfo, err := reader.Stat()
1184 if err != nil {
1185 logError(testName, function, args, startTime, "", "error during calling reader.Stat()", err)
1186 return
1187 }
1188 if statInfo.ETag != results[i].ETag {
1189 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected ETag", err)
1190 return
1191 }
1192 if statInfo.LastModified.Unix() != results[i].LastModified.Unix() {
1193 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected Last-Modified", err)
1194 return
1195 }
1196 if statInfo.Size != results[i].Size {
1197 logError(testName, function, args, startTime, "", "error during HEAD object, unexpected Content-Length", err)
1198 return
1199 }
1200
1201 tmpBuffer := bytes.NewBuffer([]byte{})
1202 _, err = io.Copy(tmpBuffer, reader)
1203 if err != nil {
1204 logError(testName, function, args, startTime, "", "unexpected io.Copy()", err)
1205 return
1206 }
1207
1208 if !bytes.Equal(tmpBuffer.Bytes(), buffers[i]) {
1209 logError(testName, function, args, startTime, "", "unexpected content of GetObject()", err)
1210 return
1211 }
1212 }
1213
1214 // Delete all objects and their versions as long as the bucket itself
1215 if err = cleanupVersionedBucket(bucketName, c); err != nil {
1216 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
1217 return
1218 }
1219
1220 successLogger(testName, function, args, startTime).Info()
1221}
1222
1223func testCopyObjectWithVersioning() {
1224 // initialize logging params
1225 startTime := time.Now()
1226 testName := getFuncName()
1227 function := "CopyObject()"
1228 args := map[string]interface{}{}
1229
1230 // Seed random based on current time.
1231 rand.Seed(time.Now().Unix())
1232
1233 // Instantiate new minio client object.
1234 c, err := minio.New(os.Getenv(serverEndpoint),
1235 &minio.Options{
1236 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
1237 Secure: mustParseBool(os.Getenv(enableHTTPS)),
1238 })
1239 if err != nil {
1240 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
1241 return
1242 }
1243
1244 // Enable tracing, write to stderr.
1245 // c.TraceOn(os.Stderr)
1246
1247 // Set user agent.
1248 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
1249
1250 // Generate a new random bucket name.
1251 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
1252 args["bucketName"] = bucketName
1253
1254 // Make a new bucket.
1255 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
1256 if err != nil {
1257 logError(testName, function, args, startTime, "", "Make bucket failed", err)
1258 return
1259 }
1260
1261 err = c.EnableVersioning(context.Background(), bucketName)
1262 if err != nil {
1263 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
1264 return
1265 }
1266
1267 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
1268 args["objectName"] = objectName
1269
1270 testFiles := []string{"datafile-1-b", "datafile-10-kB"}
1271 for _, testFile := range testFiles {
1272 r := getDataReader(testFile)
1273 buf, err := io.ReadAll(r)
1274 if err != nil {
1275 logError(testName, function, args, startTime, "", "unexpected failure", err)
1276 return
1277 }
1278 r.Close()
1279 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{})
1280 if err != nil {
1281 logError(testName, function, args, startTime, "", "PutObject failed", err)
1282 return
1283 }
1284 }
1285
1286 objectsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1287 var infos []minio.ObjectInfo
1288 for info := range objectsInfo {
1289 if info.Err != nil {
1290 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1291 return
1292 }
1293 infos = append(infos, info)
1294 }
1295
1296 sort.Slice(infos, func(i, j int) bool {
1297 return infos[i].Size < infos[j].Size
1298 })
1299
1300 reader, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{VersionID: infos[0].VersionID})
1301 if err != nil {
1302 logError(testName, function, args, startTime, "", "GetObject of the oldest version content failed", err)
1303 return
1304 }
1305
1306 oldestContent, err := io.ReadAll(reader)
1307 if err != nil {
1308 logError(testName, function, args, startTime, "", "Reading the oldest object version failed", err)
1309 return
1310 }
1311
1312 // Copy Source
1313 srcOpts := minio.CopySrcOptions{
1314 Bucket: bucketName,
1315 Object: objectName,
1316 VersionID: infos[0].VersionID,
1317 }
1318 args["src"] = srcOpts
1319
1320 dstOpts := minio.CopyDestOptions{
1321 Bucket: bucketName,
1322 Object: objectName + "-copy",
1323 }
1324 args["dst"] = dstOpts
1325
1326 // Perform the Copy
1327 if _, err = c.CopyObject(context.Background(), dstOpts, srcOpts); err != nil {
1328 logError(testName, function, args, startTime, "", "CopyObject failed", err)
1329 return
1330 }
1331
1332 // Destination object
1333 readerCopy, err := c.GetObject(context.Background(), bucketName, objectName+"-copy", minio.GetObjectOptions{})
1334 if err != nil {
1335 logError(testName, function, args, startTime, "", "GetObject failed", err)
1336 return
1337 }
1338 defer readerCopy.Close()
1339
1340 newestContent, err := io.ReadAll(readerCopy)
1341 if err != nil {
1342 logError(testName, function, args, startTime, "", "Reading from GetObject reader failed", err)
1343 return
1344 }
1345
1346 if len(newestContent) == 0 || !bytes.Equal(oldestContent, newestContent) {
1347 logError(testName, function, args, startTime, "", "Unexpected destination object content", err)
1348 return
1349 }
1350
1351 // Delete all objects and their versions as long as the bucket itself
1352 if err = cleanupVersionedBucket(bucketName, c); err != nil {
1353 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
1354 return
1355 }
1356
1357 successLogger(testName, function, args, startTime).Info()
1358}
1359
1360func testConcurrentCopyObjectWithVersioning() {
1361 // initialize logging params
1362 startTime := time.Now()
1363 testName := getFuncName()
1364 function := "CopyObject()"
1365 args := map[string]interface{}{}
1366
1367 // Seed random based on current time.
1368 rand.Seed(time.Now().Unix())
1369
1370 // Instantiate new minio client object.
1371 c, err := minio.New(os.Getenv(serverEndpoint),
1372 &minio.Options{
1373 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
1374 Secure: mustParseBool(os.Getenv(enableHTTPS)),
1375 })
1376 if err != nil {
1377 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
1378 return
1379 }
1380
1381 // Enable tracing, write to stderr.
1382 // c.TraceOn(os.Stderr)
1383
1384 // Set user agent.
1385 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
1386
1387 // Generate a new random bucket name.
1388 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
1389 args["bucketName"] = bucketName
1390
1391 // Make a new bucket.
1392 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
1393 if err != nil {
1394 logError(testName, function, args, startTime, "", "Make bucket failed", err)
1395 return
1396 }
1397
1398 err = c.EnableVersioning(context.Background(), bucketName)
1399 if err != nil {
1400 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
1401 return
1402 }
1403
1404 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
1405 args["objectName"] = objectName
1406
1407 testFiles := []string{"datafile-10-kB"}
1408 for _, testFile := range testFiles {
1409 r := getDataReader(testFile)
1410 buf, err := io.ReadAll(r)
1411 if err != nil {
1412 logError(testName, function, args, startTime, "", "unexpected failure", err)
1413 return
1414 }
1415 r.Close()
1416 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{})
1417 if err != nil {
1418 logError(testName, function, args, startTime, "", "PutObject failed", err)
1419 return
1420 }
1421 }
1422
1423 objectsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1424 var infos []minio.ObjectInfo
1425 for info := range objectsInfo {
1426 if info.Err != nil {
1427 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1428 return
1429 }
1430 infos = append(infos, info)
1431 }
1432
1433 sort.Slice(infos, func(i, j int) bool {
1434 return infos[i].Size < infos[j].Size
1435 })
1436
1437 reader, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{VersionID: infos[0].VersionID})
1438 if err != nil {
1439 logError(testName, function, args, startTime, "", "GetObject of the oldest version content failed", err)
1440 return
1441 }
1442
1443 oldestContent, err := io.ReadAll(reader)
1444 if err != nil {
1445 logError(testName, function, args, startTime, "", "Reading the oldest object version failed", err)
1446 return
1447 }
1448
1449 // Copy Source
1450 srcOpts := minio.CopySrcOptions{
1451 Bucket: bucketName,
1452 Object: objectName,
1453 VersionID: infos[0].VersionID,
1454 }
1455 args["src"] = srcOpts
1456
1457 dstOpts := minio.CopyDestOptions{
1458 Bucket: bucketName,
1459 Object: objectName + "-copy",
1460 }
1461 args["dst"] = dstOpts
1462
1463 // Perform the Copy concurrently
1464 const n = 10
1465 var wg sync.WaitGroup
1466 wg.Add(n)
1467 var errs [n]error
1468 for i := 0; i < n; i++ {
1469 go func(i int) {
1470 defer wg.Done()
1471 _, errs[i] = c.CopyObject(context.Background(), dstOpts, srcOpts)
1472 }(i)
1473 }
1474 wg.Wait()
1475 for _, err := range errs {
1476 if err != nil {
1477 logError(testName, function, args, startTime, "", "CopyObject failed", err)
1478 return
1479 }
1480 }
1481
1482 objectsInfo = c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: false, Prefix: dstOpts.Object})
1483 infos = []minio.ObjectInfo{}
1484 for info := range objectsInfo {
1485 // Destination object
1486 readerCopy, err := c.GetObject(context.Background(), bucketName, objectName+"-copy", minio.GetObjectOptions{VersionID: info.VersionID})
1487 if err != nil {
1488 logError(testName, function, args, startTime, "", "GetObject failed", err)
1489 return
1490 }
1491 defer readerCopy.Close()
1492
1493 newestContent, err := io.ReadAll(readerCopy)
1494 if err != nil {
1495 logError(testName, function, args, startTime, "", "Reading from GetObject reader failed", err)
1496 return
1497 }
1498
1499 if len(newestContent) == 0 || !bytes.Equal(oldestContent, newestContent) {
1500 logError(testName, function, args, startTime, "", "Unexpected destination object content", err)
1501 return
1502 }
1503 infos = append(infos, info)
1504 }
1505
1506 if len(infos) != n {
1507 logError(testName, function, args, startTime, "", "Unexpected number of Version elements in listing objects", nil)
1508 return
1509 }
1510
1511 // Delete all objects and their versions as long as the bucket itself
1512 if err = cleanupVersionedBucket(bucketName, c); err != nil {
1513 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
1514 return
1515 }
1516
1517 successLogger(testName, function, args, startTime).Info()
1518}
1519
1520func testComposeObjectWithVersioning() {
1521 // initialize logging params
1522 startTime := time.Now()
1523 testName := getFuncName()
1524 function := "ComposeObject()"
1525 args := map[string]interface{}{}
1526
1527 // Seed random based on current time.
1528 rand.Seed(time.Now().Unix())
1529
1530 // Instantiate new minio client object.
1531 c, err := minio.New(os.Getenv(serverEndpoint),
1532 &minio.Options{
1533 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
1534 Secure: mustParseBool(os.Getenv(enableHTTPS)),
1535 })
1536 if err != nil {
1537 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
1538 return
1539 }
1540
1541 // Enable tracing, write to stderr.
1542 // c.TraceOn(os.Stderr)
1543
1544 // Set user agent.
1545 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
1546
1547 // Generate a new random bucket name.
1548 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
1549 args["bucketName"] = bucketName
1550
1551 // Make a new bucket.
1552 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
1553 if err != nil {
1554 logError(testName, function, args, startTime, "", "Make bucket failed", err)
1555 return
1556 }
1557
1558 err = c.EnableVersioning(context.Background(), bucketName)
1559 if err != nil {
1560 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
1561 return
1562 }
1563
1564 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
1565 args["objectName"] = objectName
1566
1567 // var testFiles = []string{"datafile-5-MB", "datafile-10-kB"}
1568 testFiles := []string{"datafile-5-MB", "datafile-10-kB"}
1569 var testFilesBytes [][]byte
1570
1571 for _, testFile := range testFiles {
1572 r := getDataReader(testFile)
1573 buf, err := io.ReadAll(r)
1574 if err != nil {
1575 logError(testName, function, args, startTime, "", "unexpected failure", err)
1576 return
1577 }
1578 r.Close()
1579 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{})
1580 if err != nil {
1581 logError(testName, function, args, startTime, "", "PutObject failed", err)
1582 return
1583 }
1584 testFilesBytes = append(testFilesBytes, buf)
1585 }
1586
1587 objectsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1588
1589 var results []minio.ObjectInfo
1590 for info := range objectsInfo {
1591 if info.Err != nil {
1592 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1593 return
1594 }
1595 results = append(results, info)
1596 }
1597
1598 sort.SliceStable(results, func(i, j int) bool {
1599 return results[i].Size > results[j].Size
1600 })
1601
1602 // Source objects to concatenate. We also specify decryption
1603 // key for each
1604 src1 := minio.CopySrcOptions{
1605 Bucket: bucketName,
1606 Object: objectName,
1607 VersionID: results[0].VersionID,
1608 }
1609
1610 src2 := minio.CopySrcOptions{
1611 Bucket: bucketName,
1612 Object: objectName,
1613 VersionID: results[1].VersionID,
1614 }
1615
1616 dst := minio.CopyDestOptions{
1617 Bucket: bucketName,
1618 Object: objectName + "-copy",
1619 }
1620
1621 _, err = c.ComposeObject(context.Background(), dst, src1, src2)
1622 if err != nil {
1623 logError(testName, function, args, startTime, "", "ComposeObject failed", err)
1624 return
1625 }
1626
1627 // Destination object
1628 readerCopy, err := c.GetObject(context.Background(), bucketName, objectName+"-copy", minio.GetObjectOptions{})
1629 if err != nil {
1630 logError(testName, function, args, startTime, "", "GetObject of the copy object failed", err)
1631 return
1632 }
1633 defer readerCopy.Close()
1634
1635 copyContentBytes, err := io.ReadAll(readerCopy)
1636 if err != nil {
1637 logError(testName, function, args, startTime, "", "Reading from the copy object reader failed", err)
1638 return
1639 }
1640
1641 var expectedContent []byte
1642 for _, fileBytes := range testFilesBytes {
1643 expectedContent = append(expectedContent, fileBytes...)
1644 }
1645
1646 if len(copyContentBytes) == 0 || !bytes.Equal(copyContentBytes, expectedContent) {
1647 logError(testName, function, args, startTime, "", "Unexpected destination object content", err)
1648 return
1649 }
1650
1651 // Delete all objects and their versions as long as the bucket itself
1652 if err = cleanupVersionedBucket(bucketName, c); err != nil {
1653 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
1654 return
1655 }
1656
1657 successLogger(testName, function, args, startTime).Info()
1658}
1659
1660func testRemoveObjectWithVersioning() {
1661 // initialize logging params
1662 startTime := time.Now()
1663 testName := getFuncName()
1664 function := "DeleteObject()"
1665 args := map[string]interface{}{}
1666
1667 // Seed random based on current time.
1668 rand.Seed(time.Now().Unix())
1669
1670 // Instantiate new minio client object.
1671 c, err := minio.New(os.Getenv(serverEndpoint),
1672 &minio.Options{
1673 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
1674 Secure: mustParseBool(os.Getenv(enableHTTPS)),
1675 })
1676 if err != nil {
1677 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
1678 return
1679 }
1680
1681 // Enable tracing, write to stderr.
1682 // c.TraceOn(os.Stderr)
1683
1684 // Set user agent.
1685 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
1686
1687 // Generate a new random bucket name.
1688 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
1689 args["bucketName"] = bucketName
1690
1691 // Make a new bucket.
1692 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
1693 if err != nil {
1694 logError(testName, function, args, startTime, "", "Make bucket failed", err)
1695 return
1696 }
1697
1698 err = c.EnableVersioning(context.Background(), bucketName)
1699 if err != nil {
1700 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
1701 return
1702 }
1703
1704 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
1705 args["objectName"] = objectName
1706
1707 _, err = c.PutObject(context.Background(), bucketName, objectName, getDataReader("datafile-10-kB"), int64(dataFileMap["datafile-10-kB"]), minio.PutObjectOptions{})
1708 if err != nil {
1709 logError(testName, function, args, startTime, "", "PutObject failed", err)
1710 return
1711 }
1712
1713 objectsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1714 var version minio.ObjectInfo
1715 for info := range objectsInfo {
1716 if info.Err != nil {
1717 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1718 return
1719 }
1720 version = info
1721 break
1722 }
1723
1724 err = c.RemoveObject(context.Background(), bucketName, objectName, minio.RemoveObjectOptions{VersionID: version.VersionID})
1725 if err != nil {
1726 logError(testName, function, args, startTime, "", "DeleteObject failed", err)
1727 return
1728 }
1729
1730 objectsInfo = c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1731 for range objectsInfo {
1732 logError(testName, function, args, startTime, "", "Unexpected versioning info, should not have any one ", err)
1733 return
1734 }
1735 // test delete marker version id is non-null
1736 _, err = c.PutObject(context.Background(), bucketName, objectName, getDataReader("datafile-10-kB"), int64(dataFileMap["datafile-10-kB"]), minio.PutObjectOptions{})
1737 if err != nil {
1738 logError(testName, function, args, startTime, "", "PutObject failed", err)
1739 return
1740 }
1741 // create delete marker
1742 err = c.RemoveObject(context.Background(), bucketName, objectName, minio.RemoveObjectOptions{})
1743 if err != nil {
1744 logError(testName, function, args, startTime, "", "DeleteObject failed", err)
1745 return
1746 }
1747 objectsInfo = c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1748 idx := 0
1749 for info := range objectsInfo {
1750 if info.Err != nil {
1751 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1752 return
1753 }
1754 if idx == 0 {
1755 if !info.IsDeleteMarker {
1756 logError(testName, function, args, startTime, "", "Unexpected error - expected delete marker to have been created", err)
1757 return
1758 }
1759 if info.VersionID == "" {
1760 logError(testName, function, args, startTime, "", "Unexpected error - expected delete marker to be versioned", err)
1761 return
1762 }
1763 }
1764 idx++
1765 }
1766
1767 defer cleanupBucket(bucketName, c)
1768
1769 successLogger(testName, function, args, startTime).Info()
1770}
1771
1772func testRemoveObjectsWithVersioning() {
1773 // initialize logging params
1774 startTime := time.Now()
1775 testName := getFuncName()
1776 function := "DeleteObjects()"
1777 args := map[string]interface{}{}
1778
1779 // Seed random based on current time.
1780 rand.Seed(time.Now().Unix())
1781
1782 // Instantiate new minio client object.
1783 c, err := minio.New(os.Getenv(serverEndpoint),
1784 &minio.Options{
1785 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
1786 Secure: mustParseBool(os.Getenv(enableHTTPS)),
1787 })
1788 if err != nil {
1789 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
1790 return
1791 }
1792
1793 // Enable tracing, write to stderr.
1794 // c.TraceOn(os.Stderr)
1795
1796 // Set user agent.
1797 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
1798
1799 // Generate a new random bucket name.
1800 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
1801 args["bucketName"] = bucketName
1802
1803 // Make a new bucket.
1804 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
1805 if err != nil {
1806 logError(testName, function, args, startTime, "", "Make bucket failed", err)
1807 return
1808 }
1809
1810 err = c.EnableVersioning(context.Background(), bucketName)
1811 if err != nil {
1812 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
1813 return
1814 }
1815
1816 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
1817 args["objectName"] = objectName
1818
1819 _, err = c.PutObject(context.Background(), bucketName, objectName, getDataReader("datafile-10-kB"), int64(dataFileMap["datafile-10-kB"]), minio.PutObjectOptions{})
1820 if err != nil {
1821 logError(testName, function, args, startTime, "", "PutObject failed", err)
1822 return
1823 }
1824
1825 objectsVersions := make(chan minio.ObjectInfo)
1826 go func() {
1827 objectsVersionsInfo := c.ListObjects(context.Background(), bucketName,
1828 minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1829 for info := range objectsVersionsInfo {
1830 if info.Err != nil {
1831 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1832 return
1833 }
1834 objectsVersions <- info
1835 }
1836 close(objectsVersions)
1837 }()
1838
1839 removeErrors := c.RemoveObjects(context.Background(), bucketName, objectsVersions, minio.RemoveObjectsOptions{})
1840 if err != nil {
1841 logError(testName, function, args, startTime, "", "DeleteObjects call failed", err)
1842 return
1843 }
1844
1845 for e := range removeErrors {
1846 if e.Err != nil {
1847 logError(testName, function, args, startTime, "", "Single delete operation failed", err)
1848 return
1849 }
1850 }
1851
1852 objectsVersionsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1853 for range objectsVersionsInfo {
1854 logError(testName, function, args, startTime, "", "Unexpected versioning info, should not have any one ", err)
1855 return
1856 }
1857
1858 err = c.RemoveBucket(context.Background(), bucketName)
1859 if err != nil {
1860 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
1861 return
1862 }
1863
1864 successLogger(testName, function, args, startTime).Info()
1865}
1866
1867func testObjectTaggingWithVersioning() {
1868 // initialize logging params
1869 startTime := time.Now()
1870 testName := getFuncName()
1871 function := "{Get,Set,Remove}ObjectTagging()"
1872 args := map[string]interface{}{}
1873
1874 // Seed random based on current time.
1875 rand.Seed(time.Now().Unix())
1876
1877 // Instantiate new minio client object.
1878 c, err := minio.New(os.Getenv(serverEndpoint),
1879 &minio.Options{
1880 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
1881 Secure: mustParseBool(os.Getenv(enableHTTPS)),
1882 })
1883 if err != nil {
1884 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
1885 return
1886 }
1887
1888 // Enable tracing, write to stderr.
1889 // c.TraceOn(os.Stderr)
1890
1891 // Set user agent.
1892 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
1893
1894 // Generate a new random bucket name.
1895 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
1896 args["bucketName"] = bucketName
1897
1898 // Make a new bucket.
1899 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
1900 if err != nil {
1901 logError(testName, function, args, startTime, "", "Make bucket failed", err)
1902 return
1903 }
1904
1905 err = c.EnableVersioning(context.Background(), bucketName)
1906 if err != nil {
1907 logError(testName, function, args, startTime, "", "Enable versioning failed", err)
1908 return
1909 }
1910
1911 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
1912 args["objectName"] = objectName
1913
1914 for _, file := range []string{"datafile-1-b", "datafile-10-kB"} {
1915 _, err = c.PutObject(context.Background(), bucketName, objectName, getDataReader(file), int64(dataFileMap[file]), minio.PutObjectOptions{})
1916 if err != nil {
1917 logError(testName, function, args, startTime, "", "PutObject failed", err)
1918 return
1919 }
1920 }
1921
1922 versionsInfo := c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{WithVersions: true, Recursive: true})
1923
1924 var versions []minio.ObjectInfo
1925 for info := range versionsInfo {
1926 if info.Err != nil {
1927 logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err)
1928 return
1929 }
1930 versions = append(versions, info)
1931 }
1932
1933 sort.SliceStable(versions, func(i, j int) bool {
1934 return versions[i].Size < versions[j].Size
1935 })
1936
1937 tagsV1 := map[string]string{"key1": "val1"}
1938 t1, err := tags.MapToObjectTags(tagsV1)
1939 if err != nil {
1940 logError(testName, function, args, startTime, "", "PutObjectTagging (1) failed", err)
1941 return
1942 }
1943
1944 err = c.PutObjectTagging(context.Background(), bucketName, objectName, t1, minio.PutObjectTaggingOptions{VersionID: versions[0].VersionID})
1945 if err != nil {
1946 logError(testName, function, args, startTime, "", "PutObjectTagging (1) failed", err)
1947 return
1948 }
1949
1950 tagsV2 := map[string]string{"key2": "val2"}
1951 t2, err := tags.MapToObjectTags(tagsV2)
1952 if err != nil {
1953 logError(testName, function, args, startTime, "", "PutObjectTagging (1) failed", err)
1954 return
1955 }
1956
1957 err = c.PutObjectTagging(context.Background(), bucketName, objectName, t2, minio.PutObjectTaggingOptions{VersionID: versions[1].VersionID})
1958 if err != nil {
1959 logError(testName, function, args, startTime, "", "PutObjectTagging (2) failed", err)
1960 return
1961 }
1962
1963 tagsEqual := func(tags1, tags2 map[string]string) bool {
1964 for k1, v1 := range tags1 {
1965 v2, found := tags2[k1]
1966 if found {
1967 if v1 != v2 {
1968 return false
1969 }
1970 }
1971 }
1972 return true
1973 }
1974
1975 gotTagsV1, err := c.GetObjectTagging(context.Background(), bucketName, objectName, minio.GetObjectTaggingOptions{VersionID: versions[0].VersionID})
1976 if err != nil {
1977 logError(testName, function, args, startTime, "", "GetObjectTagging failed", err)
1978 return
1979 }
1980
1981 if !tagsEqual(t1.ToMap(), gotTagsV1.ToMap()) {
1982 logError(testName, function, args, startTime, "", "Unexpected tags content (1)", err)
1983 return
1984 }
1985
1986 gotTagsV2, err := c.GetObjectTagging(context.Background(), bucketName, objectName, minio.GetObjectTaggingOptions{})
1987 if err != nil {
1988 logError(testName, function, args, startTime, "", "GetObjectTaggingContext failed", err)
1989 return
1990 }
1991
1992 if !tagsEqual(t2.ToMap(), gotTagsV2.ToMap()) {
1993 logError(testName, function, args, startTime, "", "Unexpected tags content (2)", err)
1994 return
1995 }
1996
1997 err = c.RemoveObjectTagging(context.Background(), bucketName, objectName, minio.RemoveObjectTaggingOptions{VersionID: versions[0].VersionID})
1998 if err != nil {
1999 logError(testName, function, args, startTime, "", "PutObjectTagging (2) failed", err)
2000 return
2001 }
2002
2003 emptyTags, err := c.GetObjectTagging(context.Background(), bucketName, objectName,
2004 minio.GetObjectTaggingOptions{VersionID: versions[0].VersionID})
2005 if err != nil {
2006 logError(testName, function, args, startTime, "", "GetObjectTagging failed", err)
2007 return
2008 }
2009
2010 if len(emptyTags.ToMap()) != 0 {
2011 logError(testName, function, args, startTime, "", "Unexpected tags content (2)", err)
2012 return
2013 }
2014
2015 // Delete all objects and their versions as long as the bucket itself
2016 if err = cleanupVersionedBucket(bucketName, c); err != nil {
2017 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
2018 return
2019 }
2020
2021 successLogger(testName, function, args, startTime).Info()
2022}
2023
2024// Test PutObject with custom checksums.
2025func testPutObjectWithChecksums() {
2026 // initialize logging params
2027 startTime := time.Now()
2028 testName := getFuncName()
2029 function := "PutObject(bucketName, objectName, reader,size, opts)"
2030 args := map[string]interface{}{
2031 "bucketName": "",
2032 "objectName": "",
2033 "opts": "minio.PutObjectOptions{UserMetadata: metadata, Progress: progress}",
2034 }
2035
2036 if !isFullMode() {
2037 ignoredLog(testName, function, args, startTime, "Skipping functional tests for short/quick runs").Info()
2038 return
2039 }
2040
2041 // Seed random based on current time.
2042 rand.Seed(time.Now().Unix())
2043
2044 // Instantiate new minio client object.
2045 c, err := minio.New(os.Getenv(serverEndpoint),
2046 &minio.Options{
2047 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
2048 Secure: mustParseBool(os.Getenv(enableHTTPS)),
2049 })
2050 if err != nil {
2051 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
2052 return
2053 }
2054
2055 // Enable tracing, write to stderr.
2056 // c.TraceOn(os.Stderr)
2057
2058 // Set user agent.
2059 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
2060
2061 // Generate a new random bucket name.
2062 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
2063 args["bucketName"] = bucketName
2064
2065 // Make a new bucket.
2066 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
2067 if err != nil {
2068 logError(testName, function, args, startTime, "", "Make bucket failed", err)
2069 return
2070 }
2071
2072 defer cleanupBucket(bucketName, c)
2073 tests := []struct {
2074 header string
2075 hasher hash.Hash
2076
2077 // Checksum values
2078 ChecksumCRC32 string
2079 ChecksumCRC32C string
2080 ChecksumSHA1 string
2081 ChecksumSHA256 string
2082 }{
2083 {header: "x-amz-checksum-crc32", hasher: crc32.NewIEEE()},
2084 {header: "x-amz-checksum-crc32c", hasher: crc32.New(crc32.MakeTable(crc32.Castagnoli))},
2085 {header: "x-amz-checksum-sha1", hasher: sha1.New()},
2086 {header: "x-amz-checksum-sha256", hasher: sha256.New()},
2087 }
2088
2089 for i, test := range tests {
2090 bufSize := dataFileMap["datafile-10-kB"]
2091
2092 // Save the data
2093 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
2094 args["objectName"] = objectName
2095
2096 cmpChecksum := func(got, want string) {
2097 if want != got {
2098 logError(testName, function, args, startTime, "", "checksum mismatch", fmt.Errorf("want %s, got %s", want, got))
2099 return
2100 }
2101 }
2102
2103 meta := map[string]string{}
2104 reader := getDataReader("datafile-10-kB")
2105 b, err := io.ReadAll(reader)
2106 if err != nil {
2107 logError(testName, function, args, startTime, "", "Read failed", err)
2108 return
2109 }
2110 h := test.hasher
2111 h.Reset()
2112 // Wrong CRC.
2113 meta[test.header] = base64.StdEncoding.EncodeToString(h.Sum(nil))
2114 args["metadata"] = meta
2115 args["range"] = "false"
2116
2117 resp, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(b), int64(bufSize), minio.PutObjectOptions{
2118 DisableMultipart: true,
2119 UserMetadata: meta,
2120 })
2121 if err == nil {
2122 if i == 0 && resp.ChecksumCRC32 == "" {
2123 ignoredLog(testName, function, args, startTime, "Checksums does not appear to be supported by backend").Info()
2124 return
2125 }
2126 logError(testName, function, args, startTime, "", "PutObject failed", err)
2127 return
2128 }
2129
2130 // Set correct CRC.
2131 h.Write(b)
2132 meta[test.header] = base64.StdEncoding.EncodeToString(h.Sum(nil))
2133 reader.Close()
2134
2135 resp, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(b), int64(bufSize), minio.PutObjectOptions{
2136 DisableMultipart: true,
2137 DisableContentSha256: true,
2138 UserMetadata: meta,
2139 })
2140 if err != nil {
2141 logError(testName, function, args, startTime, "", "PutObject failed", err)
2142 return
2143 }
2144 cmpChecksum(resp.ChecksumSHA256, meta["x-amz-checksum-sha256"])
2145 cmpChecksum(resp.ChecksumSHA1, meta["x-amz-checksum-sha1"])
2146 cmpChecksum(resp.ChecksumCRC32, meta["x-amz-checksum-crc32"])
2147 cmpChecksum(resp.ChecksumCRC32C, meta["x-amz-checksum-crc32c"])
2148
2149 // Read the data back
2150 gopts := minio.GetObjectOptions{Checksum: true}
2151
2152 r, err := c.GetObject(context.Background(), bucketName, objectName, gopts)
2153 if err != nil {
2154 logError(testName, function, args, startTime, "", "GetObject failed", err)
2155 return
2156 }
2157
2158 st, err := r.Stat()
2159 if err != nil {
2160 logError(testName, function, args, startTime, "", "Stat failed", err)
2161 return
2162 }
2163 cmpChecksum(st.ChecksumSHA256, meta["x-amz-checksum-sha256"])
2164 cmpChecksum(st.ChecksumSHA1, meta["x-amz-checksum-sha1"])
2165 cmpChecksum(st.ChecksumCRC32, meta["x-amz-checksum-crc32"])
2166 cmpChecksum(st.ChecksumCRC32C, meta["x-amz-checksum-crc32c"])
2167
2168 if st.Size != int64(bufSize) {
2169 logError(testName, function, args, startTime, "", "Number of bytes returned by PutObject does not match GetObject, expected "+string(bufSize)+" got "+string(st.Size), err)
2170 return
2171 }
2172
2173 if err := r.Close(); err != nil {
2174 logError(testName, function, args, startTime, "", "Object Close failed", err)
2175 return
2176 }
2177 if err := r.Close(); err == nil {
2178 logError(testName, function, args, startTime, "", "Object already closed, should respond with error", err)
2179 return
2180 }
2181
2182 args["range"] = "true"
2183 err = gopts.SetRange(100, 1000)
2184 if err != nil {
2185 logError(testName, function, args, startTime, "", "SetRange failed", err)
2186 return
2187 }
2188 r, err = c.GetObject(context.Background(), bucketName, objectName, gopts)
2189 if err != nil {
2190 logError(testName, function, args, startTime, "", "GetObject failed", err)
2191 return
2192 }
2193
2194 b, err = io.ReadAll(r)
2195 if err != nil {
2196 logError(testName, function, args, startTime, "", "Read failed", err)
2197 return
2198 }
2199 st, err = r.Stat()
2200 if err != nil {
2201 logError(testName, function, args, startTime, "", "Stat failed", err)
2202 return
2203 }
2204
2205 // Range requests should return empty checksums...
2206 cmpChecksum(st.ChecksumSHA256, "")
2207 cmpChecksum(st.ChecksumSHA1, "")
2208 cmpChecksum(st.ChecksumCRC32, "")
2209 cmpChecksum(st.ChecksumCRC32C, "")
2210
2211 delete(args, "range")
2212 delete(args, "metadata")
2213 }
2214
2215 successLogger(testName, function, args, startTime).Info()
2216}
2217
2218// Test PutObject with custom checksums.
2219func testPutMultipartObjectWithChecksums() {
2220 // initialize logging params
2221 startTime := time.Now()
2222 testName := getFuncName()
2223 function := "PutObject(bucketName, objectName, reader,size, opts)"
2224 args := map[string]interface{}{
2225 "bucketName": "",
2226 "objectName": "",
2227 "opts": "minio.PutObjectOptions{UserMetadata: metadata, Progress: progress}",
2228 }
2229
2230 if !isFullMode() {
2231 ignoredLog(testName, function, args, startTime, "Skipping functional tests for short/quick runs").Info()
2232 return
2233 }
2234
2235 // Seed random based on current time.
2236 rand.Seed(time.Now().Unix())
2237
2238 // Instantiate new minio client object.
2239 c, err := minio.New(os.Getenv(serverEndpoint),
2240 &minio.Options{
2241 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
2242 Secure: mustParseBool(os.Getenv(enableHTTPS)),
2243 })
2244 if err != nil {
2245 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
2246 return
2247 }
2248
2249 // Enable tracing, write to stderr.
2250 // c.TraceOn(os.Stderr)
2251
2252 // Set user agent.
2253 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
2254
2255 // Generate a new random bucket name.
2256 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
2257 args["bucketName"] = bucketName
2258
2259 // Make a new bucket.
2260 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
2261 if err != nil {
2262 logError(testName, function, args, startTime, "", "Make bucket failed", err)
2263 return
2264 }
2265
2266 hashMultiPart := func(b []byte, partSize int, hasher hash.Hash) string {
2267 r := bytes.NewReader(b)
2268 tmp := make([]byte, partSize)
2269 parts := 0
2270 var all []byte
2271 for {
2272 n, err := io.ReadFull(r, tmp)
2273 if err != nil && err != io.ErrUnexpectedEOF {
2274 logError(testName, function, args, startTime, "", "Calc crc failed", err)
2275 }
2276 if n == 0 {
2277 break
2278 }
2279 parts++
2280 hasher.Reset()
2281 hasher.Write(tmp[:n])
2282 all = append(all, hasher.Sum(nil)...)
2283 if err != nil {
2284 break
2285 }
2286 }
2287 hasher.Reset()
2288 hasher.Write(all)
2289 return fmt.Sprintf("%s-%d", base64.StdEncoding.EncodeToString(hasher.Sum(nil)), parts)
2290 }
2291 defer cleanupBucket(bucketName, c)
2292 tests := []struct {
2293 header string
2294 hasher hash.Hash
2295
2296 // Checksum values
2297 ChecksumCRC32 string
2298 ChecksumCRC32C string
2299 ChecksumSHA1 string
2300 ChecksumSHA256 string
2301 }{
2302 // Currently there is no way to override the checksum type.
2303 {header: "x-amz-checksum-crc32c", hasher: crc32.New(crc32.MakeTable(crc32.Castagnoli)), ChecksumCRC32C: "OpEx0Q==-13"},
2304 }
2305
2306 for _, test := range tests {
2307 bufSize := dataFileMap["datafile-129-MB"]
2308
2309 // Save the data
2310 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
2311 args["objectName"] = objectName
2312
2313 cmpChecksum := func(got, want string) {
2314 if want != got {
2315 // logError(testName, function, args, startTime, "", "checksum mismatch", fmt.Errorf("want %s, got %s", want, got))
2316 fmt.Printf("want %s, got %s\n", want, got)
2317 return
2318 }
2319 }
2320
2321 const partSize = 10 << 20
2322 reader := getDataReader("datafile-129-MB")
2323 b, err := io.ReadAll(reader)
2324 if err != nil {
2325 logError(testName, function, args, startTime, "", "Read failed", err)
2326 return
2327 }
2328 reader.Close()
2329 h := test.hasher
2330 h.Reset()
2331 test.ChecksumCRC32C = hashMultiPart(b, partSize, test.hasher)
2332
2333 // Set correct CRC.
2334
2335 resp, err := c.PutObject(context.Background(), bucketName, objectName, io.NopCloser(bytes.NewReader(b)), int64(bufSize), minio.PutObjectOptions{
2336 DisableContentSha256: true,
2337 DisableMultipart: false,
2338 UserMetadata: nil,
2339 PartSize: partSize,
2340 })
2341 if err != nil {
2342 logError(testName, function, args, startTime, "", "PutObject failed", err)
2343 return
2344 }
2345 cmpChecksum(resp.ChecksumSHA256, test.ChecksumSHA256)
2346 cmpChecksum(resp.ChecksumSHA1, test.ChecksumSHA1)
2347 cmpChecksum(resp.ChecksumCRC32, test.ChecksumCRC32)
2348 cmpChecksum(resp.ChecksumCRC32C, test.ChecksumCRC32C)
2349
2350 // Read the data back
2351 gopts := minio.GetObjectOptions{Checksum: true}
2352 gopts.PartNumber = 2
2353
2354 // We cannot use StatObject, since it ignores partnumber.
2355 r, err := c.GetObject(context.Background(), bucketName, objectName, gopts)
2356 if err != nil {
2357 logError(testName, function, args, startTime, "", "GetObject failed", err)
2358 return
2359 }
2360 io.Copy(io.Discard, r)
2361 st, err := r.Stat()
2362 if err != nil {
2363 logError(testName, function, args, startTime, "", "Stat failed", err)
2364 return
2365 }
2366
2367 // Test part 2 checksum...
2368 h.Reset()
2369 h.Write(b[partSize : 2*partSize])
2370 got := base64.StdEncoding.EncodeToString(h.Sum(nil))
2371 if test.ChecksumSHA256 != "" {
2372 cmpChecksum(st.ChecksumSHA256, got)
2373 }
2374 if test.ChecksumSHA1 != "" {
2375 cmpChecksum(st.ChecksumSHA1, got)
2376 }
2377 if test.ChecksumCRC32 != "" {
2378 cmpChecksum(st.ChecksumCRC32, got)
2379 }
2380 if test.ChecksumCRC32C != "" {
2381 cmpChecksum(st.ChecksumCRC32C, got)
2382 }
2383
2384 delete(args, "metadata")
2385 }
2386
2387 successLogger(testName, function, args, startTime).Info()
2388}
2389
2390// Test PutObject with trailing checksums.
2391func testTrailingChecksums() {
2392 // initialize logging params
2393 startTime := time.Now()
2394 testName := getFuncName()
2395 function := "PutObject(bucketName, objectName, reader,size, opts)"
2396 args := map[string]interface{}{
2397 "bucketName": "",
2398 "objectName": "",
2399 "opts": "minio.PutObjectOptions{UserMetadata: metadata, Progress: progress}",
2400 }
2401
2402 if !isFullMode() {
2403 ignoredLog(testName, function, args, startTime, "Skipping functional tests for short/quick runs").Info()
2404 return
2405 }
2406
2407 // Instantiate new minio client object.
2408 c, err := minio.New(os.Getenv(serverEndpoint),
2409 &minio.Options{
2410 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
2411 Secure: mustParseBool(os.Getenv(enableHTTPS)),
2412 TrailingHeaders: true,
2413 })
2414 if err != nil {
2415 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
2416 return
2417 }
2418
2419 // Enable tracing, write to stderr.
2420 // c.TraceOn(os.Stderr)
2421
2422 // Set user agent.
2423 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
2424
2425 // Generate a new random bucket name.
2426 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
2427 args["bucketName"] = bucketName
2428
2429 // Make a new bucket.
2430 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
2431 if err != nil {
2432 logError(testName, function, args, startTime, "", "Make bucket failed", err)
2433 return
2434 }
2435
2436 hashMultiPart := func(b []byte, partSize int, hasher hash.Hash) string {
2437 r := bytes.NewReader(b)
2438 tmp := make([]byte, partSize)
2439 parts := 0
2440 var all []byte
2441 for {
2442 n, err := io.ReadFull(r, tmp)
2443 if err != nil && err != io.ErrUnexpectedEOF {
2444 logError(testName, function, args, startTime, "", "Calc crc failed", err)
2445 }
2446 if n == 0 {
2447 break
2448 }
2449 parts++
2450 hasher.Reset()
2451 hasher.Write(tmp[:n])
2452 all = append(all, hasher.Sum(nil)...)
2453 if err != nil {
2454 break
2455 }
2456 }
2457 hasher.Reset()
2458 hasher.Write(all)
2459 return fmt.Sprintf("%s-%d", base64.StdEncoding.EncodeToString(hasher.Sum(nil)), parts)
2460 }
2461 defer cleanupBucket(bucketName, c)
2462 tests := []struct {
2463 header string
2464 hasher hash.Hash
2465
2466 // Checksum values
2467 ChecksumCRC32 string
2468 ChecksumCRC32C string
2469 ChecksumSHA1 string
2470 ChecksumSHA256 string
2471 PO minio.PutObjectOptions
2472 }{
2473 // Currently there is no way to override the checksum type.
2474 {
2475 header: "x-amz-checksum-crc32c",
2476 hasher: crc32.New(crc32.MakeTable(crc32.Castagnoli)),
2477 ChecksumCRC32C: "set",
2478 PO: minio.PutObjectOptions{
2479 DisableContentSha256: true,
2480 DisableMultipart: false,
2481 UserMetadata: nil,
2482 PartSize: 5 << 20,
2483 },
2484 },
2485 {
2486 header: "x-amz-checksum-crc32c",
2487 hasher: crc32.New(crc32.MakeTable(crc32.Castagnoli)),
2488 ChecksumCRC32C: "set",
2489 PO: minio.PutObjectOptions{
2490 DisableContentSha256: true,
2491 DisableMultipart: false,
2492 UserMetadata: nil,
2493 PartSize: 6_645_654, // Rather arbitrary size
2494 },
2495 },
2496 {
2497 header: "x-amz-checksum-crc32c",
2498 hasher: crc32.New(crc32.MakeTable(crc32.Castagnoli)),
2499 ChecksumCRC32C: "set",
2500 PO: minio.PutObjectOptions{
2501 DisableContentSha256: false,
2502 DisableMultipart: false,
2503 UserMetadata: nil,
2504 PartSize: 5 << 20,
2505 },
2506 },
2507 {
2508 header: "x-amz-checksum-crc32c",
2509 hasher: crc32.New(crc32.MakeTable(crc32.Castagnoli)),
2510 ChecksumCRC32C: "set",
2511 PO: minio.PutObjectOptions{
2512 DisableContentSha256: false,
2513 DisableMultipart: false,
2514 UserMetadata: nil,
2515 PartSize: 6_645_654, // Rather arbitrary size
2516 },
2517 },
2518 }
2519
2520 for _, test := range tests {
2521 bufSize := dataFileMap["datafile-11-MB"]
2522
2523 // Save the data
2524 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
2525 args["objectName"] = objectName
2526
2527 cmpChecksum := func(got, want string) {
2528 if want != got {
2529 logError(testName, function, args, startTime, "", "checksum mismatch", fmt.Errorf("want %q, got %q", want, got))
2530 return
2531 }
2532 }
2533
2534 reader := getDataReader("datafile-11-MB")
2535 b, err := io.ReadAll(reader)
2536 if err != nil {
2537 logError(testName, function, args, startTime, "", "Read failed", err)
2538 return
2539 }
2540 reader.Close()
2541 h := test.hasher
2542 h.Reset()
2543 test.ChecksumCRC32C = hashMultiPart(b, int(test.PO.PartSize), test.hasher)
2544
2545 // Set correct CRC.
2546 // c.TraceOn(os.Stderr)
2547 resp, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(b), int64(bufSize), test.PO)
2548 if err != nil {
2549 logError(testName, function, args, startTime, "", "PutObject failed", err)
2550 return
2551 }
2552 // c.TraceOff()
2553 cmpChecksum(resp.ChecksumSHA256, test.ChecksumSHA256)
2554 cmpChecksum(resp.ChecksumSHA1, test.ChecksumSHA1)
2555 cmpChecksum(resp.ChecksumCRC32, test.ChecksumCRC32)
2556 cmpChecksum(resp.ChecksumCRC32C, test.ChecksumCRC32C)
2557
2558 // Read the data back
2559 gopts := minio.GetObjectOptions{Checksum: true}
2560 gopts.PartNumber = 2
2561
2562 // We cannot use StatObject, since it ignores partnumber.
2563 r, err := c.GetObject(context.Background(), bucketName, objectName, gopts)
2564 if err != nil {
2565 logError(testName, function, args, startTime, "", "GetObject failed", err)
2566 return
2567 }
2568 io.Copy(io.Discard, r)
2569 st, err := r.Stat()
2570 if err != nil {
2571 logError(testName, function, args, startTime, "", "Stat failed", err)
2572 return
2573 }
2574
2575 // Test part 2 checksum...
2576 h.Reset()
2577 p2 := b[test.PO.PartSize:]
2578 if len(p2) > int(test.PO.PartSize) {
2579 p2 = p2[:test.PO.PartSize]
2580 }
2581 h.Write(p2)
2582 got := base64.StdEncoding.EncodeToString(h.Sum(nil))
2583 if test.ChecksumSHA256 != "" {
2584 cmpChecksum(st.ChecksumSHA256, got)
2585 }
2586 if test.ChecksumSHA1 != "" {
2587 cmpChecksum(st.ChecksumSHA1, got)
2588 }
2589 if test.ChecksumCRC32 != "" {
2590 cmpChecksum(st.ChecksumCRC32, got)
2591 }
2592 if test.ChecksumCRC32C != "" {
2593 cmpChecksum(st.ChecksumCRC32C, got)
2594 }
2595
2596 delete(args, "metadata")
2597 }
2598}
2599
2600// Test PutObject with custom checksums.
2601func testPutObjectWithAutomaticChecksums() {
2602 // initialize logging params
2603 startTime := time.Now()
2604 testName := getFuncName()
2605 function := "PutObject(bucketName, objectName, reader,size, opts)"
2606 args := map[string]interface{}{
2607 "bucketName": "",
2608 "objectName": "",
2609 "opts": "minio.PutObjectOptions{UserMetadata: metadata, Progress: progress}",
2610 }
2611
2612 if !isFullMode() {
2613 ignoredLog(testName, function, args, startTime, "Skipping functional tests for short/quick runs").Info()
2614 return
2615 }
2616
2617 // Seed random based on current time.
2618 rand.Seed(time.Now().Unix())
2619
2620 // Instantiate new minio client object.
2621 c, err := minio.New(os.Getenv(serverEndpoint),
2622 &minio.Options{
2623 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
2624 Secure: mustParseBool(os.Getenv(enableHTTPS)),
2625 TrailingHeaders: true,
2626 })
2627 if err != nil {
2628 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
2629 return
2630 }
2631
2632 // Set user agent.
2633 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
2634
2635 // Generate a new random bucket name.
2636 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
2637 args["bucketName"] = bucketName
2638
2639 // Make a new bucket.
2640 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
2641 if err != nil {
2642 logError(testName, function, args, startTime, "", "Make bucket failed", err)
2643 return
2644 }
2645
2646 defer cleanupBucket(bucketName, c)
2647 tests := []struct {
2648 header string
2649 hasher hash.Hash
2650
2651 // Checksum values
2652 ChecksumCRC32 string
2653 ChecksumCRC32C string
2654 ChecksumSHA1 string
2655 ChecksumSHA256 string
2656 }{
2657 // Built-in will only add crc32c, when no MD5 nor SHA256.
2658 {header: "x-amz-checksum-crc32c", hasher: crc32.New(crc32.MakeTable(crc32.Castagnoli))},
2659 }
2660
2661 // Enable tracing, write to stderr.
2662 // c.TraceOn(os.Stderr)
2663 // defer c.TraceOff()
2664
2665 for i, test := range tests {
2666 bufSize := dataFileMap["datafile-10-kB"]
2667
2668 // Save the data
2669 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
2670 args["objectName"] = objectName
2671
2672 cmpChecksum := func(got, want string) {
2673 if want != got {
2674 logError(testName, function, args, startTime, "", "checksum mismatch", fmt.Errorf("want %s, got %s", want, got))
2675 return
2676 }
2677 }
2678
2679 meta := map[string]string{}
2680 reader := getDataReader("datafile-10-kB")
2681 b, err := io.ReadAll(reader)
2682 if err != nil {
2683 logError(testName, function, args, startTime, "", "Read failed", err)
2684 return
2685 }
2686
2687 h := test.hasher
2688 h.Reset()
2689 h.Write(b)
2690 meta[test.header] = base64.StdEncoding.EncodeToString(h.Sum(nil))
2691 args["metadata"] = meta
2692
2693 resp, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(b), int64(bufSize), minio.PutObjectOptions{
2694 DisableMultipart: true,
2695 UserMetadata: nil,
2696 DisableContentSha256: true,
2697 SendContentMd5: false,
2698 })
2699 if err == nil {
2700 if i == 0 && resp.ChecksumCRC32C == "" {
2701 ignoredLog(testName, function, args, startTime, "Checksums does not appear to be supported by backend").Info()
2702 return
2703 }
2704 } else {
2705 logError(testName, function, args, startTime, "", "PutObject failed", err)
2706 return
2707 }
2708 cmpChecksum(resp.ChecksumSHA256, meta["x-amz-checksum-sha256"])
2709 cmpChecksum(resp.ChecksumSHA1, meta["x-amz-checksum-sha1"])
2710 cmpChecksum(resp.ChecksumCRC32, meta["x-amz-checksum-crc32"])
2711 cmpChecksum(resp.ChecksumCRC32C, meta["x-amz-checksum-crc32c"])
2712
2713 // Usually this will be the same as above, since we skip automatic checksum when SHA256 content is sent.
2714 // When/if we add a checksum control to PutObjectOptions this will make more sense.
2715 resp, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(b), int64(bufSize), minio.PutObjectOptions{
2716 DisableMultipart: true,
2717 UserMetadata: nil,
2718 DisableContentSha256: false,
2719 SendContentMd5: false,
2720 })
2721 if err != nil {
2722 logError(testName, function, args, startTime, "", "PutObject failed", err)
2723 return
2724 }
2725 // The checksum will not be enabled on HTTP, since it uses SHA256 blocks.
2726 if mustParseBool(os.Getenv(enableHTTPS)) {
2727 cmpChecksum(resp.ChecksumSHA256, meta["x-amz-checksum-sha256"])
2728 cmpChecksum(resp.ChecksumSHA1, meta["x-amz-checksum-sha1"])
2729 cmpChecksum(resp.ChecksumCRC32, meta["x-amz-checksum-crc32"])
2730 cmpChecksum(resp.ChecksumCRC32C, meta["x-amz-checksum-crc32c"])
2731 }
2732
2733 // Set SHA256 header manually
2734 sh256 := sha256.Sum256(b)
2735 meta = map[string]string{"x-amz-checksum-sha256": base64.StdEncoding.EncodeToString(sh256[:])}
2736 args["metadata"] = meta
2737 resp, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(b), int64(bufSize), minio.PutObjectOptions{
2738 DisableMultipart: true,
2739 UserMetadata: meta,
2740 DisableContentSha256: true,
2741 SendContentMd5: false,
2742 })
2743 if err != nil {
2744 logError(testName, function, args, startTime, "", "PutObject failed", err)
2745 return
2746 }
2747 cmpChecksum(resp.ChecksumSHA256, meta["x-amz-checksum-sha256"])
2748 cmpChecksum(resp.ChecksumSHA1, meta["x-amz-checksum-sha1"])
2749 cmpChecksum(resp.ChecksumCRC32, meta["x-amz-checksum-crc32"])
2750 cmpChecksum(resp.ChecksumCRC32C, meta["x-amz-checksum-crc32c"])
2751 delete(args, "metadata")
2752 }
2753
2754 successLogger(testName, function, args, startTime).Info()
2755}
2756
2757// Test PutObject using a large data to trigger multipart readat
2758func testPutObjectWithMetadata() {
2759 // initialize logging params
2760 startTime := time.Now()
2761 testName := getFuncName()
2762 function := "PutObject(bucketName, objectName, reader,size, opts)"
2763 args := map[string]interface{}{
2764 "bucketName": "",
2765 "objectName": "",
2766 "opts": "minio.PutObjectOptions{UserMetadata: metadata, Progress: progress}",
2767 }
2768
2769 if !isFullMode() {
2770 ignoredLog(testName, function, args, startTime, "Skipping functional tests for short/quick runs").Info()
2771 return
2772 }
2773
2774 // Seed random based on current time.
2775 rand.Seed(time.Now().Unix())
2776
2777 // Instantiate new minio client object.
2778 c, err := minio.New(os.Getenv(serverEndpoint),
2779 &minio.Options{
2780 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
2781 Secure: mustParseBool(os.Getenv(enableHTTPS)),
2782 })
2783 if err != nil {
2784 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
2785 return
2786 }
2787
2788 // Enable tracing, write to stderr.
2789 // c.TraceOn(os.Stderr)
2790
2791 // Set user agent.
2792 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
2793
2794 // Generate a new random bucket name.
2795 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
2796 args["bucketName"] = bucketName
2797
2798 // Make a new bucket.
2799 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
2800 if err != nil {
2801 logError(testName, function, args, startTime, "", "Make bucket failed", err)
2802 return
2803 }
2804
2805 defer cleanupBucket(bucketName, c)
2806
2807 bufSize := dataFileMap["datafile-129-MB"]
2808 reader := getDataReader("datafile-129-MB")
2809 defer reader.Close()
2810
2811 // Save the data
2812 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
2813 args["objectName"] = objectName
2814
2815 // Object custom metadata
2816 customContentType := "custom/contenttype"
2817
2818 args["metadata"] = map[string][]string{
2819 "Content-Type": {customContentType},
2820 "X-Amz-Meta-CustomKey": {"extra spaces in value"},
2821 }
2822
2823 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{
2824 ContentType: customContentType,
2825 })
2826 if err != nil {
2827 logError(testName, function, args, startTime, "", "PutObject failed", err)
2828 return
2829 }
2830
2831 // Read the data back
2832 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
2833 if err != nil {
2834 logError(testName, function, args, startTime, "", "GetObject failed", err)
2835 return
2836 }
2837
2838 st, err := r.Stat()
2839 if err != nil {
2840 logError(testName, function, args, startTime, "", "Stat failed", err)
2841 return
2842 }
2843 if st.Size != int64(bufSize) {
2844 logError(testName, function, args, startTime, "", "Number of bytes returned by PutObject does not match GetObject, expected "+string(bufSize)+" got "+string(st.Size), err)
2845 return
2846 }
2847 if st.ContentType != customContentType && st.ContentType != "application/octet-stream" {
2848 logError(testName, function, args, startTime, "", "ContentType does not match, expected "+customContentType+" got "+st.ContentType, err)
2849 return
2850 }
2851 if err := crcMatchesName(r, "datafile-129-MB"); err != nil {
2852 logError(testName, function, args, startTime, "", "data CRC check failed", err)
2853 return
2854 }
2855 if err := r.Close(); err != nil {
2856 logError(testName, function, args, startTime, "", "Object Close failed", err)
2857 return
2858 }
2859 if err := r.Close(); err == nil {
2860 logError(testName, function, args, startTime, "", "Object already closed, should respond with error", err)
2861 return
2862 }
2863
2864 successLogger(testName, function, args, startTime).Info()
2865}
2866
2867func testPutObjectWithContentLanguage() {
2868 // initialize logging params
2869 objectName := "test-object"
2870 startTime := time.Now()
2871 testName := getFuncName()
2872 function := "PutObject(bucketName, objectName, reader, size, opts)"
2873 args := map[string]interface{}{
2874 "bucketName": "",
2875 "objectName": objectName,
2876 "size": -1,
2877 "opts": "",
2878 }
2879
2880 // Seed random based on current time.
2881 rand.Seed(time.Now().Unix())
2882
2883 // Instantiate new minio client object.
2884 c, err := minio.New(os.Getenv(serverEndpoint),
2885 &minio.Options{
2886 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
2887 Secure: mustParseBool(os.Getenv(enableHTTPS)),
2888 })
2889 if err != nil {
2890 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
2891 return
2892 }
2893
2894 // Enable tracing, write to stderr.
2895 // c.TraceOn(os.Stderr)
2896
2897 // Set user agent.
2898 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
2899
2900 // Generate a new random bucket name.
2901 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
2902 args["bucketName"] = bucketName
2903 // Make a new bucket.
2904 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
2905 if err != nil {
2906 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
2907 return
2908 }
2909
2910 defer cleanupBucket(bucketName, c)
2911
2912 data := []byte{}
2913 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(data), int64(0), minio.PutObjectOptions{
2914 ContentLanguage: "en",
2915 })
2916 if err != nil {
2917 logError(testName, function, args, startTime, "", "PutObject failed", err)
2918 return
2919 }
2920
2921 objInfo, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
2922 if err != nil {
2923 logError(testName, function, args, startTime, "", "StatObject failed", err)
2924 return
2925 }
2926
2927 if objInfo.Metadata.Get("Content-Language") != "en" {
2928 logError(testName, function, args, startTime, "", "Expected content-language 'en' doesn't match with StatObject return value", err)
2929 return
2930 }
2931
2932 successLogger(testName, function, args, startTime).Info()
2933}
2934
2935// Test put object with streaming signature.
2936func testPutObjectStreaming() {
2937 // initialize logging params
2938 objectName := "test-object"
2939 startTime := time.Now()
2940 testName := getFuncName()
2941 function := "PutObject(bucketName, objectName, reader,size,opts)"
2942 args := map[string]interface{}{
2943 "bucketName": "",
2944 "objectName": objectName,
2945 "size": -1,
2946 "opts": "",
2947 }
2948
2949 // Seed random based on current time.
2950 rand.Seed(time.Now().Unix())
2951
2952 // Instantiate new minio client object.
2953 c, err := minio.New(os.Getenv(serverEndpoint),
2954 &minio.Options{
2955 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
2956 Secure: mustParseBool(os.Getenv(enableHTTPS)),
2957 })
2958 if err != nil {
2959 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
2960 return
2961 }
2962
2963 // Enable tracing, write to stderr.
2964 // c.TraceOn(os.Stderr)
2965
2966 // Set user agent.
2967 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
2968
2969 // Generate a new random bucket name.
2970 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
2971 args["bucketName"] = bucketName
2972 // Make a new bucket.
2973 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
2974 if err != nil {
2975 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
2976 return
2977 }
2978
2979 defer cleanupBucket(bucketName, c)
2980
2981 // Upload an object.
2982 sizes := []int64{0, 64*1024 - 1, 64 * 1024}
2983
2984 for _, size := range sizes {
2985 data := newRandomReader(size, size)
2986 ui, err := c.PutObject(context.Background(), bucketName, objectName, data, int64(size), minio.PutObjectOptions{})
2987 if err != nil {
2988 logError(testName, function, args, startTime, "", "PutObjectStreaming failed", err)
2989 return
2990 }
2991
2992 if ui.Size != size {
2993 logError(testName, function, args, startTime, "", "PutObjectStreaming result has unexpected size", nil)
2994 return
2995 }
2996
2997 objInfo, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
2998 if err != nil {
2999 logError(testName, function, args, startTime, "", "StatObject failed", err)
3000 return
3001 }
3002 if objInfo.Size != size {
3003 logError(testName, function, args, startTime, "", "Unexpected size", err)
3004 return
3005 }
3006
3007 }
3008
3009 successLogger(testName, function, args, startTime).Info()
3010}
3011
3012// Test get object seeker from the end, using whence set to '2'.
3013func testGetObjectSeekEnd() {
3014 // initialize logging params
3015 startTime := time.Now()
3016 testName := getFuncName()
3017 function := "GetObject(bucketName, objectName)"
3018 args := map[string]interface{}{}
3019
3020 // Seed random based on current time.
3021 rand.Seed(time.Now().Unix())
3022
3023 // Instantiate new minio client object.
3024 c, err := minio.New(os.Getenv(serverEndpoint),
3025 &minio.Options{
3026 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3027 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3028 })
3029 if err != nil {
3030 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3031 return
3032 }
3033
3034 // Enable tracing, write to stderr.
3035 // c.TraceOn(os.Stderr)
3036
3037 // Set user agent.
3038 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3039
3040 // Generate a new random bucket name.
3041 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3042 args["bucketName"] = bucketName
3043
3044 // Make a new bucket.
3045 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
3046 if err != nil {
3047 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3048 return
3049 }
3050
3051 defer cleanupBucket(bucketName, c)
3052
3053 // Generate 33K of data.
3054 bufSize := dataFileMap["datafile-33-kB"]
3055 reader := getDataReader("datafile-33-kB")
3056 defer reader.Close()
3057
3058 // Save the data
3059 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
3060 args["objectName"] = objectName
3061
3062 buf, err := io.ReadAll(reader)
3063 if err != nil {
3064 logError(testName, function, args, startTime, "", "ReadAll failed", err)
3065 return
3066 }
3067
3068 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
3069 if err != nil {
3070 logError(testName, function, args, startTime, "", "PutObject failed", err)
3071 return
3072 }
3073
3074 // Read the data back
3075 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
3076 if err != nil {
3077 logError(testName, function, args, startTime, "", "GetObject failed", err)
3078 return
3079 }
3080
3081 st, err := r.Stat()
3082 if err != nil {
3083 logError(testName, function, args, startTime, "", "Stat failed", err)
3084 return
3085 }
3086
3087 if st.Size != int64(bufSize) {
3088 logError(testName, function, args, startTime, "", "Number of bytes read does not match, expected "+string(int64(bufSize))+" got "+string(st.Size), err)
3089 return
3090 }
3091
3092 pos, err := r.Seek(-100, 2)
3093 if err != nil {
3094 logError(testName, function, args, startTime, "", "Object Seek failed", err)
3095 return
3096 }
3097 if pos != st.Size-100 {
3098 logError(testName, function, args, startTime, "", "Incorrect position", err)
3099 return
3100 }
3101 buf2 := make([]byte, 100)
3102 m, err := readFull(r, buf2)
3103 if err != nil {
3104 logError(testName, function, args, startTime, "", "Error reading through readFull", err)
3105 return
3106 }
3107 if m != len(buf2) {
3108 logError(testName, function, args, startTime, "", "Number of bytes dont match, expected "+string(len(buf2))+" got "+string(m), err)
3109 return
3110 }
3111 hexBuf1 := fmt.Sprintf("%02x", buf[len(buf)-100:])
3112 hexBuf2 := fmt.Sprintf("%02x", buf2[:m])
3113 if hexBuf1 != hexBuf2 {
3114 logError(testName, function, args, startTime, "", "Values at same index dont match", err)
3115 return
3116 }
3117 pos, err = r.Seek(-100, 2)
3118 if err != nil {
3119 logError(testName, function, args, startTime, "", "Object Seek failed", err)
3120 return
3121 }
3122 if pos != st.Size-100 {
3123 logError(testName, function, args, startTime, "", "Incorrect position", err)
3124 return
3125 }
3126 if err = r.Close(); err != nil {
3127 logError(testName, function, args, startTime, "", "ObjectClose failed", err)
3128 return
3129 }
3130
3131 successLogger(testName, function, args, startTime).Info()
3132}
3133
3134// Test get object reader to not throw error on being closed twice.
3135func testGetObjectClosedTwice() {
3136 // initialize logging params
3137 startTime := time.Now()
3138 testName := getFuncName()
3139 function := "GetObject(bucketName, objectName)"
3140 args := map[string]interface{}{}
3141
3142 // Seed random based on current time.
3143 rand.Seed(time.Now().Unix())
3144
3145 // Instantiate new minio client object.
3146 c, err := minio.New(os.Getenv(serverEndpoint),
3147 &minio.Options{
3148 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3149 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3150 })
3151 if err != nil {
3152 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3153 return
3154 }
3155
3156 // Enable tracing, write to stderr.
3157 // c.TraceOn(os.Stderr)
3158
3159 // Set user agent.
3160 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3161
3162 // Generate a new random bucket name.
3163 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3164 args["bucketName"] = bucketName
3165
3166 // Make a new bucket.
3167 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
3168 if err != nil {
3169 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3170 return
3171 }
3172
3173 defer cleanupBucket(bucketName, c)
3174
3175 // Generate 33K of data.
3176 bufSize := dataFileMap["datafile-33-kB"]
3177 reader := getDataReader("datafile-33-kB")
3178 defer reader.Close()
3179
3180 // Save the data
3181 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
3182 args["objectName"] = objectName
3183
3184 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
3185 if err != nil {
3186 logError(testName, function, args, startTime, "", "PutObject failed", err)
3187 return
3188 }
3189
3190 // Read the data back
3191 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
3192 if err != nil {
3193 logError(testName, function, args, startTime, "", "GetObject failed", err)
3194 return
3195 }
3196
3197 st, err := r.Stat()
3198 if err != nil {
3199 logError(testName, function, args, startTime, "", "Stat failed", err)
3200 return
3201 }
3202 if st.Size != int64(bufSize) {
3203 logError(testName, function, args, startTime, "", "Number of bytes in stat does not match, expected "+string(int64(bufSize))+" got "+string(st.Size), err)
3204 return
3205 }
3206 if err := crcMatchesName(r, "datafile-33-kB"); err != nil {
3207 logError(testName, function, args, startTime, "", "data CRC check failed", err)
3208 return
3209 }
3210 if err := r.Close(); err != nil {
3211 logError(testName, function, args, startTime, "", "Object Close failed", err)
3212 return
3213 }
3214 if err := r.Close(); err == nil {
3215 logError(testName, function, args, startTime, "", "Already closed object. No error returned", err)
3216 return
3217 }
3218
3219 successLogger(testName, function, args, startTime).Info()
3220}
3221
3222// Test RemoveObjects request where context cancels after timeout
3223func testRemoveObjectsContext() {
3224 // Initialize logging params.
3225 startTime := time.Now()
3226 testName := getFuncName()
3227 function := "RemoveObjects(ctx, bucketName, objectsCh)"
3228 args := map[string]interface{}{
3229 "bucketName": "",
3230 }
3231
3232 // Seed random based on current tie.
3233 rand.Seed(time.Now().Unix())
3234
3235 // Instantiate new minio client.
3236 c, err := minio.New(os.Getenv(serverEndpoint),
3237 &minio.Options{
3238 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3239 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3240 })
3241 if err != nil {
3242 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3243 return
3244 }
3245
3246 // Set user agent.
3247 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3248 // Enable tracing, write to stdout.
3249 // c.TraceOn(os.Stderr)
3250
3251 // Generate a new random bucket name.
3252 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3253 args["bucketName"] = bucketName
3254
3255 // Make a new bucket.
3256 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
3257 if err != nil {
3258 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3259 return
3260 }
3261
3262 defer cleanupBucket(bucketName, c)
3263
3264 // Generate put data.
3265 r := bytes.NewReader(bytes.Repeat([]byte("a"), 8))
3266
3267 // Multi remove of 20 objects.
3268 nrObjects := 20
3269 objectsCh := make(chan minio.ObjectInfo)
3270 go func() {
3271 defer close(objectsCh)
3272 for i := 0; i < nrObjects; i++ {
3273 objectName := "sample" + strconv.Itoa(i) + ".txt"
3274 info, err := c.PutObject(context.Background(), bucketName, objectName, r, 8,
3275 minio.PutObjectOptions{ContentType: "application/octet-stream"})
3276 if err != nil {
3277 logError(testName, function, args, startTime, "", "PutObject failed", err)
3278 continue
3279 }
3280 objectsCh <- minio.ObjectInfo{
3281 Key: info.Key,
3282 VersionID: info.VersionID,
3283 }
3284 }
3285 }()
3286 // Set context to cancel in 1 nanosecond.
3287 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
3288 args["ctx"] = ctx
3289 defer cancel()
3290
3291 // Call RemoveObjects API with short timeout.
3292 errorCh := c.RemoveObjects(ctx, bucketName, objectsCh, minio.RemoveObjectsOptions{})
3293 // Check for error.
3294 select {
3295 case r := <-errorCh:
3296 if r.Err == nil {
3297 logError(testName, function, args, startTime, "", "RemoveObjects should fail on short timeout", err)
3298 return
3299 }
3300 }
3301 // Set context with longer timeout.
3302 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
3303 args["ctx"] = ctx
3304 defer cancel()
3305 // Perform RemoveObjects with the longer timeout. Expect the removals to succeed.
3306 errorCh = c.RemoveObjects(ctx, bucketName, objectsCh, minio.RemoveObjectsOptions{})
3307 select {
3308 case r, more := <-errorCh:
3309 if more || r.Err != nil {
3310 logError(testName, function, args, startTime, "", "Unexpected error", r.Err)
3311 return
3312 }
3313 }
3314
3315 successLogger(testName, function, args, startTime).Info()
3316}
3317
3318// Test removing multiple objects with Remove API
3319func testRemoveMultipleObjects() {
3320 // initialize logging params
3321 startTime := time.Now()
3322 testName := getFuncName()
3323 function := "RemoveObjects(bucketName, objectsCh)"
3324 args := map[string]interface{}{
3325 "bucketName": "",
3326 }
3327
3328 // Seed random based on current time.
3329 rand.Seed(time.Now().Unix())
3330
3331 // Instantiate new minio client object.
3332 c, err := minio.New(os.Getenv(serverEndpoint),
3333 &minio.Options{
3334 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3335 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3336 })
3337 if err != nil {
3338 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3339 return
3340 }
3341
3342 // Set user agent.
3343 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3344
3345 // Enable tracing, write to stdout.
3346 // c.TraceOn(os.Stderr)
3347
3348 // Generate a new random bucket name.
3349 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3350 args["bucketName"] = bucketName
3351
3352 // Make a new bucket.
3353 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
3354 if err != nil {
3355 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3356 return
3357 }
3358
3359 defer cleanupBucket(bucketName, c)
3360
3361 r := bytes.NewReader(bytes.Repeat([]byte("a"), 8))
3362
3363 // Multi remove of 1100 objects
3364 nrObjects := 200
3365
3366 objectsCh := make(chan minio.ObjectInfo)
3367
3368 go func() {
3369 defer close(objectsCh)
3370 // Upload objects and send them to objectsCh
3371 for i := 0; i < nrObjects; i++ {
3372 objectName := "sample" + strconv.Itoa(i) + ".txt"
3373 info, err := c.PutObject(context.Background(), bucketName, objectName, r, 8,
3374 minio.PutObjectOptions{ContentType: "application/octet-stream"})
3375 if err != nil {
3376 logError(testName, function, args, startTime, "", "PutObject failed", err)
3377 continue
3378 }
3379 objectsCh <- minio.ObjectInfo{
3380 Key: info.Key,
3381 VersionID: info.VersionID,
3382 }
3383 }
3384 }()
3385
3386 // Call RemoveObjects API
3387 errorCh := c.RemoveObjects(context.Background(), bucketName, objectsCh, minio.RemoveObjectsOptions{})
3388
3389 // Check if errorCh doesn't receive any error
3390 select {
3391 case r, more := <-errorCh:
3392 if more {
3393 logError(testName, function, args, startTime, "", "Unexpected error", r.Err)
3394 return
3395 }
3396 }
3397
3398 successLogger(testName, function, args, startTime).Info()
3399}
3400
3401// Test removing multiple objects and check for results
3402func testRemoveMultipleObjectsWithResult() {
3403 // initialize logging params
3404 startTime := time.Now()
3405 testName := getFuncName()
3406 function := "RemoveObjects(bucketName, objectsCh)"
3407 args := map[string]interface{}{
3408 "bucketName": "",
3409 }
3410
3411 // Seed random based on current time.
3412 rand.Seed(time.Now().Unix())
3413
3414 // Instantiate new minio client object.
3415 c, err := minio.New(os.Getenv(serverEndpoint),
3416 &minio.Options{
3417 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3418 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3419 })
3420 if err != nil {
3421 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3422 return
3423 }
3424
3425 // Set user agent.
3426 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3427
3428 // Enable tracing, write to stdout.
3429 // c.TraceOn(os.Stderr)
3430
3431 // Generate a new random bucket name.
3432 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3433 args["bucketName"] = bucketName
3434
3435 // Make a new bucket.
3436 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
3437 if err != nil {
3438 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3439 return
3440 }
3441
3442 defer cleanupVersionedBucket(bucketName, c)
3443
3444 r := bytes.NewReader(bytes.Repeat([]byte("a"), 8))
3445
3446 nrObjects := 10
3447 nrLockedObjects := 5
3448
3449 objectsCh := make(chan minio.ObjectInfo)
3450
3451 go func() {
3452 defer close(objectsCh)
3453 // Upload objects and send them to objectsCh
3454 for i := 0; i < nrObjects; i++ {
3455 objectName := "sample" + strconv.Itoa(i) + ".txt"
3456 info, err := c.PutObject(context.Background(), bucketName, objectName, r, 8,
3457 minio.PutObjectOptions{ContentType: "application/octet-stream"})
3458 if err != nil {
3459 logError(testName, function, args, startTime, "", "PutObject failed", err)
3460 return
3461 }
3462 if i < nrLockedObjects {
3463 // t := time.Date(2130, time.April, 25, 14, 0, 0, 0, time.UTC)
3464 t := time.Now().Add(5 * time.Minute)
3465 m := minio.RetentionMode(minio.Governance)
3466 opts := minio.PutObjectRetentionOptions{
3467 GovernanceBypass: false,
3468 RetainUntilDate: &t,
3469 Mode: &m,
3470 VersionID: info.VersionID,
3471 }
3472 err = c.PutObjectRetention(context.Background(), bucketName, objectName, opts)
3473 if err != nil {
3474 logError(testName, function, args, startTime, "", "Error setting retention", err)
3475 return
3476 }
3477 }
3478
3479 objectsCh <- minio.ObjectInfo{
3480 Key: info.Key,
3481 VersionID: info.VersionID,
3482 }
3483 }
3484 }()
3485
3486 // Call RemoveObjects API
3487 resultCh := c.RemoveObjectsWithResult(context.Background(), bucketName, objectsCh, minio.RemoveObjectsOptions{})
3488
3489 var foundNil, foundErr int
3490
3491 for {
3492 // Check if errorCh doesn't receive any error
3493 select {
3494 case deleteRes, ok := <-resultCh:
3495 if !ok {
3496 goto out
3497 }
3498 if deleteRes.ObjectName == "" {
3499 logError(testName, function, args, startTime, "", "Unexpected object name", nil)
3500 return
3501 }
3502 if deleteRes.ObjectVersionID == "" {
3503 logError(testName, function, args, startTime, "", "Unexpected object version ID", nil)
3504 return
3505 }
3506
3507 if deleteRes.Err == nil {
3508 foundNil++
3509 } else {
3510 foundErr++
3511 }
3512 }
3513 }
3514out:
3515 if foundNil+foundErr != nrObjects {
3516 logError(testName, function, args, startTime, "", "Unexpected number of results", nil)
3517 return
3518 }
3519
3520 if foundNil != nrObjects-nrLockedObjects {
3521 logError(testName, function, args, startTime, "", "Unexpected number of nil errors", nil)
3522 return
3523 }
3524
3525 if foundErr != nrLockedObjects {
3526 logError(testName, function, args, startTime, "", "Unexpected number of errors", nil)
3527 return
3528 }
3529
3530 successLogger(testName, function, args, startTime).Info()
3531}
3532
3533// Tests FPutObject of a big file to trigger multipart
3534func testFPutObjectMultipart() {
3535 // initialize logging params
3536 startTime := time.Now()
3537 testName := getFuncName()
3538 function := "FPutObject(bucketName, objectName, fileName, opts)"
3539 args := map[string]interface{}{
3540 "bucketName": "",
3541 "objectName": "",
3542 "fileName": "",
3543 "opts": "",
3544 }
3545
3546 // Seed random based on current time.
3547 rand.Seed(time.Now().Unix())
3548
3549 // Instantiate new minio client object.
3550 c, err := minio.New(os.Getenv(serverEndpoint),
3551 &minio.Options{
3552 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3553 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3554 })
3555 if err != nil {
3556 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3557 return
3558 }
3559
3560 // Enable tracing, write to stderr.
3561 // c.TraceOn(os.Stderr)
3562
3563 // Set user agent.
3564 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3565
3566 // Generate a new random bucket name.
3567 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3568 args["bucketName"] = bucketName
3569
3570 // Make a new bucket.
3571 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
3572 if err != nil {
3573 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3574 return
3575 }
3576
3577 defer cleanupBucket(bucketName, c)
3578
3579 // Upload 4 parts to utilize all 3 'workers' in multipart and still have a part to upload.
3580 fileName := getMintDataDirFilePath("datafile-129-MB")
3581 if fileName == "" {
3582 // Make a temp file with minPartSize bytes of data.
3583 file, err := os.CreateTemp(os.TempDir(), "FPutObjectTest")
3584 if err != nil {
3585 logError(testName, function, args, startTime, "", "TempFile creation failed", err)
3586 return
3587 }
3588 // Upload 2 parts to utilize all 3 'workers' in multipart and still have a part to upload.
3589 if _, err = io.Copy(file, getDataReader("datafile-129-MB")); err != nil {
3590 logError(testName, function, args, startTime, "", "Copy failed", err)
3591 return
3592 }
3593 if err = file.Close(); err != nil {
3594 logError(testName, function, args, startTime, "", "File Close failed", err)
3595 return
3596 }
3597 fileName = file.Name()
3598 args["fileName"] = fileName
3599 }
3600 totalSize := dataFileMap["datafile-129-MB"]
3601 // Set base object name
3602 objectName := bucketName + "FPutObject" + "-standard"
3603 args["objectName"] = objectName
3604
3605 objectContentType := "testapplication/octet-stream"
3606 args["objectContentType"] = objectContentType
3607
3608 // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
3609 _, err = c.FPutObject(context.Background(), bucketName, objectName, fileName, minio.PutObjectOptions{ContentType: objectContentType})
3610 if err != nil {
3611 logError(testName, function, args, startTime, "", "FPutObject failed", err)
3612 return
3613 }
3614
3615 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
3616 if err != nil {
3617 logError(testName, function, args, startTime, "", "GetObject failed", err)
3618 return
3619 }
3620 objInfo, err := r.Stat()
3621 if err != nil {
3622 logError(testName, function, args, startTime, "", "Unexpected error", err)
3623 return
3624 }
3625 if objInfo.Size != int64(totalSize) {
3626 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(int64(totalSize))+" got "+string(objInfo.Size), err)
3627 return
3628 }
3629 if objInfo.ContentType != objectContentType && objInfo.ContentType != "application/octet-stream" {
3630 logError(testName, function, args, startTime, "", "ContentType doesn't match", err)
3631 return
3632 }
3633
3634 successLogger(testName, function, args, startTime).Info()
3635}
3636
3637// Tests FPutObject with null contentType (default = application/octet-stream)
3638func testFPutObject() {
3639 // initialize logging params
3640 startTime := time.Now()
3641 testName := getFuncName()
3642 function := "FPutObject(bucketName, objectName, fileName, opts)"
3643
3644 args := map[string]interface{}{
3645 "bucketName": "",
3646 "objectName": "",
3647 "fileName": "",
3648 "opts": "",
3649 }
3650
3651 // Seed random based on current time.
3652 rand.Seed(time.Now().Unix())
3653
3654 // Instantiate new minio client object.
3655 c, err := minio.New(os.Getenv(serverEndpoint),
3656 &minio.Options{
3657 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3658 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3659 })
3660 if err != nil {
3661 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3662 return
3663 }
3664
3665 // Enable tracing, write to stderr.
3666 // c.TraceOn(os.Stderr)
3667
3668 // Set user agent.
3669 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3670
3671 // Generate a new random bucket name.
3672 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3673 location := "us-east-1"
3674
3675 // Make a new bucket.
3676 args["bucketName"] = bucketName
3677 args["location"] = location
3678 function = "MakeBucket(bucketName, location)"
3679 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: location})
3680 if err != nil {
3681 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3682 return
3683 }
3684
3685 defer cleanupBucket(bucketName, c)
3686
3687 // Upload 3 parts worth of data to use all 3 of multiparts 'workers' and have an extra part.
3688 // Use different data in part for multipart tests to check parts are uploaded in correct order.
3689 fName := getMintDataDirFilePath("datafile-129-MB")
3690 if fName == "" {
3691 // Make a temp file with minPartSize bytes of data.
3692 file, err := os.CreateTemp(os.TempDir(), "FPutObjectTest")
3693 if err != nil {
3694 logError(testName, function, args, startTime, "", "TempFile creation failed", err)
3695 return
3696 }
3697
3698 // Upload 3 parts to utilize all 3 'workers' in multipart and still have a part to upload.
3699 if _, err = io.Copy(file, getDataReader("datafile-129-MB")); err != nil {
3700 logError(testName, function, args, startTime, "", "File copy failed", err)
3701 return
3702 }
3703 // Close the file pro-actively for windows.
3704 if err = file.Close(); err != nil {
3705 logError(testName, function, args, startTime, "", "File close failed", err)
3706 return
3707 }
3708 defer os.Remove(file.Name())
3709 fName = file.Name()
3710 }
3711
3712 // Set base object name
3713 function = "FPutObject(bucketName, objectName, fileName, opts)"
3714 objectName := bucketName + "FPutObject"
3715 args["objectName"] = objectName + "-standard"
3716 args["fileName"] = fName
3717 args["opts"] = minio.PutObjectOptions{ContentType: "application/octet-stream"}
3718
3719 // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
3720 ui, err := c.FPutObject(context.Background(), bucketName, objectName+"-standard", fName, minio.PutObjectOptions{ContentType: "application/octet-stream"})
3721 if err != nil {
3722 logError(testName, function, args, startTime, "", "FPutObject failed", err)
3723 return
3724 }
3725
3726 if ui.Size != int64(dataFileMap["datafile-129-MB"]) {
3727 logError(testName, function, args, startTime, "", "FPutObject returned an unexpected upload size", err)
3728 return
3729 }
3730
3731 // Perform FPutObject with no contentType provided (Expecting application/octet-stream)
3732 args["objectName"] = objectName + "-Octet"
3733 _, err = c.FPutObject(context.Background(), bucketName, objectName+"-Octet", fName, minio.PutObjectOptions{})
3734 if err != nil {
3735 logError(testName, function, args, startTime, "", "File close failed", err)
3736 return
3737 }
3738
3739 srcFile, err := os.Open(fName)
3740 if err != nil {
3741 logError(testName, function, args, startTime, "", "File open failed", err)
3742 return
3743 }
3744 defer srcFile.Close()
3745 // Add extension to temp file name
3746 tmpFile, err := os.Create(fName + ".gtar")
3747 if err != nil {
3748 logError(testName, function, args, startTime, "", "File create failed", err)
3749 return
3750 }
3751 _, err = io.Copy(tmpFile, srcFile)
3752 if err != nil {
3753 logError(testName, function, args, startTime, "", "File copy failed", err)
3754 return
3755 }
3756 tmpFile.Close()
3757
3758 // Perform FPutObject with no contentType provided (Expecting application/x-gtar)
3759 args["objectName"] = objectName + "-GTar"
3760 args["opts"] = minio.PutObjectOptions{}
3761 _, err = c.FPutObject(context.Background(), bucketName, objectName+"-GTar", fName+".gtar", minio.PutObjectOptions{})
3762 if err != nil {
3763 logError(testName, function, args, startTime, "", "FPutObject failed", err)
3764 return
3765 }
3766
3767 // Check headers
3768 function = "StatObject(bucketName, objectName, opts)"
3769 args["objectName"] = objectName + "-standard"
3770 rStandard, err := c.StatObject(context.Background(), bucketName, objectName+"-standard", minio.StatObjectOptions{})
3771 if err != nil {
3772 logError(testName, function, args, startTime, "", "StatObject failed", err)
3773 return
3774 }
3775 if rStandard.ContentType != "application/octet-stream" {
3776 logError(testName, function, args, startTime, "", "ContentType does not match, expected application/octet-stream, got "+rStandard.ContentType, err)
3777 return
3778 }
3779
3780 function = "StatObject(bucketName, objectName, opts)"
3781 args["objectName"] = objectName + "-Octet"
3782 rOctet, err := c.StatObject(context.Background(), bucketName, objectName+"-Octet", minio.StatObjectOptions{})
3783 if err != nil {
3784 logError(testName, function, args, startTime, "", "StatObject failed", err)
3785 return
3786 }
3787 if rOctet.ContentType != "application/octet-stream" {
3788 logError(testName, function, args, startTime, "", "ContentType does not match, expected application/octet-stream, got "+rOctet.ContentType, err)
3789 return
3790 }
3791
3792 function = "StatObject(bucketName, objectName, opts)"
3793 args["objectName"] = objectName + "-GTar"
3794 rGTar, err := c.StatObject(context.Background(), bucketName, objectName+"-GTar", minio.StatObjectOptions{})
3795 if err != nil {
3796 logError(testName, function, args, startTime, "", "StatObject failed", err)
3797 return
3798 }
3799 if rGTar.ContentType != "application/x-gtar" && rGTar.ContentType != "application/octet-stream" && rGTar.ContentType != "application/x-tar" {
3800 logError(testName, function, args, startTime, "", "ContentType does not match, expected application/x-tar or application/octet-stream, got "+rGTar.ContentType, err)
3801 return
3802 }
3803
3804 os.Remove(fName + ".gtar")
3805 successLogger(testName, function, args, startTime).Info()
3806}
3807
3808// Tests FPutObject request when context cancels after timeout
3809func testFPutObjectContext() {
3810 // initialize logging params
3811 startTime := time.Now()
3812 testName := getFuncName()
3813 function := "FPutObject(bucketName, objectName, fileName, opts)"
3814 args := map[string]interface{}{
3815 "bucketName": "",
3816 "objectName": "",
3817 "fileName": "",
3818 "opts": "",
3819 }
3820 // Seed random based on current time.
3821 rand.Seed(time.Now().Unix())
3822
3823 // Instantiate new minio client object.
3824 c, err := minio.New(os.Getenv(serverEndpoint),
3825 &minio.Options{
3826 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3827 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3828 })
3829 if err != nil {
3830 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3831 return
3832 }
3833
3834 // Enable tracing, write to stderr.
3835 // c.TraceOn(os.Stderr)
3836
3837 // Set user agent.
3838 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3839
3840 // Generate a new random bucket name.
3841 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3842 args["bucketName"] = bucketName
3843
3844 // Make a new bucket.
3845 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
3846 if err != nil {
3847 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3848 return
3849 }
3850
3851 defer cleanupBucket(bucketName, c)
3852
3853 // Upload 1 parts worth of data to use multipart upload.
3854 // Use different data in part for multipart tests to check parts are uploaded in correct order.
3855 fName := getMintDataDirFilePath("datafile-1-MB")
3856 if fName == "" {
3857 // Make a temp file with 1 MiB bytes of data.
3858 file, err := os.CreateTemp(os.TempDir(), "FPutObjectContextTest")
3859 if err != nil {
3860 logError(testName, function, args, startTime, "", "TempFile creation failed", err)
3861 return
3862 }
3863
3864 // Upload 1 parts to trigger multipart upload
3865 if _, err = io.Copy(file, getDataReader("datafile-1-MB")); err != nil {
3866 logError(testName, function, args, startTime, "", "File copy failed", err)
3867 return
3868 }
3869 // Close the file pro-actively for windows.
3870 if err = file.Close(); err != nil {
3871 logError(testName, function, args, startTime, "", "File close failed", err)
3872 return
3873 }
3874 defer os.Remove(file.Name())
3875 fName = file.Name()
3876 }
3877
3878 // Set base object name
3879 objectName := bucketName + "FPutObjectContext"
3880 args["objectName"] = objectName
3881 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
3882 args["ctx"] = ctx
3883 defer cancel()
3884
3885 // Perform FPutObject with contentType provided (Expecting application/octet-stream)
3886 _, err = c.FPutObject(ctx, bucketName, objectName+"-Shorttimeout", fName, minio.PutObjectOptions{ContentType: "application/octet-stream"})
3887 if err == nil {
3888 logError(testName, function, args, startTime, "", "FPutObject should fail on short timeout", err)
3889 return
3890 }
3891 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
3892 defer cancel()
3893 // Perform FPutObject with a long timeout. Expect the put object to succeed
3894 _, err = c.FPutObject(ctx, bucketName, objectName+"-Longtimeout", fName, minio.PutObjectOptions{})
3895 if err != nil {
3896 logError(testName, function, args, startTime, "", "FPutObject shouldn't fail on long timeout", err)
3897 return
3898 }
3899
3900 _, err = c.StatObject(context.Background(), bucketName, objectName+"-Longtimeout", minio.StatObjectOptions{})
3901 if err != nil {
3902 logError(testName, function, args, startTime, "", "StatObject failed", err)
3903 return
3904 }
3905
3906 successLogger(testName, function, args, startTime).Info()
3907}
3908
3909// Tests FPutObject request when context cancels after timeout
3910func testFPutObjectContextV2() {
3911 // initialize logging params
3912 startTime := time.Now()
3913 testName := getFuncName()
3914 function := "FPutObjectContext(ctx, bucketName, objectName, fileName, opts)"
3915 args := map[string]interface{}{
3916 "bucketName": "",
3917 "objectName": "",
3918 "opts": "minio.PutObjectOptions{ContentType:objectContentType}",
3919 }
3920 // Seed random based on current time.
3921 rand.Seed(time.Now().Unix())
3922
3923 // Instantiate new minio client object.
3924 c, err := minio.New(os.Getenv(serverEndpoint),
3925 &minio.Options{
3926 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
3927 Secure: mustParseBool(os.Getenv(enableHTTPS)),
3928 })
3929 if err != nil {
3930 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
3931 return
3932 }
3933
3934 // Enable tracing, write to stderr.
3935 // c.TraceOn(os.Stderr)
3936
3937 // Set user agent.
3938 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
3939
3940 // Generate a new random bucket name.
3941 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
3942 args["bucketName"] = bucketName
3943
3944 // Make a new bucket.
3945 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
3946 if err != nil {
3947 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
3948 return
3949 }
3950
3951 defer cleanupBucket(bucketName, c)
3952
3953 // Upload 1 parts worth of data to use multipart upload.
3954 // Use different data in part for multipart tests to check parts are uploaded in correct order.
3955 fName := getMintDataDirFilePath("datafile-1-MB")
3956 if fName == "" {
3957 // Make a temp file with 1 MiB bytes of data.
3958 file, err := os.CreateTemp(os.TempDir(), "FPutObjectContextTest")
3959 if err != nil {
3960 logError(testName, function, args, startTime, "", "Temp file creation failed", err)
3961 return
3962 }
3963
3964 // Upload 1 parts to trigger multipart upload
3965 if _, err = io.Copy(file, getDataReader("datafile-1-MB")); err != nil {
3966 logError(testName, function, args, startTime, "", "File copy failed", err)
3967 return
3968 }
3969
3970 // Close the file pro-actively for windows.
3971 if err = file.Close(); err != nil {
3972 logError(testName, function, args, startTime, "", "File close failed", err)
3973 return
3974 }
3975 defer os.Remove(file.Name())
3976 fName = file.Name()
3977 }
3978
3979 // Set base object name
3980 objectName := bucketName + "FPutObjectContext"
3981 args["objectName"] = objectName
3982
3983 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
3984 args["ctx"] = ctx
3985 defer cancel()
3986
3987 // Perform FPutObject with contentType provided (Expecting application/octet-stream)
3988 _, err = c.FPutObject(ctx, bucketName, objectName+"-Shorttimeout", fName, minio.PutObjectOptions{ContentType: "application/octet-stream"})
3989 if err == nil {
3990 logError(testName, function, args, startTime, "", "FPutObject should fail on short timeout", err)
3991 return
3992 }
3993 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
3994 defer cancel()
3995 // Perform FPutObject with a long timeout. Expect the put object to succeed
3996 _, err = c.FPutObject(ctx, bucketName, objectName+"-Longtimeout", fName, minio.PutObjectOptions{})
3997 if err != nil {
3998 logError(testName, function, args, startTime, "", "FPutObject shouldn't fail on longer timeout", err)
3999 return
4000 }
4001
4002 _, err = c.StatObject(context.Background(), bucketName, objectName+"-Longtimeout", minio.StatObjectOptions{})
4003 if err != nil {
4004 logError(testName, function, args, startTime, "", "StatObject failed", err)
4005 return
4006 }
4007
4008 successLogger(testName, function, args, startTime).Info()
4009}
4010
4011// Test validates putObject with context to see if request cancellation is honored.
4012func testPutObjectContext() {
4013 // initialize logging params
4014 startTime := time.Now()
4015 testName := getFuncName()
4016 function := "PutObject(ctx, bucketName, objectName, fileName, opts)"
4017 args := map[string]interface{}{
4018 "ctx": "",
4019 "bucketName": "",
4020 "objectName": "",
4021 "opts": "",
4022 }
4023
4024 // Instantiate new minio client object.
4025 c, err := minio.New(os.Getenv(serverEndpoint),
4026 &minio.Options{
4027 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
4028 Secure: mustParseBool(os.Getenv(enableHTTPS)),
4029 })
4030 if err != nil {
4031 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
4032 return
4033 }
4034
4035 // Enable tracing, write to stderr.
4036 // c.TraceOn(os.Stderr)
4037
4038 // Set user agent.
4039 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
4040
4041 // Make a new bucket.
4042 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
4043 args["bucketName"] = bucketName
4044
4045 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
4046 if err != nil {
4047 logError(testName, function, args, startTime, "", "MakeBucket call failed", err)
4048 return
4049 }
4050
4051 defer cleanupBucket(bucketName, c)
4052
4053 bufSize := dataFileMap["datafile-33-kB"]
4054 reader := getDataReader("datafile-33-kB")
4055 defer reader.Close()
4056 objectName := fmt.Sprintf("test-file-%v", rand.Uint32())
4057 args["objectName"] = objectName
4058
4059 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
4060 cancel()
4061 args["ctx"] = ctx
4062 args["opts"] = minio.PutObjectOptions{ContentType: "binary/octet-stream"}
4063
4064 _, err = c.PutObject(ctx, bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
4065 if err == nil {
4066 logError(testName, function, args, startTime, "", "PutObject should fail on short timeout", err)
4067 return
4068 }
4069
4070 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
4071 args["ctx"] = ctx
4072
4073 defer cancel()
4074 reader = getDataReader("datafile-33-kB")
4075 defer reader.Close()
4076 _, err = c.PutObject(ctx, bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
4077 if err != nil {
4078 logError(testName, function, args, startTime, "", "PutObject with long timeout failed", err)
4079 return
4080 }
4081
4082 successLogger(testName, function, args, startTime).Info()
4083}
4084
4085// Tests get object with s3zip extensions.
4086func testGetObjectS3Zip() {
4087 // initialize logging params
4088 startTime := time.Now()
4089 testName := getFuncName()
4090 function := "GetObject(bucketName, objectName)"
4091 args := map[string]interface{}{"x-minio-extract": true}
4092
4093 // Seed random based on current time.
4094 rand.Seed(time.Now().Unix())
4095
4096 // Instantiate new minio client object.
4097 c, err := minio.New(os.Getenv(serverEndpoint),
4098 &minio.Options{
4099 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
4100 Secure: mustParseBool(os.Getenv(enableHTTPS)),
4101 })
4102 if err != nil {
4103 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
4104 return
4105 }
4106
4107 // Enable tracing, write to stderr.
4108 // c.TraceOn(os.Stderr)
4109
4110 // Set user agent.
4111 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
4112
4113 // Generate a new random bucket name.
4114 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
4115 args["bucketName"] = bucketName
4116
4117 // Make a new bucket.
4118 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
4119 if err != nil {
4120 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
4121 return
4122 }
4123
4124 defer func() {
4125 // Delete all objects and buckets
4126 if err = cleanupBucket(bucketName, c); err != nil {
4127 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
4128 return
4129 }
4130 }()
4131
4132 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") + ".zip"
4133 args["objectName"] = objectName
4134
4135 var zipFile bytes.Buffer
4136 zw := zip.NewWriter(&zipFile)
4137 rng := rand.New(rand.NewSource(0xc0cac01a))
4138 const nFiles = 500
4139 for i := 0; i <= nFiles; i++ {
4140 if i == nFiles {
4141 // Make one large, compressible file.
4142 i = 1000000
4143 }
4144 b := make([]byte, i)
4145 if i < nFiles {
4146 rng.Read(b)
4147 }
4148 wc, err := zw.Create(fmt.Sprintf("test/small/file-%d.bin", i))
4149 if err != nil {
4150 logError(testName, function, args, startTime, "", "zw.Create failed", err)
4151 return
4152 }
4153 wc.Write(b)
4154 }
4155 err = zw.Close()
4156 if err != nil {
4157 logError(testName, function, args, startTime, "", "zw.Close failed", err)
4158 return
4159 }
4160 buf := zipFile.Bytes()
4161
4162 // Save the data
4163 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
4164 if err != nil {
4165 logError(testName, function, args, startTime, "", "PutObject failed", err)
4166 return
4167 }
4168
4169 // Read the data back
4170 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
4171 if err != nil {
4172 logError(testName, function, args, startTime, "", "GetObject failed", err)
4173 return
4174 }
4175
4176 st, err := r.Stat()
4177 if err != nil {
4178 logError(testName, function, args, startTime, "", "Stat object failed", err)
4179 return
4180 }
4181
4182 if st.Size != int64(len(buf)) {
4183 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(len(buf))+", got "+string(st.Size), err)
4184 return
4185 }
4186 r.Close()
4187
4188 zr, err := zip.NewReader(bytes.NewReader(buf), int64(len(buf)))
4189 if err != nil {
4190 logError(testName, function, args, startTime, "", "zip.NewReader failed", err)
4191 return
4192 }
4193 lOpts := minio.ListObjectsOptions{}
4194 lOpts.Set("x-minio-extract", "true")
4195 lOpts.Prefix = objectName + "/"
4196 lOpts.Recursive = true
4197 list := c.ListObjects(context.Background(), bucketName, lOpts)
4198 listed := map[string]minio.ObjectInfo{}
4199 for item := range list {
4200 if item.Err != nil {
4201 break
4202 }
4203 listed[item.Key] = item
4204 }
4205 if len(listed) == 0 {
4206 // Assume we are running against non-minio.
4207 args["SKIPPED"] = true
4208 ignoredLog(testName, function, args, startTime, "s3zip does not appear to be present").Info()
4209 return
4210 }
4211
4212 for _, file := range zr.File {
4213 if file.FileInfo().IsDir() {
4214 continue
4215 }
4216 args["zipfile"] = file.Name
4217 zfr, err := file.Open()
4218 if err != nil {
4219 logError(testName, function, args, startTime, "", "file.Open failed", err)
4220 return
4221 }
4222 want, err := io.ReadAll(zfr)
4223 if err != nil {
4224 logError(testName, function, args, startTime, "", "fzip file read failed", err)
4225 return
4226 }
4227
4228 opts := minio.GetObjectOptions{}
4229 opts.Set("x-minio-extract", "true")
4230 key := path.Join(objectName, file.Name)
4231 r, err = c.GetObject(context.Background(), bucketName, key, opts)
4232 if err != nil {
4233 terr := minio.ToErrorResponse(err)
4234 if terr.StatusCode != http.StatusNotFound {
4235 logError(testName, function, args, startTime, "", "GetObject failed", err)
4236 }
4237 return
4238 }
4239 got, err := io.ReadAll(r)
4240 if err != nil {
4241 logError(testName, function, args, startTime, "", "ReadAll failed", err)
4242 return
4243 }
4244 r.Close()
4245 if !bytes.Equal(want, got) {
4246 logError(testName, function, args, startTime, "", "Content mismatch", err)
4247 return
4248 }
4249 oi, ok := listed[key]
4250 if !ok {
4251 logError(testName, function, args, startTime, "", "Object Missing", fmt.Errorf("%s not present in listing", key))
4252 return
4253 }
4254 if int(oi.Size) != len(got) {
4255 logError(testName, function, args, startTime, "", "Object Size Incorrect", fmt.Errorf("listing %d, read %d", oi.Size, len(got)))
4256 return
4257 }
4258 delete(listed, key)
4259 }
4260 delete(args, "zipfile")
4261 if len(listed) > 0 {
4262 logError(testName, function, args, startTime, "", "Extra listed objects", fmt.Errorf("left over: %v", listed))
4263 return
4264 }
4265 successLogger(testName, function, args, startTime).Info()
4266}
4267
4268// Tests get object ReaderSeeker interface methods.
4269func testGetObjectReadSeekFunctional() {
4270 // initialize logging params
4271 startTime := time.Now()
4272 testName := getFuncName()
4273 function := "GetObject(bucketName, objectName)"
4274 args := map[string]interface{}{}
4275
4276 // Seed random based on current time.
4277 rand.Seed(time.Now().Unix())
4278
4279 // Instantiate new minio client object.
4280 c, err := minio.New(os.Getenv(serverEndpoint),
4281 &minio.Options{
4282 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
4283 Secure: mustParseBool(os.Getenv(enableHTTPS)),
4284 })
4285 if err != nil {
4286 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
4287 return
4288 }
4289
4290 // Enable tracing, write to stderr.
4291 // c.TraceOn(os.Stderr)
4292
4293 // Set user agent.
4294 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
4295
4296 // Generate a new random bucket name.
4297 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
4298 args["bucketName"] = bucketName
4299
4300 // Make a new bucket.
4301 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
4302 if err != nil {
4303 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
4304 return
4305 }
4306
4307 defer func() {
4308 // Delete all objects and buckets
4309 if err = cleanupBucket(bucketName, c); err != nil {
4310 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
4311 return
4312 }
4313 }()
4314
4315 // Generate 33K of data.
4316 bufSize := dataFileMap["datafile-33-kB"]
4317 reader := getDataReader("datafile-33-kB")
4318 defer reader.Close()
4319
4320 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
4321 args["objectName"] = objectName
4322
4323 buf, err := io.ReadAll(reader)
4324 if err != nil {
4325 logError(testName, function, args, startTime, "", "ReadAll failed", err)
4326 return
4327 }
4328
4329 // Save the data
4330 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
4331 if err != nil {
4332 logError(testName, function, args, startTime, "", "PutObject failed", err)
4333 return
4334 }
4335
4336 // Read the data back
4337 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
4338 if err != nil {
4339 logError(testName, function, args, startTime, "", "GetObject failed", err)
4340 return
4341 }
4342
4343 st, err := r.Stat()
4344 if err != nil {
4345 logError(testName, function, args, startTime, "", "Stat object failed", err)
4346 return
4347 }
4348
4349 if st.Size != int64(bufSize) {
4350 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(int64(bufSize))+", got "+string(st.Size), err)
4351 return
4352 }
4353
4354 // This following function helps us to compare data from the reader after seek
4355 // with the data from the original buffer
4356 cmpData := func(r io.Reader, start, end int) {
4357 if end-start == 0 {
4358 return
4359 }
4360 buffer := bytes.NewBuffer([]byte{})
4361 if _, err := io.CopyN(buffer, r, int64(bufSize)); err != nil {
4362 if err != io.EOF {
4363 logError(testName, function, args, startTime, "", "CopyN failed", err)
4364 return
4365 }
4366 }
4367 if !bytes.Equal(buf[start:end], buffer.Bytes()) {
4368 logError(testName, function, args, startTime, "", "Incorrect read bytes v/s original buffer", err)
4369 return
4370 }
4371 }
4372
4373 // Generic seek error for errors other than io.EOF
4374 seekErr := errors.New("seek error")
4375
4376 testCases := []struct {
4377 offset int64
4378 whence int
4379 pos int64
4380 err error
4381 shouldCmp bool
4382 start int
4383 end int
4384 }{
4385 // Start from offset 0, fetch data and compare
4386 {0, 0, 0, nil, true, 0, 0},
4387 // Start from offset 2048, fetch data and compare
4388 {2048, 0, 2048, nil, true, 2048, bufSize},
4389 // Start from offset larger than possible
4390 {int64(bufSize) + 1024, 0, 0, seekErr, false, 0, 0},
4391 // Move to offset 0 without comparing
4392 {0, 0, 0, nil, false, 0, 0},
4393 // Move one step forward and compare
4394 {1, 1, 1, nil, true, 1, bufSize},
4395 // Move larger than possible
4396 {int64(bufSize), 1, 0, seekErr, false, 0, 0},
4397 // Provide negative offset with CUR_SEEK
4398 {int64(-1), 1, 0, seekErr, false, 0, 0},
4399 // Test with whence SEEK_END and with positive offset
4400 {1024, 2, int64(bufSize) - 1024, io.EOF, true, 0, 0},
4401 // Test with whence SEEK_END and with negative offset
4402 {-1024, 2, int64(bufSize) - 1024, nil, true, bufSize - 1024, bufSize},
4403 // Test with whence SEEK_END and with large negative offset
4404 {-int64(bufSize) * 2, 2, 0, seekErr, true, 0, 0},
4405 }
4406
4407 for i, testCase := range testCases {
4408 // Perform seek operation
4409 n, err := r.Seek(testCase.offset, testCase.whence)
4410 // We expect an error
4411 if testCase.err == seekErr && err == nil {
4412 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", unexpected err value: expected: "+testCase.err.Error()+", found: "+err.Error(), err)
4413 return
4414 }
4415 // We expect a specific error
4416 if testCase.err != seekErr && testCase.err != err {
4417 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", unexpected err value: expected: "+testCase.err.Error()+", found: "+err.Error(), err)
4418 return
4419 }
4420 // If we expect an error go to the next loop
4421 if testCase.err != nil {
4422 continue
4423 }
4424 // Check the returned seek pos
4425 if n != testCase.pos {
4426 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", number of bytes seeked does not match, expected "+string(testCase.pos)+", got "+string(n), err)
4427 return
4428 }
4429 // Compare only if shouldCmp is activated
4430 if testCase.shouldCmp {
4431 cmpData(r, testCase.start, testCase.end)
4432 }
4433 }
4434 successLogger(testName, function, args, startTime).Info()
4435}
4436
4437// Tests get object ReaderAt interface methods.
4438func testGetObjectReadAtFunctional() {
4439 // initialize logging params
4440 startTime := time.Now()
4441 testName := getFuncName()
4442 function := "GetObject(bucketName, objectName)"
4443 args := map[string]interface{}{}
4444
4445 // Seed random based on current time.
4446 rand.Seed(time.Now().Unix())
4447
4448 // Instantiate new minio client object.
4449 c, err := minio.New(os.Getenv(serverEndpoint),
4450 &minio.Options{
4451 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
4452 Secure: mustParseBool(os.Getenv(enableHTTPS)),
4453 })
4454 if err != nil {
4455 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
4456 return
4457 }
4458
4459 // Enable tracing, write to stderr.
4460 // c.TraceOn(os.Stderr)
4461
4462 // Set user agent.
4463 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
4464
4465 // Generate a new random bucket name.
4466 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
4467 args["bucketName"] = bucketName
4468
4469 // Make a new bucket.
4470 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
4471 if err != nil {
4472 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
4473 return
4474 }
4475
4476 defer cleanupBucket(bucketName, c)
4477
4478 // Generate 33K of data.
4479 bufSize := dataFileMap["datafile-33-kB"]
4480 reader := getDataReader("datafile-33-kB")
4481 defer reader.Close()
4482
4483 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
4484 args["objectName"] = objectName
4485
4486 buf, err := io.ReadAll(reader)
4487 if err != nil {
4488 logError(testName, function, args, startTime, "", "ReadAll failed", err)
4489 return
4490 }
4491
4492 // Save the data
4493 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
4494 if err != nil {
4495 logError(testName, function, args, startTime, "", "PutObject failed", err)
4496 return
4497 }
4498
4499 // read the data back
4500 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
4501 if err != nil {
4502 logError(testName, function, args, startTime, "", "PutObject failed", err)
4503 return
4504 }
4505 offset := int64(2048)
4506
4507 // read directly
4508 buf1 := make([]byte, 512)
4509 buf2 := make([]byte, 512)
4510 buf3 := make([]byte, 512)
4511 buf4 := make([]byte, 512)
4512
4513 // Test readAt before stat is called such that objectInfo doesn't change.
4514 m, err := r.ReadAt(buf1, offset)
4515 if err != nil {
4516 logError(testName, function, args, startTime, "", "ReadAt failed", err)
4517 return
4518 }
4519 if m != len(buf1) {
4520 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf1))+", got "+string(m), err)
4521 return
4522 }
4523 if !bytes.Equal(buf1, buf[offset:offset+512]) {
4524 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
4525 return
4526 }
4527 offset += 512
4528
4529 st, err := r.Stat()
4530 if err != nil {
4531 logError(testName, function, args, startTime, "", "Stat failed", err)
4532 return
4533 }
4534
4535 if st.Size != int64(bufSize) {
4536 logError(testName, function, args, startTime, "", "Number of bytes in stat does not match, expected "+string(int64(bufSize))+", got "+string(st.Size), err)
4537 return
4538 }
4539
4540 m, err = r.ReadAt(buf2, offset)
4541 if err != nil {
4542 logError(testName, function, args, startTime, "", "ReadAt failed", err)
4543 return
4544 }
4545 if m != len(buf2) {
4546 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf2))+", got "+string(m), err)
4547 return
4548 }
4549 if !bytes.Equal(buf2, buf[offset:offset+512]) {
4550 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
4551 return
4552 }
4553
4554 offset += 512
4555 m, err = r.ReadAt(buf3, offset)
4556 if err != nil {
4557 logError(testName, function, args, startTime, "", "ReadAt failed", err)
4558 return
4559 }
4560 if m != len(buf3) {
4561 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf3))+", got "+string(m), err)
4562 return
4563 }
4564 if !bytes.Equal(buf3, buf[offset:offset+512]) {
4565 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
4566 return
4567 }
4568 offset += 512
4569 m, err = r.ReadAt(buf4, offset)
4570 if err != nil {
4571 logError(testName, function, args, startTime, "", "ReadAt failed", err)
4572 return
4573 }
4574 if m != len(buf4) {
4575 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf4))+", got "+string(m), err)
4576 return
4577 }
4578 if !bytes.Equal(buf4, buf[offset:offset+512]) {
4579 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
4580 return
4581 }
4582
4583 buf5 := make([]byte, len(buf))
4584 // Read the whole object.
4585 m, err = r.ReadAt(buf5, 0)
4586 if err != nil {
4587 if err != io.EOF {
4588 logError(testName, function, args, startTime, "", "ReadAt failed", err)
4589 return
4590 }
4591 }
4592 if m != len(buf5) {
4593 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf5))+", got "+string(m), err)
4594 return
4595 }
4596 if !bytes.Equal(buf, buf5) {
4597 logError(testName, function, args, startTime, "", "Incorrect data read in GetObject, than what was previously uploaded", err)
4598 return
4599 }
4600
4601 buf6 := make([]byte, len(buf)+1)
4602 // Read the whole object and beyond.
4603 _, err = r.ReadAt(buf6, 0)
4604 if err != nil {
4605 if err != io.EOF {
4606 logError(testName, function, args, startTime, "", "ReadAt failed", err)
4607 return
4608 }
4609 }
4610
4611 successLogger(testName, function, args, startTime).Info()
4612}
4613
4614// Reproduces issue https://github.com/minio/minio-go/issues/1137
4615func testGetObjectReadAtWhenEOFWasReached() {
4616 // initialize logging params
4617 startTime := time.Now()
4618 testName := getFuncName()
4619 function := "GetObject(bucketName, objectName)"
4620 args := map[string]interface{}{}
4621
4622 // Seed random based on current time.
4623 rand.Seed(time.Now().Unix())
4624
4625 // Instantiate new minio client object.
4626 c, err := minio.New(os.Getenv(serverEndpoint),
4627 &minio.Options{
4628 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
4629 Secure: mustParseBool(os.Getenv(enableHTTPS)),
4630 })
4631 if err != nil {
4632 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
4633 return
4634 }
4635
4636 // Enable tracing, write to stderr.
4637 // c.TraceOn(os.Stderr)
4638
4639 // Set user agent.
4640 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
4641
4642 // Generate a new random bucket name.
4643 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
4644 args["bucketName"] = bucketName
4645
4646 // Make a new bucket.
4647 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
4648 if err != nil {
4649 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
4650 return
4651 }
4652
4653 defer cleanupBucket(bucketName, c)
4654
4655 // Generate 33K of data.
4656 bufSize := dataFileMap["datafile-33-kB"]
4657 reader := getDataReader("datafile-33-kB")
4658 defer reader.Close()
4659
4660 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
4661 args["objectName"] = objectName
4662
4663 buf, err := io.ReadAll(reader)
4664 if err != nil {
4665 logError(testName, function, args, startTime, "", "ReadAll failed", err)
4666 return
4667 }
4668
4669 // Save the data
4670 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
4671 if err != nil {
4672 logError(testName, function, args, startTime, "", "PutObject failed", err)
4673 return
4674 }
4675
4676 // read the data back
4677 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
4678 if err != nil {
4679 logError(testName, function, args, startTime, "", "PutObject failed", err)
4680 return
4681 }
4682
4683 // read directly
4684 buf1 := make([]byte, len(buf))
4685 buf2 := make([]byte, 512)
4686
4687 m, err := r.Read(buf1)
4688 if err != nil {
4689 if err != io.EOF {
4690 logError(testName, function, args, startTime, "", "Read failed", err)
4691 return
4692 }
4693 }
4694 if m != len(buf1) {
4695 logError(testName, function, args, startTime, "", "Read read shorter bytes before reaching EOF, expected "+string(len(buf1))+", got "+string(m), err)
4696 return
4697 }
4698 if !bytes.Equal(buf1, buf) {
4699 logError(testName, function, args, startTime, "", "Incorrect count of Read data", err)
4700 return
4701 }
4702
4703 st, err := r.Stat()
4704 if err != nil {
4705 logError(testName, function, args, startTime, "", "Stat failed", err)
4706 return
4707 }
4708
4709 if st.Size != int64(bufSize) {
4710 logError(testName, function, args, startTime, "", "Number of bytes in stat does not match, expected "+string(int64(bufSize))+", got "+string(st.Size), err)
4711 return
4712 }
4713
4714 m, err = r.ReadAt(buf2, 512)
4715 if err != nil {
4716 logError(testName, function, args, startTime, "", "ReadAt failed", err)
4717 return
4718 }
4719 if m != len(buf2) {
4720 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf2))+", got "+string(m), err)
4721 return
4722 }
4723 if !bytes.Equal(buf2, buf[512:1024]) {
4724 logError(testName, function, args, startTime, "", "Incorrect count of ReadAt data", err)
4725 return
4726 }
4727
4728 successLogger(testName, function, args, startTime).Info()
4729}
4730
4731// Test Presigned Post Policy
4732func testPresignedPostPolicy() {
4733 // initialize logging params
4734 startTime := time.Now()
4735 testName := getFuncName()
4736 function := "PresignedPostPolicy(policy)"
4737 args := map[string]interface{}{
4738 "policy": "",
4739 }
4740
4741 // Seed random based on current time.
4742 rand.Seed(time.Now().Unix())
4743
4744 // Instantiate new minio client object
4745 c, err := minio.New(os.Getenv(serverEndpoint),
4746 &minio.Options{
4747 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
4748 Secure: mustParseBool(os.Getenv(enableHTTPS)),
4749 })
4750 if err != nil {
4751 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
4752 return
4753 }
4754
4755 // Enable tracing, write to stderr.
4756 // c.TraceOn(os.Stderr)
4757
4758 // Set user agent.
4759 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
4760
4761 // Generate a new random bucket name.
4762 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
4763
4764 // Make a new bucket in 'us-east-1' (source bucket).
4765 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
4766 if err != nil {
4767 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
4768 return
4769 }
4770
4771 defer cleanupBucket(bucketName, c)
4772
4773 // Generate 33K of data.
4774 reader := getDataReader("datafile-33-kB")
4775 defer reader.Close()
4776
4777 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
4778 // Azure requires the key to not start with a number
4779 metadataKey := randString(60, rand.NewSource(time.Now().UnixNano()), "user")
4780 metadataValue := randString(60, rand.NewSource(time.Now().UnixNano()), "")
4781
4782 buf, err := io.ReadAll(reader)
4783 if err != nil {
4784 logError(testName, function, args, startTime, "", "ReadAll failed", err)
4785 return
4786 }
4787
4788 // Save the data
4789 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
4790 if err != nil {
4791 logError(testName, function, args, startTime, "", "PutObject failed", err)
4792 return
4793 }
4794
4795 policy := minio.NewPostPolicy()
4796
4797 if err := policy.SetBucket(""); err == nil {
4798 logError(testName, function, args, startTime, "", "SetBucket did not fail for invalid conditions", err)
4799 return
4800 }
4801 if err := policy.SetKey(""); err == nil {
4802 logError(testName, function, args, startTime, "", "SetKey did not fail for invalid conditions", err)
4803 return
4804 }
4805 if err := policy.SetExpires(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)); err == nil {
4806 logError(testName, function, args, startTime, "", "SetExpires did not fail for invalid conditions", err)
4807 return
4808 }
4809 if err := policy.SetContentType(""); err == nil {
4810 logError(testName, function, args, startTime, "", "SetContentType did not fail for invalid conditions", err)
4811 return
4812 }
4813 if err := policy.SetContentLengthRange(1024*1024, 1024); err == nil {
4814 logError(testName, function, args, startTime, "", "SetContentLengthRange did not fail for invalid conditions", err)
4815 return
4816 }
4817 if err := policy.SetUserMetadata("", ""); err == nil {
4818 logError(testName, function, args, startTime, "", "SetUserMetadata did not fail for invalid conditions", err)
4819 return
4820 }
4821
4822 policy.SetBucket(bucketName)
4823 policy.SetKey(objectName)
4824 policy.SetExpires(time.Now().UTC().AddDate(0, 0, 10)) // expires in 10 days
4825 policy.SetContentType("binary/octet-stream")
4826 policy.SetContentLengthRange(10, 1024*1024)
4827 policy.SetUserMetadata(metadataKey, metadataValue)
4828
4829 // Add CRC32C
4830 checksum := minio.ChecksumCRC32C.ChecksumBytes(buf)
4831 policy.SetChecksum(checksum)
4832
4833 args["policy"] = policy.String()
4834
4835 presignedPostPolicyURL, formData, err := c.PresignedPostPolicy(context.Background(), policy)
4836 if err != nil {
4837 logError(testName, function, args, startTime, "", "PresignedPostPolicy failed", err)
4838 return
4839 }
4840
4841 var formBuf bytes.Buffer
4842 writer := multipart.NewWriter(&formBuf)
4843 for k, v := range formData {
4844 writer.WriteField(k, v)
4845 }
4846
4847 // Get a 33KB file to upload and test if set post policy works
4848 filePath := getMintDataDirFilePath("datafile-33-kB")
4849 if filePath == "" {
4850 // Make a temp file with 33 KB data.
4851 file, err := os.CreateTemp(os.TempDir(), "PresignedPostPolicyTest")
4852 if err != nil {
4853 logError(testName, function, args, startTime, "", "TempFile creation failed", err)
4854 return
4855 }
4856 if _, err = io.Copy(file, getDataReader("datafile-33-kB")); err != nil {
4857 logError(testName, function, args, startTime, "", "Copy failed", err)
4858 return
4859 }
4860 if err = file.Close(); err != nil {
4861 logError(testName, function, args, startTime, "", "File Close failed", err)
4862 return
4863 }
4864 filePath = file.Name()
4865 }
4866
4867 // add file to post request
4868 f, err := os.Open(filePath)
4869 defer f.Close()
4870 if err != nil {
4871 logError(testName, function, args, startTime, "", "File open failed", err)
4872 return
4873 }
4874 w, err := writer.CreateFormFile("file", filePath)
4875 if err != nil {
4876 logError(testName, function, args, startTime, "", "CreateFormFile failed", err)
4877 return
4878 }
4879
4880 _, err = io.Copy(w, f)
4881 if err != nil {
4882 logError(testName, function, args, startTime, "", "Copy failed", err)
4883 return
4884 }
4885 writer.Close()
4886
4887 transport, err := minio.DefaultTransport(mustParseBool(os.Getenv(enableHTTPS)))
4888 if err != nil {
4889 logError(testName, function, args, startTime, "", "DefaultTransport failed", err)
4890 return
4891 }
4892
4893 httpClient := &http.Client{
4894 // Setting a sensible time out of 30secs to wait for response
4895 // headers. Request is pro-actively canceled after 30secs
4896 // with no response.
4897 Timeout: 30 * time.Second,
4898 Transport: transport,
4899 }
4900 args["url"] = presignedPostPolicyURL.String()
4901
4902 req, err := http.NewRequest(http.MethodPost, presignedPostPolicyURL.String(), bytes.NewReader(formBuf.Bytes()))
4903 if err != nil {
4904 logError(testName, function, args, startTime, "", "Http request failed", err)
4905 return
4906 }
4907
4908 req.Header.Set("Content-Type", writer.FormDataContentType())
4909
4910 // make post request with correct form data
4911 res, err := httpClient.Do(req)
4912 if err != nil {
4913 logError(testName, function, args, startTime, "", "Http request failed", err)
4914 return
4915 }
4916 defer res.Body.Close()
4917 if res.StatusCode != http.StatusNoContent {
4918 logError(testName, function, args, startTime, "", "Http request failed", errors.New(res.Status))
4919 return
4920 }
4921
4922 // expected path should be absolute path of the object
4923 var scheme string
4924 if mustParseBool(os.Getenv(enableHTTPS)) {
4925 scheme = "https://"
4926 } else {
4927 scheme = "http://"
4928 }
4929
4930 expectedLocation := scheme + os.Getenv(serverEndpoint) + "/" + bucketName + "/" + objectName
4931 expectedLocationBucketDNS := scheme + bucketName + "." + os.Getenv(serverEndpoint) + "/" + objectName
4932
4933 if !strings.Contains(expectedLocation, "s3.amazonaws.com/") {
4934 // Test when not against AWS S3.
4935 if val, ok := res.Header["Location"]; ok {
4936 if val[0] != expectedLocation && val[0] != expectedLocationBucketDNS {
4937 logError(testName, function, args, startTime, "", fmt.Sprintf("Location in header response is incorrect. Want %q or %q, got %q", expectedLocation, expectedLocationBucketDNS, val[0]), err)
4938 return
4939 }
4940 } else {
4941 logError(testName, function, args, startTime, "", "Location not found in header response", err)
4942 return
4943 }
4944 }
4945 want := checksum.Encoded()
4946 if got := res.Header.Get("X-Amz-Checksum-Crc32c"); got != want {
4947 logError(testName, function, args, startTime, "", fmt.Sprintf("Want checksum %q, got %q", want, got), nil)
4948 return
4949 }
4950
4951 successLogger(testName, function, args, startTime).Info()
4952}
4953
4954// Tests copy object
4955func testCopyObject() {
4956 // initialize logging params
4957 startTime := time.Now()
4958 testName := getFuncName()
4959 function := "CopyObject(dst, src)"
4960 args := map[string]interface{}{}
4961
4962 // Seed random based on current time.
4963 rand.Seed(time.Now().Unix())
4964
4965 // Instantiate new minio client object
4966 c, err := minio.New(os.Getenv(serverEndpoint),
4967 &minio.Options{
4968 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
4969 Secure: mustParseBool(os.Getenv(enableHTTPS)),
4970 })
4971 if err != nil {
4972 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
4973 return
4974 }
4975
4976 // Enable tracing, write to stderr.
4977 // c.TraceOn(os.Stderr)
4978
4979 // Set user agent.
4980 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
4981
4982 // Generate a new random bucket name.
4983 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
4984
4985 // Make a new bucket in 'us-east-1' (source bucket).
4986 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
4987 if err != nil {
4988 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
4989 return
4990 }
4991
4992 defer cleanupBucket(bucketName, c)
4993
4994 // Make a new bucket in 'us-east-1' (destination bucket).
4995 err = c.MakeBucket(context.Background(), bucketName+"-copy", minio.MakeBucketOptions{Region: "us-east-1"})
4996 if err != nil {
4997 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
4998 return
4999 }
5000 defer cleanupBucket(bucketName+"-copy", c)
5001
5002 // Generate 33K of data.
5003 bufSize := dataFileMap["datafile-33-kB"]
5004 reader := getDataReader("datafile-33-kB")
5005
5006 // Save the data
5007 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
5008 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
5009 if err != nil {
5010 logError(testName, function, args, startTime, "", "PutObject failed", err)
5011 return
5012 }
5013
5014 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
5015 if err != nil {
5016 logError(testName, function, args, startTime, "", "GetObject failed", err)
5017 return
5018 }
5019 // Check the various fields of source object against destination object.
5020 objInfo, err := r.Stat()
5021 if err != nil {
5022 logError(testName, function, args, startTime, "", "Stat failed", err)
5023 return
5024 }
5025
5026 // Copy Source
5027 src := minio.CopySrcOptions{
5028 Bucket: bucketName,
5029 Object: objectName,
5030 // Set copy conditions.
5031 MatchETag: objInfo.ETag,
5032 MatchModifiedSince: time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC),
5033 }
5034 args["src"] = src
5035
5036 dst := minio.CopyDestOptions{
5037 Bucket: bucketName + "-copy",
5038 Object: objectName + "-copy",
5039 }
5040
5041 // Perform the Copy
5042 if _, err = c.CopyObject(context.Background(), dst, src); err != nil {
5043 logError(testName, function, args, startTime, "", "CopyObject failed", err)
5044 return
5045 }
5046
5047 // Source object
5048 r, err = c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
5049 if err != nil {
5050 logError(testName, function, args, startTime, "", "GetObject failed", err)
5051 return
5052 }
5053
5054 // Destination object
5055 readerCopy, err := c.GetObject(context.Background(), bucketName+"-copy", objectName+"-copy", minio.GetObjectOptions{})
5056 if err != nil {
5057 logError(testName, function, args, startTime, "", "GetObject failed", err)
5058 return
5059 }
5060
5061 // Check the various fields of source object against destination object.
5062 objInfo, err = r.Stat()
5063 if err != nil {
5064 logError(testName, function, args, startTime, "", "Stat failed", err)
5065 return
5066 }
5067 objInfoCopy, err := readerCopy.Stat()
5068 if err != nil {
5069 logError(testName, function, args, startTime, "", "Stat failed", err)
5070 return
5071 }
5072 if objInfo.Size != objInfoCopy.Size {
5073 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(objInfoCopy.Size)+", got "+string(objInfo.Size), err)
5074 return
5075 }
5076
5077 if err := crcMatchesName(r, "datafile-33-kB"); err != nil {
5078 logError(testName, function, args, startTime, "", "data CRC check failed", err)
5079 return
5080 }
5081 if err := crcMatchesName(readerCopy, "datafile-33-kB"); err != nil {
5082 logError(testName, function, args, startTime, "", "copy data CRC check failed", err)
5083 return
5084 }
5085 // Close all the get readers before proceeding with CopyObject operations.
5086 r.Close()
5087 readerCopy.Close()
5088
5089 // CopyObject again but with wrong conditions
5090 src = minio.CopySrcOptions{
5091 Bucket: bucketName,
5092 Object: objectName,
5093 MatchUnmodifiedSince: time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC),
5094 NoMatchETag: objInfo.ETag,
5095 }
5096
5097 // Perform the Copy which should fail
5098 _, err = c.CopyObject(context.Background(), dst, src)
5099 if err == nil {
5100 logError(testName, function, args, startTime, "", "CopyObject did not fail for invalid conditions", err)
5101 return
5102 }
5103
5104 src = minio.CopySrcOptions{
5105 Bucket: bucketName,
5106 Object: objectName,
5107 }
5108
5109 dst = minio.CopyDestOptions{
5110 Bucket: bucketName,
5111 Object: objectName,
5112 ReplaceMetadata: true,
5113 UserMetadata: map[string]string{
5114 "Copy": "should be same",
5115 },
5116 }
5117 args["dst"] = dst
5118 args["src"] = src
5119
5120 _, err = c.CopyObject(context.Background(), dst, src)
5121 if err != nil {
5122 logError(testName, function, args, startTime, "", "CopyObject shouldn't fail", err)
5123 return
5124 }
5125
5126 oi, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
5127 if err != nil {
5128 logError(testName, function, args, startTime, "", "StatObject failed", err)
5129 return
5130 }
5131
5132 stOpts := minio.StatObjectOptions{}
5133 stOpts.SetMatchETag(oi.ETag)
5134 objInfo, err = c.StatObject(context.Background(), bucketName, objectName, stOpts)
5135 if err != nil {
5136 logError(testName, function, args, startTime, "", "CopyObject ETag should match and not fail", err)
5137 return
5138 }
5139
5140 if objInfo.Metadata.Get("x-amz-meta-copy") != "should be same" {
5141 logError(testName, function, args, startTime, "", "CopyObject modified metadata should match", err)
5142 return
5143 }
5144
5145 successLogger(testName, function, args, startTime).Info()
5146}
5147
5148// Tests SSE-C get object ReaderSeeker interface methods.
5149func testSSECEncryptedGetObjectReadSeekFunctional() {
5150 // initialize logging params
5151 startTime := time.Now()
5152 testName := getFuncName()
5153 function := "GetObject(bucketName, objectName)"
5154 args := map[string]interface{}{}
5155
5156 // Seed random based on current time.
5157 rand.Seed(time.Now().Unix())
5158
5159 // Instantiate new minio client object.
5160 c, err := minio.New(os.Getenv(serverEndpoint),
5161 &minio.Options{
5162 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
5163 Secure: mustParseBool(os.Getenv(enableHTTPS)),
5164 })
5165 if err != nil {
5166 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
5167 return
5168 }
5169
5170 // Enable tracing, write to stderr.
5171 // c.TraceOn(os.Stderr)
5172
5173 // Set user agent.
5174 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
5175
5176 // Generate a new random bucket name.
5177 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
5178 args["bucketName"] = bucketName
5179
5180 // Make a new bucket.
5181 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
5182 if err != nil {
5183 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
5184 return
5185 }
5186
5187 defer func() {
5188 // Delete all objects and buckets
5189 if err = cleanupBucket(bucketName, c); err != nil {
5190 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
5191 return
5192 }
5193 }()
5194
5195 // Generate 129MiB of data.
5196 bufSize := dataFileMap["datafile-129-MB"]
5197 reader := getDataReader("datafile-129-MB")
5198 defer reader.Close()
5199
5200 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
5201 args["objectName"] = objectName
5202
5203 buf, err := io.ReadAll(reader)
5204 if err != nil {
5205 logError(testName, function, args, startTime, "", "ReadAll failed", err)
5206 return
5207 }
5208
5209 // Save the data
5210 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{
5211 ContentType: "binary/octet-stream",
5212 ServerSideEncryption: encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+objectName)),
5213 })
5214 if err != nil {
5215 logError(testName, function, args, startTime, "", "PutObject failed", err)
5216 return
5217 }
5218
5219 // Read the data back
5220 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{
5221 ServerSideEncryption: encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+objectName)),
5222 })
5223 if err != nil {
5224 logError(testName, function, args, startTime, "", "GetObject failed", err)
5225 return
5226 }
5227 defer r.Close()
5228
5229 st, err := r.Stat()
5230 if err != nil {
5231 logError(testName, function, args, startTime, "", "Stat object failed", err)
5232 return
5233 }
5234
5235 if st.Size != int64(bufSize) {
5236 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(int64(bufSize))+", got "+string(st.Size), err)
5237 return
5238 }
5239
5240 // This following function helps us to compare data from the reader after seek
5241 // with the data from the original buffer
5242 cmpData := func(r io.Reader, start, end int) {
5243 if end-start == 0 {
5244 return
5245 }
5246 buffer := bytes.NewBuffer([]byte{})
5247 if _, err := io.CopyN(buffer, r, int64(bufSize)); err != nil {
5248 if err != io.EOF {
5249 logError(testName, function, args, startTime, "", "CopyN failed", err)
5250 return
5251 }
5252 }
5253 if !bytes.Equal(buf[start:end], buffer.Bytes()) {
5254 logError(testName, function, args, startTime, "", "Incorrect read bytes v/s original buffer", err)
5255 return
5256 }
5257 }
5258
5259 testCases := []struct {
5260 offset int64
5261 whence int
5262 pos int64
5263 err error
5264 shouldCmp bool
5265 start int
5266 end int
5267 }{
5268 // Start from offset 0, fetch data and compare
5269 {0, 0, 0, nil, true, 0, 0},
5270 // Start from offset 2048, fetch data and compare
5271 {2048, 0, 2048, nil, true, 2048, bufSize},
5272 // Start from offset larger than possible
5273 {int64(bufSize) + 1024, 0, 0, io.EOF, false, 0, 0},
5274 // Move to offset 0 without comparing
5275 {0, 0, 0, nil, false, 0, 0},
5276 // Move one step forward and compare
5277 {1, 1, 1, nil, true, 1, bufSize},
5278 // Move larger than possible
5279 {int64(bufSize), 1, 0, io.EOF, false, 0, 0},
5280 // Provide negative offset with CUR_SEEK
5281 {int64(-1), 1, 0, fmt.Errorf("Negative position not allowed for 1"), false, 0, 0},
5282 // Test with whence SEEK_END and with positive offset
5283 {1024, 2, 0, io.EOF, false, 0, 0},
5284 // Test with whence SEEK_END and with negative offset
5285 {-1024, 2, int64(bufSize) - 1024, nil, true, bufSize - 1024, bufSize},
5286 // Test with whence SEEK_END and with large negative offset
5287 {-int64(bufSize) * 2, 2, 0, fmt.Errorf("Seeking at negative offset not allowed for 2"), false, 0, 0},
5288 // Test with invalid whence
5289 {0, 3, 0, fmt.Errorf("Invalid whence 3"), false, 0, 0},
5290 }
5291
5292 for i, testCase := range testCases {
5293 // Perform seek operation
5294 n, err := r.Seek(testCase.offset, testCase.whence)
5295 if err != nil && testCase.err == nil {
5296 // We expected success.
5297 logError(testName, function, args, startTime, "",
5298 fmt.Sprintf("Test %d, unexpected err value: expected: %s, found: %s", i+1, testCase.err, err), err)
5299 return
5300 }
5301 if err == nil && testCase.err != nil {
5302 // We expected failure, but got success.
5303 logError(testName, function, args, startTime, "",
5304 fmt.Sprintf("Test %d, unexpected err value: expected: %s, found: %s", i+1, testCase.err, err), err)
5305 return
5306 }
5307 if err != nil && testCase.err != nil {
5308 if err.Error() != testCase.err.Error() {
5309 // We expect a specific error
5310 logError(testName, function, args, startTime, "",
5311 fmt.Sprintf("Test %d, unexpected err value: expected: %s, found: %s", i+1, testCase.err, err), err)
5312 return
5313 }
5314 }
5315 // Check the returned seek pos
5316 if n != testCase.pos {
5317 logError(testName, function, args, startTime, "",
5318 fmt.Sprintf("Test %d, number of bytes seeked does not match, expected %d, got %d", i+1, testCase.pos, n), err)
5319 return
5320 }
5321 // Compare only if shouldCmp is activated
5322 if testCase.shouldCmp {
5323 cmpData(r, testCase.start, testCase.end)
5324 }
5325 }
5326
5327 successLogger(testName, function, args, startTime).Info()
5328}
5329
5330// Tests SSE-S3 get object ReaderSeeker interface methods.
5331func testSSES3EncryptedGetObjectReadSeekFunctional() {
5332 // initialize logging params
5333 startTime := time.Now()
5334 testName := getFuncName()
5335 function := "GetObject(bucketName, objectName)"
5336 args := map[string]interface{}{}
5337
5338 // Seed random based on current time.
5339 rand.Seed(time.Now().Unix())
5340
5341 // Instantiate new minio client object.
5342 c, err := minio.New(os.Getenv(serverEndpoint),
5343 &minio.Options{
5344 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
5345 Secure: mustParseBool(os.Getenv(enableHTTPS)),
5346 })
5347 if err != nil {
5348 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
5349 return
5350 }
5351
5352 // Enable tracing, write to stderr.
5353 // c.TraceOn(os.Stderr)
5354
5355 // Set user agent.
5356 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
5357
5358 // Generate a new random bucket name.
5359 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
5360 args["bucketName"] = bucketName
5361
5362 // Make a new bucket.
5363 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
5364 if err != nil {
5365 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
5366 return
5367 }
5368
5369 defer func() {
5370 // Delete all objects and buckets
5371 if err = cleanupBucket(bucketName, c); err != nil {
5372 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
5373 return
5374 }
5375 }()
5376
5377 // Generate 129MiB of data.
5378 bufSize := dataFileMap["datafile-129-MB"]
5379 reader := getDataReader("datafile-129-MB")
5380 defer reader.Close()
5381
5382 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
5383 args["objectName"] = objectName
5384
5385 buf, err := io.ReadAll(reader)
5386 if err != nil {
5387 logError(testName, function, args, startTime, "", "ReadAll failed", err)
5388 return
5389 }
5390
5391 // Save the data
5392 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{
5393 ContentType: "binary/octet-stream",
5394 ServerSideEncryption: encrypt.NewSSE(),
5395 })
5396 if err != nil {
5397 logError(testName, function, args, startTime, "", "PutObject failed", err)
5398 return
5399 }
5400
5401 // Read the data back
5402 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
5403 if err != nil {
5404 logError(testName, function, args, startTime, "", "GetObject failed", err)
5405 return
5406 }
5407 defer r.Close()
5408
5409 st, err := r.Stat()
5410 if err != nil {
5411 logError(testName, function, args, startTime, "", "Stat object failed", err)
5412 return
5413 }
5414
5415 if st.Size != int64(bufSize) {
5416 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(int64(bufSize))+", got "+string(st.Size), err)
5417 return
5418 }
5419
5420 // This following function helps us to compare data from the reader after seek
5421 // with the data from the original buffer
5422 cmpData := func(r io.Reader, start, end int) {
5423 if end-start == 0 {
5424 return
5425 }
5426 buffer := bytes.NewBuffer([]byte{})
5427 if _, err := io.CopyN(buffer, r, int64(bufSize)); err != nil {
5428 if err != io.EOF {
5429 logError(testName, function, args, startTime, "", "CopyN failed", err)
5430 return
5431 }
5432 }
5433 if !bytes.Equal(buf[start:end], buffer.Bytes()) {
5434 logError(testName, function, args, startTime, "", "Incorrect read bytes v/s original buffer", err)
5435 return
5436 }
5437 }
5438
5439 testCases := []struct {
5440 offset int64
5441 whence int
5442 pos int64
5443 err error
5444 shouldCmp bool
5445 start int
5446 end int
5447 }{
5448 // Start from offset 0, fetch data and compare
5449 {0, 0, 0, nil, true, 0, 0},
5450 // Start from offset 2048, fetch data and compare
5451 {2048, 0, 2048, nil, true, 2048, bufSize},
5452 // Start from offset larger than possible
5453 {int64(bufSize) + 1024, 0, 0, io.EOF, false, 0, 0},
5454 // Move to offset 0 without comparing
5455 {0, 0, 0, nil, false, 0, 0},
5456 // Move one step forward and compare
5457 {1, 1, 1, nil, true, 1, bufSize},
5458 // Move larger than possible
5459 {int64(bufSize), 1, 0, io.EOF, false, 0, 0},
5460 // Provide negative offset with CUR_SEEK
5461 {int64(-1), 1, 0, fmt.Errorf("Negative position not allowed for 1"), false, 0, 0},
5462 // Test with whence SEEK_END and with positive offset
5463 {1024, 2, 0, io.EOF, false, 0, 0},
5464 // Test with whence SEEK_END and with negative offset
5465 {-1024, 2, int64(bufSize) - 1024, nil, true, bufSize - 1024, bufSize},
5466 // Test with whence SEEK_END and with large negative offset
5467 {-int64(bufSize) * 2, 2, 0, fmt.Errorf("Seeking at negative offset not allowed for 2"), false, 0, 0},
5468 // Test with invalid whence
5469 {0, 3, 0, fmt.Errorf("Invalid whence 3"), false, 0, 0},
5470 }
5471
5472 for i, testCase := range testCases {
5473 // Perform seek operation
5474 n, err := r.Seek(testCase.offset, testCase.whence)
5475 if err != nil && testCase.err == nil {
5476 // We expected success.
5477 logError(testName, function, args, startTime, "",
5478 fmt.Sprintf("Test %d, unexpected err value: expected: %s, found: %s", i+1, testCase.err, err), err)
5479 return
5480 }
5481 if err == nil && testCase.err != nil {
5482 // We expected failure, but got success.
5483 logError(testName, function, args, startTime, "",
5484 fmt.Sprintf("Test %d, unexpected err value: expected: %s, found: %s", i+1, testCase.err, err), err)
5485 return
5486 }
5487 if err != nil && testCase.err != nil {
5488 if err.Error() != testCase.err.Error() {
5489 // We expect a specific error
5490 logError(testName, function, args, startTime, "",
5491 fmt.Sprintf("Test %d, unexpected err value: expected: %s, found: %s", i+1, testCase.err, err), err)
5492 return
5493 }
5494 }
5495 // Check the returned seek pos
5496 if n != testCase.pos {
5497 logError(testName, function, args, startTime, "",
5498 fmt.Sprintf("Test %d, number of bytes seeked does not match, expected %d, got %d", i+1, testCase.pos, n), err)
5499 return
5500 }
5501 // Compare only if shouldCmp is activated
5502 if testCase.shouldCmp {
5503 cmpData(r, testCase.start, testCase.end)
5504 }
5505 }
5506
5507 successLogger(testName, function, args, startTime).Info()
5508}
5509
5510// Tests SSE-C get object ReaderAt interface methods.
5511func testSSECEncryptedGetObjectReadAtFunctional() {
5512 // initialize logging params
5513 startTime := time.Now()
5514 testName := getFuncName()
5515 function := "GetObject(bucketName, objectName)"
5516 args := map[string]interface{}{}
5517
5518 // Seed random based on current time.
5519 rand.Seed(time.Now().Unix())
5520
5521 // Instantiate new minio client object.
5522 c, err := minio.New(os.Getenv(serverEndpoint),
5523 &minio.Options{
5524 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
5525 Secure: mustParseBool(os.Getenv(enableHTTPS)),
5526 })
5527 if err != nil {
5528 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
5529 return
5530 }
5531
5532 // Enable tracing, write to stderr.
5533 // c.TraceOn(os.Stderr)
5534
5535 // Set user agent.
5536 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
5537
5538 // Generate a new random bucket name.
5539 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
5540 args["bucketName"] = bucketName
5541
5542 // Make a new bucket.
5543 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
5544 if err != nil {
5545 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
5546 return
5547 }
5548
5549 defer cleanupBucket(bucketName, c)
5550
5551 // Generate 129MiB of data.
5552 bufSize := dataFileMap["datafile-129-MB"]
5553 reader := getDataReader("datafile-129-MB")
5554 defer reader.Close()
5555
5556 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
5557 args["objectName"] = objectName
5558
5559 buf, err := io.ReadAll(reader)
5560 if err != nil {
5561 logError(testName, function, args, startTime, "", "ReadAll failed", err)
5562 return
5563 }
5564
5565 // Save the data
5566 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{
5567 ContentType: "binary/octet-stream",
5568 ServerSideEncryption: encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+objectName)),
5569 })
5570 if err != nil {
5571 logError(testName, function, args, startTime, "", "PutObject failed", err)
5572 return
5573 }
5574
5575 // read the data back
5576 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{
5577 ServerSideEncryption: encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+objectName)),
5578 })
5579 if err != nil {
5580 logError(testName, function, args, startTime, "", "PutObject failed", err)
5581 return
5582 }
5583 defer r.Close()
5584
5585 offset := int64(2048)
5586
5587 // read directly
5588 buf1 := make([]byte, 512)
5589 buf2 := make([]byte, 512)
5590 buf3 := make([]byte, 512)
5591 buf4 := make([]byte, 512)
5592
5593 // Test readAt before stat is called such that objectInfo doesn't change.
5594 m, err := r.ReadAt(buf1, offset)
5595 if err != nil {
5596 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5597 return
5598 }
5599 if m != len(buf1) {
5600 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf1))+", got "+string(m), err)
5601 return
5602 }
5603 if !bytes.Equal(buf1, buf[offset:offset+512]) {
5604 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
5605 return
5606 }
5607 offset += 512
5608
5609 st, err := r.Stat()
5610 if err != nil {
5611 logError(testName, function, args, startTime, "", "Stat failed", err)
5612 return
5613 }
5614
5615 if st.Size != int64(bufSize) {
5616 logError(testName, function, args, startTime, "", "Number of bytes in stat does not match, expected "+string(int64(bufSize))+", got "+string(st.Size), err)
5617 return
5618 }
5619
5620 m, err = r.ReadAt(buf2, offset)
5621 if err != nil {
5622 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5623 return
5624 }
5625 if m != len(buf2) {
5626 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf2))+", got "+string(m), err)
5627 return
5628 }
5629 if !bytes.Equal(buf2, buf[offset:offset+512]) {
5630 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
5631 return
5632 }
5633 offset += 512
5634 m, err = r.ReadAt(buf3, offset)
5635 if err != nil {
5636 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5637 return
5638 }
5639 if m != len(buf3) {
5640 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf3))+", got "+string(m), err)
5641 return
5642 }
5643 if !bytes.Equal(buf3, buf[offset:offset+512]) {
5644 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
5645 return
5646 }
5647 offset += 512
5648 m, err = r.ReadAt(buf4, offset)
5649 if err != nil {
5650 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5651 return
5652 }
5653 if m != len(buf4) {
5654 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf4))+", got "+string(m), err)
5655 return
5656 }
5657 if !bytes.Equal(buf4, buf[offset:offset+512]) {
5658 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
5659 return
5660 }
5661
5662 buf5 := make([]byte, len(buf))
5663 // Read the whole object.
5664 m, err = r.ReadAt(buf5, 0)
5665 if err != nil {
5666 if err != io.EOF {
5667 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5668 return
5669 }
5670 }
5671 if m != len(buf5) {
5672 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf5))+", got "+string(m), err)
5673 return
5674 }
5675 if !bytes.Equal(buf, buf5) {
5676 logError(testName, function, args, startTime, "", "Incorrect data read in GetObject, than what was previously uploaded", err)
5677 return
5678 }
5679
5680 buf6 := make([]byte, len(buf)+1)
5681 // Read the whole object and beyond.
5682 _, err = r.ReadAt(buf6, 0)
5683 if err != nil {
5684 if err != io.EOF {
5685 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5686 return
5687 }
5688 }
5689
5690 successLogger(testName, function, args, startTime).Info()
5691}
5692
5693// Tests SSE-S3 get object ReaderAt interface methods.
5694func testSSES3EncryptedGetObjectReadAtFunctional() {
5695 // initialize logging params
5696 startTime := time.Now()
5697 testName := getFuncName()
5698 function := "GetObject(bucketName, objectName)"
5699 args := map[string]interface{}{}
5700
5701 // Seed random based on current time.
5702 rand.Seed(time.Now().Unix())
5703
5704 // Instantiate new minio client object.
5705 c, err := minio.New(os.Getenv(serverEndpoint),
5706 &minio.Options{
5707 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
5708 Secure: mustParseBool(os.Getenv(enableHTTPS)),
5709 })
5710 if err != nil {
5711 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
5712 return
5713 }
5714
5715 // Enable tracing, write to stderr.
5716 // c.TraceOn(os.Stderr)
5717
5718 // Set user agent.
5719 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
5720
5721 // Generate a new random bucket name.
5722 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
5723 args["bucketName"] = bucketName
5724
5725 // Make a new bucket.
5726 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
5727 if err != nil {
5728 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
5729 return
5730 }
5731
5732 defer cleanupBucket(bucketName, c)
5733
5734 // Generate 129MiB of data.
5735 bufSize := dataFileMap["datafile-129-MB"]
5736 reader := getDataReader("datafile-129-MB")
5737 defer reader.Close()
5738
5739 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
5740 args["objectName"] = objectName
5741
5742 buf, err := io.ReadAll(reader)
5743 if err != nil {
5744 logError(testName, function, args, startTime, "", "ReadAll failed", err)
5745 return
5746 }
5747
5748 // Save the data
5749 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{
5750 ContentType: "binary/octet-stream",
5751 ServerSideEncryption: encrypt.NewSSE(),
5752 })
5753 if err != nil {
5754 logError(testName, function, args, startTime, "", "PutObject failed", err)
5755 return
5756 }
5757
5758 // read the data back
5759 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
5760 if err != nil {
5761 logError(testName, function, args, startTime, "", "PutObject failed", err)
5762 return
5763 }
5764 defer r.Close()
5765
5766 offset := int64(2048)
5767
5768 // read directly
5769 buf1 := make([]byte, 512)
5770 buf2 := make([]byte, 512)
5771 buf3 := make([]byte, 512)
5772 buf4 := make([]byte, 512)
5773
5774 // Test readAt before stat is called such that objectInfo doesn't change.
5775 m, err := r.ReadAt(buf1, offset)
5776 if err != nil {
5777 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5778 return
5779 }
5780 if m != len(buf1) {
5781 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf1))+", got "+string(m), err)
5782 return
5783 }
5784 if !bytes.Equal(buf1, buf[offset:offset+512]) {
5785 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
5786 return
5787 }
5788 offset += 512
5789
5790 st, err := r.Stat()
5791 if err != nil {
5792 logError(testName, function, args, startTime, "", "Stat failed", err)
5793 return
5794 }
5795
5796 if st.Size != int64(bufSize) {
5797 logError(testName, function, args, startTime, "", "Number of bytes in stat does not match, expected "+string(int64(bufSize))+", got "+string(st.Size), err)
5798 return
5799 }
5800
5801 m, err = r.ReadAt(buf2, offset)
5802 if err != nil {
5803 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5804 return
5805 }
5806 if m != len(buf2) {
5807 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf2))+", got "+string(m), err)
5808 return
5809 }
5810 if !bytes.Equal(buf2, buf[offset:offset+512]) {
5811 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
5812 return
5813 }
5814 offset += 512
5815 m, err = r.ReadAt(buf3, offset)
5816 if err != nil {
5817 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5818 return
5819 }
5820 if m != len(buf3) {
5821 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf3))+", got "+string(m), err)
5822 return
5823 }
5824 if !bytes.Equal(buf3, buf[offset:offset+512]) {
5825 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
5826 return
5827 }
5828 offset += 512
5829 m, err = r.ReadAt(buf4, offset)
5830 if err != nil {
5831 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5832 return
5833 }
5834 if m != len(buf4) {
5835 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf4))+", got "+string(m), err)
5836 return
5837 }
5838 if !bytes.Equal(buf4, buf[offset:offset+512]) {
5839 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
5840 return
5841 }
5842
5843 buf5 := make([]byte, len(buf))
5844 // Read the whole object.
5845 m, err = r.ReadAt(buf5, 0)
5846 if err != nil {
5847 if err != io.EOF {
5848 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5849 return
5850 }
5851 }
5852 if m != len(buf5) {
5853 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf5))+", got "+string(m), err)
5854 return
5855 }
5856 if !bytes.Equal(buf, buf5) {
5857 logError(testName, function, args, startTime, "", "Incorrect data read in GetObject, than what was previously uploaded", err)
5858 return
5859 }
5860
5861 buf6 := make([]byte, len(buf)+1)
5862 // Read the whole object and beyond.
5863 _, err = r.ReadAt(buf6, 0)
5864 if err != nil {
5865 if err != io.EOF {
5866 logError(testName, function, args, startTime, "", "ReadAt failed", err)
5867 return
5868 }
5869 }
5870
5871 successLogger(testName, function, args, startTime).Info()
5872}
5873
5874// testSSECEncryptionPutGet tests encryption with customer provided encryption keys
5875func testSSECEncryptionPutGet() {
5876 // initialize logging params
5877 startTime := time.Now()
5878 testName := getFuncName()
5879 function := "PutEncryptedObject(bucketName, objectName, reader, sse)"
5880 args := map[string]interface{}{
5881 "bucketName": "",
5882 "objectName": "",
5883 "sse": "",
5884 }
5885 // Seed random based on current time.
5886 rand.Seed(time.Now().Unix())
5887
5888 // Instantiate new minio client object
5889 c, err := minio.New(os.Getenv(serverEndpoint),
5890 &minio.Options{
5891 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
5892 Secure: mustParseBool(os.Getenv(enableHTTPS)),
5893 })
5894 if err != nil {
5895 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
5896 return
5897 }
5898
5899 // Enable tracing, write to stderr.
5900 // c.TraceOn(os.Stderr)
5901
5902 // Set user agent.
5903 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
5904
5905 // Generate a new random bucket name.
5906 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
5907 args["bucketName"] = bucketName
5908
5909 // Make a new bucket.
5910 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
5911 if err != nil {
5912 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
5913 return
5914 }
5915
5916 defer cleanupBucket(bucketName, c)
5917
5918 testCases := []struct {
5919 buf []byte
5920 }{
5921 {buf: bytes.Repeat([]byte("F"), 1)},
5922 {buf: bytes.Repeat([]byte("F"), 15)},
5923 {buf: bytes.Repeat([]byte("F"), 16)},
5924 {buf: bytes.Repeat([]byte("F"), 17)},
5925 {buf: bytes.Repeat([]byte("F"), 31)},
5926 {buf: bytes.Repeat([]byte("F"), 32)},
5927 {buf: bytes.Repeat([]byte("F"), 33)},
5928 {buf: bytes.Repeat([]byte("F"), 1024)},
5929 {buf: bytes.Repeat([]byte("F"), 1024*2)},
5930 {buf: bytes.Repeat([]byte("F"), 1024*1024)},
5931 }
5932
5933 const password = "correct horse battery staple" // https://xkcd.com/936/
5934
5935 for i, testCase := range testCases {
5936 // Generate a random object name
5937 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
5938 args["objectName"] = objectName
5939
5940 // Secured object
5941 sse := encrypt.DefaultPBKDF([]byte(password), []byte(bucketName+objectName))
5942 args["sse"] = sse
5943
5944 // Put encrypted data
5945 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(testCase.buf), int64(len(testCase.buf)), minio.PutObjectOptions{ServerSideEncryption: sse})
5946 if err != nil {
5947 logError(testName, function, args, startTime, "", "PutEncryptedObject failed", err)
5948 return
5949 }
5950
5951 // Read the data back
5952 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{ServerSideEncryption: sse})
5953 if err != nil {
5954 logError(testName, function, args, startTime, "", "GetEncryptedObject failed", err)
5955 return
5956 }
5957 defer r.Close()
5958
5959 // Compare the sent object with the received one
5960 recvBuffer := bytes.NewBuffer([]byte{})
5961 if _, err = io.Copy(recvBuffer, r); err != nil {
5962 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", error: "+err.Error(), err)
5963 return
5964 }
5965 if recvBuffer.Len() != len(testCase.buf) {
5966 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", Number of bytes of received object does not match, expected "+string(len(testCase.buf))+", got "+string(recvBuffer.Len()), err)
5967 return
5968 }
5969 if !bytes.Equal(testCase.buf, recvBuffer.Bytes()) {
5970 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", Encrypted sent is not equal to decrypted, expected "+string(testCase.buf)+", got "+string(recvBuffer.Bytes()), err)
5971 return
5972 }
5973
5974 successLogger(testName, function, args, startTime).Info()
5975
5976 }
5977
5978 successLogger(testName, function, args, startTime).Info()
5979}
5980
5981// TestEncryptionFPut tests encryption with customer specified encryption keys
5982func testSSECEncryptionFPut() {
5983 // initialize logging params
5984 startTime := time.Now()
5985 testName := getFuncName()
5986 function := "FPutEncryptedObject(bucketName, objectName, filePath, contentType, sse)"
5987 args := map[string]interface{}{
5988 "bucketName": "",
5989 "objectName": "",
5990 "filePath": "",
5991 "contentType": "",
5992 "sse": "",
5993 }
5994 // Seed random based on current time.
5995 rand.Seed(time.Now().Unix())
5996
5997 // Instantiate new minio client object
5998 c, err := minio.New(os.Getenv(serverEndpoint),
5999 &minio.Options{
6000 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
6001 Secure: mustParseBool(os.Getenv(enableHTTPS)),
6002 })
6003 if err != nil {
6004 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
6005 return
6006 }
6007
6008 // Enable tracing, write to stderr.
6009 // c.TraceOn(os.Stderr)
6010
6011 // Set user agent.
6012 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
6013
6014 // Generate a new random bucket name.
6015 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
6016 args["bucketName"] = bucketName
6017
6018 // Make a new bucket.
6019 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
6020 if err != nil {
6021 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
6022 return
6023 }
6024
6025 defer cleanupBucket(bucketName, c)
6026
6027 // Object custom metadata
6028 customContentType := "custom/contenttype"
6029 args["metadata"] = customContentType
6030
6031 testCases := []struct {
6032 buf []byte
6033 }{
6034 {buf: bytes.Repeat([]byte("F"), 0)},
6035 {buf: bytes.Repeat([]byte("F"), 1)},
6036 {buf: bytes.Repeat([]byte("F"), 15)},
6037 {buf: bytes.Repeat([]byte("F"), 16)},
6038 {buf: bytes.Repeat([]byte("F"), 17)},
6039 {buf: bytes.Repeat([]byte("F"), 31)},
6040 {buf: bytes.Repeat([]byte("F"), 32)},
6041 {buf: bytes.Repeat([]byte("F"), 33)},
6042 {buf: bytes.Repeat([]byte("F"), 1024)},
6043 {buf: bytes.Repeat([]byte("F"), 1024*2)},
6044 {buf: bytes.Repeat([]byte("F"), 1024*1024)},
6045 }
6046
6047 const password = "correct horse battery staple" // https://xkcd.com/936/
6048 for i, testCase := range testCases {
6049 // Generate a random object name
6050 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
6051 args["objectName"] = objectName
6052
6053 // Secured object
6054 sse := encrypt.DefaultPBKDF([]byte(password), []byte(bucketName+objectName))
6055 args["sse"] = sse
6056
6057 // Generate a random file name.
6058 fileName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
6059 file, err := os.Create(fileName)
6060 if err != nil {
6061 logError(testName, function, args, startTime, "", "file create failed", err)
6062 return
6063 }
6064 _, err = file.Write(testCase.buf)
6065 if err != nil {
6066 logError(testName, function, args, startTime, "", "file write failed", err)
6067 return
6068 }
6069 file.Close()
6070 // Put encrypted data
6071 if _, err = c.FPutObject(context.Background(), bucketName, objectName, fileName, minio.PutObjectOptions{ServerSideEncryption: sse}); err != nil {
6072 logError(testName, function, args, startTime, "", "FPutEncryptedObject failed", err)
6073 return
6074 }
6075
6076 // Read the data back
6077 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{ServerSideEncryption: sse})
6078 if err != nil {
6079 logError(testName, function, args, startTime, "", "GetEncryptedObject failed", err)
6080 return
6081 }
6082 defer r.Close()
6083
6084 // Compare the sent object with the received one
6085 recvBuffer := bytes.NewBuffer([]byte{})
6086 if _, err = io.Copy(recvBuffer, r); err != nil {
6087 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", error: "+err.Error(), err)
6088 return
6089 }
6090 if recvBuffer.Len() != len(testCase.buf) {
6091 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", Number of bytes of received object does not match, expected "+string(len(testCase.buf))+", got "+string(recvBuffer.Len()), err)
6092 return
6093 }
6094 if !bytes.Equal(testCase.buf, recvBuffer.Bytes()) {
6095 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", Encrypted sent is not equal to decrypted, expected "+string(testCase.buf)+", got "+string(recvBuffer.Bytes()), err)
6096 return
6097 }
6098
6099 os.Remove(fileName)
6100 }
6101
6102 successLogger(testName, function, args, startTime).Info()
6103}
6104
6105// testSSES3EncryptionPutGet tests SSE-S3 encryption
6106func testSSES3EncryptionPutGet() {
6107 // initialize logging params
6108 startTime := time.Now()
6109 testName := getFuncName()
6110 function := "PutEncryptedObject(bucketName, objectName, reader, sse)"
6111 args := map[string]interface{}{
6112 "bucketName": "",
6113 "objectName": "",
6114 "sse": "",
6115 }
6116 // Seed random based on current time.
6117 rand.Seed(time.Now().Unix())
6118
6119 // Instantiate new minio client object
6120 c, err := minio.New(os.Getenv(serverEndpoint),
6121 &minio.Options{
6122 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
6123 Secure: mustParseBool(os.Getenv(enableHTTPS)),
6124 })
6125 if err != nil {
6126 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
6127 return
6128 }
6129
6130 // Enable tracing, write to stderr.
6131 // c.TraceOn(os.Stderr)
6132
6133 // Set user agent.
6134 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
6135
6136 // Generate a new random bucket name.
6137 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
6138 args["bucketName"] = bucketName
6139
6140 // Make a new bucket.
6141 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
6142 if err != nil {
6143 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
6144 return
6145 }
6146
6147 defer cleanupBucket(bucketName, c)
6148
6149 testCases := []struct {
6150 buf []byte
6151 }{
6152 {buf: bytes.Repeat([]byte("F"), 1)},
6153 {buf: bytes.Repeat([]byte("F"), 15)},
6154 {buf: bytes.Repeat([]byte("F"), 16)},
6155 {buf: bytes.Repeat([]byte("F"), 17)},
6156 {buf: bytes.Repeat([]byte("F"), 31)},
6157 {buf: bytes.Repeat([]byte("F"), 32)},
6158 {buf: bytes.Repeat([]byte("F"), 33)},
6159 {buf: bytes.Repeat([]byte("F"), 1024)},
6160 {buf: bytes.Repeat([]byte("F"), 1024*2)},
6161 {buf: bytes.Repeat([]byte("F"), 1024*1024)},
6162 }
6163
6164 for i, testCase := range testCases {
6165 // Generate a random object name
6166 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
6167 args["objectName"] = objectName
6168
6169 // Secured object
6170 sse := encrypt.NewSSE()
6171 args["sse"] = sse
6172
6173 // Put encrypted data
6174 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(testCase.buf), int64(len(testCase.buf)), minio.PutObjectOptions{ServerSideEncryption: sse})
6175 if err != nil {
6176 logError(testName, function, args, startTime, "", "PutEncryptedObject failed", err)
6177 return
6178 }
6179
6180 // Read the data back without any encryption headers
6181 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
6182 if err != nil {
6183 logError(testName, function, args, startTime, "", "GetEncryptedObject failed", err)
6184 return
6185 }
6186 defer r.Close()
6187
6188 // Compare the sent object with the received one
6189 recvBuffer := bytes.NewBuffer([]byte{})
6190 if _, err = io.Copy(recvBuffer, r); err != nil {
6191 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", error: "+err.Error(), err)
6192 return
6193 }
6194 if recvBuffer.Len() != len(testCase.buf) {
6195 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", Number of bytes of received object does not match, expected "+string(len(testCase.buf))+", got "+string(recvBuffer.Len()), err)
6196 return
6197 }
6198 if !bytes.Equal(testCase.buf, recvBuffer.Bytes()) {
6199 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", Encrypted sent is not equal to decrypted, expected "+string(testCase.buf)+", got "+string(recvBuffer.Bytes()), err)
6200 return
6201 }
6202
6203 successLogger(testName, function, args, startTime).Info()
6204
6205 }
6206
6207 successLogger(testName, function, args, startTime).Info()
6208}
6209
6210// TestSSES3EncryptionFPut tests server side encryption
6211func testSSES3EncryptionFPut() {
6212 // initialize logging params
6213 startTime := time.Now()
6214 testName := getFuncName()
6215 function := "FPutEncryptedObject(bucketName, objectName, filePath, contentType, sse)"
6216 args := map[string]interface{}{
6217 "bucketName": "",
6218 "objectName": "",
6219 "filePath": "",
6220 "contentType": "",
6221 "sse": "",
6222 }
6223 // Seed random based on current time.
6224 rand.Seed(time.Now().Unix())
6225
6226 // Instantiate new minio client object
6227 c, err := minio.New(os.Getenv(serverEndpoint),
6228 &minio.Options{
6229 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
6230 Secure: mustParseBool(os.Getenv(enableHTTPS)),
6231 })
6232 if err != nil {
6233 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
6234 return
6235 }
6236
6237 // Enable tracing, write to stderr.
6238 // c.TraceOn(os.Stderr)
6239
6240 // Set user agent.
6241 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
6242
6243 // Generate a new random bucket name.
6244 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
6245 args["bucketName"] = bucketName
6246
6247 // Make a new bucket.
6248 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
6249 if err != nil {
6250 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
6251 return
6252 }
6253
6254 defer cleanupBucket(bucketName, c)
6255
6256 // Object custom metadata
6257 customContentType := "custom/contenttype"
6258 args["metadata"] = customContentType
6259
6260 testCases := []struct {
6261 buf []byte
6262 }{
6263 {buf: bytes.Repeat([]byte("F"), 0)},
6264 {buf: bytes.Repeat([]byte("F"), 1)},
6265 {buf: bytes.Repeat([]byte("F"), 15)},
6266 {buf: bytes.Repeat([]byte("F"), 16)},
6267 {buf: bytes.Repeat([]byte("F"), 17)},
6268 {buf: bytes.Repeat([]byte("F"), 31)},
6269 {buf: bytes.Repeat([]byte("F"), 32)},
6270 {buf: bytes.Repeat([]byte("F"), 33)},
6271 {buf: bytes.Repeat([]byte("F"), 1024)},
6272 {buf: bytes.Repeat([]byte("F"), 1024*2)},
6273 {buf: bytes.Repeat([]byte("F"), 1024*1024)},
6274 }
6275
6276 for i, testCase := range testCases {
6277 // Generate a random object name
6278 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
6279 args["objectName"] = objectName
6280
6281 // Secured object
6282 sse := encrypt.NewSSE()
6283 args["sse"] = sse
6284
6285 // Generate a random file name.
6286 fileName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
6287 file, err := os.Create(fileName)
6288 if err != nil {
6289 logError(testName, function, args, startTime, "", "file create failed", err)
6290 return
6291 }
6292 _, err = file.Write(testCase.buf)
6293 if err != nil {
6294 logError(testName, function, args, startTime, "", "file write failed", err)
6295 return
6296 }
6297 file.Close()
6298 // Put encrypted data
6299 if _, err = c.FPutObject(context.Background(), bucketName, objectName, fileName, minio.PutObjectOptions{ServerSideEncryption: sse}); err != nil {
6300 logError(testName, function, args, startTime, "", "FPutEncryptedObject failed", err)
6301 return
6302 }
6303
6304 // Read the data back
6305 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
6306 if err != nil {
6307 logError(testName, function, args, startTime, "", "GetEncryptedObject failed", err)
6308 return
6309 }
6310 defer r.Close()
6311
6312 // Compare the sent object with the received one
6313 recvBuffer := bytes.NewBuffer([]byte{})
6314 if _, err = io.Copy(recvBuffer, r); err != nil {
6315 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", error: "+err.Error(), err)
6316 return
6317 }
6318 if recvBuffer.Len() != len(testCase.buf) {
6319 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", Number of bytes of received object does not match, expected "+string(len(testCase.buf))+", got "+string(recvBuffer.Len()), err)
6320 return
6321 }
6322 if !bytes.Equal(testCase.buf, recvBuffer.Bytes()) {
6323 logError(testName, function, args, startTime, "", "Test "+string(i+1)+", Encrypted sent is not equal to decrypted, expected "+string(testCase.buf)+", got "+string(recvBuffer.Bytes()), err)
6324 return
6325 }
6326
6327 os.Remove(fileName)
6328 }
6329
6330 successLogger(testName, function, args, startTime).Info()
6331}
6332
6333func testBucketNotification() {
6334 // initialize logging params
6335 startTime := time.Now()
6336 testName := getFuncName()
6337 function := "SetBucketNotification(bucketName)"
6338 args := map[string]interface{}{
6339 "bucketName": "",
6340 }
6341
6342 if os.Getenv("NOTIFY_BUCKET") == "" ||
6343 os.Getenv("NOTIFY_SERVICE") == "" ||
6344 os.Getenv("NOTIFY_REGION") == "" ||
6345 os.Getenv("NOTIFY_ACCOUNTID") == "" ||
6346 os.Getenv("NOTIFY_RESOURCE") == "" {
6347 ignoredLog(testName, function, args, startTime, "Skipped notification test as it is not configured").Info()
6348 return
6349 }
6350
6351 // Seed random based on current time.
6352 rand.Seed(time.Now().Unix())
6353
6354 c, err := minio.New(os.Getenv(serverEndpoint),
6355 &minio.Options{
6356 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
6357 Secure: mustParseBool(os.Getenv(enableHTTPS)),
6358 })
6359 if err != nil {
6360 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
6361 return
6362 }
6363
6364 // Enable to debug
6365 // c.TraceOn(os.Stderr)
6366
6367 // Set user agent.
6368 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
6369
6370 bucketName := os.Getenv("NOTIFY_BUCKET")
6371 args["bucketName"] = bucketName
6372
6373 topicArn := notification.NewArn("aws", os.Getenv("NOTIFY_SERVICE"), os.Getenv("NOTIFY_REGION"), os.Getenv("NOTIFY_ACCOUNTID"), os.Getenv("NOTIFY_RESOURCE"))
6374 queueArn := notification.NewArn("aws", "dummy-service", "dummy-region", "dummy-accountid", "dummy-resource")
6375
6376 topicConfig := notification.NewConfig(topicArn)
6377 topicConfig.AddEvents(notification.ObjectCreatedAll, notification.ObjectRemovedAll)
6378 topicConfig.AddFilterSuffix("jpg")
6379
6380 queueConfig := notification.NewConfig(queueArn)
6381 queueConfig.AddEvents(notification.ObjectCreatedAll)
6382 queueConfig.AddFilterPrefix("photos/")
6383
6384 config := notification.Configuration{}
6385 config.AddTopic(topicConfig)
6386
6387 // Add the same topicConfig again, should have no effect
6388 // because it is duplicated
6389 config.AddTopic(topicConfig)
6390 if len(config.TopicConfigs) != 1 {
6391 logError(testName, function, args, startTime, "", "Duplicate entry added", err)
6392 return
6393 }
6394
6395 // Add and remove a queue config
6396 config.AddQueue(queueConfig)
6397 config.RemoveQueueByArn(queueArn)
6398
6399 err = c.SetBucketNotification(context.Background(), bucketName, config)
6400 if err != nil {
6401 logError(testName, function, args, startTime, "", "SetBucketNotification failed", err)
6402 return
6403 }
6404
6405 config, err = c.GetBucketNotification(context.Background(), bucketName)
6406 if err != nil {
6407 logError(testName, function, args, startTime, "", "GetBucketNotification failed", err)
6408 return
6409 }
6410
6411 if len(config.TopicConfigs) != 1 {
6412 logError(testName, function, args, startTime, "", "Topic config is empty", err)
6413 return
6414 }
6415
6416 if config.TopicConfigs[0].Filter.S3Key.FilterRules[0].Value != "jpg" {
6417 logError(testName, function, args, startTime, "", "Couldn't get the suffix", err)
6418 return
6419 }
6420
6421 err = c.RemoveAllBucketNotification(context.Background(), bucketName)
6422 if err != nil {
6423 logError(testName, function, args, startTime, "", "RemoveAllBucketNotification failed", err)
6424 return
6425 }
6426
6427 // Delete all objects and buckets
6428 if err = cleanupBucket(bucketName, c); err != nil {
6429 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
6430 return
6431 }
6432
6433 successLogger(testName, function, args, startTime).Info()
6434}
6435
6436// Tests comprehensive list of all methods.
6437func testFunctional() {
6438 // initialize logging params
6439 startTime := time.Now()
6440 testName := getFuncName()
6441 function := "testFunctional()"
6442 functionAll := ""
6443 args := map[string]interface{}{}
6444
6445 // Seed random based on current time.
6446 rand.Seed(time.Now().Unix())
6447
6448 c, err := minio.New(os.Getenv(serverEndpoint),
6449 &minio.Options{
6450 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
6451 Secure: mustParseBool(os.Getenv(enableHTTPS)),
6452 })
6453 if err != nil {
6454 logError(testName, function, nil, startTime, "", "MinIO client object creation failed", err)
6455 return
6456 }
6457
6458 // Enable to debug
6459 // c.TraceOn(os.Stderr)
6460
6461 // Set user agent.
6462 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
6463
6464 // Generate a new random bucket name.
6465 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
6466
6467 // Make a new bucket.
6468 function = "MakeBucket(bucketName, region)"
6469 functionAll = "MakeBucket(bucketName, region)"
6470 args["bucketName"] = bucketName
6471 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
6472
6473 defer cleanupBucket(bucketName, c)
6474 if err != nil {
6475 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
6476 return
6477 }
6478
6479 // Generate a random file name.
6480 fileName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
6481 file, err := os.Create(fileName)
6482 if err != nil {
6483 logError(testName, function, args, startTime, "", "File creation failed", err)
6484 return
6485 }
6486 for i := 0; i < 3; i++ {
6487 buf := make([]byte, rand.Intn(1<<19))
6488 _, err = file.Write(buf)
6489 if err != nil {
6490 logError(testName, function, args, startTime, "", "File write failed", err)
6491 return
6492 }
6493 }
6494 file.Close()
6495
6496 // Verify if bucket exits and you have access.
6497 var exists bool
6498 function = "BucketExists(bucketName)"
6499 functionAll += ", " + function
6500 args = map[string]interface{}{
6501 "bucketName": bucketName,
6502 }
6503 exists, err = c.BucketExists(context.Background(), bucketName)
6504
6505 if err != nil {
6506 logError(testName, function, args, startTime, "", "BucketExists failed", err)
6507 return
6508 }
6509 if !exists {
6510 logError(testName, function, args, startTime, "", "Could not find the bucket", err)
6511 return
6512 }
6513
6514 // Asserting the default bucket policy.
6515 function = "GetBucketPolicy(ctx, bucketName)"
6516 functionAll += ", " + function
6517 args = map[string]interface{}{
6518 "bucketName": bucketName,
6519 }
6520 nilPolicy, err := c.GetBucketPolicy(context.Background(), bucketName)
6521 if err != nil {
6522 logError(testName, function, args, startTime, "", "GetBucketPolicy failed", err)
6523 return
6524 }
6525 if nilPolicy != "" {
6526 logError(testName, function, args, startTime, "", "policy should be set to nil", err)
6527 return
6528 }
6529
6530 // Set the bucket policy to 'public readonly'.
6531 function = "SetBucketPolicy(bucketName, readOnlyPolicy)"
6532 functionAll += ", " + function
6533
6534 readOnlyPolicy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:ListBucket"],"Resource":["arn:aws:s3:::` + bucketName + `"]}]}`
6535 args = map[string]interface{}{
6536 "bucketName": bucketName,
6537 "bucketPolicy": readOnlyPolicy,
6538 }
6539
6540 err = c.SetBucketPolicy(context.Background(), bucketName, readOnlyPolicy)
6541 if err != nil {
6542 logError(testName, function, args, startTime, "", "SetBucketPolicy failed", err)
6543 return
6544 }
6545 // should return policy `readonly`.
6546 function = "GetBucketPolicy(ctx, bucketName)"
6547 functionAll += ", " + function
6548 args = map[string]interface{}{
6549 "bucketName": bucketName,
6550 }
6551 _, err = c.GetBucketPolicy(context.Background(), bucketName)
6552 if err != nil {
6553 logError(testName, function, args, startTime, "", "GetBucketPolicy failed", err)
6554 return
6555 }
6556
6557 // Make the bucket 'public writeonly'.
6558 function = "SetBucketPolicy(bucketName, writeOnlyPolicy)"
6559 functionAll += ", " + function
6560
6561 writeOnlyPolicy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::` + bucketName + `"]}]}`
6562 args = map[string]interface{}{
6563 "bucketName": bucketName,
6564 "bucketPolicy": writeOnlyPolicy,
6565 }
6566 err = c.SetBucketPolicy(context.Background(), bucketName, writeOnlyPolicy)
6567
6568 if err != nil {
6569 logError(testName, function, args, startTime, "", "SetBucketPolicy failed", err)
6570 return
6571 }
6572 // should return policy `writeonly`.
6573 function = "GetBucketPolicy(ctx, bucketName)"
6574 functionAll += ", " + function
6575 args = map[string]interface{}{
6576 "bucketName": bucketName,
6577 }
6578
6579 _, err = c.GetBucketPolicy(context.Background(), bucketName)
6580 if err != nil {
6581 logError(testName, function, args, startTime, "", "GetBucketPolicy failed", err)
6582 return
6583 }
6584
6585 // Make the bucket 'public read/write'.
6586 function = "SetBucketPolicy(bucketName, readWritePolicy)"
6587 functionAll += ", " + function
6588
6589 readWritePolicy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:ListBucket","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::` + bucketName + `"]}]}`
6590
6591 args = map[string]interface{}{
6592 "bucketName": bucketName,
6593 "bucketPolicy": readWritePolicy,
6594 }
6595 err = c.SetBucketPolicy(context.Background(), bucketName, readWritePolicy)
6596
6597 if err != nil {
6598 logError(testName, function, args, startTime, "", "SetBucketPolicy failed", err)
6599 return
6600 }
6601 // should return policy `readwrite`.
6602 function = "GetBucketPolicy(bucketName)"
6603 functionAll += ", " + function
6604 args = map[string]interface{}{
6605 "bucketName": bucketName,
6606 }
6607 _, err = c.GetBucketPolicy(context.Background(), bucketName)
6608 if err != nil {
6609 logError(testName, function, args, startTime, "", "GetBucketPolicy failed", err)
6610 return
6611 }
6612
6613 // List all buckets.
6614 function = "ListBuckets()"
6615 functionAll += ", " + function
6616 args = nil
6617 buckets, err := c.ListBuckets(context.Background())
6618
6619 if len(buckets) == 0 {
6620 logError(testName, function, args, startTime, "", "Found bucket list to be empty", err)
6621 return
6622 }
6623 if err != nil {
6624 logError(testName, function, args, startTime, "", "ListBuckets failed", err)
6625 return
6626 }
6627
6628 // Verify if previously created bucket is listed in list buckets.
6629 bucketFound := false
6630 for _, bucket := range buckets {
6631 if bucket.Name == bucketName {
6632 bucketFound = true
6633 }
6634 }
6635
6636 // If bucket not found error out.
6637 if !bucketFound {
6638 logError(testName, function, args, startTime, "", "Bucket: "+bucketName+" not found", err)
6639 return
6640 }
6641
6642 objectName := bucketName + "unique"
6643
6644 // Generate data
6645 buf := bytes.Repeat([]byte("f"), 1<<19)
6646
6647 function = "PutObject(bucketName, objectName, reader, contentType)"
6648 functionAll += ", " + function
6649 args = map[string]interface{}{
6650 "bucketName": bucketName,
6651 "objectName": objectName,
6652 "contentType": "",
6653 }
6654
6655 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{})
6656 if err != nil {
6657 logError(testName, function, args, startTime, "", "PutObject failed", err)
6658 return
6659 }
6660
6661 args = map[string]interface{}{
6662 "bucketName": bucketName,
6663 "objectName": objectName + "-nolength",
6664 "contentType": "binary/octet-stream",
6665 }
6666
6667 _, err = c.PutObject(context.Background(), bucketName, objectName+"-nolength", bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
6668 if err != nil {
6669 logError(testName, function, args, startTime, "", "PutObject failed", err)
6670 return
6671 }
6672
6673 // Instantiate a done channel to close all listing.
6674 doneCh := make(chan struct{})
6675 defer close(doneCh)
6676
6677 objFound := false
6678 isRecursive := true // Recursive is true.
6679
6680 function = "ListObjects(bucketName, objectName, isRecursive, doneCh)"
6681 functionAll += ", " + function
6682 args = map[string]interface{}{
6683 "bucketName": bucketName,
6684 "objectName": objectName,
6685 "isRecursive": isRecursive,
6686 }
6687
6688 for obj := range c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{UseV1: true, Prefix: objectName, Recursive: true}) {
6689 if obj.Key == objectName {
6690 objFound = true
6691 break
6692 }
6693 }
6694 if !objFound {
6695 logError(testName, function, args, startTime, "", "Object "+objectName+" not found", err)
6696 return
6697 }
6698
6699 objFound = false
6700 isRecursive = true // Recursive is true.
6701 function = "ListObjects()"
6702 functionAll += ", " + function
6703 args = map[string]interface{}{
6704 "bucketName": bucketName,
6705 "objectName": objectName,
6706 "isRecursive": isRecursive,
6707 }
6708
6709 for obj := range c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{Prefix: objectName, Recursive: isRecursive}) {
6710 if obj.Key == objectName {
6711 objFound = true
6712 break
6713 }
6714 }
6715 if !objFound {
6716 logError(testName, function, args, startTime, "", "Object "+objectName+" not found", err)
6717 return
6718 }
6719
6720 incompObjNotFound := true
6721
6722 function = "ListIncompleteUploads(bucketName, objectName, isRecursive, doneCh)"
6723 functionAll += ", " + function
6724 args = map[string]interface{}{
6725 "bucketName": bucketName,
6726 "objectName": objectName,
6727 "isRecursive": isRecursive,
6728 }
6729
6730 for objIncompl := range c.ListIncompleteUploads(context.Background(), bucketName, objectName, isRecursive) {
6731 if objIncompl.Key != "" {
6732 incompObjNotFound = false
6733 break
6734 }
6735 }
6736 if !incompObjNotFound {
6737 logError(testName, function, args, startTime, "", "Unexpected dangling incomplete upload found", err)
6738 return
6739 }
6740
6741 function = "GetObject(bucketName, objectName)"
6742 functionAll += ", " + function
6743 args = map[string]interface{}{
6744 "bucketName": bucketName,
6745 "objectName": objectName,
6746 }
6747 newReader, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
6748 if err != nil {
6749 logError(testName, function, args, startTime, "", "GetObject failed", err)
6750 return
6751 }
6752
6753 newReadBytes, err := io.ReadAll(newReader)
6754 if err != nil {
6755 logError(testName, function, args, startTime, "", "ReadAll failed", err)
6756 return
6757 }
6758
6759 if !bytes.Equal(newReadBytes, buf) {
6760 logError(testName, function, args, startTime, "", "GetObject bytes mismatch", err)
6761 return
6762 }
6763 newReader.Close()
6764
6765 function = "FGetObject(bucketName, objectName, fileName)"
6766 functionAll += ", " + function
6767 args = map[string]interface{}{
6768 "bucketName": bucketName,
6769 "objectName": objectName,
6770 "fileName": fileName + "-f",
6771 }
6772 err = c.FGetObject(context.Background(), bucketName, objectName, fileName+"-f", minio.GetObjectOptions{})
6773
6774 if err != nil {
6775 logError(testName, function, args, startTime, "", "FGetObject failed", err)
6776 return
6777 }
6778
6779 function = "PresignedHeadObject(bucketName, objectName, expires, reqParams)"
6780 functionAll += ", " + function
6781 args = map[string]interface{}{
6782 "bucketName": bucketName,
6783 "objectName": "",
6784 "expires": 3600 * time.Second,
6785 }
6786 if _, err = c.PresignedHeadObject(context.Background(), bucketName, "", 3600*time.Second, nil); err == nil {
6787 logError(testName, function, args, startTime, "", "PresignedHeadObject success", err)
6788 return
6789 }
6790
6791 // Generate presigned HEAD object url.
6792 function = "PresignedHeadObject(bucketName, objectName, expires, reqParams)"
6793 functionAll += ", " + function
6794 args = map[string]interface{}{
6795 "bucketName": bucketName,
6796 "objectName": objectName,
6797 "expires": 3600 * time.Second,
6798 }
6799 presignedHeadURL, err := c.PresignedHeadObject(context.Background(), bucketName, objectName, 3600*time.Second, nil)
6800 if err != nil {
6801 logError(testName, function, args, startTime, "", "PresignedHeadObject failed", err)
6802 return
6803 }
6804
6805 transport, err := minio.DefaultTransport(mustParseBool(os.Getenv(enableHTTPS)))
6806 if err != nil {
6807 logError(testName, function, args, startTime, "", "DefaultTransport failed", err)
6808 return
6809 }
6810
6811 httpClient := &http.Client{
6812 // Setting a sensible time out of 30secs to wait for response
6813 // headers. Request is pro-actively canceled after 30secs
6814 // with no response.
6815 Timeout: 30 * time.Second,
6816 Transport: transport,
6817 }
6818
6819 req, err := http.NewRequest(http.MethodHead, presignedHeadURL.String(), nil)
6820 if err != nil {
6821 logError(testName, function, args, startTime, "", "PresignedHeadObject request was incorrect", err)
6822 return
6823 }
6824
6825 // Verify if presigned url works.
6826 resp, err := httpClient.Do(req)
6827 if err != nil {
6828 logError(testName, function, args, startTime, "", "PresignedHeadObject response incorrect", err)
6829 return
6830 }
6831 if resp.StatusCode != http.StatusOK {
6832 logError(testName, function, args, startTime, "", "PresignedHeadObject response incorrect, status "+string(resp.StatusCode), err)
6833 return
6834 }
6835 if resp.Header.Get("ETag") == "" {
6836 logError(testName, function, args, startTime, "", "PresignedHeadObject response incorrect", err)
6837 return
6838 }
6839 resp.Body.Close()
6840
6841 function = "PresignedGetObject(bucketName, objectName, expires, reqParams)"
6842 functionAll += ", " + function
6843 args = map[string]interface{}{
6844 "bucketName": bucketName,
6845 "objectName": "",
6846 "expires": 3600 * time.Second,
6847 }
6848 _, err = c.PresignedGetObject(context.Background(), bucketName, "", 3600*time.Second, nil)
6849 if err == nil {
6850 logError(testName, function, args, startTime, "", "PresignedGetObject success", err)
6851 return
6852 }
6853
6854 // Generate presigned GET object url.
6855 function = "PresignedGetObject(bucketName, objectName, expires, reqParams)"
6856 functionAll += ", " + function
6857 args = map[string]interface{}{
6858 "bucketName": bucketName,
6859 "objectName": objectName,
6860 "expires": 3600 * time.Second,
6861 }
6862 presignedGetURL, err := c.PresignedGetObject(context.Background(), bucketName, objectName, 3600*time.Second, nil)
6863 if err != nil {
6864 logError(testName, function, args, startTime, "", "PresignedGetObject failed", err)
6865 return
6866 }
6867
6868 // Verify if presigned url works.
6869 req, err = http.NewRequest(http.MethodGet, presignedGetURL.String(), nil)
6870 if err != nil {
6871 logError(testName, function, args, startTime, "", "PresignedGetObject request incorrect", err)
6872 return
6873 }
6874
6875 resp, err = httpClient.Do(req)
6876 if err != nil {
6877 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect", err)
6878 return
6879 }
6880 if resp.StatusCode != http.StatusOK {
6881 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect, status "+string(resp.StatusCode), err)
6882 return
6883 }
6884 newPresignedBytes, err := io.ReadAll(resp.Body)
6885 if err != nil {
6886 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect", err)
6887 return
6888 }
6889 resp.Body.Close()
6890 if !bytes.Equal(newPresignedBytes, buf) {
6891 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect", err)
6892 return
6893 }
6894
6895 // Set request parameters.
6896 reqParams := make(url.Values)
6897 reqParams.Set("response-content-disposition", "attachment; filename=\"test.txt\"")
6898 args = map[string]interface{}{
6899 "bucketName": bucketName,
6900 "objectName": objectName,
6901 "expires": 3600 * time.Second,
6902 "reqParams": reqParams,
6903 }
6904 presignedGetURL, err = c.PresignedGetObject(context.Background(), bucketName, objectName, 3600*time.Second, reqParams)
6905
6906 if err != nil {
6907 logError(testName, function, args, startTime, "", "PresignedGetObject failed", err)
6908 return
6909 }
6910
6911 // Verify if presigned url works.
6912 req, err = http.NewRequest(http.MethodGet, presignedGetURL.String(), nil)
6913 if err != nil {
6914 logError(testName, function, args, startTime, "", "PresignedGetObject request incorrect", err)
6915 return
6916 }
6917
6918 resp, err = httpClient.Do(req)
6919 if err != nil {
6920 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect", err)
6921 return
6922 }
6923 if resp.StatusCode != http.StatusOK {
6924 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect, status "+string(resp.StatusCode), err)
6925 return
6926 }
6927 newPresignedBytes, err = io.ReadAll(resp.Body)
6928 if err != nil {
6929 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect", err)
6930 return
6931 }
6932 if !bytes.Equal(newPresignedBytes, buf) {
6933 logError(testName, function, args, startTime, "", "Bytes mismatch for presigned GET URL", err)
6934 return
6935 }
6936 if resp.Header.Get("Content-Disposition") != "attachment; filename=\"test.txt\"" {
6937 logError(testName, function, args, startTime, "", "wrong Content-Disposition received "+string(resp.Header.Get("Content-Disposition")), err)
6938 return
6939 }
6940
6941 function = "PresignedPutObject(bucketName, objectName, expires)"
6942 functionAll += ", " + function
6943 args = map[string]interface{}{
6944 "bucketName": bucketName,
6945 "objectName": "",
6946 "expires": 3600 * time.Second,
6947 }
6948 _, err = c.PresignedPutObject(context.Background(), bucketName, "", 3600*time.Second)
6949 if err == nil {
6950 logError(testName, function, args, startTime, "", "PresignedPutObject success", err)
6951 return
6952 }
6953
6954 function = "PresignedPutObject(bucketName, objectName, expires)"
6955 functionAll += ", " + function
6956 args = map[string]interface{}{
6957 "bucketName": bucketName,
6958 "objectName": objectName + "-presigned",
6959 "expires": 3600 * time.Second,
6960 }
6961 presignedPutURL, err := c.PresignedPutObject(context.Background(), bucketName, objectName+"-presigned", 3600*time.Second)
6962 if err != nil {
6963 logError(testName, function, args, startTime, "", "PresignedPutObject failed", err)
6964 return
6965 }
6966
6967 buf = bytes.Repeat([]byte("g"), 1<<19)
6968
6969 req, err = http.NewRequest(http.MethodPut, presignedPutURL.String(), bytes.NewReader(buf))
6970 if err != nil {
6971 logError(testName, function, args, startTime, "", "Couldn't make HTTP request with PresignedPutObject URL", err)
6972 return
6973 }
6974
6975 resp, err = httpClient.Do(req)
6976 if err != nil {
6977 logError(testName, function, args, startTime, "", "PresignedPutObject failed", err)
6978 return
6979 }
6980
6981 newReader, err = c.GetObject(context.Background(), bucketName, objectName+"-presigned", minio.GetObjectOptions{})
6982 if err != nil {
6983 logError(testName, function, args, startTime, "", "GetObject after PresignedPutObject failed", err)
6984 return
6985 }
6986
6987 newReadBytes, err = io.ReadAll(newReader)
6988 if err != nil {
6989 logError(testName, function, args, startTime, "", "ReadAll after GetObject failed", err)
6990 return
6991 }
6992
6993 if !bytes.Equal(newReadBytes, buf) {
6994 logError(testName, function, args, startTime, "", "Bytes mismatch", err)
6995 return
6996 }
6997
6998 function = "PresignHeader(method, bucketName, objectName, expires, reqParams, extraHeaders)"
6999 functionAll += ", " + function
7000 presignExtraHeaders := map[string][]string{
7001 "mysecret": {"abcxxx"},
7002 }
7003 args = map[string]interface{}{
7004 "method": "PUT",
7005 "bucketName": bucketName,
7006 "objectName": objectName + "-presign-custom",
7007 "expires": 3600 * time.Second,
7008 "extraHeaders": presignExtraHeaders,
7009 }
7010 presignedURL, err := c.PresignHeader(context.Background(), "PUT", bucketName, objectName+"-presign-custom", 3600*time.Second, nil, presignExtraHeaders)
7011 if err != nil {
7012 logError(testName, function, args, startTime, "", "Presigned failed", err)
7013 return
7014 }
7015
7016 // Generate data more than 32K
7017 buf = bytes.Repeat([]byte("1"), rand.Intn(1<<10)+32*1024)
7018
7019 req, err = http.NewRequest(http.MethodPut, presignedURL.String(), bytes.NewReader(buf))
7020 if err != nil {
7021 logError(testName, function, args, startTime, "", "HTTP request to Presigned URL failed", err)
7022 return
7023 }
7024
7025 req.Header.Add("mysecret", "abcxxx")
7026 resp, err = httpClient.Do(req)
7027 if err != nil {
7028 logError(testName, function, args, startTime, "", "HTTP request to Presigned URL failed", err)
7029 return
7030 }
7031
7032 // Download the uploaded object to verify
7033 args = map[string]interface{}{
7034 "bucketName": bucketName,
7035 "objectName": objectName + "-presign-custom",
7036 }
7037 newReader, err = c.GetObject(context.Background(), bucketName, objectName+"-presign-custom", minio.GetObjectOptions{})
7038 if err != nil {
7039 logError(testName, function, args, startTime, "", "GetObject of uploaded custom-presigned object failed", err)
7040 return
7041 }
7042
7043 newReadBytes, err = io.ReadAll(newReader)
7044 if err != nil {
7045 logError(testName, function, args, startTime, "", "ReadAll failed during get on custom-presigned put object", err)
7046 return
7047 }
7048 newReader.Close()
7049
7050 if !bytes.Equal(newReadBytes, buf) {
7051 logError(testName, function, args, startTime, "", "Bytes mismatch on custom-presigned object upload verification", err)
7052 return
7053 }
7054
7055 function = "RemoveObject(bucketName, objectName)"
7056 functionAll += ", " + function
7057 args = map[string]interface{}{
7058 "bucketName": bucketName,
7059 "objectName": objectName,
7060 }
7061 err = c.RemoveObject(context.Background(), bucketName, objectName, minio.RemoveObjectOptions{})
7062
7063 if err != nil {
7064 logError(testName, function, args, startTime, "", "RemoveObject failed", err)
7065 return
7066 }
7067 args["objectName"] = objectName + "-f"
7068 err = c.RemoveObject(context.Background(), bucketName, objectName+"-f", minio.RemoveObjectOptions{})
7069
7070 if err != nil {
7071 logError(testName, function, args, startTime, "", "RemoveObject failed", err)
7072 return
7073 }
7074
7075 args["objectName"] = objectName + "-nolength"
7076 err = c.RemoveObject(context.Background(), bucketName, objectName+"-nolength", minio.RemoveObjectOptions{})
7077
7078 if err != nil {
7079 logError(testName, function, args, startTime, "", "RemoveObject failed", err)
7080 return
7081 }
7082
7083 args["objectName"] = objectName + "-presigned"
7084 err = c.RemoveObject(context.Background(), bucketName, objectName+"-presigned", minio.RemoveObjectOptions{})
7085
7086 if err != nil {
7087 logError(testName, function, args, startTime, "", "RemoveObject failed", err)
7088 return
7089 }
7090
7091 args["objectName"] = objectName + "-presign-custom"
7092 err = c.RemoveObject(context.Background(), bucketName, objectName+"-presign-custom", minio.RemoveObjectOptions{})
7093
7094 if err != nil {
7095 logError(testName, function, args, startTime, "", "RemoveObject failed", err)
7096 return
7097 }
7098
7099 function = "RemoveBucket(bucketName)"
7100 functionAll += ", " + function
7101 args = map[string]interface{}{
7102 "bucketName": bucketName,
7103 }
7104 err = c.RemoveBucket(context.Background(), bucketName)
7105
7106 if err != nil {
7107 logError(testName, function, args, startTime, "", "RemoveBucket failed", err)
7108 return
7109 }
7110 err = c.RemoveBucket(context.Background(), bucketName)
7111 if err == nil {
7112 logError(testName, function, args, startTime, "", "RemoveBucket did not fail for invalid bucket name", err)
7113 return
7114 }
7115 if err.Error() != "The specified bucket does not exist" {
7116 logError(testName, function, args, startTime, "", "RemoveBucket failed", err)
7117 return
7118 }
7119
7120 os.Remove(fileName)
7121 os.Remove(fileName + "-f")
7122 successLogger(testName, functionAll, args, startTime).Info()
7123}
7124
7125// Test for validating GetObject Reader* methods functioning when the
7126// object is modified in the object store.
7127func testGetObjectModified() {
7128 // initialize logging params
7129 startTime := time.Now()
7130 testName := getFuncName()
7131 function := "GetObject(bucketName, objectName)"
7132 args := map[string]interface{}{}
7133
7134 // Instantiate new minio client object.
7135 c, err := minio.New(os.Getenv(serverEndpoint),
7136 &minio.Options{
7137 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
7138 Secure: mustParseBool(os.Getenv(enableHTTPS)),
7139 })
7140 if err != nil {
7141 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
7142 return
7143 }
7144
7145 // Enable tracing, write to stderr.
7146 // c.TraceOn(os.Stderr)
7147
7148 // Set user agent.
7149 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
7150
7151 // Make a new bucket.
7152 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
7153 args["bucketName"] = bucketName
7154
7155 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
7156 if err != nil {
7157 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
7158 return
7159 }
7160
7161 defer cleanupBucket(bucketName, c)
7162
7163 // Upload an object.
7164 objectName := "myobject"
7165 args["objectName"] = objectName
7166 content := "helloworld"
7167 _, err = c.PutObject(context.Background(), bucketName, objectName, strings.NewReader(content), int64(len(content)), minio.PutObjectOptions{ContentType: "application/text"})
7168 if err != nil {
7169 logError(testName, function, args, startTime, "", "Failed to upload "+objectName+", to bucket "+bucketName, err)
7170 return
7171 }
7172
7173 defer c.RemoveObject(context.Background(), bucketName, objectName, minio.RemoveObjectOptions{})
7174
7175 reader, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
7176 if err != nil {
7177 logError(testName, function, args, startTime, "", "Failed to GetObject "+objectName+", from bucket "+bucketName, err)
7178 return
7179 }
7180 defer reader.Close()
7181
7182 // Read a few bytes of the object.
7183 b := make([]byte, 5)
7184 n, err := reader.ReadAt(b, 0)
7185 if err != nil {
7186 logError(testName, function, args, startTime, "", "Failed to read object "+objectName+", from bucket "+bucketName+" at an offset", err)
7187 return
7188 }
7189
7190 // Upload different contents to the same object while object is being read.
7191 newContent := "goodbyeworld"
7192 _, err = c.PutObject(context.Background(), bucketName, objectName, strings.NewReader(newContent), int64(len(newContent)), minio.PutObjectOptions{ContentType: "application/text"})
7193 if err != nil {
7194 logError(testName, function, args, startTime, "", "Failed to upload "+objectName+", to bucket "+bucketName, err)
7195 return
7196 }
7197
7198 // Confirm that a Stat() call in between doesn't change the Object's cached etag.
7199 _, err = reader.Stat()
7200 expectedError := "At least one of the pre-conditions you specified did not hold"
7201 if err.Error() != expectedError {
7202 logError(testName, function, args, startTime, "", "Expected Stat to fail with error "+expectedError+", but received "+err.Error(), err)
7203 return
7204 }
7205
7206 // Read again only to find object contents have been modified since last read.
7207 _, err = reader.ReadAt(b, int64(n))
7208 if err.Error() != expectedError {
7209 logError(testName, function, args, startTime, "", "Expected ReadAt to fail with error "+expectedError+", but received "+err.Error(), err)
7210 return
7211 }
7212
7213 successLogger(testName, function, args, startTime).Info()
7214}
7215
7216// Test validates putObject to upload a file seeked at a given offset.
7217func testPutObjectUploadSeekedObject() {
7218 // initialize logging params
7219 startTime := time.Now()
7220 testName := getFuncName()
7221 function := "PutObject(bucketName, objectName, fileToUpload, contentType)"
7222 args := map[string]interface{}{
7223 "bucketName": "",
7224 "objectName": "",
7225 "fileToUpload": "",
7226 "contentType": "binary/octet-stream",
7227 }
7228
7229 // Instantiate new minio client object.
7230 c, err := minio.New(os.Getenv(serverEndpoint),
7231 &minio.Options{
7232 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
7233 Secure: mustParseBool(os.Getenv(enableHTTPS)),
7234 })
7235 if err != nil {
7236 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
7237 return
7238 }
7239
7240 // Enable tracing, write to stderr.
7241 // c.TraceOn(os.Stderr)
7242
7243 // Set user agent.
7244 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
7245
7246 // Make a new bucket.
7247 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
7248 args["bucketName"] = bucketName
7249
7250 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
7251 if err != nil {
7252 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
7253 return
7254 }
7255 defer cleanupBucket(bucketName, c)
7256
7257 var tempfile *os.File
7258
7259 if fileName := getMintDataDirFilePath("datafile-100-kB"); fileName != "" {
7260 tempfile, err = os.Open(fileName)
7261 if err != nil {
7262 logError(testName, function, args, startTime, "", "File open failed", err)
7263 return
7264 }
7265 args["fileToUpload"] = fileName
7266 } else {
7267 tempfile, err = os.CreateTemp("", "minio-go-upload-test-")
7268 if err != nil {
7269 logError(testName, function, args, startTime, "", "TempFile create failed", err)
7270 return
7271 }
7272 args["fileToUpload"] = tempfile.Name()
7273
7274 // Generate 100kB data
7275 if _, err = io.Copy(tempfile, getDataReader("datafile-100-kB")); err != nil {
7276 logError(testName, function, args, startTime, "", "File copy failed", err)
7277 return
7278 }
7279
7280 defer os.Remove(tempfile.Name())
7281
7282 // Seek back to the beginning of the file.
7283 tempfile.Seek(0, 0)
7284 }
7285 length := 100 * humanize.KiByte
7286 objectName := fmt.Sprintf("test-file-%v", rand.Uint32())
7287 args["objectName"] = objectName
7288
7289 offset := length / 2
7290 if _, err = tempfile.Seek(int64(offset), 0); err != nil {
7291 logError(testName, function, args, startTime, "", "TempFile seek failed", err)
7292 return
7293 }
7294
7295 _, err = c.PutObject(context.Background(), bucketName, objectName, tempfile, int64(length-offset), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
7296 if err != nil {
7297 logError(testName, function, args, startTime, "", "PutObject failed", err)
7298 return
7299 }
7300 tempfile.Close()
7301
7302 obj, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
7303 if err != nil {
7304 logError(testName, function, args, startTime, "", "GetObject failed", err)
7305 return
7306 }
7307 defer obj.Close()
7308
7309 n, err := obj.Seek(int64(offset), 0)
7310 if err != nil {
7311 logError(testName, function, args, startTime, "", "Seek failed", err)
7312 return
7313 }
7314 if n != int64(offset) {
7315 logError(testName, function, args, startTime, "", fmt.Sprintf("Invalid offset returned, expected %d got %d", int64(offset), n), err)
7316 return
7317 }
7318
7319 _, err = c.PutObject(context.Background(), bucketName, objectName+"getobject", obj, int64(length-offset), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
7320 if err != nil {
7321 logError(testName, function, args, startTime, "", "PutObject failed", err)
7322 return
7323 }
7324 st, err := c.StatObject(context.Background(), bucketName, objectName+"getobject", minio.StatObjectOptions{})
7325 if err != nil {
7326 logError(testName, function, args, startTime, "", "StatObject failed", err)
7327 return
7328 }
7329 if st.Size != int64(length-offset) {
7330 logError(testName, function, args, startTime, "", fmt.Sprintf("Invalid offset returned, expected %d got %d", int64(length-offset), n), err)
7331 return
7332 }
7333
7334 successLogger(testName, function, args, startTime).Info()
7335}
7336
7337// Tests bucket re-create errors.
7338func testMakeBucketErrorV2() {
7339 // initialize logging params
7340 startTime := time.Now()
7341 testName := getFuncName()
7342 function := "MakeBucket(bucketName, region)"
7343 args := map[string]interface{}{
7344 "bucketName": "",
7345 "region": "eu-west-1",
7346 }
7347
7348 // Seed random based on current time.
7349 rand.Seed(time.Now().Unix())
7350
7351 // Instantiate new minio client object.
7352 c, err := minio.New(os.Getenv(serverEndpoint),
7353 &minio.Options{
7354 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
7355 Secure: mustParseBool(os.Getenv(enableHTTPS)),
7356 })
7357 if err != nil {
7358 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
7359 return
7360 }
7361
7362 // Enable tracing, write to stderr.
7363 // c.TraceOn(os.Stderr)
7364
7365 // Set user agent.
7366 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
7367
7368 // Generate a new random bucket name.
7369 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
7370 region := "eu-west-1"
7371 args["bucketName"] = bucketName
7372 args["region"] = region
7373
7374 // Make a new bucket in 'eu-west-1'.
7375 if err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: region}); err != nil {
7376 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
7377 return
7378 }
7379
7380 defer cleanupBucket(bucketName, c)
7381
7382 if err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: region}); err == nil {
7383 logError(testName, function, args, startTime, "", "MakeBucket did not fail for existing bucket name", err)
7384 return
7385 }
7386 // Verify valid error response from server.
7387 if minio.ToErrorResponse(err).Code != "BucketAlreadyExists" &&
7388 minio.ToErrorResponse(err).Code != "BucketAlreadyOwnedByYou" {
7389 logError(testName, function, args, startTime, "", "Invalid error returned by server", err)
7390 return
7391 }
7392
7393 successLogger(testName, function, args, startTime).Info()
7394}
7395
7396// Test get object reader to not throw error on being closed twice.
7397func testGetObjectClosedTwiceV2() {
7398 // initialize logging params
7399 startTime := time.Now()
7400 testName := getFuncName()
7401 function := "MakeBucket(bucketName, region)"
7402 args := map[string]interface{}{
7403 "bucketName": "",
7404 "region": "eu-west-1",
7405 }
7406
7407 // Seed random based on current time.
7408 rand.Seed(time.Now().Unix())
7409
7410 // Instantiate new minio client object.
7411 c, err := minio.New(os.Getenv(serverEndpoint),
7412 &minio.Options{
7413 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
7414 Secure: mustParseBool(os.Getenv(enableHTTPS)),
7415 })
7416 if err != nil {
7417 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
7418 return
7419 }
7420
7421 // Enable tracing, write to stderr.
7422 // c.TraceOn(os.Stderr)
7423
7424 // Set user agent.
7425 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
7426
7427 // Generate a new random bucket name.
7428 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
7429 args["bucketName"] = bucketName
7430
7431 // Make a new bucket.
7432 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
7433 if err != nil {
7434 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
7435 return
7436 }
7437
7438 defer cleanupBucket(bucketName, c)
7439
7440 // Generate 33K of data.
7441 bufSize := dataFileMap["datafile-33-kB"]
7442 reader := getDataReader("datafile-33-kB")
7443 defer reader.Close()
7444
7445 // Save the data
7446 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
7447 args["objectName"] = objectName
7448
7449 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
7450 if err != nil {
7451 logError(testName, function, args, startTime, "", "PutObject failed", err)
7452 return
7453 }
7454
7455 // Read the data back
7456 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
7457 if err != nil {
7458 logError(testName, function, args, startTime, "", "GetObject failed", err)
7459 return
7460 }
7461
7462 st, err := r.Stat()
7463 if err != nil {
7464 logError(testName, function, args, startTime, "", "Stat failed", err)
7465 return
7466 }
7467
7468 if st.Size != int64(bufSize) {
7469 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(bufSize)+" got "+string(st.Size), err)
7470 return
7471 }
7472 if err := r.Close(); err != nil {
7473 logError(testName, function, args, startTime, "", "Stat failed", err)
7474 return
7475 }
7476 if err := r.Close(); err == nil {
7477 logError(testName, function, args, startTime, "", "Object is already closed, should return error", err)
7478 return
7479 }
7480
7481 successLogger(testName, function, args, startTime).Info()
7482}
7483
7484// Tests FPutObject hidden contentType setting
7485func testFPutObjectV2() {
7486 // initialize logging params
7487 startTime := time.Now()
7488 testName := getFuncName()
7489 function := "FPutObject(bucketName, objectName, fileName, opts)"
7490 args := map[string]interface{}{
7491 "bucketName": "",
7492 "objectName": "",
7493 "fileName": "",
7494 "opts": "",
7495 }
7496
7497 // Seed random based on current time.
7498 rand.Seed(time.Now().Unix())
7499
7500 // Instantiate new minio client object.
7501 c, err := minio.New(os.Getenv(serverEndpoint),
7502 &minio.Options{
7503 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
7504 Secure: mustParseBool(os.Getenv(enableHTTPS)),
7505 })
7506 if err != nil {
7507 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
7508 return
7509 }
7510
7511 // Enable tracing, write to stderr.
7512 // c.TraceOn(os.Stderr)
7513
7514 // Set user agent.
7515 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
7516
7517 // Generate a new random bucket name.
7518 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
7519 args["bucketName"] = bucketName
7520
7521 // Make a new bucket.
7522 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
7523 if err != nil {
7524 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
7525 return
7526 }
7527
7528 defer cleanupBucket(bucketName, c)
7529
7530 // Make a temp file with 11*1024*1024 bytes of data.
7531 file, err := os.CreateTemp(os.TempDir(), "FPutObjectTest")
7532 if err != nil {
7533 logError(testName, function, args, startTime, "", "TempFile creation failed", err)
7534 return
7535 }
7536
7537 r := bytes.NewReader(bytes.Repeat([]byte("b"), 11*1024*1024))
7538 n, err := io.CopyN(file, r, 11*1024*1024)
7539 if err != nil {
7540 logError(testName, function, args, startTime, "", "Copy failed", err)
7541 return
7542 }
7543 if n != int64(11*1024*1024) {
7544 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(int64(11*1024*1024))+" got "+string(n), err)
7545 return
7546 }
7547
7548 // Close the file pro-actively for windows.
7549 err = file.Close()
7550 if err != nil {
7551 logError(testName, function, args, startTime, "", "File close failed", err)
7552 return
7553 }
7554
7555 // Set base object name
7556 objectName := bucketName + "FPutObject"
7557 args["objectName"] = objectName
7558 args["fileName"] = file.Name()
7559
7560 // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
7561 _, err = c.FPutObject(context.Background(), bucketName, objectName+"-standard", file.Name(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
7562 if err != nil {
7563 logError(testName, function, args, startTime, "", "FPutObject failed", err)
7564 return
7565 }
7566
7567 // Perform FPutObject with no contentType provided (Expecting application/octet-stream)
7568 args["objectName"] = objectName + "-Octet"
7569 args["contentType"] = ""
7570
7571 _, err = c.FPutObject(context.Background(), bucketName, objectName+"-Octet", file.Name(), minio.PutObjectOptions{})
7572 if err != nil {
7573 logError(testName, function, args, startTime, "", "FPutObject failed", err)
7574 return
7575 }
7576
7577 // Add extension to temp file name
7578 fileName := file.Name()
7579 err = os.Rename(fileName, fileName+".gtar")
7580 if err != nil {
7581 logError(testName, function, args, startTime, "", "Rename failed", err)
7582 return
7583 }
7584
7585 // Perform FPutObject with no contentType provided (Expecting application/x-gtar)
7586 args["objectName"] = objectName + "-Octet"
7587 args["contentType"] = ""
7588 args["fileName"] = fileName + ".gtar"
7589
7590 _, err = c.FPutObject(context.Background(), bucketName, objectName+"-GTar", fileName+".gtar", minio.PutObjectOptions{})
7591 if err != nil {
7592 logError(testName, function, args, startTime, "", "FPutObject failed", err)
7593 return
7594 }
7595
7596 // Check headers and sizes
7597 rStandard, err := c.StatObject(context.Background(), bucketName, objectName+"-standard", minio.StatObjectOptions{})
7598 if err != nil {
7599 logError(testName, function, args, startTime, "", "StatObject failed", err)
7600 return
7601 }
7602
7603 if rStandard.Size != 11*1024*1024 {
7604 logError(testName, function, args, startTime, "", "Unexpected size", nil)
7605 return
7606 }
7607
7608 if rStandard.ContentType != "application/octet-stream" {
7609 logError(testName, function, args, startTime, "", "Content-Type headers mismatched, expected: application/octet-stream , got "+rStandard.ContentType, err)
7610 return
7611 }
7612
7613 rOctet, err := c.StatObject(context.Background(), bucketName, objectName+"-Octet", minio.StatObjectOptions{})
7614 if err != nil {
7615 logError(testName, function, args, startTime, "", "StatObject failed", err)
7616 return
7617 }
7618 if rOctet.ContentType != "application/octet-stream" {
7619 logError(testName, function, args, startTime, "", "Content-Type headers mismatched, expected: application/octet-stream , got "+rOctet.ContentType, err)
7620 return
7621 }
7622
7623 if rOctet.Size != 11*1024*1024 {
7624 logError(testName, function, args, startTime, "", "Unexpected size", nil)
7625 return
7626 }
7627
7628 rGTar, err := c.StatObject(context.Background(), bucketName, objectName+"-GTar", minio.StatObjectOptions{})
7629 if err != nil {
7630 logError(testName, function, args, startTime, "", "StatObject failed", err)
7631 return
7632 }
7633 if rGTar.Size != 11*1024*1024 {
7634 logError(testName, function, args, startTime, "", "Unexpected size", nil)
7635 return
7636 }
7637 if rGTar.ContentType != "application/x-gtar" && rGTar.ContentType != "application/octet-stream" && rGTar.ContentType != "application/x-tar" {
7638 logError(testName, function, args, startTime, "", "Content-Type headers mismatched, expected: application/x-tar , got "+rGTar.ContentType, err)
7639 return
7640 }
7641
7642 os.Remove(fileName + ".gtar")
7643 successLogger(testName, function, args, startTime).Info()
7644}
7645
7646// Tests various bucket supported formats.
7647func testMakeBucketRegionsV2() {
7648 // initialize logging params
7649 startTime := time.Now()
7650 testName := getFuncName()
7651 function := "MakeBucket(bucketName, region)"
7652 args := map[string]interface{}{
7653 "bucketName": "",
7654 "region": "eu-west-1",
7655 }
7656
7657 // Seed random based on current time.
7658 rand.Seed(time.Now().Unix())
7659
7660 // Instantiate new minio client object.
7661 c, err := minio.New(os.Getenv(serverEndpoint),
7662 &minio.Options{
7663 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
7664 Secure: mustParseBool(os.Getenv(enableHTTPS)),
7665 })
7666 if err != nil {
7667 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
7668 return
7669 }
7670
7671 // Enable tracing, write to stderr.
7672 // c.TraceOn(os.Stderr)
7673
7674 // Set user agent.
7675 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
7676
7677 // Generate a new random bucket name.
7678 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
7679 args["bucketName"] = bucketName
7680
7681 // Make a new bucket in 'eu-central-1'.
7682 if err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "eu-west-1"}); err != nil {
7683 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
7684 return
7685 }
7686
7687 if err = cleanupBucket(bucketName, c); err != nil {
7688 logError(testName, function, args, startTime, "", "CleanupBucket failed while removing bucket recursively", err)
7689 return
7690 }
7691
7692 // Make a new bucket with '.' in its name, in 'us-west-2'. This
7693 // request is internally staged into a path style instead of
7694 // virtual host style.
7695 if err = c.MakeBucket(context.Background(), bucketName+".withperiod", minio.MakeBucketOptions{Region: "us-west-2"}); err != nil {
7696 args["bucketName"] = bucketName + ".withperiod"
7697 args["region"] = "us-west-2"
7698 logError(testName, function, args, startTime, "", "MakeBucket test with a bucket name with period, '.', failed", err)
7699 return
7700 }
7701
7702 // Delete all objects and buckets
7703 if err = cleanupBucket(bucketName+".withperiod", c); err != nil {
7704 logError(testName, function, args, startTime, "", "CleanupBucket failed while removing bucket recursively", err)
7705 return
7706 }
7707
7708 successLogger(testName, function, args, startTime).Info()
7709}
7710
7711// Tests get object ReaderSeeker interface methods.
7712func testGetObjectReadSeekFunctionalV2() {
7713 // initialize logging params
7714 startTime := time.Now()
7715 testName := getFuncName()
7716 function := "GetObject(bucketName, objectName)"
7717 args := map[string]interface{}{}
7718
7719 // Seed random based on current time.
7720 rand.Seed(time.Now().Unix())
7721
7722 // Instantiate new minio client object.
7723 c, err := minio.New(os.Getenv(serverEndpoint),
7724 &minio.Options{
7725 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
7726 Secure: mustParseBool(os.Getenv(enableHTTPS)),
7727 })
7728 if err != nil {
7729 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
7730 return
7731 }
7732
7733 // Enable tracing, write to stderr.
7734 // c.TraceOn(os.Stderr)
7735
7736 // Set user agent.
7737 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
7738
7739 // Generate a new random bucket name.
7740 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
7741 args["bucketName"] = bucketName
7742
7743 // Make a new bucket.
7744 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
7745 if err != nil {
7746 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
7747 return
7748 }
7749
7750 defer cleanupBucket(bucketName, c)
7751
7752 // Generate 33K of data.
7753 bufSize := dataFileMap["datafile-33-kB"]
7754 reader := getDataReader("datafile-33-kB")
7755 defer reader.Close()
7756
7757 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
7758 args["objectName"] = objectName
7759
7760 buf, err := io.ReadAll(reader)
7761 if err != nil {
7762 logError(testName, function, args, startTime, "", "ReadAll failed", err)
7763 return
7764 }
7765
7766 // Save the data.
7767 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
7768 if err != nil {
7769 logError(testName, function, args, startTime, "", "PutObject failed", err)
7770 return
7771 }
7772
7773 // Read the data back
7774 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
7775 if err != nil {
7776 logError(testName, function, args, startTime, "", "GetObject failed", err)
7777 return
7778 }
7779 defer r.Close()
7780
7781 st, err := r.Stat()
7782 if err != nil {
7783 logError(testName, function, args, startTime, "", "Stat failed", err)
7784 return
7785 }
7786
7787 if st.Size != int64(bufSize) {
7788 logError(testName, function, args, startTime, "", "Number of bytes in stat does not match, expected "+string(int64(bufSize))+" got "+string(st.Size), err)
7789 return
7790 }
7791
7792 offset := int64(2048)
7793 n, err := r.Seek(offset, 0)
7794 if err != nil {
7795 logError(testName, function, args, startTime, "", "Seek failed", err)
7796 return
7797 }
7798 if n != offset {
7799 logError(testName, function, args, startTime, "", "Number of seeked bytes does not match, expected "+string(offset)+" got "+string(n), err)
7800 return
7801 }
7802 n, err = r.Seek(0, 1)
7803 if err != nil {
7804 logError(testName, function, args, startTime, "", "Seek failed", err)
7805 return
7806 }
7807 if n != offset {
7808 logError(testName, function, args, startTime, "", "Number of seeked bytes does not match, expected "+string(offset)+" got "+string(n), err)
7809 return
7810 }
7811 _, err = r.Seek(offset, 2)
7812 if err == nil {
7813 logError(testName, function, args, startTime, "", "Seek on positive offset for whence '2' should error out", err)
7814 return
7815 }
7816 n, err = r.Seek(-offset, 2)
7817 if err != nil {
7818 logError(testName, function, args, startTime, "", "Seek failed", err)
7819 return
7820 }
7821 if n != st.Size-offset {
7822 logError(testName, function, args, startTime, "", "Number of seeked bytes does not match, expected "+string(st.Size-offset)+" got "+string(n), err)
7823 return
7824 }
7825
7826 var buffer1 bytes.Buffer
7827 if _, err = io.CopyN(&buffer1, r, st.Size); err != nil {
7828 if err != io.EOF {
7829 logError(testName, function, args, startTime, "", "Copy failed", err)
7830 return
7831 }
7832 }
7833 if !bytes.Equal(buf[len(buf)-int(offset):], buffer1.Bytes()) {
7834 logError(testName, function, args, startTime, "", "Incorrect read bytes v/s original buffer", err)
7835 return
7836 }
7837
7838 // Seek again and read again.
7839 n, err = r.Seek(offset-1, 0)
7840 if err != nil {
7841 logError(testName, function, args, startTime, "", "Seek failed", err)
7842 return
7843 }
7844 if n != (offset - 1) {
7845 logError(testName, function, args, startTime, "", "Number of seeked bytes does not match, expected "+string(offset-1)+" got "+string(n), err)
7846 return
7847 }
7848
7849 var buffer2 bytes.Buffer
7850 if _, err = io.CopyN(&buffer2, r, st.Size); err != nil {
7851 if err != io.EOF {
7852 logError(testName, function, args, startTime, "", "Copy failed", err)
7853 return
7854 }
7855 }
7856 // Verify now lesser bytes.
7857 if !bytes.Equal(buf[2047:], buffer2.Bytes()) {
7858 logError(testName, function, args, startTime, "", "Incorrect read bytes v/s original buffer", err)
7859 return
7860 }
7861
7862 successLogger(testName, function, args, startTime).Info()
7863}
7864
7865// Tests get object ReaderAt interface methods.
7866func testGetObjectReadAtFunctionalV2() {
7867 // initialize logging params
7868 startTime := time.Now()
7869 testName := getFuncName()
7870 function := "GetObject(bucketName, objectName)"
7871 args := map[string]interface{}{}
7872
7873 // Seed random based on current time.
7874 rand.Seed(time.Now().Unix())
7875
7876 // Instantiate new minio client object.
7877 c, err := minio.New(os.Getenv(serverEndpoint),
7878 &minio.Options{
7879 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
7880 Secure: mustParseBool(os.Getenv(enableHTTPS)),
7881 })
7882 if err != nil {
7883 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
7884 return
7885 }
7886
7887 // Enable tracing, write to stderr.
7888 // c.TraceOn(os.Stderr)
7889
7890 // Set user agent.
7891 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
7892
7893 // Generate a new random bucket name.
7894 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
7895 args["bucketName"] = bucketName
7896
7897 // Make a new bucket.
7898 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
7899 if err != nil {
7900 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
7901 return
7902 }
7903
7904 defer cleanupBucket(bucketName, c)
7905
7906 // Generate 33K of data.
7907 bufSize := dataFileMap["datafile-33-kB"]
7908 reader := getDataReader("datafile-33-kB")
7909 defer reader.Close()
7910
7911 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
7912 args["objectName"] = objectName
7913
7914 buf, err := io.ReadAll(reader)
7915 if err != nil {
7916 logError(testName, function, args, startTime, "", "ReadAll failed", err)
7917 return
7918 }
7919
7920 // Save the data
7921 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
7922 if err != nil {
7923 logError(testName, function, args, startTime, "", "PutObject failed", err)
7924 return
7925 }
7926
7927 // Read the data back
7928 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
7929 if err != nil {
7930 logError(testName, function, args, startTime, "", "GetObject failed", err)
7931 return
7932 }
7933 defer r.Close()
7934
7935 st, err := r.Stat()
7936 if err != nil {
7937 logError(testName, function, args, startTime, "", "Stat failed", err)
7938 return
7939 }
7940
7941 if st.Size != int64(bufSize) {
7942 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(bufSize)+" got "+string(st.Size), err)
7943 return
7944 }
7945
7946 offset := int64(2048)
7947
7948 // Read directly
7949 buf2 := make([]byte, 512)
7950 buf3 := make([]byte, 512)
7951 buf4 := make([]byte, 512)
7952
7953 m, err := r.ReadAt(buf2, offset)
7954 if err != nil {
7955 logError(testName, function, args, startTime, "", "ReadAt failed", err)
7956 return
7957 }
7958 if m != len(buf2) {
7959 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf2))+" got "+string(m), err)
7960 return
7961 }
7962 if !bytes.Equal(buf2, buf[offset:offset+512]) {
7963 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
7964 return
7965 }
7966 offset += 512
7967 m, err = r.ReadAt(buf3, offset)
7968 if err != nil {
7969 logError(testName, function, args, startTime, "", "ReadAt failed", err)
7970 return
7971 }
7972 if m != len(buf3) {
7973 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf3))+" got "+string(m), err)
7974 return
7975 }
7976 if !bytes.Equal(buf3, buf[offset:offset+512]) {
7977 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
7978 return
7979 }
7980 offset += 512
7981 m, err = r.ReadAt(buf4, offset)
7982 if err != nil {
7983 logError(testName, function, args, startTime, "", "ReadAt failed", err)
7984 return
7985 }
7986 if m != len(buf4) {
7987 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf4))+" got "+string(m), err)
7988 return
7989 }
7990 if !bytes.Equal(buf4, buf[offset:offset+512]) {
7991 logError(testName, function, args, startTime, "", "Incorrect read between two ReadAt from same offset", err)
7992 return
7993 }
7994
7995 buf5 := make([]byte, bufSize)
7996 // Read the whole object.
7997 m, err = r.ReadAt(buf5, 0)
7998 if err != nil {
7999 if err != io.EOF {
8000 logError(testName, function, args, startTime, "", "ReadAt failed", err)
8001 return
8002 }
8003 }
8004 if m != len(buf5) {
8005 logError(testName, function, args, startTime, "", "ReadAt read shorter bytes before reaching EOF, expected "+string(len(buf5))+" got "+string(m), err)
8006 return
8007 }
8008 if !bytes.Equal(buf, buf5) {
8009 logError(testName, function, args, startTime, "", "Incorrect data read in GetObject, than what was previously uploaded", err)
8010 return
8011 }
8012
8013 buf6 := make([]byte, bufSize+1)
8014 // Read the whole object and beyond.
8015 _, err = r.ReadAt(buf6, 0)
8016 if err != nil {
8017 if err != io.EOF {
8018 logError(testName, function, args, startTime, "", "ReadAt failed", err)
8019 return
8020 }
8021 }
8022
8023 successLogger(testName, function, args, startTime).Info()
8024}
8025
8026// Tests copy object
8027func testCopyObjectV2() {
8028 // initialize logging params
8029 startTime := time.Now()
8030 testName := getFuncName()
8031 function := "CopyObject(destination, source)"
8032 args := map[string]interface{}{}
8033
8034 // Seed random based on current time.
8035 rand.Seed(time.Now().Unix())
8036
8037 // Instantiate new minio client object
8038 c, err := minio.New(os.Getenv(serverEndpoint),
8039 &minio.Options{
8040 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8041 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8042 })
8043 if err != nil {
8044 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8045 return
8046 }
8047
8048 // Enable tracing, write to stderr.
8049 // c.TraceOn(os.Stderr)
8050
8051 // Set user agent.
8052 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
8053
8054 // Generate a new random bucket name.
8055 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8056
8057 // Make a new bucket in 'us-east-1' (source bucket).
8058 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
8059 if err != nil {
8060 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
8061 return
8062 }
8063 defer cleanupBucket(bucketName, c)
8064
8065 // Make a new bucket in 'us-east-1' (destination bucket).
8066 err = c.MakeBucket(context.Background(), bucketName+"-copy", minio.MakeBucketOptions{Region: "us-east-1"})
8067 if err != nil {
8068 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
8069 return
8070 }
8071 defer cleanupBucket(bucketName+"-copy", c)
8072
8073 // Generate 33K of data.
8074 bufSize := dataFileMap["datafile-33-kB"]
8075 reader := getDataReader("datafile-33-kB")
8076 defer reader.Close()
8077
8078 // Save the data
8079 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
8080 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
8081 if err != nil {
8082 logError(testName, function, args, startTime, "", "PutObject failed", err)
8083 return
8084 }
8085
8086 r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
8087 if err != nil {
8088 logError(testName, function, args, startTime, "", "GetObject failed", err)
8089 return
8090 }
8091 // Check the various fields of source object against destination object.
8092 objInfo, err := r.Stat()
8093 if err != nil {
8094 logError(testName, function, args, startTime, "", "Stat failed", err)
8095 return
8096 }
8097 r.Close()
8098
8099 // Copy Source
8100 src := minio.CopySrcOptions{
8101 Bucket: bucketName,
8102 Object: objectName,
8103 MatchModifiedSince: time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC),
8104 MatchETag: objInfo.ETag,
8105 }
8106 args["source"] = src
8107
8108 // Set copy conditions.
8109 dst := minio.CopyDestOptions{
8110 Bucket: bucketName + "-copy",
8111 Object: objectName + "-copy",
8112 }
8113 args["destination"] = dst
8114
8115 // Perform the Copy
8116 _, err = c.CopyObject(context.Background(), dst, src)
8117 if err != nil {
8118 logError(testName, function, args, startTime, "", "CopyObject failed", err)
8119 return
8120 }
8121
8122 // Source object
8123 r, err = c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
8124 if err != nil {
8125 logError(testName, function, args, startTime, "", "GetObject failed", err)
8126 return
8127 }
8128 // Destination object
8129 readerCopy, err := c.GetObject(context.Background(), bucketName+"-copy", objectName+"-copy", minio.GetObjectOptions{})
8130 if err != nil {
8131 logError(testName, function, args, startTime, "", "GetObject failed", err)
8132 return
8133 }
8134 // Check the various fields of source object against destination object.
8135 objInfo, err = r.Stat()
8136 if err != nil {
8137 logError(testName, function, args, startTime, "", "Stat failed", err)
8138 return
8139 }
8140 objInfoCopy, err := readerCopy.Stat()
8141 if err != nil {
8142 logError(testName, function, args, startTime, "", "Stat failed", err)
8143 return
8144 }
8145 if objInfo.Size != objInfoCopy.Size {
8146 logError(testName, function, args, startTime, "", "Number of bytes does not match, expected "+string(objInfoCopy.Size)+" got "+string(objInfo.Size), err)
8147 return
8148 }
8149
8150 // Close all the readers.
8151 r.Close()
8152 readerCopy.Close()
8153
8154 // CopyObject again but with wrong conditions
8155 src = minio.CopySrcOptions{
8156 Bucket: bucketName,
8157 Object: objectName,
8158 MatchUnmodifiedSince: time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC),
8159 NoMatchETag: objInfo.ETag,
8160 }
8161
8162 // Perform the Copy which should fail
8163 _, err = c.CopyObject(context.Background(), dst, src)
8164 if err == nil {
8165 logError(testName, function, args, startTime, "", "CopyObject did not fail for invalid conditions", err)
8166 return
8167 }
8168
8169 successLogger(testName, function, args, startTime).Info()
8170}
8171
8172func testComposeObjectErrorCasesWrapper(c *minio.Client) {
8173 // initialize logging params
8174 startTime := time.Now()
8175 testName := getFuncName()
8176 function := "ComposeObject(destination, sourceList)"
8177 args := map[string]interface{}{}
8178
8179 // Generate a new random bucket name.
8180 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8181
8182 // Make a new bucket in 'us-east-1' (source bucket).
8183 err := c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
8184 if err != nil {
8185 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
8186 return
8187 }
8188
8189 defer cleanupBucket(bucketName, c)
8190
8191 // Test that more than 10K source objects cannot be
8192 // concatenated.
8193 srcArr := [10001]minio.CopySrcOptions{}
8194 srcSlice := srcArr[:]
8195 dst := minio.CopyDestOptions{
8196 Bucket: bucketName,
8197 Object: "object",
8198 }
8199
8200 args["destination"] = dst
8201 // Just explain about srcArr in args["sourceList"]
8202 // to stop having 10,001 null headers logged
8203 args["sourceList"] = "source array of 10,001 elements"
8204 if _, err := c.ComposeObject(context.Background(), dst, srcSlice...); err == nil {
8205 logError(testName, function, args, startTime, "", "Expected error in ComposeObject", err)
8206 return
8207 } else if err.Error() != "There must be as least one and up to 10000 source objects." {
8208 logError(testName, function, args, startTime, "", "Got unexpected error", err)
8209 return
8210 }
8211
8212 // Create a source with invalid offset spec and check that
8213 // error is returned:
8214 // 1. Create the source object.
8215 const badSrcSize = 5 * 1024 * 1024
8216 buf := bytes.Repeat([]byte("1"), badSrcSize)
8217 _, err = c.PutObject(context.Background(), bucketName, "badObject", bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{})
8218 if err != nil {
8219 logError(testName, function, args, startTime, "", "PutObject failed", err)
8220 return
8221 }
8222 // 2. Set invalid range spec on the object (going beyond
8223 // object size)
8224 badSrc := minio.CopySrcOptions{
8225 Bucket: bucketName,
8226 Object: "badObject",
8227 MatchRange: true,
8228 Start: 1,
8229 End: badSrcSize,
8230 }
8231
8232 // 3. ComposeObject call should fail.
8233 if _, err := c.ComposeObject(context.Background(), dst, badSrc); err == nil {
8234 logError(testName, function, args, startTime, "", "ComposeObject expected to fail", err)
8235 return
8236 } else if !strings.Contains(err.Error(), "has invalid segment-to-copy") {
8237 logError(testName, function, args, startTime, "", "Got invalid error", err)
8238 return
8239 }
8240
8241 successLogger(testName, function, args, startTime).Info()
8242}
8243
8244// Test expected error cases
8245func testComposeObjectErrorCasesV2() {
8246 // initialize logging params
8247 startTime := time.Now()
8248 testName := getFuncName()
8249 function := "ComposeObject(destination, sourceList)"
8250 args := map[string]interface{}{}
8251
8252 // Instantiate new minio client object
8253 c, err := minio.New(os.Getenv(serverEndpoint),
8254 &minio.Options{
8255 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8256 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8257 })
8258 if err != nil {
8259 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8260 return
8261 }
8262
8263 testComposeObjectErrorCasesWrapper(c)
8264}
8265
8266func testComposeMultipleSources(c *minio.Client) {
8267 // initialize logging params
8268 startTime := time.Now()
8269 testName := getFuncName()
8270 function := "ComposeObject(destination, sourceList)"
8271 args := map[string]interface{}{
8272 "destination": "",
8273 "sourceList": "",
8274 }
8275
8276 // Generate a new random bucket name.
8277 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8278 // Make a new bucket in 'us-east-1' (source bucket).
8279 err := c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
8280 if err != nil {
8281 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
8282 return
8283 }
8284
8285 defer cleanupBucket(bucketName, c)
8286
8287 // Upload a small source object
8288 const srcSize = 1024 * 1024 * 5
8289 buf := bytes.Repeat([]byte("1"), srcSize)
8290 _, err = c.PutObject(context.Background(), bucketName, "srcObject", bytes.NewReader(buf), int64(srcSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
8291 if err != nil {
8292 logError(testName, function, args, startTime, "", "PutObject failed", err)
8293 return
8294 }
8295
8296 // We will append 10 copies of the object.
8297 srcs := []minio.CopySrcOptions{}
8298 for i := 0; i < 10; i++ {
8299 srcs = append(srcs, minio.CopySrcOptions{
8300 Bucket: bucketName,
8301 Object: "srcObject",
8302 })
8303 }
8304
8305 // make the last part very small
8306 srcs[9].MatchRange = true
8307
8308 args["sourceList"] = srcs
8309
8310 dst := minio.CopyDestOptions{
8311 Bucket: bucketName,
8312 Object: "dstObject",
8313 }
8314 args["destination"] = dst
8315
8316 ui, err := c.ComposeObject(context.Background(), dst, srcs...)
8317 if err != nil {
8318 logError(testName, function, args, startTime, "", "ComposeObject failed", err)
8319 return
8320 }
8321
8322 if ui.Size != 9*srcSize+1 {
8323 logError(testName, function, args, startTime, "", "ComposeObject returned unexpected size", err)
8324 return
8325 }
8326
8327 objProps, err := c.StatObject(context.Background(), bucketName, "dstObject", minio.StatObjectOptions{})
8328 if err != nil {
8329 logError(testName, function, args, startTime, "", "StatObject failed", err)
8330 return
8331 }
8332
8333 if objProps.Size != 9*srcSize+1 {
8334 logError(testName, function, args, startTime, "", "Size mismatched! Expected "+string(10000*srcSize)+" got "+string(objProps.Size), err)
8335 return
8336 }
8337
8338 successLogger(testName, function, args, startTime).Info()
8339}
8340
8341// Test concatenating multiple 10K objects V2
8342func testCompose10KSourcesV2() {
8343 // initialize logging params
8344 startTime := time.Now()
8345 testName := getFuncName()
8346 function := "ComposeObject(destination, sourceList)"
8347 args := map[string]interface{}{}
8348
8349 // Instantiate new minio client object
8350 c, err := minio.New(os.Getenv(serverEndpoint),
8351 &minio.Options{
8352 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8353 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8354 })
8355 if err != nil {
8356 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8357 return
8358 }
8359
8360 testComposeMultipleSources(c)
8361}
8362
8363func testEncryptedEmptyObject() {
8364 // initialize logging params
8365 startTime := time.Now()
8366 testName := getFuncName()
8367 function := "PutObject(bucketName, objectName, reader, objectSize, opts)"
8368 args := map[string]interface{}{}
8369
8370 // Instantiate new minio client object
8371 c, err := minio.New(os.Getenv(serverEndpoint),
8372 &minio.Options{
8373 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8374 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8375 })
8376 if err != nil {
8377 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
8378 return
8379 }
8380
8381 // Generate a new random bucket name.
8382 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8383 args["bucketName"] = bucketName
8384 // Make a new bucket in 'us-east-1' (source bucket).
8385 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
8386 if err != nil {
8387 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
8388 return
8389 }
8390
8391 defer cleanupBucket(bucketName, c)
8392
8393 sse := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"object"))
8394
8395 // 1. create an sse-c encrypted object to copy by uploading
8396 const srcSize = 0
8397 var buf []byte // Empty buffer
8398 args["objectName"] = "object"
8399 _, err = c.PutObject(context.Background(), bucketName, "object", bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ServerSideEncryption: sse})
8400 if err != nil {
8401 logError(testName, function, args, startTime, "", "PutObject call failed", err)
8402 return
8403 }
8404
8405 // 2. Test CopyObject for an empty object
8406 src := minio.CopySrcOptions{
8407 Bucket: bucketName,
8408 Object: "object",
8409 Encryption: sse,
8410 }
8411
8412 dst := minio.CopyDestOptions{
8413 Bucket: bucketName,
8414 Object: "new-object",
8415 Encryption: sse,
8416 }
8417
8418 if _, err = c.CopyObject(context.Background(), dst, src); err != nil {
8419 function = "CopyObject(dst, src)"
8420 logError(testName, function, map[string]interface{}{}, startTime, "", "CopyObject failed", err)
8421 return
8422 }
8423
8424 // 3. Test Key rotation
8425 newSSE := encrypt.DefaultPBKDF([]byte("Don't Panic"), []byte(bucketName+"new-object"))
8426 src = minio.CopySrcOptions{
8427 Bucket: bucketName,
8428 Object: "new-object",
8429 Encryption: sse,
8430 }
8431
8432 dst = minio.CopyDestOptions{
8433 Bucket: bucketName,
8434 Object: "new-object",
8435 Encryption: newSSE,
8436 }
8437
8438 if _, err = c.CopyObject(context.Background(), dst, src); err != nil {
8439 function = "CopyObject(dst, src)"
8440 logError(testName, function, map[string]interface{}{}, startTime, "", "CopyObject with key rotation failed", err)
8441 return
8442 }
8443
8444 // 4. Download the object.
8445 reader, err := c.GetObject(context.Background(), bucketName, "new-object", minio.GetObjectOptions{ServerSideEncryption: newSSE})
8446 if err != nil {
8447 logError(testName, function, args, startTime, "", "GetObject failed", err)
8448 return
8449 }
8450 defer reader.Close()
8451
8452 decBytes, err := io.ReadAll(reader)
8453 if err != nil {
8454 logError(testName, function, map[string]interface{}{}, startTime, "", "ReadAll failed", err)
8455 return
8456 }
8457 if !bytes.Equal(decBytes, buf) {
8458 logError(testName, function, map[string]interface{}{}, startTime, "", "Downloaded object doesn't match the empty encrypted object", err)
8459 return
8460 }
8461
8462 delete(args, "objectName")
8463 successLogger(testName, function, args, startTime).Info()
8464}
8465
8466func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, sseDst encrypt.ServerSide) {
8467 // initialize logging params
8468 startTime := time.Now()
8469 testName := getFuncNameLoc(2)
8470 function := "CopyObject(destination, source)"
8471 args := map[string]interface{}{}
8472 var srcEncryption, dstEncryption encrypt.ServerSide
8473
8474 // Make a new bucket in 'us-east-1' (source bucket).
8475 err := c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
8476 if err != nil {
8477 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
8478 return
8479 }
8480
8481 defer cleanupBucket(bucketName, c)
8482
8483 // 1. create an sse-c encrypted object to copy by uploading
8484 const srcSize = 1024 * 1024
8485 buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 5MiB
8486 _, err = c.PutObject(context.Background(), bucketName, "srcObject", bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{
8487 ServerSideEncryption: sseSrc,
8488 })
8489 if err != nil {
8490 logError(testName, function, args, startTime, "", "PutObject call failed", err)
8491 return
8492 }
8493
8494 if sseSrc != nil && sseSrc.Type() != encrypt.S3 {
8495 srcEncryption = sseSrc
8496 }
8497
8498 // 2. copy object and change encryption key
8499 src := minio.CopySrcOptions{
8500 Bucket: bucketName,
8501 Object: "srcObject",
8502 Encryption: srcEncryption,
8503 }
8504 args["source"] = src
8505
8506 dst := minio.CopyDestOptions{
8507 Bucket: bucketName,
8508 Object: "dstObject",
8509 Encryption: sseDst,
8510 }
8511 args["destination"] = dst
8512
8513 _, err = c.CopyObject(context.Background(), dst, src)
8514 if err != nil {
8515 logError(testName, function, args, startTime, "", "CopyObject failed", err)
8516 return
8517 }
8518
8519 if sseDst != nil && sseDst.Type() != encrypt.S3 {
8520 dstEncryption = sseDst
8521 }
8522 // 3. get copied object and check if content is equal
8523 coreClient := minio.Core{c}
8524 reader, _, _, err := coreClient.GetObject(context.Background(), bucketName, "dstObject", minio.GetObjectOptions{ServerSideEncryption: dstEncryption})
8525 if err != nil {
8526 logError(testName, function, args, startTime, "", "GetObject failed", err)
8527 return
8528 }
8529
8530 decBytes, err := io.ReadAll(reader)
8531 if err != nil {
8532 logError(testName, function, args, startTime, "", "ReadAll failed", err)
8533 return
8534 }
8535 if !bytes.Equal(decBytes, buf) {
8536 logError(testName, function, args, startTime, "", "Downloaded object mismatched for encrypted object", err)
8537 return
8538 }
8539 reader.Close()
8540
8541 // Test key rotation for source object in-place.
8542 var newSSE encrypt.ServerSide
8543 if sseSrc != nil && sseSrc.Type() == encrypt.SSEC {
8544 newSSE = encrypt.DefaultPBKDF([]byte("Don't Panic"), []byte(bucketName+"srcObject")) // replace key
8545 }
8546 if sseSrc != nil && sseSrc.Type() == encrypt.S3 {
8547 newSSE = encrypt.NewSSE()
8548 }
8549 if newSSE != nil {
8550 dst = minio.CopyDestOptions{
8551 Bucket: bucketName,
8552 Object: "srcObject",
8553 Encryption: newSSE,
8554 }
8555 args["destination"] = dst
8556
8557 _, err = c.CopyObject(context.Background(), dst, src)
8558 if err != nil {
8559 logError(testName, function, args, startTime, "", "CopyObject failed", err)
8560 return
8561 }
8562
8563 // Get copied object and check if content is equal
8564 reader, _, _, err = coreClient.GetObject(context.Background(), bucketName, "srcObject", minio.GetObjectOptions{ServerSideEncryption: newSSE})
8565 if err != nil {
8566 logError(testName, function, args, startTime, "", "GetObject failed", err)
8567 return
8568 }
8569
8570 decBytes, err = io.ReadAll(reader)
8571 if err != nil {
8572 logError(testName, function, args, startTime, "", "ReadAll failed", err)
8573 return
8574 }
8575 if !bytes.Equal(decBytes, buf) {
8576 logError(testName, function, args, startTime, "", "Downloaded object mismatched for encrypted object", err)
8577 return
8578 }
8579 reader.Close()
8580
8581 // Test in-place decryption.
8582 dst = minio.CopyDestOptions{
8583 Bucket: bucketName,
8584 Object: "srcObject",
8585 }
8586 args["destination"] = dst
8587
8588 src = minio.CopySrcOptions{
8589 Bucket: bucketName,
8590 Object: "srcObject",
8591 Encryption: newSSE,
8592 }
8593 args["source"] = src
8594 _, err = c.CopyObject(context.Background(), dst, src)
8595 if err != nil {
8596 logError(testName, function, args, startTime, "", "CopyObject Key rotation failed", err)
8597 return
8598 }
8599 }
8600
8601 // Get copied decrypted object and check if content is equal
8602 reader, _, _, err = coreClient.GetObject(context.Background(), bucketName, "srcObject", minio.GetObjectOptions{})
8603 if err != nil {
8604 logError(testName, function, args, startTime, "", "GetObject failed", err)
8605 return
8606 }
8607 defer reader.Close()
8608
8609 decBytes, err = io.ReadAll(reader)
8610 if err != nil {
8611 logError(testName, function, args, startTime, "", "ReadAll failed", err)
8612 return
8613 }
8614 if !bytes.Equal(decBytes, buf) {
8615 logError(testName, function, args, startTime, "", "Downloaded object mismatched for encrypted object", err)
8616 return
8617 }
8618
8619 successLogger(testName, function, args, startTime).Info()
8620}
8621
8622// Test encrypted copy object
8623func testUnencryptedToSSECCopyObject() {
8624 // initialize logging params
8625 startTime := time.Now()
8626 testName := getFuncName()
8627 function := "CopyObject(destination, source)"
8628 args := map[string]interface{}{}
8629
8630 // Instantiate new minio client object
8631 c, err := minio.New(os.Getenv(serverEndpoint),
8632 &minio.Options{
8633 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8634 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8635 })
8636 if err != nil {
8637 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8638 return
8639 }
8640 // Generate a new random bucket name.
8641 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8642
8643 sseDst := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"dstObject"))
8644 // c.TraceOn(os.Stderr)
8645 testEncryptedCopyObjectWrapper(c, bucketName, nil, sseDst)
8646}
8647
8648// Test encrypted copy object
8649func testUnencryptedToSSES3CopyObject() {
8650 // initialize logging params
8651 startTime := time.Now()
8652 testName := getFuncName()
8653 function := "CopyObject(destination, source)"
8654 args := map[string]interface{}{}
8655
8656 // Instantiate new minio client object
8657 c, err := minio.New(os.Getenv(serverEndpoint),
8658 &minio.Options{
8659 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8660 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8661 })
8662 if err != nil {
8663 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8664 return
8665 }
8666 // Generate a new random bucket name.
8667 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8668
8669 var sseSrc encrypt.ServerSide
8670 sseDst := encrypt.NewSSE()
8671 // c.TraceOn(os.Stderr)
8672 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8673}
8674
8675// Test encrypted copy object
8676func testUnencryptedToUnencryptedCopyObject() {
8677 // initialize logging params
8678 startTime := time.Now()
8679 testName := getFuncName()
8680 function := "CopyObject(destination, source)"
8681 args := map[string]interface{}{}
8682
8683 // Instantiate new minio client object
8684 c, err := minio.New(os.Getenv(serverEndpoint),
8685 &minio.Options{
8686 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8687 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8688 })
8689 if err != nil {
8690 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8691 return
8692 }
8693 // Generate a new random bucket name.
8694 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8695
8696 var sseSrc, sseDst encrypt.ServerSide
8697 // c.TraceOn(os.Stderr)
8698 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8699}
8700
8701// Test encrypted copy object
8702func testEncryptedSSECToSSECCopyObject() {
8703 // initialize logging params
8704 startTime := time.Now()
8705 testName := getFuncName()
8706 function := "CopyObject(destination, source)"
8707 args := map[string]interface{}{}
8708
8709 // Instantiate new minio client object
8710 c, err := minio.New(os.Getenv(serverEndpoint),
8711 &minio.Options{
8712 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8713 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8714 })
8715 if err != nil {
8716 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8717 return
8718 }
8719 // Generate a new random bucket name.
8720 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8721
8722 sseSrc := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"srcObject"))
8723 sseDst := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"dstObject"))
8724 // c.TraceOn(os.Stderr)
8725 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8726}
8727
8728// Test encrypted copy object
8729func testEncryptedSSECToSSES3CopyObject() {
8730 // initialize logging params
8731 startTime := time.Now()
8732 testName := getFuncName()
8733 function := "CopyObject(destination, source)"
8734 args := map[string]interface{}{}
8735
8736 // Instantiate new minio client object
8737 c, err := minio.New(os.Getenv(serverEndpoint),
8738 &minio.Options{
8739 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8740 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8741 })
8742 if err != nil {
8743 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8744 return
8745 }
8746 // Generate a new random bucket name.
8747 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8748
8749 sseSrc := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"srcObject"))
8750 sseDst := encrypt.NewSSE()
8751 // c.TraceOn(os.Stderr)
8752 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8753}
8754
8755// Test encrypted copy object
8756func testEncryptedSSECToUnencryptedCopyObject() {
8757 // initialize logging params
8758 startTime := time.Now()
8759 testName := getFuncName()
8760 function := "CopyObject(destination, source)"
8761 args := map[string]interface{}{}
8762
8763 // Instantiate new minio client object
8764 c, err := minio.New(os.Getenv(serverEndpoint),
8765 &minio.Options{
8766 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8767 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8768 })
8769 if err != nil {
8770 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8771 return
8772 }
8773 // Generate a new random bucket name.
8774 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8775
8776 sseSrc := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"srcObject"))
8777 var sseDst encrypt.ServerSide
8778 // c.TraceOn(os.Stderr)
8779 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8780}
8781
8782// Test encrypted copy object
8783func testEncryptedSSES3ToSSECCopyObject() {
8784 // initialize logging params
8785 startTime := time.Now()
8786 testName := getFuncName()
8787 function := "CopyObject(destination, source)"
8788 args := map[string]interface{}{}
8789
8790 // Instantiate new minio client object
8791 c, err := minio.New(os.Getenv(serverEndpoint),
8792 &minio.Options{
8793 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8794 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8795 })
8796 if err != nil {
8797 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8798 return
8799 }
8800 // Generate a new random bucket name.
8801 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8802
8803 sseSrc := encrypt.NewSSE()
8804 sseDst := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"dstObject"))
8805 // c.TraceOn(os.Stderr)
8806 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8807}
8808
8809// Test encrypted copy object
8810func testEncryptedSSES3ToSSES3CopyObject() {
8811 // initialize logging params
8812 startTime := time.Now()
8813 testName := getFuncName()
8814 function := "CopyObject(destination, source)"
8815 args := map[string]interface{}{}
8816
8817 // Instantiate new minio client object
8818 c, err := minio.New(os.Getenv(serverEndpoint),
8819 &minio.Options{
8820 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8821 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8822 })
8823 if err != nil {
8824 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8825 return
8826 }
8827 // Generate a new random bucket name.
8828 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8829
8830 sseSrc := encrypt.NewSSE()
8831 sseDst := encrypt.NewSSE()
8832 // c.TraceOn(os.Stderr)
8833 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8834}
8835
8836// Test encrypted copy object
8837func testEncryptedSSES3ToUnencryptedCopyObject() {
8838 // initialize logging params
8839 startTime := time.Now()
8840 testName := getFuncName()
8841 function := "CopyObject(destination, source)"
8842 args := map[string]interface{}{}
8843
8844 // Instantiate new minio client object
8845 c, err := minio.New(os.Getenv(serverEndpoint),
8846 &minio.Options{
8847 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8848 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8849 })
8850 if err != nil {
8851 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8852 return
8853 }
8854 // Generate a new random bucket name.
8855 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8856
8857 sseSrc := encrypt.NewSSE()
8858 var sseDst encrypt.ServerSide
8859 // c.TraceOn(os.Stderr)
8860 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8861}
8862
8863// Test encrypted copy object
8864func testEncryptedCopyObjectV2() {
8865 // initialize logging params
8866 startTime := time.Now()
8867 testName := getFuncName()
8868 function := "CopyObject(destination, source)"
8869 args := map[string]interface{}{}
8870
8871 // Instantiate new minio client object
8872 c, err := minio.New(os.Getenv(serverEndpoint),
8873 &minio.Options{
8874 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8875 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8876 })
8877 if err != nil {
8878 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8879 return
8880 }
8881 // Generate a new random bucket name.
8882 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
8883
8884 sseSrc := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"srcObject"))
8885 sseDst := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+"dstObject"))
8886 // c.TraceOn(os.Stderr)
8887 testEncryptedCopyObjectWrapper(c, bucketName, sseSrc, sseDst)
8888}
8889
8890func testDecryptedCopyObject() {
8891 // initialize logging params
8892 startTime := time.Now()
8893 testName := getFuncName()
8894 function := "CopyObject(destination, source)"
8895 args := map[string]interface{}{}
8896
8897 // Instantiate new minio client object
8898 c, err := minio.New(os.Getenv(serverEndpoint),
8899 &minio.Options{
8900 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8901 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8902 })
8903 if err != nil {
8904 logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err)
8905 return
8906 }
8907
8908 bucketName, objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-"), "object"
8909 if err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"}); err != nil {
8910 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
8911 return
8912 }
8913
8914 defer cleanupBucket(bucketName, c)
8915
8916 encryption := encrypt.DefaultPBKDF([]byte("correct horse battery staple"), []byte(bucketName+objectName))
8917 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(bytes.Repeat([]byte("a"), 1024*1024)), 1024*1024, minio.PutObjectOptions{
8918 ServerSideEncryption: encryption,
8919 })
8920 if err != nil {
8921 logError(testName, function, args, startTime, "", "PutObject call failed", err)
8922 return
8923 }
8924
8925 src := minio.CopySrcOptions{
8926 Bucket: bucketName,
8927 Object: objectName,
8928 Encryption: encrypt.SSECopy(encryption),
8929 }
8930 args["source"] = src
8931
8932 dst := minio.CopyDestOptions{
8933 Bucket: bucketName,
8934 Object: "decrypted-" + objectName,
8935 }
8936 args["destination"] = dst
8937
8938 if _, err = c.CopyObject(context.Background(), dst, src); err != nil {
8939 logError(testName, function, args, startTime, "", "CopyObject failed", err)
8940 return
8941 }
8942 if _, err = c.GetObject(context.Background(), bucketName, "decrypted-"+objectName, minio.GetObjectOptions{}); err != nil {
8943 logError(testName, function, args, startTime, "", "GetObject failed", err)
8944 return
8945 }
8946 successLogger(testName, function, args, startTime).Info()
8947}
8948
8949func testSSECMultipartEncryptedToSSECCopyObjectPart() {
8950 // initialize logging params
8951 startTime := time.Now()
8952 testName := getFuncName()
8953 function := "CopyObjectPart(destination, source)"
8954 args := map[string]interface{}{}
8955
8956 // Instantiate new minio client object
8957 client, err := minio.New(os.Getenv(serverEndpoint),
8958 &minio.Options{
8959 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
8960 Secure: mustParseBool(os.Getenv(enableHTTPS)),
8961 })
8962 if err != nil {
8963 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
8964 return
8965 }
8966
8967 // Instantiate new core client object.
8968 c := minio.Core{client}
8969
8970 // Enable tracing, write to stderr.
8971 // c.TraceOn(os.Stderr)
8972
8973 // Set user agent.
8974 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
8975
8976 // Generate a new random bucket name.
8977 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
8978
8979 // Make a new bucket.
8980 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
8981 if err != nil {
8982 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
8983 return
8984 }
8985 defer cleanupBucket(bucketName, client)
8986 // Make a buffer with 6MB of data
8987 buf := bytes.Repeat([]byte("abcdef"), 1024*1024)
8988
8989 // Save the data
8990 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
8991 password := "correct horse battery staple"
8992 srcencryption := encrypt.DefaultPBKDF([]byte(password), []byte(bucketName+objectName))
8993
8994 // Upload a 6MB object using multipart mechanism
8995 uploadID, err := c.NewMultipartUpload(context.Background(), bucketName, objectName, minio.PutObjectOptions{ServerSideEncryption: srcencryption})
8996 if err != nil {
8997 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
8998 return
8999 }
9000
9001 var completeParts []minio.CompletePart
9002
9003 part, err := c.PutObjectPart(context.Background(), bucketName, objectName, uploadID, 1,
9004 bytes.NewReader(buf[:5*1024*1024]), 5*1024*1024,
9005 minio.PutObjectPartOptions{SSE: srcencryption},
9006 )
9007 if err != nil {
9008 logError(testName, function, args, startTime, "", "PutObjectPart call failed", err)
9009 return
9010 }
9011 completeParts = append(completeParts, minio.CompletePart{PartNumber: part.PartNumber, ETag: part.ETag})
9012
9013 part, err = c.PutObjectPart(context.Background(), bucketName, objectName, uploadID, 2,
9014 bytes.NewReader(buf[5*1024*1024:]), 1024*1024,
9015 minio.PutObjectPartOptions{SSE: srcencryption},
9016 )
9017 if err != nil {
9018 logError(testName, function, args, startTime, "", "PutObjectPart call failed", err)
9019 return
9020 }
9021 completeParts = append(completeParts, minio.CompletePart{PartNumber: part.PartNumber, ETag: part.ETag})
9022
9023 // Complete the multipart upload
9024 _, err = c.CompleteMultipartUpload(context.Background(), bucketName, objectName, uploadID, completeParts, minio.PutObjectOptions{})
9025 if err != nil {
9026 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
9027 return
9028 }
9029
9030 // Stat the object and check its length matches
9031 objInfo, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{ServerSideEncryption: srcencryption})
9032 if err != nil {
9033 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9034 return
9035 }
9036
9037 destBucketName := bucketName
9038 destObjectName := objectName + "-dest"
9039 dstencryption := encrypt.DefaultPBKDF([]byte(password), []byte(destBucketName+destObjectName))
9040
9041 uploadID, err = c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{ServerSideEncryption: dstencryption})
9042 if err != nil {
9043 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
9044 return
9045 }
9046
9047 // Content of the destination object will be two copies of
9048 // `objectName` concatenated, followed by first byte of
9049 // `objectName`.
9050 metadata := make(map[string]string)
9051 header := make(http.Header)
9052 encrypt.SSECopy(srcencryption).Marshal(header)
9053 dstencryption.Marshal(header)
9054 for k, v := range header {
9055 metadata[k] = v[0]
9056 }
9057
9058 metadata["x-amz-copy-source-if-match"] = objInfo.ETag
9059
9060 // First of three parts
9061 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
9062 if err != nil {
9063 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9064 return
9065 }
9066
9067 // Second of three parts
9068 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
9069 if err != nil {
9070 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9071 return
9072 }
9073
9074 // Last of three parts
9075 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
9076 if err != nil {
9077 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9078 return
9079 }
9080
9081 // Complete the multipart upload
9082 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
9083 if err != nil {
9084 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
9085 return
9086 }
9087
9088 // Stat the object and check its length matches
9089 objInfo, err = c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{ServerSideEncryption: dstencryption})
9090 if err != nil {
9091 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9092 return
9093 }
9094
9095 if objInfo.Size != (6*1024*1024)*2+1 {
9096 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
9097 return
9098 }
9099
9100 // Now we read the data back
9101 getOpts := minio.GetObjectOptions{ServerSideEncryption: dstencryption}
9102 getOpts.SetRange(0, 6*1024*1024-1)
9103 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9104 if err != nil {
9105 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9106 return
9107 }
9108 getBuf := make([]byte, 6*1024*1024)
9109 _, err = readFull(r, getBuf)
9110 if err != nil {
9111 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9112 return
9113 }
9114 if !bytes.Equal(getBuf, buf) {
9115 logError(testName, function, args, startTime, "", "Got unexpected data in first 6MB", err)
9116 return
9117 }
9118
9119 getOpts.SetRange(6*1024*1024, 0)
9120 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9121 if err != nil {
9122 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9123 return
9124 }
9125 getBuf = make([]byte, 6*1024*1024+1)
9126 _, err = readFull(r, getBuf)
9127 if err != nil {
9128 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9129 return
9130 }
9131 if !bytes.Equal(getBuf[:6*1024*1024], buf) {
9132 logError(testName, function, args, startTime, "", "Got unexpected data in second 6MB", err)
9133 return
9134 }
9135 if getBuf[6*1024*1024] != buf[0] {
9136 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
9137 return
9138 }
9139
9140 successLogger(testName, function, args, startTime).Info()
9141
9142 // Do not need to remove destBucketName its same as bucketName.
9143}
9144
9145// Test Core CopyObjectPart implementation
9146func testSSECEncryptedToSSECCopyObjectPart() {
9147 // initialize logging params
9148 startTime := time.Now()
9149 testName := getFuncName()
9150 function := "CopyObjectPart(destination, source)"
9151 args := map[string]interface{}{}
9152
9153 // Instantiate new minio client object
9154 client, err := minio.New(os.Getenv(serverEndpoint),
9155 &minio.Options{
9156 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
9157 Secure: mustParseBool(os.Getenv(enableHTTPS)),
9158 })
9159 if err != nil {
9160 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
9161 return
9162 }
9163
9164 // Instantiate new core client object.
9165 c := minio.Core{client}
9166
9167 // Enable tracing, write to stderr.
9168 // c.TraceOn(os.Stderr)
9169
9170 // Set user agent.
9171 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
9172
9173 // Generate a new random bucket name.
9174 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
9175
9176 // Make a new bucket.
9177 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
9178 if err != nil {
9179 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
9180 return
9181 }
9182 defer cleanupBucket(bucketName, client)
9183 // Make a buffer with 5MB of data
9184 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
9185
9186 // Save the data
9187 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
9188 password := "correct horse battery staple"
9189 srcencryption := encrypt.DefaultPBKDF([]byte(password), []byte(bucketName+objectName))
9190 putmetadata := map[string]string{
9191 "Content-Type": "binary/octet-stream",
9192 }
9193 opts := minio.PutObjectOptions{
9194 UserMetadata: putmetadata,
9195 ServerSideEncryption: srcencryption,
9196 }
9197 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
9198 if err != nil {
9199 logError(testName, function, args, startTime, "", "PutObject call failed", err)
9200 return
9201 }
9202
9203 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{ServerSideEncryption: srcencryption})
9204 if err != nil {
9205 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9206 return
9207 }
9208
9209 if st.Size != int64(len(buf)) {
9210 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
9211 return
9212 }
9213
9214 destBucketName := bucketName
9215 destObjectName := objectName + "-dest"
9216 dstencryption := encrypt.DefaultPBKDF([]byte(password), []byte(destBucketName+destObjectName))
9217
9218 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{ServerSideEncryption: dstencryption})
9219 if err != nil {
9220 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
9221 return
9222 }
9223
9224 // Content of the destination object will be two copies of
9225 // `objectName` concatenated, followed by first byte of
9226 // `objectName`.
9227 metadata := make(map[string]string)
9228 header := make(http.Header)
9229 encrypt.SSECopy(srcencryption).Marshal(header)
9230 dstencryption.Marshal(header)
9231 for k, v := range header {
9232 metadata[k] = v[0]
9233 }
9234
9235 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
9236
9237 // First of three parts
9238 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
9239 if err != nil {
9240 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9241 return
9242 }
9243
9244 // Second of three parts
9245 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
9246 if err != nil {
9247 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9248 return
9249 }
9250
9251 // Last of three parts
9252 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
9253 if err != nil {
9254 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9255 return
9256 }
9257
9258 // Complete the multipart upload
9259 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
9260 if err != nil {
9261 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
9262 return
9263 }
9264
9265 // Stat the object and check its length matches
9266 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{ServerSideEncryption: dstencryption})
9267 if err != nil {
9268 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9269 return
9270 }
9271
9272 if objInfo.Size != (5*1024*1024)*2+1 {
9273 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
9274 return
9275 }
9276
9277 // Now we read the data back
9278 getOpts := minio.GetObjectOptions{ServerSideEncryption: dstencryption}
9279 getOpts.SetRange(0, 5*1024*1024-1)
9280 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9281 if err != nil {
9282 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9283 return
9284 }
9285 getBuf := make([]byte, 5*1024*1024)
9286 _, err = readFull(r, getBuf)
9287 if err != nil {
9288 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9289 return
9290 }
9291 if !bytes.Equal(getBuf, buf) {
9292 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
9293 return
9294 }
9295
9296 getOpts.SetRange(5*1024*1024, 0)
9297 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9298 if err != nil {
9299 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9300 return
9301 }
9302 getBuf = make([]byte, 5*1024*1024+1)
9303 _, err = readFull(r, getBuf)
9304 if err != nil {
9305 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9306 return
9307 }
9308 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
9309 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
9310 return
9311 }
9312 if getBuf[5*1024*1024] != buf[0] {
9313 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
9314 return
9315 }
9316
9317 successLogger(testName, function, args, startTime).Info()
9318
9319 // Do not need to remove destBucketName its same as bucketName.
9320}
9321
9322// Test Core CopyObjectPart implementation for SSEC encrypted to unencrypted copy
9323func testSSECEncryptedToUnencryptedCopyPart() {
9324 // initialize logging params
9325 startTime := time.Now()
9326 testName := getFuncName()
9327 function := "CopyObjectPart(destination, source)"
9328 args := map[string]interface{}{}
9329
9330 // Instantiate new minio client object
9331 client, err := minio.New(os.Getenv(serverEndpoint),
9332 &minio.Options{
9333 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
9334 Secure: mustParseBool(os.Getenv(enableHTTPS)),
9335 })
9336 if err != nil {
9337 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
9338 return
9339 }
9340
9341 // Instantiate new core client object.
9342 c := minio.Core{client}
9343
9344 // Enable tracing, write to stderr.
9345 // c.TraceOn(os.Stderr)
9346
9347 // Set user agent.
9348 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
9349
9350 // Generate a new random bucket name.
9351 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
9352
9353 // Make a new bucket.
9354 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
9355 if err != nil {
9356 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
9357 return
9358 }
9359 defer cleanupBucket(bucketName, client)
9360 // Make a buffer with 5MB of data
9361 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
9362
9363 // Save the data
9364 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
9365 password := "correct horse battery staple"
9366 srcencryption := encrypt.DefaultPBKDF([]byte(password), []byte(bucketName+objectName))
9367
9368 opts := minio.PutObjectOptions{
9369 UserMetadata: map[string]string{
9370 "Content-Type": "binary/octet-stream",
9371 },
9372 ServerSideEncryption: srcencryption,
9373 }
9374 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
9375 if err != nil {
9376 logError(testName, function, args, startTime, "", "PutObject call failed", err)
9377 return
9378 }
9379
9380 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{ServerSideEncryption: srcencryption})
9381 if err != nil {
9382 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9383 return
9384 }
9385
9386 if st.Size != int64(len(buf)) {
9387 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
9388 return
9389 }
9390
9391 destBucketName := bucketName
9392 destObjectName := objectName + "-dest"
9393 var dstencryption encrypt.ServerSide
9394
9395 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{ServerSideEncryption: dstencryption})
9396 if err != nil {
9397 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
9398 return
9399 }
9400
9401 // Content of the destination object will be two copies of
9402 // `objectName` concatenated, followed by first byte of
9403 // `objectName`.
9404 metadata := make(map[string]string)
9405 header := make(http.Header)
9406 encrypt.SSECopy(srcencryption).Marshal(header)
9407 for k, v := range header {
9408 metadata[k] = v[0]
9409 }
9410
9411 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
9412
9413 // First of three parts
9414 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
9415 if err != nil {
9416 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9417 return
9418 }
9419
9420 // Second of three parts
9421 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
9422 if err != nil {
9423 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9424 return
9425 }
9426
9427 // Last of three parts
9428 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
9429 if err != nil {
9430 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9431 return
9432 }
9433
9434 // Complete the multipart upload
9435 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
9436 if err != nil {
9437 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
9438 return
9439 }
9440
9441 // Stat the object and check its length matches
9442 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{})
9443 if err != nil {
9444 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9445 return
9446 }
9447
9448 if objInfo.Size != (5*1024*1024)*2+1 {
9449 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
9450 return
9451 }
9452
9453 // Now we read the data back
9454 getOpts := minio.GetObjectOptions{}
9455 getOpts.SetRange(0, 5*1024*1024-1)
9456 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9457 if err != nil {
9458 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9459 return
9460 }
9461 getBuf := make([]byte, 5*1024*1024)
9462 _, err = readFull(r, getBuf)
9463 if err != nil {
9464 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9465 return
9466 }
9467 if !bytes.Equal(getBuf, buf) {
9468 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
9469 return
9470 }
9471
9472 getOpts.SetRange(5*1024*1024, 0)
9473 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9474 if err != nil {
9475 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9476 return
9477 }
9478 getBuf = make([]byte, 5*1024*1024+1)
9479 _, err = readFull(r, getBuf)
9480 if err != nil {
9481 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9482 return
9483 }
9484 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
9485 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
9486 return
9487 }
9488 if getBuf[5*1024*1024] != buf[0] {
9489 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
9490 return
9491 }
9492
9493 successLogger(testName, function, args, startTime).Info()
9494
9495 // Do not need to remove destBucketName its same as bucketName.
9496}
9497
9498// Test Core CopyObjectPart implementation for SSEC encrypted to SSE-S3 encrypted copy
9499func testSSECEncryptedToSSES3CopyObjectPart() {
9500 // initialize logging params
9501 startTime := time.Now()
9502 testName := getFuncName()
9503 function := "CopyObjectPart(destination, source)"
9504 args := map[string]interface{}{}
9505
9506 // Instantiate new minio client object
9507 client, err := minio.New(os.Getenv(serverEndpoint),
9508 &minio.Options{
9509 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
9510 Secure: mustParseBool(os.Getenv(enableHTTPS)),
9511 })
9512 if err != nil {
9513 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
9514 return
9515 }
9516
9517 // Instantiate new core client object.
9518 c := minio.Core{client}
9519
9520 // Enable tracing, write to stderr.
9521 // c.TraceOn(os.Stderr)
9522
9523 // Set user agent.
9524 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
9525
9526 // Generate a new random bucket name.
9527 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
9528
9529 // Make a new bucket.
9530 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
9531 if err != nil {
9532 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
9533 return
9534 }
9535 defer cleanupBucket(bucketName, client)
9536 // Make a buffer with 5MB of data
9537 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
9538
9539 // Save the data
9540 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
9541 password := "correct horse battery staple"
9542 srcencryption := encrypt.DefaultPBKDF([]byte(password), []byte(bucketName+objectName))
9543 putmetadata := map[string]string{
9544 "Content-Type": "binary/octet-stream",
9545 }
9546 opts := minio.PutObjectOptions{
9547 UserMetadata: putmetadata,
9548 ServerSideEncryption: srcencryption,
9549 }
9550
9551 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
9552 if err != nil {
9553 logError(testName, function, args, startTime, "", "PutObject call failed", err)
9554 return
9555 }
9556
9557 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{ServerSideEncryption: srcencryption})
9558 if err != nil {
9559 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9560 return
9561 }
9562
9563 if st.Size != int64(len(buf)) {
9564 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
9565 return
9566 }
9567
9568 destBucketName := bucketName
9569 destObjectName := objectName + "-dest"
9570 dstencryption := encrypt.NewSSE()
9571
9572 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{ServerSideEncryption: dstencryption})
9573 if err != nil {
9574 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
9575 return
9576 }
9577
9578 // Content of the destination object will be two copies of
9579 // `objectName` concatenated, followed by first byte of
9580 // `objectName`.
9581 metadata := make(map[string]string)
9582 header := make(http.Header)
9583 encrypt.SSECopy(srcencryption).Marshal(header)
9584 dstencryption.Marshal(header)
9585
9586 for k, v := range header {
9587 metadata[k] = v[0]
9588 }
9589
9590 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
9591
9592 // First of three parts
9593 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
9594 if err != nil {
9595 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9596 return
9597 }
9598
9599 // Second of three parts
9600 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
9601 if err != nil {
9602 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9603 return
9604 }
9605
9606 // Last of three parts
9607 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
9608 if err != nil {
9609 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9610 return
9611 }
9612
9613 // Complete the multipart upload
9614 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
9615 if err != nil {
9616 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
9617 return
9618 }
9619
9620 // Stat the object and check its length matches
9621 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{})
9622 if err != nil {
9623 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9624 return
9625 }
9626
9627 if objInfo.Size != (5*1024*1024)*2+1 {
9628 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
9629 return
9630 }
9631
9632 // Now we read the data back
9633 getOpts := minio.GetObjectOptions{}
9634 getOpts.SetRange(0, 5*1024*1024-1)
9635 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9636 if err != nil {
9637 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9638 return
9639 }
9640 getBuf := make([]byte, 5*1024*1024)
9641 _, err = readFull(r, getBuf)
9642 if err != nil {
9643 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9644 return
9645 }
9646 if !bytes.Equal(getBuf, buf) {
9647 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
9648 return
9649 }
9650
9651 getOpts.SetRange(5*1024*1024, 0)
9652 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9653 if err != nil {
9654 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9655 return
9656 }
9657 getBuf = make([]byte, 5*1024*1024+1)
9658 _, err = readFull(r, getBuf)
9659 if err != nil {
9660 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9661 return
9662 }
9663 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
9664 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
9665 return
9666 }
9667 if getBuf[5*1024*1024] != buf[0] {
9668 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
9669 return
9670 }
9671
9672 successLogger(testName, function, args, startTime).Info()
9673
9674 // Do not need to remove destBucketName its same as bucketName.
9675}
9676
9677// Test Core CopyObjectPart implementation for unencrypted to SSEC encryption copy part
9678func testUnencryptedToSSECCopyObjectPart() {
9679 // initialize logging params
9680 startTime := time.Now()
9681 testName := getFuncName()
9682 function := "CopyObjectPart(destination, source)"
9683 args := map[string]interface{}{}
9684
9685 // Instantiate new minio client object
9686 client, err := minio.New(os.Getenv(serverEndpoint),
9687 &minio.Options{
9688 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
9689 Secure: mustParseBool(os.Getenv(enableHTTPS)),
9690 })
9691 if err != nil {
9692 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
9693 return
9694 }
9695
9696 // Instantiate new core client object.
9697 c := minio.Core{client}
9698
9699 // Enable tracing, write to stderr.
9700 // c.TraceOn(os.Stderr)
9701
9702 // Set user agent.
9703 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
9704
9705 // Generate a new random bucket name.
9706 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
9707
9708 // Make a new bucket.
9709 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
9710 if err != nil {
9711 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
9712 return
9713 }
9714 defer cleanupBucket(bucketName, client)
9715 // Make a buffer with 5MB of data
9716 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
9717
9718 // Save the data
9719 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
9720 password := "correct horse battery staple"
9721 putmetadata := map[string]string{
9722 "Content-Type": "binary/octet-stream",
9723 }
9724 opts := minio.PutObjectOptions{
9725 UserMetadata: putmetadata,
9726 }
9727 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
9728 if err != nil {
9729 logError(testName, function, args, startTime, "", "PutObject call failed", err)
9730 return
9731 }
9732
9733 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
9734 if err != nil {
9735 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9736 return
9737 }
9738
9739 if st.Size != int64(len(buf)) {
9740 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
9741 return
9742 }
9743
9744 destBucketName := bucketName
9745 destObjectName := objectName + "-dest"
9746 dstencryption := encrypt.DefaultPBKDF([]byte(password), []byte(destBucketName+destObjectName))
9747
9748 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{ServerSideEncryption: dstencryption})
9749 if err != nil {
9750 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
9751 return
9752 }
9753
9754 // Content of the destination object will be two copies of
9755 // `objectName` concatenated, followed by first byte of
9756 // `objectName`.
9757 metadata := make(map[string]string)
9758 header := make(http.Header)
9759 dstencryption.Marshal(header)
9760 for k, v := range header {
9761 metadata[k] = v[0]
9762 }
9763
9764 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
9765
9766 // First of three parts
9767 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
9768 if err != nil {
9769 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9770 return
9771 }
9772
9773 // Second of three parts
9774 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
9775 if err != nil {
9776 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9777 return
9778 }
9779
9780 // Last of three parts
9781 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
9782 if err != nil {
9783 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9784 return
9785 }
9786
9787 // Complete the multipart upload
9788 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
9789 if err != nil {
9790 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
9791 return
9792 }
9793
9794 // Stat the object and check its length matches
9795 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{ServerSideEncryption: dstencryption})
9796 if err != nil {
9797 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9798 return
9799 }
9800
9801 if objInfo.Size != (5*1024*1024)*2+1 {
9802 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
9803 return
9804 }
9805
9806 // Now we read the data back
9807 getOpts := minio.GetObjectOptions{ServerSideEncryption: dstencryption}
9808 getOpts.SetRange(0, 5*1024*1024-1)
9809 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9810 if err != nil {
9811 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9812 return
9813 }
9814 getBuf := make([]byte, 5*1024*1024)
9815 _, err = readFull(r, getBuf)
9816 if err != nil {
9817 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9818 return
9819 }
9820 if !bytes.Equal(getBuf, buf) {
9821 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
9822 return
9823 }
9824
9825 getOpts.SetRange(5*1024*1024, 0)
9826 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9827 if err != nil {
9828 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9829 return
9830 }
9831 getBuf = make([]byte, 5*1024*1024+1)
9832 _, err = readFull(r, getBuf)
9833 if err != nil {
9834 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9835 return
9836 }
9837 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
9838 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
9839 return
9840 }
9841 if getBuf[5*1024*1024] != buf[0] {
9842 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
9843 return
9844 }
9845
9846 successLogger(testName, function, args, startTime).Info()
9847
9848 // Do not need to remove destBucketName its same as bucketName.
9849}
9850
9851// Test Core CopyObjectPart implementation for unencrypted to unencrypted copy
9852func testUnencryptedToUnencryptedCopyPart() {
9853 // initialize logging params
9854 startTime := time.Now()
9855 testName := getFuncName()
9856 function := "CopyObjectPart(destination, source)"
9857 args := map[string]interface{}{}
9858
9859 // Instantiate new minio client object
9860 client, err := minio.New(os.Getenv(serverEndpoint),
9861 &minio.Options{
9862 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
9863 Secure: mustParseBool(os.Getenv(enableHTTPS)),
9864 })
9865 if err != nil {
9866 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
9867 return
9868 }
9869
9870 // Instantiate new core client object.
9871 c := minio.Core{client}
9872
9873 // Enable tracing, write to stderr.
9874 // c.TraceOn(os.Stderr)
9875
9876 // Set user agent.
9877 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
9878
9879 // Generate a new random bucket name.
9880 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
9881
9882 // Make a new bucket.
9883 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
9884 if err != nil {
9885 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
9886 return
9887 }
9888 defer cleanupBucket(bucketName, client)
9889 // Make a buffer with 5MB of data
9890 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
9891
9892 // Save the data
9893 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
9894 putmetadata := map[string]string{
9895 "Content-Type": "binary/octet-stream",
9896 }
9897 opts := minio.PutObjectOptions{
9898 UserMetadata: putmetadata,
9899 }
9900 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
9901 if err != nil {
9902 logError(testName, function, args, startTime, "", "PutObject call failed", err)
9903 return
9904 }
9905 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
9906 if err != nil {
9907 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9908 return
9909 }
9910
9911 if st.Size != int64(len(buf)) {
9912 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
9913 return
9914 }
9915
9916 destBucketName := bucketName
9917 destObjectName := objectName + "-dest"
9918
9919 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{})
9920 if err != nil {
9921 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
9922 return
9923 }
9924
9925 // Content of the destination object will be two copies of
9926 // `objectName` concatenated, followed by first byte of
9927 // `objectName`.
9928 metadata := make(map[string]string)
9929 header := make(http.Header)
9930 for k, v := range header {
9931 metadata[k] = v[0]
9932 }
9933
9934 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
9935
9936 // First of three parts
9937 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
9938 if err != nil {
9939 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9940 return
9941 }
9942
9943 // Second of three parts
9944 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
9945 if err != nil {
9946 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9947 return
9948 }
9949
9950 // Last of three parts
9951 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
9952 if err != nil {
9953 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
9954 return
9955 }
9956
9957 // Complete the multipart upload
9958 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
9959 if err != nil {
9960 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
9961 return
9962 }
9963
9964 // Stat the object and check its length matches
9965 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{})
9966 if err != nil {
9967 logError(testName, function, args, startTime, "", "StatObject call failed", err)
9968 return
9969 }
9970
9971 if objInfo.Size != (5*1024*1024)*2+1 {
9972 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
9973 return
9974 }
9975
9976 // Now we read the data back
9977 getOpts := minio.GetObjectOptions{}
9978 getOpts.SetRange(0, 5*1024*1024-1)
9979 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9980 if err != nil {
9981 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9982 return
9983 }
9984 getBuf := make([]byte, 5*1024*1024)
9985 _, err = readFull(r, getBuf)
9986 if err != nil {
9987 logError(testName, function, args, startTime, "", "Read buffer failed", err)
9988 return
9989 }
9990 if !bytes.Equal(getBuf, buf) {
9991 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
9992 return
9993 }
9994
9995 getOpts.SetRange(5*1024*1024, 0)
9996 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
9997 if err != nil {
9998 logError(testName, function, args, startTime, "", "GetObject call failed", err)
9999 return
10000 }
10001 getBuf = make([]byte, 5*1024*1024+1)
10002 _, err = readFull(r, getBuf)
10003 if err != nil {
10004 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10005 return
10006 }
10007 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
10008 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
10009 return
10010 }
10011 if getBuf[5*1024*1024] != buf[0] {
10012 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
10013 return
10014 }
10015
10016 successLogger(testName, function, args, startTime).Info()
10017
10018 // Do not need to remove destBucketName its same as bucketName.
10019}
10020
10021// Test Core CopyObjectPart implementation for unencrypted to SSE-S3 encrypted copy
10022func testUnencryptedToSSES3CopyObjectPart() {
10023 // initialize logging params
10024 startTime := time.Now()
10025 testName := getFuncName()
10026 function := "CopyObjectPart(destination, source)"
10027 args := map[string]interface{}{}
10028
10029 // Instantiate new minio client object
10030 client, err := minio.New(os.Getenv(serverEndpoint),
10031 &minio.Options{
10032 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
10033 Secure: mustParseBool(os.Getenv(enableHTTPS)),
10034 })
10035 if err != nil {
10036 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
10037 return
10038 }
10039
10040 // Instantiate new core client object.
10041 c := minio.Core{client}
10042
10043 // Enable tracing, write to stderr.
10044 // c.TraceOn(os.Stderr)
10045
10046 // Set user agent.
10047 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
10048
10049 // Generate a new random bucket name.
10050 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
10051
10052 // Make a new bucket.
10053 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
10054 if err != nil {
10055 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
10056 return
10057 }
10058 defer cleanupBucket(bucketName, client)
10059 // Make a buffer with 5MB of data
10060 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
10061
10062 // Save the data
10063 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
10064 opts := minio.PutObjectOptions{
10065 UserMetadata: map[string]string{
10066 "Content-Type": "binary/octet-stream",
10067 },
10068 }
10069 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
10070 if err != nil {
10071 logError(testName, function, args, startTime, "", "PutObject call failed", err)
10072 return
10073 }
10074 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
10075 if err != nil {
10076 logError(testName, function, args, startTime, "", "StatObject call failed", err)
10077 return
10078 }
10079
10080 if st.Size != int64(len(buf)) {
10081 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
10082 return
10083 }
10084
10085 destBucketName := bucketName
10086 destObjectName := objectName + "-dest"
10087 dstencryption := encrypt.NewSSE()
10088
10089 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{ServerSideEncryption: dstencryption})
10090 if err != nil {
10091 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
10092 return
10093 }
10094
10095 // Content of the destination object will be two copies of
10096 // `objectName` concatenated, followed by first byte of
10097 // `objectName`.
10098 metadata := make(map[string]string)
10099 header := make(http.Header)
10100 dstencryption.Marshal(header)
10101
10102 for k, v := range header {
10103 metadata[k] = v[0]
10104 }
10105
10106 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
10107
10108 // First of three parts
10109 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
10110 if err != nil {
10111 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10112 return
10113 }
10114
10115 // Second of three parts
10116 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
10117 if err != nil {
10118 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10119 return
10120 }
10121
10122 // Last of three parts
10123 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
10124 if err != nil {
10125 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10126 return
10127 }
10128
10129 // Complete the multipart upload
10130 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
10131 if err != nil {
10132 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
10133 return
10134 }
10135
10136 // Stat the object and check its length matches
10137 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{})
10138 if err != nil {
10139 logError(testName, function, args, startTime, "", "StatObject call failed", err)
10140 return
10141 }
10142
10143 if objInfo.Size != (5*1024*1024)*2+1 {
10144 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
10145 return
10146 }
10147
10148 // Now we read the data back
10149 getOpts := minio.GetObjectOptions{}
10150 getOpts.SetRange(0, 5*1024*1024-1)
10151 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
10152 if err != nil {
10153 logError(testName, function, args, startTime, "", "GetObject call failed", err)
10154 return
10155 }
10156 getBuf := make([]byte, 5*1024*1024)
10157 _, err = readFull(r, getBuf)
10158 if err != nil {
10159 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10160 return
10161 }
10162 if !bytes.Equal(getBuf, buf) {
10163 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
10164 return
10165 }
10166
10167 getOpts.SetRange(5*1024*1024, 0)
10168 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
10169 if err != nil {
10170 logError(testName, function, args, startTime, "", "GetObject call failed", err)
10171 return
10172 }
10173 getBuf = make([]byte, 5*1024*1024+1)
10174 _, err = readFull(r, getBuf)
10175 if err != nil {
10176 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10177 return
10178 }
10179 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
10180 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
10181 return
10182 }
10183 if getBuf[5*1024*1024] != buf[0] {
10184 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
10185 return
10186 }
10187
10188 successLogger(testName, function, args, startTime).Info()
10189
10190 // Do not need to remove destBucketName its same as bucketName.
10191}
10192
10193// Test Core CopyObjectPart implementation for SSE-S3 to SSEC encryption copy part
10194func testSSES3EncryptedToSSECCopyObjectPart() {
10195 // initialize logging params
10196 startTime := time.Now()
10197 testName := getFuncName()
10198 function := "CopyObjectPart(destination, source)"
10199 args := map[string]interface{}{}
10200
10201 // Instantiate new minio client object
10202 client, err := minio.New(os.Getenv(serverEndpoint),
10203 &minio.Options{
10204 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
10205 Secure: mustParseBool(os.Getenv(enableHTTPS)),
10206 })
10207 if err != nil {
10208 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
10209 return
10210 }
10211
10212 // Instantiate new core client object.
10213 c := minio.Core{client}
10214
10215 // Enable tracing, write to stderr.
10216 // c.TraceOn(os.Stderr)
10217
10218 // Set user agent.
10219 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
10220
10221 // Generate a new random bucket name.
10222 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
10223
10224 // Make a new bucket.
10225 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
10226 if err != nil {
10227 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
10228 return
10229 }
10230 defer cleanupBucket(bucketName, client)
10231 // Make a buffer with 5MB of data
10232 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
10233
10234 // Save the data
10235 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
10236 password := "correct horse battery staple"
10237 srcEncryption := encrypt.NewSSE()
10238 opts := minio.PutObjectOptions{
10239 UserMetadata: map[string]string{
10240 "Content-Type": "binary/octet-stream",
10241 },
10242 ServerSideEncryption: srcEncryption,
10243 }
10244 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
10245 if err != nil {
10246 logError(testName, function, args, startTime, "", "PutObject call failed", err)
10247 return
10248 }
10249
10250 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{ServerSideEncryption: srcEncryption})
10251 if err != nil {
10252 logError(testName, function, args, startTime, "", "StatObject call failed", err)
10253 return
10254 }
10255
10256 if st.Size != int64(len(buf)) {
10257 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
10258 return
10259 }
10260
10261 destBucketName := bucketName
10262 destObjectName := objectName + "-dest"
10263 dstencryption := encrypt.DefaultPBKDF([]byte(password), []byte(destBucketName+destObjectName))
10264
10265 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{ServerSideEncryption: dstencryption})
10266 if err != nil {
10267 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
10268 return
10269 }
10270
10271 // Content of the destination object will be two copies of
10272 // `objectName` concatenated, followed by first byte of
10273 // `objectName`.
10274 metadata := make(map[string]string)
10275 header := make(http.Header)
10276 dstencryption.Marshal(header)
10277 for k, v := range header {
10278 metadata[k] = v[0]
10279 }
10280
10281 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
10282
10283 // First of three parts
10284 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
10285 if err != nil {
10286 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10287 return
10288 }
10289
10290 // Second of three parts
10291 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
10292 if err != nil {
10293 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10294 return
10295 }
10296
10297 // Last of three parts
10298 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
10299 if err != nil {
10300 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10301 return
10302 }
10303
10304 // Complete the multipart upload
10305 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
10306 if err != nil {
10307 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
10308 return
10309 }
10310
10311 // Stat the object and check its length matches
10312 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{ServerSideEncryption: dstencryption})
10313 if err != nil {
10314 logError(testName, function, args, startTime, "", "StatObject call failed", err)
10315 return
10316 }
10317
10318 if objInfo.Size != (5*1024*1024)*2+1 {
10319 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
10320 return
10321 }
10322
10323 // Now we read the data back
10324 getOpts := minio.GetObjectOptions{ServerSideEncryption: dstencryption}
10325 getOpts.SetRange(0, 5*1024*1024-1)
10326 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
10327 if err != nil {
10328 logError(testName, function, args, startTime, "", "GetObject call failed", err)
10329 return
10330 }
10331 getBuf := make([]byte, 5*1024*1024)
10332 _, err = readFull(r, getBuf)
10333 if err != nil {
10334 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10335 return
10336 }
10337 if !bytes.Equal(getBuf, buf) {
10338 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
10339 return
10340 }
10341
10342 getOpts.SetRange(5*1024*1024, 0)
10343 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
10344 if err != nil {
10345 logError(testName, function, args, startTime, "", "GetObject call failed", err)
10346 return
10347 }
10348 getBuf = make([]byte, 5*1024*1024+1)
10349 _, err = readFull(r, getBuf)
10350 if err != nil {
10351 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10352 return
10353 }
10354 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
10355 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
10356 return
10357 }
10358 if getBuf[5*1024*1024] != buf[0] {
10359 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
10360 return
10361 }
10362
10363 successLogger(testName, function, args, startTime).Info()
10364
10365 // Do not need to remove destBucketName its same as bucketName.
10366}
10367
10368// Test Core CopyObjectPart implementation for unencrypted to unencrypted copy
10369func testSSES3EncryptedToUnencryptedCopyPart() {
10370 // initialize logging params
10371 startTime := time.Now()
10372 testName := getFuncName()
10373 function := "CopyObjectPart(destination, source)"
10374 args := map[string]interface{}{}
10375
10376 // Instantiate new minio client object
10377 client, err := minio.New(os.Getenv(serverEndpoint),
10378 &minio.Options{
10379 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
10380 Secure: mustParseBool(os.Getenv(enableHTTPS)),
10381 })
10382 if err != nil {
10383 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
10384 return
10385 }
10386
10387 // Instantiate new core client object.
10388 c := minio.Core{client}
10389
10390 // Enable tracing, write to stderr.
10391 // c.TraceOn(os.Stderr)
10392
10393 // Set user agent.
10394 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
10395
10396 // Generate a new random bucket name.
10397 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
10398
10399 // Make a new bucket.
10400 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
10401 if err != nil {
10402 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
10403 return
10404 }
10405 defer cleanupBucket(bucketName, client)
10406 // Make a buffer with 5MB of data
10407 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
10408
10409 // Save the data
10410 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
10411 srcEncryption := encrypt.NewSSE()
10412 opts := minio.PutObjectOptions{
10413 UserMetadata: map[string]string{
10414 "Content-Type": "binary/octet-stream",
10415 },
10416 ServerSideEncryption: srcEncryption,
10417 }
10418 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
10419 if err != nil {
10420 logError(testName, function, args, startTime, "", "PutObject call failed", err)
10421 return
10422 }
10423 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{ServerSideEncryption: srcEncryption})
10424 if err != nil {
10425 logError(testName, function, args, startTime, "", "StatObject call failed", err)
10426 return
10427 }
10428
10429 if st.Size != int64(len(buf)) {
10430 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
10431 return
10432 }
10433
10434 destBucketName := bucketName
10435 destObjectName := objectName + "-dest"
10436
10437 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{})
10438 if err != nil {
10439 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
10440 return
10441 }
10442
10443 // Content of the destination object will be two copies of
10444 // `objectName` concatenated, followed by first byte of
10445 // `objectName`.
10446 metadata := make(map[string]string)
10447 header := make(http.Header)
10448 for k, v := range header {
10449 metadata[k] = v[0]
10450 }
10451
10452 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
10453
10454 // First of three parts
10455 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
10456 if err != nil {
10457 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10458 return
10459 }
10460
10461 // Second of three parts
10462 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
10463 if err != nil {
10464 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10465 return
10466 }
10467
10468 // Last of three parts
10469 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
10470 if err != nil {
10471 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10472 return
10473 }
10474
10475 // Complete the multipart upload
10476 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
10477 if err != nil {
10478 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
10479 return
10480 }
10481
10482 // Stat the object and check its length matches
10483 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{})
10484 if err != nil {
10485 logError(testName, function, args, startTime, "", "StatObject call failed", err)
10486 return
10487 }
10488
10489 if objInfo.Size != (5*1024*1024)*2+1 {
10490 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
10491 return
10492 }
10493
10494 // Now we read the data back
10495 getOpts := minio.GetObjectOptions{}
10496 getOpts.SetRange(0, 5*1024*1024-1)
10497 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
10498 if err != nil {
10499 logError(testName, function, args, startTime, "", "GetObject call failed", err)
10500 return
10501 }
10502 getBuf := make([]byte, 5*1024*1024)
10503 _, err = readFull(r, getBuf)
10504 if err != nil {
10505 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10506 return
10507 }
10508 if !bytes.Equal(getBuf, buf) {
10509 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
10510 return
10511 }
10512
10513 getOpts.SetRange(5*1024*1024, 0)
10514 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
10515 if err != nil {
10516 logError(testName, function, args, startTime, "", "GetObject call failed", err)
10517 return
10518 }
10519 getBuf = make([]byte, 5*1024*1024+1)
10520 _, err = readFull(r, getBuf)
10521 if err != nil {
10522 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10523 return
10524 }
10525 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
10526 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
10527 return
10528 }
10529 if getBuf[5*1024*1024] != buf[0] {
10530 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
10531 return
10532 }
10533
10534 successLogger(testName, function, args, startTime).Info()
10535
10536 // Do not need to remove destBucketName its same as bucketName.
10537}
10538
10539// Test Core CopyObjectPart implementation for unencrypted to SSE-S3 encrypted copy
10540func testSSES3EncryptedToSSES3CopyObjectPart() {
10541 // initialize logging params
10542 startTime := time.Now()
10543 testName := getFuncName()
10544 function := "CopyObjectPart(destination, source)"
10545 args := map[string]interface{}{}
10546
10547 // Instantiate new minio client object
10548 client, err := minio.New(os.Getenv(serverEndpoint),
10549 &minio.Options{
10550 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
10551 Secure: mustParseBool(os.Getenv(enableHTTPS)),
10552 })
10553 if err != nil {
10554 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
10555 return
10556 }
10557
10558 // Instantiate new core client object.
10559 c := minio.Core{client}
10560
10561 // Enable tracing, write to stderr.
10562 // c.TraceOn(os.Stderr)
10563
10564 // Set user agent.
10565 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
10566
10567 // Generate a new random bucket name.
10568 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
10569
10570 // Make a new bucket.
10571 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
10572 if err != nil {
10573 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
10574 return
10575 }
10576 defer cleanupBucket(bucketName, client)
10577 // Make a buffer with 5MB of data
10578 buf := bytes.Repeat([]byte("abcde"), 1024*1024)
10579
10580 // Save the data
10581 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
10582 srcEncryption := encrypt.NewSSE()
10583 opts := minio.PutObjectOptions{
10584 UserMetadata: map[string]string{
10585 "Content-Type": "binary/octet-stream",
10586 },
10587 ServerSideEncryption: srcEncryption,
10588 }
10589
10590 uploadInfo, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), "", "", opts)
10591 if err != nil {
10592 logError(testName, function, args, startTime, "", "PutObject call failed", err)
10593 return
10594 }
10595 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{ServerSideEncryption: srcEncryption})
10596 if err != nil {
10597 logError(testName, function, args, startTime, "", "StatObject call failed", err)
10598 return
10599 }
10600 if st.Size != int64(len(buf)) {
10601 logError(testName, function, args, startTime, "", fmt.Sprintf("Error: number of bytes does not match, want %v, got %v\n", len(buf), st.Size), err)
10602 return
10603 }
10604
10605 destBucketName := bucketName
10606 destObjectName := objectName + "-dest"
10607 dstencryption := encrypt.NewSSE()
10608
10609 uploadID, err := c.NewMultipartUpload(context.Background(), destBucketName, destObjectName, minio.PutObjectOptions{ServerSideEncryption: dstencryption})
10610 if err != nil {
10611 logError(testName, function, args, startTime, "", "NewMultipartUpload call failed", err)
10612 return
10613 }
10614
10615 // Content of the destination object will be two copies of
10616 // `objectName` concatenated, followed by first byte of
10617 // `objectName`.
10618 metadata := make(map[string]string)
10619 header := make(http.Header)
10620 dstencryption.Marshal(header)
10621
10622 for k, v := range header {
10623 metadata[k] = v[0]
10624 }
10625
10626 metadata["x-amz-copy-source-if-match"] = uploadInfo.ETag
10627
10628 // First of three parts
10629 fstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 1, 0, -1, metadata)
10630 if err != nil {
10631 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10632 return
10633 }
10634
10635 // Second of three parts
10636 sndPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 2, 0, -1, metadata)
10637 if err != nil {
10638 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10639 return
10640 }
10641
10642 // Last of three parts
10643 lstPart, err := c.CopyObjectPart(context.Background(), bucketName, objectName, destBucketName, destObjectName, uploadID, 3, 0, 1, metadata)
10644 if err != nil {
10645 logError(testName, function, args, startTime, "", "CopyObjectPart call failed", err)
10646 return
10647 }
10648
10649 // Complete the multipart upload
10650 _, err = c.CompleteMultipartUpload(context.Background(), destBucketName, destObjectName, uploadID, []minio.CompletePart{fstPart, sndPart, lstPart}, minio.PutObjectOptions{})
10651 if err != nil {
10652 logError(testName, function, args, startTime, "", "CompleteMultipartUpload call failed", err)
10653 return
10654 }
10655
10656 // Stat the object and check its length matches
10657 objInfo, err := c.StatObject(context.Background(), destBucketName, destObjectName, minio.StatObjectOptions{})
10658 if err != nil {
10659 logError(testName, function, args, startTime, "", "StatObject call failed", err)
10660 return
10661 }
10662
10663 if objInfo.Size != (5*1024*1024)*2+1 {
10664 logError(testName, function, args, startTime, "", "Destination object has incorrect size!", err)
10665 return
10666 }
10667
10668 // Now we read the data back
10669 getOpts := minio.GetObjectOptions{}
10670 getOpts.SetRange(0, 5*1024*1024-1)
10671 r, _, _, err := c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
10672 if err != nil {
10673 logError(testName, function, args, startTime, "", "GetObject call failed", err)
10674 return
10675 }
10676 getBuf := make([]byte, 5*1024*1024)
10677 _, err = readFull(r, getBuf)
10678 if err != nil {
10679 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10680 return
10681 }
10682 if !bytes.Equal(getBuf, buf) {
10683 logError(testName, function, args, startTime, "", "Got unexpected data in first 5MB", err)
10684 return
10685 }
10686
10687 getOpts.SetRange(5*1024*1024, 0)
10688 r, _, _, err = c.GetObject(context.Background(), destBucketName, destObjectName, getOpts)
10689 if err != nil {
10690 logError(testName, function, args, startTime, "", "GetObject call failed", err)
10691 return
10692 }
10693 getBuf = make([]byte, 5*1024*1024+1)
10694 _, err = readFull(r, getBuf)
10695 if err != nil {
10696 logError(testName, function, args, startTime, "", "Read buffer failed", err)
10697 return
10698 }
10699 if !bytes.Equal(getBuf[:5*1024*1024], buf) {
10700 logError(testName, function, args, startTime, "", "Got unexpected data in second 5MB", err)
10701 return
10702 }
10703 if getBuf[5*1024*1024] != buf[0] {
10704 logError(testName, function, args, startTime, "", "Got unexpected data in last byte of copied object!", err)
10705 return
10706 }
10707
10708 successLogger(testName, function, args, startTime).Info()
10709
10710 // Do not need to remove destBucketName its same as bucketName.
10711}
10712
10713func testUserMetadataCopying() {
10714 // initialize logging params
10715 startTime := time.Now()
10716 testName := getFuncName()
10717 function := "CopyObject(destination, source)"
10718 args := map[string]interface{}{}
10719
10720 // Instantiate new minio client object
10721 c, err := minio.New(os.Getenv(serverEndpoint),
10722 &minio.Options{
10723 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
10724 Secure: mustParseBool(os.Getenv(enableHTTPS)),
10725 })
10726 if err != nil {
10727 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
10728 return
10729 }
10730
10731 // c.TraceOn(os.Stderr)
10732 testUserMetadataCopyingWrapper(c)
10733}
10734
10735func testUserMetadataCopyingWrapper(c *minio.Client) {
10736 // initialize logging params
10737 startTime := time.Now()
10738 testName := getFuncName()
10739 function := "CopyObject(destination, source)"
10740 args := map[string]interface{}{}
10741
10742 // Generate a new random bucket name.
10743 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
10744 // Make a new bucket in 'us-east-1' (source bucket).
10745 err := c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
10746 if err != nil {
10747 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
10748 return
10749 }
10750
10751 defer cleanupBucket(bucketName, c)
10752
10753 fetchMeta := func(object string) (h http.Header) {
10754 objInfo, err := c.StatObject(context.Background(), bucketName, object, minio.StatObjectOptions{})
10755 if err != nil {
10756 logError(testName, function, args, startTime, "", "Stat failed", err)
10757 return
10758 }
10759 h = make(http.Header)
10760 for k, vs := range objInfo.Metadata {
10761 if strings.HasPrefix(strings.ToLower(k), "x-amz-meta-") {
10762 h.Add(k, vs[0])
10763 }
10764 }
10765 return h
10766 }
10767
10768 // 1. create a client encrypted object to copy by uploading
10769 const srcSize = 1024 * 1024
10770 buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 5MiB
10771 metadata := make(http.Header)
10772 metadata.Set("x-amz-meta-myheader", "myvalue")
10773 m := make(map[string]string)
10774 m["x-amz-meta-myheader"] = "myvalue"
10775 _, err = c.PutObject(context.Background(), bucketName, "srcObject",
10776 bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{UserMetadata: m})
10777 if err != nil {
10778 logError(testName, function, args, startTime, "", "PutObjectWithMetadata failed", err)
10779 return
10780 }
10781 if !reflect.DeepEqual(metadata, fetchMeta("srcObject")) {
10782 logError(testName, function, args, startTime, "", "Metadata match failed", err)
10783 return
10784 }
10785
10786 // 2. create source
10787 src := minio.CopySrcOptions{
10788 Bucket: bucketName,
10789 Object: "srcObject",
10790 }
10791
10792 // 2.1 create destination with metadata set
10793 dst1 := minio.CopyDestOptions{
10794 Bucket: bucketName,
10795 Object: "dstObject-1",
10796 UserMetadata: map[string]string{"notmyheader": "notmyvalue"},
10797 ReplaceMetadata: true,
10798 }
10799
10800 // 3. Check that copying to an object with metadata set resets
10801 // the headers on the copy.
10802 args["source"] = src
10803 args["destination"] = dst1
10804 _, err = c.CopyObject(context.Background(), dst1, src)
10805 if err != nil {
10806 logError(testName, function, args, startTime, "", "CopyObject failed", err)
10807 return
10808 }
10809
10810 expectedHeaders := make(http.Header)
10811 expectedHeaders.Set("x-amz-meta-notmyheader", "notmyvalue")
10812 if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-1")) {
10813 logError(testName, function, args, startTime, "", "Metadata match failed", err)
10814 return
10815 }
10816
10817 // 4. create destination with no metadata set and same source
10818 dst2 := minio.CopyDestOptions{
10819 Bucket: bucketName,
10820 Object: "dstObject-2",
10821 }
10822
10823 // 5. Check that copying to an object with no metadata set,
10824 // copies metadata.
10825 args["source"] = src
10826 args["destination"] = dst2
10827 _, err = c.CopyObject(context.Background(), dst2, src)
10828 if err != nil {
10829 logError(testName, function, args, startTime, "", "CopyObject failed", err)
10830 return
10831 }
10832
10833 expectedHeaders = metadata
10834 if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-2")) {
10835 logError(testName, function, args, startTime, "", "Metadata match failed", err)
10836 return
10837 }
10838
10839 // 6. Compose a pair of sources.
10840 dst3 := minio.CopyDestOptions{
10841 Bucket: bucketName,
10842 Object: "dstObject-3",
10843 ReplaceMetadata: true,
10844 }
10845
10846 function = "ComposeObject(destination, sources)"
10847 args["source"] = []minio.CopySrcOptions{src, src}
10848 args["destination"] = dst3
10849 _, err = c.ComposeObject(context.Background(), dst3, src, src)
10850 if err != nil {
10851 logError(testName, function, args, startTime, "", "ComposeObject failed", err)
10852 return
10853 }
10854
10855 // Check that no headers are copied in this case
10856 if !reflect.DeepEqual(make(http.Header), fetchMeta("dstObject-3")) {
10857 logError(testName, function, args, startTime, "", "Metadata match failed", err)
10858 return
10859 }
10860
10861 // 7. Compose a pair of sources with dest user metadata set.
10862 dst4 := minio.CopyDestOptions{
10863 Bucket: bucketName,
10864 Object: "dstObject-4",
10865 UserMetadata: map[string]string{"notmyheader": "notmyvalue"},
10866 ReplaceMetadata: true,
10867 }
10868
10869 function = "ComposeObject(destination, sources)"
10870 args["source"] = []minio.CopySrcOptions{src, src}
10871 args["destination"] = dst4
10872 _, err = c.ComposeObject(context.Background(), dst4, src, src)
10873 if err != nil {
10874 logError(testName, function, args, startTime, "", "ComposeObject failed", err)
10875 return
10876 }
10877
10878 // Check that no headers are copied in this case
10879 expectedHeaders = make(http.Header)
10880 expectedHeaders.Set("x-amz-meta-notmyheader", "notmyvalue")
10881 if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-4")) {
10882 logError(testName, function, args, startTime, "", "Metadata match failed", err)
10883 return
10884 }
10885
10886 successLogger(testName, function, args, startTime).Info()
10887}
10888
10889func testUserMetadataCopyingV2() {
10890 // initialize logging params
10891 startTime := time.Now()
10892 testName := getFuncName()
10893 function := "CopyObject(destination, source)"
10894 args := map[string]interface{}{}
10895
10896 // Instantiate new minio client object
10897 c, err := minio.New(os.Getenv(serverEndpoint),
10898 &minio.Options{
10899 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
10900 Secure: mustParseBool(os.Getenv(enableHTTPS)),
10901 })
10902 if err != nil {
10903 logError(testName, function, args, startTime, "", "MinIO client v2 object creation failed", err)
10904 return
10905 }
10906
10907 // c.TraceOn(os.Stderr)
10908 testUserMetadataCopyingWrapper(c)
10909}
10910
10911func testStorageClassMetadataPutObject() {
10912 // initialize logging params
10913 startTime := time.Now()
10914 function := "testStorageClassMetadataPutObject()"
10915 args := map[string]interface{}{}
10916 testName := getFuncName()
10917
10918 // Instantiate new minio client object
10919 c, err := minio.New(os.Getenv(serverEndpoint),
10920 &minio.Options{
10921 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
10922 Secure: mustParseBool(os.Getenv(enableHTTPS)),
10923 })
10924 if err != nil {
10925 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
10926 return
10927 }
10928
10929 // Generate a new random bucket name.
10930 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
10931 // Make a new bucket in 'us-east-1' (source bucket).
10932 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
10933 if err != nil {
10934 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
10935 return
10936 }
10937
10938 defer cleanupBucket(bucketName, c)
10939
10940 fetchMeta := func(object string) (h http.Header) {
10941 objInfo, err := c.StatObject(context.Background(), bucketName, object, minio.StatObjectOptions{})
10942 if err != nil {
10943 logError(testName, function, args, startTime, "", "Stat failed", err)
10944 return
10945 }
10946 h = make(http.Header)
10947 for k, vs := range objInfo.Metadata {
10948 if strings.HasPrefix(strings.ToLower(k), "x-amz-storage-class") {
10949 for _, v := range vs {
10950 h.Add(k, v)
10951 }
10952 }
10953 }
10954 return h
10955 }
10956
10957 metadata := make(http.Header)
10958 metadata.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
10959
10960 emptyMetadata := make(http.Header)
10961
10962 const srcSize = 1024 * 1024
10963 buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 1MiB
10964
10965 _, err = c.PutObject(context.Background(), bucketName, "srcObjectRRSClass",
10966 bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{StorageClass: "REDUCED_REDUNDANCY"})
10967 if err != nil {
10968 logError(testName, function, args, startTime, "", "PutObject failed", err)
10969 return
10970 }
10971
10972 // Get the returned metadata
10973 returnedMeta := fetchMeta("srcObjectRRSClass")
10974
10975 // The response metada should either be equal to metadata (with REDUCED_REDUNDANCY) or emptyMetadata (in case of gateways)
10976 if !reflect.DeepEqual(metadata, returnedMeta) && !reflect.DeepEqual(emptyMetadata, returnedMeta) {
10977 logError(testName, function, args, startTime, "", "Metadata match failed", err)
10978 return
10979 }
10980
10981 metadata = make(http.Header)
10982 metadata.Set("x-amz-storage-class", "STANDARD")
10983
10984 _, err = c.PutObject(context.Background(), bucketName, "srcObjectSSClass",
10985 bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{StorageClass: "STANDARD"})
10986 if err != nil {
10987 logError(testName, function, args, startTime, "", "PutObject failed", err)
10988 return
10989 }
10990 if reflect.DeepEqual(metadata, fetchMeta("srcObjectSSClass")) {
10991 logError(testName, function, args, startTime, "", "Metadata verification failed, STANDARD storage class should not be a part of response metadata", err)
10992 return
10993 }
10994
10995 successLogger(testName, function, args, startTime).Info()
10996}
10997
10998func testStorageClassInvalidMetadataPutObject() {
10999 // initialize logging params
11000 startTime := time.Now()
11001 function := "testStorageClassInvalidMetadataPutObject()"
11002 args := map[string]interface{}{}
11003 testName := getFuncName()
11004
11005 // Instantiate new minio client object
11006 c, err := minio.New(os.Getenv(serverEndpoint),
11007 &minio.Options{
11008 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11009 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11010 })
11011 if err != nil {
11012 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
11013 return
11014 }
11015
11016 // Generate a new random bucket name.
11017 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
11018 // Make a new bucket in 'us-east-1' (source bucket).
11019 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
11020 if err != nil {
11021 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
11022 return
11023 }
11024
11025 defer cleanupBucket(bucketName, c)
11026
11027 const srcSize = 1024 * 1024
11028 buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 1MiB
11029
11030 _, err = c.PutObject(context.Background(), bucketName, "srcObjectRRSClass",
11031 bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{StorageClass: "INVALID_STORAGE_CLASS"})
11032 if err == nil {
11033 logError(testName, function, args, startTime, "", "PutObject with invalid storage class passed, was expected to fail", err)
11034 return
11035 }
11036
11037 successLogger(testName, function, args, startTime).Info()
11038}
11039
11040func testStorageClassMetadataCopyObject() {
11041 // initialize logging params
11042 startTime := time.Now()
11043 function := "testStorageClassMetadataCopyObject()"
11044 args := map[string]interface{}{}
11045 testName := getFuncName()
11046
11047 // Instantiate new minio client object
11048 c, err := minio.New(os.Getenv(serverEndpoint),
11049 &minio.Options{
11050 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11051 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11052 })
11053 if err != nil {
11054 logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err)
11055 return
11056 }
11057
11058 // Generate a new random bucket name.
11059 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
11060 // Make a new bucket in 'us-east-1' (source bucket).
11061 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
11062 if err != nil {
11063 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
11064 return
11065 }
11066
11067 defer cleanupBucket(bucketName, c)
11068
11069 fetchMeta := func(object string) (h http.Header) {
11070 objInfo, err := c.StatObject(context.Background(), bucketName, object, minio.StatObjectOptions{})
11071 args["bucket"] = bucketName
11072 args["object"] = object
11073 if err != nil {
11074 logError(testName, function, args, startTime, "", "Stat failed", err)
11075 return
11076 }
11077 h = make(http.Header)
11078 for k, vs := range objInfo.Metadata {
11079 if strings.HasPrefix(strings.ToLower(k), "x-amz-storage-class") {
11080 for _, v := range vs {
11081 h.Add(k, v)
11082 }
11083 }
11084 }
11085 return h
11086 }
11087
11088 metadata := make(http.Header)
11089 metadata.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
11090
11091 emptyMetadata := make(http.Header)
11092
11093 const srcSize = 1024 * 1024
11094 buf := bytes.Repeat([]byte("abcde"), srcSize)
11095
11096 // Put an object with RRS Storage class
11097 _, err = c.PutObject(context.Background(), bucketName, "srcObjectRRSClass",
11098 bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{StorageClass: "REDUCED_REDUNDANCY"})
11099 if err != nil {
11100 logError(testName, function, args, startTime, "", "PutObject failed", err)
11101 return
11102 }
11103
11104 // Make server side copy of object uploaded in previous step
11105 src := minio.CopySrcOptions{
11106 Bucket: bucketName,
11107 Object: "srcObjectRRSClass",
11108 }
11109 dst := minio.CopyDestOptions{
11110 Bucket: bucketName,
11111 Object: "srcObjectRRSClassCopy",
11112 }
11113 if _, err = c.CopyObject(context.Background(), dst, src); err != nil {
11114 logError(testName, function, args, startTime, "", "CopyObject failed on RRS", err)
11115 return
11116 }
11117
11118 // Get the returned metadata
11119 returnedMeta := fetchMeta("srcObjectRRSClassCopy")
11120
11121 // The response metada should either be equal to metadata (with REDUCED_REDUNDANCY) or emptyMetadata (in case of gateways)
11122 if !reflect.DeepEqual(metadata, returnedMeta) && !reflect.DeepEqual(emptyMetadata, returnedMeta) {
11123 logError(testName, function, args, startTime, "", "Metadata match failed", err)
11124 return
11125 }
11126
11127 metadata = make(http.Header)
11128 metadata.Set("x-amz-storage-class", "STANDARD")
11129
11130 // Put an object with Standard Storage class
11131 _, err = c.PutObject(context.Background(), bucketName, "srcObjectSSClass",
11132 bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{StorageClass: "STANDARD"})
11133 if err != nil {
11134 logError(testName, function, args, startTime, "", "PutObject failed", err)
11135 return
11136 }
11137
11138 // Make server side copy of object uploaded in previous step
11139 src = minio.CopySrcOptions{
11140 Bucket: bucketName,
11141 Object: "srcObjectSSClass",
11142 }
11143 dst = minio.CopyDestOptions{
11144 Bucket: bucketName,
11145 Object: "srcObjectSSClassCopy",
11146 }
11147 if _, err = c.CopyObject(context.Background(), dst, src); err != nil {
11148 logError(testName, function, args, startTime, "", "CopyObject failed on SS", err)
11149 return
11150 }
11151 // Fetch the meta data of copied object
11152 if reflect.DeepEqual(metadata, fetchMeta("srcObjectSSClassCopy")) {
11153 logError(testName, function, args, startTime, "", "Metadata verification failed, STANDARD storage class should not be a part of response metadata", err)
11154 return
11155 }
11156
11157 successLogger(testName, function, args, startTime).Info()
11158}
11159
11160// Test put object with size -1 byte object.
11161func testPutObjectNoLengthV2() {
11162 // initialize logging params
11163 startTime := time.Now()
11164 testName := getFuncName()
11165 function := "PutObject(bucketName, objectName, reader, size, opts)"
11166 args := map[string]interface{}{
11167 "bucketName": "",
11168 "objectName": "",
11169 "size": -1,
11170 "opts": "",
11171 }
11172
11173 // Seed random based on current time.
11174 rand.Seed(time.Now().Unix())
11175
11176 // Instantiate new minio client object.
11177 c, err := minio.New(os.Getenv(serverEndpoint),
11178 &minio.Options{
11179 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11180 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11181 })
11182 if err != nil {
11183 logError(testName, function, args, startTime, "", "MinIO client v2 object creation failed", err)
11184 return
11185 }
11186
11187 // Enable tracing, write to stderr.
11188 // c.TraceOn(os.Stderr)
11189
11190 // Set user agent.
11191 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
11192
11193 // Generate a new random bucket name.
11194 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
11195 args["bucketName"] = bucketName
11196
11197 // Make a new bucket.
11198 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
11199 if err != nil {
11200 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
11201 return
11202 }
11203
11204 defer cleanupBucket(bucketName, c)
11205
11206 objectName := bucketName + "unique"
11207 args["objectName"] = objectName
11208
11209 bufSize := dataFileMap["datafile-129-MB"]
11210 reader := getDataReader("datafile-129-MB")
11211 defer reader.Close()
11212 args["size"] = bufSize
11213
11214 // Upload an object.
11215 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, -1, minio.PutObjectOptions{})
11216 if err != nil {
11217 logError(testName, function, args, startTime, "", "PutObjectWithSize failed", err)
11218 return
11219 }
11220
11221 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
11222 if err != nil {
11223 logError(testName, function, args, startTime, "", "StatObject failed", err)
11224 return
11225 }
11226
11227 if st.Size != int64(bufSize) {
11228 logError(testName, function, args, startTime, "", "Expected upload object size "+string(bufSize)+" got "+string(st.Size), err)
11229 return
11230 }
11231
11232 successLogger(testName, function, args, startTime).Info()
11233}
11234
11235// Test put objects of unknown size.
11236func testPutObjectsUnknownV2() {
11237 // initialize logging params
11238 startTime := time.Now()
11239 testName := getFuncName()
11240 function := "PutObject(bucketName, objectName, reader,size,opts)"
11241 args := map[string]interface{}{
11242 "bucketName": "",
11243 "objectName": "",
11244 "size": "",
11245 "opts": "",
11246 }
11247
11248 // Seed random based on current time.
11249 rand.Seed(time.Now().Unix())
11250
11251 // Instantiate new minio client object.
11252 c, err := minio.New(os.Getenv(serverEndpoint),
11253 &minio.Options{
11254 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11255 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11256 })
11257 if err != nil {
11258 logError(testName, function, args, startTime, "", "MinIO client v2 object creation failed", err)
11259 return
11260 }
11261
11262 // Enable tracing, write to stderr.
11263 // c.TraceOn(os.Stderr)
11264
11265 // Set user agent.
11266 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
11267
11268 // Generate a new random bucket name.
11269 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
11270 args["bucketName"] = bucketName
11271
11272 // Make a new bucket.
11273 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
11274 if err != nil {
11275 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
11276 return
11277 }
11278
11279 defer cleanupBucket(bucketName, c)
11280
11281 // Issues are revealed by trying to upload multiple files of unknown size
11282 // sequentially (on 4GB machines)
11283 for i := 1; i <= 4; i++ {
11284 // Simulate that we could be receiving byte slices of data that we want
11285 // to upload as a file
11286 rpipe, wpipe := io.Pipe()
11287 defer rpipe.Close()
11288 go func() {
11289 b := []byte("test")
11290 wpipe.Write(b)
11291 wpipe.Close()
11292 }()
11293
11294 // Upload the object.
11295 objectName := fmt.Sprintf("%sunique%d", bucketName, i)
11296 args["objectName"] = objectName
11297
11298 ui, err := c.PutObject(context.Background(), bucketName, objectName, rpipe, -1, minio.PutObjectOptions{})
11299 if err != nil {
11300 logError(testName, function, args, startTime, "", "PutObjectStreaming failed", err)
11301 return
11302 }
11303
11304 if ui.Size != 4 {
11305 logError(testName, function, args, startTime, "", "Expected upload object size "+string(4)+" got "+string(ui.Size), nil)
11306 return
11307 }
11308
11309 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
11310 if err != nil {
11311 logError(testName, function, args, startTime, "", "StatObjectStreaming failed", err)
11312 return
11313 }
11314
11315 if st.Size != int64(4) {
11316 logError(testName, function, args, startTime, "", "Expected upload object size "+string(4)+" got "+string(st.Size), err)
11317 return
11318 }
11319
11320 }
11321
11322 successLogger(testName, function, args, startTime).Info()
11323}
11324
11325// Test put object with 0 byte object.
11326func testPutObject0ByteV2() {
11327 // initialize logging params
11328 startTime := time.Now()
11329 testName := getFuncName()
11330 function := "PutObject(bucketName, objectName, reader, size, opts)"
11331 args := map[string]interface{}{
11332 "bucketName": "",
11333 "objectName": "",
11334 "size": 0,
11335 "opts": "",
11336 }
11337
11338 // Seed random based on current time.
11339 rand.Seed(time.Now().Unix())
11340
11341 // Instantiate new minio client object.
11342 c, err := minio.New(os.Getenv(serverEndpoint),
11343 &minio.Options{
11344 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11345 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11346 })
11347 if err != nil {
11348 logError(testName, function, args, startTime, "", "MinIO client v2 object creation failed", err)
11349 return
11350 }
11351
11352 // Enable tracing, write to stderr.
11353 // c.TraceOn(os.Stderr)
11354
11355 // Set user agent.
11356 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
11357
11358 // Generate a new random bucket name.
11359 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
11360 args["bucketName"] = bucketName
11361
11362 // Make a new bucket.
11363 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
11364 if err != nil {
11365 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
11366 return
11367 }
11368
11369 defer cleanupBucket(bucketName, c)
11370
11371 objectName := bucketName + "unique"
11372 args["objectName"] = objectName
11373 args["opts"] = minio.PutObjectOptions{}
11374
11375 // Upload an object.
11376 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader([]byte("")), 0, minio.PutObjectOptions{})
11377 if err != nil {
11378 logError(testName, function, args, startTime, "", "PutObjectWithSize failed", err)
11379 return
11380 }
11381 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
11382 if err != nil {
11383 logError(testName, function, args, startTime, "", "StatObjectWithSize failed", err)
11384 return
11385 }
11386 if st.Size != 0 {
11387 logError(testName, function, args, startTime, "", "Expected upload object size 0 but got "+string(st.Size), err)
11388 return
11389 }
11390
11391 successLogger(testName, function, args, startTime).Info()
11392}
11393
11394// Test expected error cases
11395func testComposeObjectErrorCases() {
11396 // initialize logging params
11397 startTime := time.Now()
11398 testName := getFuncName()
11399 function := "ComposeObject(destination, sourceList)"
11400 args := map[string]interface{}{}
11401
11402 // Instantiate new minio client object
11403 c, err := minio.New(os.Getenv(serverEndpoint),
11404 &minio.Options{
11405 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11406 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11407 })
11408 if err != nil {
11409 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
11410 return
11411 }
11412
11413 testComposeObjectErrorCasesWrapper(c)
11414}
11415
11416// Test concatenating multiple 10K objects V4
11417func testCompose10KSources() {
11418 // initialize logging params
11419 startTime := time.Now()
11420 testName := getFuncName()
11421 function := "ComposeObject(destination, sourceList)"
11422 args := map[string]interface{}{}
11423
11424 // Instantiate new minio client object
11425 c, err := minio.New(os.Getenv(serverEndpoint),
11426 &minio.Options{
11427 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11428 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11429 })
11430 if err != nil {
11431 logError(testName, function, args, startTime, "", "MinIO client object creation failed", err)
11432 return
11433 }
11434
11435 testComposeMultipleSources(c)
11436}
11437
11438// Tests comprehensive list of all methods.
11439func testFunctionalV2() {
11440 // initialize logging params
11441 startTime := time.Now()
11442 testName := getFuncName()
11443 function := "testFunctionalV2()"
11444 functionAll := ""
11445 args := map[string]interface{}{}
11446
11447 // Seed random based on current time.
11448 rand.Seed(time.Now().Unix())
11449
11450 c, err := minio.New(os.Getenv(serverEndpoint),
11451 &minio.Options{
11452 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11453 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11454 })
11455 if err != nil {
11456 logError(testName, function, args, startTime, "", "MinIO client v2 object creation failed", err)
11457 return
11458 }
11459
11460 // Enable to debug
11461 // c.TraceOn(os.Stderr)
11462
11463 // Set user agent.
11464 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
11465
11466 // Generate a new random bucket name.
11467 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
11468 location := "us-east-1"
11469 // Make a new bucket.
11470 function = "MakeBucket(bucketName, location)"
11471 functionAll = "MakeBucket(bucketName, location)"
11472 args = map[string]interface{}{
11473 "bucketName": bucketName,
11474 "location": location,
11475 }
11476 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: location})
11477 if err != nil {
11478 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
11479 return
11480 }
11481
11482 defer cleanupBucket(bucketName, c)
11483
11484 // Generate a random file name.
11485 fileName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
11486 file, err := os.Create(fileName)
11487 if err != nil {
11488 logError(testName, function, args, startTime, "", "file create failed", err)
11489 return
11490 }
11491 for i := 0; i < 3; i++ {
11492 buf := make([]byte, rand.Intn(1<<19))
11493 _, err = file.Write(buf)
11494 if err != nil {
11495 logError(testName, function, args, startTime, "", "file write failed", err)
11496 return
11497 }
11498 }
11499 file.Close()
11500
11501 // Verify if bucket exits and you have access.
11502 var exists bool
11503 function = "BucketExists(bucketName)"
11504 functionAll += ", " + function
11505 args = map[string]interface{}{
11506 "bucketName": bucketName,
11507 }
11508 exists, err = c.BucketExists(context.Background(), bucketName)
11509 if err != nil {
11510 logError(testName, function, args, startTime, "", "BucketExists failed", err)
11511 return
11512 }
11513 if !exists {
11514 logError(testName, function, args, startTime, "", "Could not find existing bucket "+bucketName, err)
11515 return
11516 }
11517
11518 // Make the bucket 'public read/write'.
11519 function = "SetBucketPolicy(bucketName, bucketPolicy)"
11520 functionAll += ", " + function
11521
11522 readWritePolicy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:ListBucketMultipartUploads", "s3:ListBucket"],"Effect": "Allow","Principal": {"AWS": ["*"]},"Resource": ["arn:aws:s3:::` + bucketName + `"],"Sid": ""}]}`
11523
11524 args = map[string]interface{}{
11525 "bucketName": bucketName,
11526 "bucketPolicy": readWritePolicy,
11527 }
11528 err = c.SetBucketPolicy(context.Background(), bucketName, readWritePolicy)
11529
11530 if err != nil {
11531 logError(testName, function, args, startTime, "", "SetBucketPolicy failed", err)
11532 return
11533 }
11534
11535 // List all buckets.
11536 function = "ListBuckets()"
11537 functionAll += ", " + function
11538 args = nil
11539 buckets, err := c.ListBuckets(context.Background())
11540 if len(buckets) == 0 {
11541 logError(testName, function, args, startTime, "", "List buckets cannot be empty", err)
11542 return
11543 }
11544 if err != nil {
11545 logError(testName, function, args, startTime, "", "ListBuckets failed", err)
11546 return
11547 }
11548
11549 // Verify if previously created bucket is listed in list buckets.
11550 bucketFound := false
11551 for _, bucket := range buckets {
11552 if bucket.Name == bucketName {
11553 bucketFound = true
11554 }
11555 }
11556
11557 // If bucket not found error out.
11558 if !bucketFound {
11559 logError(testName, function, args, startTime, "", "Bucket "+bucketName+"not found", err)
11560 return
11561 }
11562
11563 objectName := bucketName + "unique"
11564
11565 // Generate data
11566 buf := bytes.Repeat([]byte("n"), rand.Intn(1<<19))
11567
11568 args = map[string]interface{}{
11569 "bucketName": bucketName,
11570 "objectName": objectName,
11571 "contentType": "",
11572 }
11573 _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{})
11574 if err != nil {
11575 logError(testName, function, args, startTime, "", "PutObject failed", err)
11576 return
11577 }
11578
11579 st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
11580 if err != nil {
11581 logError(testName, function, args, startTime, "", "StatObject failed", err)
11582 return
11583 }
11584 if st.Size != int64(len(buf)) {
11585 logError(testName, function, args, startTime, "", "Expected uploaded object length "+string(len(buf))+" got "+string(st.Size), err)
11586 return
11587 }
11588
11589 objectNameNoLength := objectName + "-nolength"
11590 args["objectName"] = objectNameNoLength
11591 _, err = c.PutObject(context.Background(), bucketName, objectNameNoLength, bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
11592 if err != nil {
11593 logError(testName, function, args, startTime, "", "PutObject failed", err)
11594 return
11595 }
11596 st, err = c.StatObject(context.Background(), bucketName, objectNameNoLength, minio.StatObjectOptions{})
11597 if err != nil {
11598 logError(testName, function, args, startTime, "", "StatObject failed", err)
11599 return
11600 }
11601 if st.Size != int64(len(buf)) {
11602 logError(testName, function, args, startTime, "", "Expected uploaded object length "+string(len(buf))+" got "+string(st.Size), err)
11603 return
11604 }
11605
11606 // Instantiate a done channel to close all listing.
11607 doneCh := make(chan struct{})
11608 defer close(doneCh)
11609
11610 objFound := false
11611 isRecursive := true // Recursive is true.
11612 function = "ListObjects(bucketName, objectName, isRecursive, doneCh)"
11613 functionAll += ", " + function
11614 args = map[string]interface{}{
11615 "bucketName": bucketName,
11616 "objectName": objectName,
11617 "isRecursive": isRecursive,
11618 }
11619 for obj := range c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{UseV1: true, Prefix: objectName, Recursive: isRecursive}) {
11620 if obj.Key == objectName {
11621 objFound = true
11622 break
11623 }
11624 }
11625 if !objFound {
11626 logError(testName, function, args, startTime, "", "Could not find existing object "+objectName, err)
11627 return
11628 }
11629
11630 incompObjNotFound := true
11631 function = "ListIncompleteUploads(bucketName, objectName, isRecursive, doneCh)"
11632 functionAll += ", " + function
11633 args = map[string]interface{}{
11634 "bucketName": bucketName,
11635 "objectName": objectName,
11636 "isRecursive": isRecursive,
11637 }
11638 for objIncompl := range c.ListIncompleteUploads(context.Background(), bucketName, objectName, isRecursive) {
11639 if objIncompl.Key != "" {
11640 incompObjNotFound = false
11641 break
11642 }
11643 }
11644 if !incompObjNotFound {
11645 logError(testName, function, args, startTime, "", "Unexpected dangling incomplete upload found", err)
11646 return
11647 }
11648
11649 function = "GetObject(bucketName, objectName)"
11650 functionAll += ", " + function
11651 args = map[string]interface{}{
11652 "bucketName": bucketName,
11653 "objectName": objectName,
11654 }
11655 newReader, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
11656 if err != nil {
11657 logError(testName, function, args, startTime, "", "GetObject failed", err)
11658 return
11659 }
11660
11661 newReadBytes, err := io.ReadAll(newReader)
11662 if err != nil {
11663 logError(testName, function, args, startTime, "", "ReadAll failed", err)
11664 return
11665 }
11666 newReader.Close()
11667
11668 if !bytes.Equal(newReadBytes, buf) {
11669 logError(testName, function, args, startTime, "", "Bytes mismatch", err)
11670 return
11671 }
11672
11673 function = "FGetObject(bucketName, objectName, fileName)"
11674 functionAll += ", " + function
11675 args = map[string]interface{}{
11676 "bucketName": bucketName,
11677 "objectName": objectName,
11678 "fileName": fileName + "-f",
11679 }
11680 err = c.FGetObject(context.Background(), bucketName, objectName, fileName+"-f", minio.GetObjectOptions{})
11681 if err != nil {
11682 logError(testName, function, args, startTime, "", "FgetObject failed", err)
11683 return
11684 }
11685
11686 // Generate presigned HEAD object url.
11687 function = "PresignedHeadObject(bucketName, objectName, expires, reqParams)"
11688 functionAll += ", " + function
11689 args = map[string]interface{}{
11690 "bucketName": bucketName,
11691 "objectName": objectName,
11692 "expires": 3600 * time.Second,
11693 }
11694 presignedHeadURL, err := c.PresignedHeadObject(context.Background(), bucketName, objectName, 3600*time.Second, nil)
11695 if err != nil {
11696 logError(testName, function, args, startTime, "", "PresignedHeadObject failed", err)
11697 return
11698 }
11699
11700 transport, err := minio.DefaultTransport(mustParseBool(os.Getenv(enableHTTPS)))
11701 if err != nil {
11702 logError(testName, function, args, startTime, "", "DefaultTransport failed", err)
11703 return
11704 }
11705
11706 httpClient := &http.Client{
11707 // Setting a sensible time out of 30secs to wait for response
11708 // headers. Request is pro-actively canceled after 30secs
11709 // with no response.
11710 Timeout: 30 * time.Second,
11711 Transport: transport,
11712 }
11713
11714 req, err := http.NewRequest(http.MethodHead, presignedHeadURL.String(), nil)
11715 if err != nil {
11716 logError(testName, function, args, startTime, "", "PresignedHeadObject URL head request failed", err)
11717 return
11718 }
11719
11720 // Verify if presigned url works.
11721 resp, err := httpClient.Do(req)
11722 if err != nil {
11723 logError(testName, function, args, startTime, "", "PresignedHeadObject URL head request failed", err)
11724 return
11725 }
11726 if resp.StatusCode != http.StatusOK {
11727 logError(testName, function, args, startTime, "", "PresignedHeadObject URL returns status "+string(resp.StatusCode), err)
11728 return
11729 }
11730 if resp.Header.Get("ETag") == "" {
11731 logError(testName, function, args, startTime, "", "Got empty ETag", err)
11732 return
11733 }
11734 resp.Body.Close()
11735
11736 // Generate presigned GET object url.
11737 function = "PresignedGetObject(bucketName, objectName, expires, reqParams)"
11738 functionAll += ", " + function
11739 args = map[string]interface{}{
11740 "bucketName": bucketName,
11741 "objectName": objectName,
11742 "expires": 3600 * time.Second,
11743 }
11744 presignedGetURL, err := c.PresignedGetObject(context.Background(), bucketName, objectName, 3600*time.Second, nil)
11745 if err != nil {
11746 logError(testName, function, args, startTime, "", "PresignedGetObject failed", err)
11747 return
11748 }
11749
11750 // Verify if presigned url works.
11751 req, err = http.NewRequest(http.MethodGet, presignedGetURL.String(), nil)
11752 if err != nil {
11753 logError(testName, function, args, startTime, "", "PresignedGetObject request incorrect", err)
11754 return
11755 }
11756
11757 resp, err = httpClient.Do(req)
11758 if err != nil {
11759 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect", err)
11760 return
11761 }
11762
11763 if resp.StatusCode != http.StatusOK {
11764 logError(testName, function, args, startTime, "", "PresignedGetObject URL returns status "+string(resp.StatusCode), err)
11765 return
11766 }
11767 newPresignedBytes, err := io.ReadAll(resp.Body)
11768 if err != nil {
11769 logError(testName, function, args, startTime, "", "ReadAll failed", err)
11770 return
11771 }
11772 resp.Body.Close()
11773 if !bytes.Equal(newPresignedBytes, buf) {
11774 logError(testName, function, args, startTime, "", "Bytes mismatch", err)
11775 return
11776 }
11777
11778 // Set request parameters.
11779 reqParams := make(url.Values)
11780 reqParams.Set("response-content-disposition", "attachment; filename=\"test.txt\"")
11781 // Generate presigned GET object url.
11782 args["reqParams"] = reqParams
11783 presignedGetURL, err = c.PresignedGetObject(context.Background(), bucketName, objectName, 3600*time.Second, reqParams)
11784 if err != nil {
11785 logError(testName, function, args, startTime, "", "PresignedGetObject failed", err)
11786 return
11787 }
11788
11789 // Verify if presigned url works.
11790 req, err = http.NewRequest(http.MethodGet, presignedGetURL.String(), nil)
11791 if err != nil {
11792 logError(testName, function, args, startTime, "", "PresignedGetObject request incorrect", err)
11793 return
11794 }
11795
11796 resp, err = httpClient.Do(req)
11797 if err != nil {
11798 logError(testName, function, args, startTime, "", "PresignedGetObject response incorrect", err)
11799 return
11800 }
11801
11802 if resp.StatusCode != http.StatusOK {
11803 logError(testName, function, args, startTime, "", "PresignedGetObject URL returns status "+string(resp.StatusCode), err)
11804 return
11805 }
11806 newPresignedBytes, err = io.ReadAll(resp.Body)
11807 if err != nil {
11808 logError(testName, function, args, startTime, "", "ReadAll failed", err)
11809 return
11810 }
11811 if !bytes.Equal(newPresignedBytes, buf) {
11812 logError(testName, function, args, startTime, "", "Bytes mismatch", err)
11813 return
11814 }
11815 // Verify content disposition.
11816 if resp.Header.Get("Content-Disposition") != "attachment; filename=\"test.txt\"" {
11817 logError(testName, function, args, startTime, "", "wrong Content-Disposition received ", err)
11818 return
11819 }
11820
11821 function = "PresignedPutObject(bucketName, objectName, expires)"
11822 functionAll += ", " + function
11823 args = map[string]interface{}{
11824 "bucketName": bucketName,
11825 "objectName": objectName + "-presigned",
11826 "expires": 3600 * time.Second,
11827 }
11828 presignedPutURL, err := c.PresignedPutObject(context.Background(), bucketName, objectName+"-presigned", 3600*time.Second)
11829 if err != nil {
11830 logError(testName, function, args, startTime, "", "PresignedPutObject failed", err)
11831 return
11832 }
11833
11834 // Generate data more than 32K
11835 buf = bytes.Repeat([]byte("1"), rand.Intn(1<<10)+32*1024)
11836
11837 req, err = http.NewRequest(http.MethodPut, presignedPutURL.String(), bytes.NewReader(buf))
11838 if err != nil {
11839 logError(testName, function, args, startTime, "", "HTTP request to PresignedPutObject URL failed", err)
11840 return
11841 }
11842
11843 resp, err = httpClient.Do(req)
11844 if err != nil {
11845 logError(testName, function, args, startTime, "", "HTTP request to PresignedPutObject URL failed", err)
11846 return
11847 }
11848
11849 // Download the uploaded object to verify
11850 args = map[string]interface{}{
11851 "bucketName": bucketName,
11852 "objectName": objectName + "-presigned",
11853 }
11854 newReader, err = c.GetObject(context.Background(), bucketName, objectName+"-presigned", minio.GetObjectOptions{})
11855 if err != nil {
11856 logError(testName, function, args, startTime, "", "GetObject of uploaded presigned object failed", err)
11857 return
11858 }
11859
11860 newReadBytes, err = io.ReadAll(newReader)
11861 if err != nil {
11862 logError(testName, function, args, startTime, "", "ReadAll failed during get on presigned put object", err)
11863 return
11864 }
11865 newReader.Close()
11866
11867 if !bytes.Equal(newReadBytes, buf) {
11868 logError(testName, function, args, startTime, "", "Bytes mismatch on presigned object upload verification", err)
11869 return
11870 }
11871
11872 function = "PresignHeader(method, bucketName, objectName, expires, reqParams, extraHeaders)"
11873 functionAll += ", " + function
11874 presignExtraHeaders := map[string][]string{
11875 "mysecret": {"abcxxx"},
11876 }
11877 args = map[string]interface{}{
11878 "method": "PUT",
11879 "bucketName": bucketName,
11880 "objectName": objectName + "-presign-custom",
11881 "expires": 3600 * time.Second,
11882 "extraHeaders": presignExtraHeaders,
11883 }
11884 _, err = c.PresignHeader(context.Background(), "PUT", bucketName, objectName+"-presign-custom", 3600*time.Second, nil, presignExtraHeaders)
11885 if err == nil {
11886 logError(testName, function, args, startTime, "", "Presigned with extra headers succeeded", err)
11887 return
11888 }
11889
11890 os.Remove(fileName)
11891 os.Remove(fileName + "-f")
11892 successLogger(testName, functionAll, args, startTime).Info()
11893}
11894
11895// Test get object with GetObject with context
11896func testGetObjectContext() {
11897 // initialize logging params
11898 startTime := time.Now()
11899 testName := getFuncName()
11900 function := "GetObject(ctx, bucketName, objectName)"
11901 args := map[string]interface{}{
11902 "ctx": "",
11903 "bucketName": "",
11904 "objectName": "",
11905 }
11906 // Seed random based on current time.
11907 rand.Seed(time.Now().Unix())
11908
11909 // Instantiate new minio client object.
11910 c, err := minio.New(os.Getenv(serverEndpoint),
11911 &minio.Options{
11912 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
11913 Secure: mustParseBool(os.Getenv(enableHTTPS)),
11914 })
11915 if err != nil {
11916 logError(testName, function, args, startTime, "", "MinIO client v4 object creation failed", err)
11917 return
11918 }
11919
11920 // Enable tracing, write to stderr.
11921 // c.TraceOn(os.Stderr)
11922
11923 // Set user agent.
11924 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
11925
11926 // Generate a new random bucket name.
11927 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
11928 args["bucketName"] = bucketName
11929
11930 // Make a new bucket.
11931 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
11932 if err != nil {
11933 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
11934 return
11935 }
11936
11937 defer cleanupBucket(bucketName, c)
11938
11939 bufSize := dataFileMap["datafile-33-kB"]
11940 reader := getDataReader("datafile-33-kB")
11941 defer reader.Close()
11942 // Save the data
11943 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
11944 args["objectName"] = objectName
11945
11946 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
11947 if err != nil {
11948 logError(testName, function, args, startTime, "", "PutObject failed", err)
11949 return
11950 }
11951
11952 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
11953 args["ctx"] = ctx
11954 cancel()
11955
11956 r, err := c.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
11957 if err != nil {
11958 logError(testName, function, args, startTime, "", "GetObject failed unexpectedly", err)
11959 return
11960 }
11961
11962 if _, err = r.Stat(); err == nil {
11963 logError(testName, function, args, startTime, "", "GetObject should fail on short timeout", err)
11964 return
11965 }
11966 r.Close()
11967
11968 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
11969 args["ctx"] = ctx
11970 defer cancel()
11971
11972 // Read the data back
11973 r, err = c.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
11974 if err != nil {
11975 logError(testName, function, args, startTime, "", "GetObject failed", err)
11976 return
11977 }
11978
11979 st, err := r.Stat()
11980 if err != nil {
11981 logError(testName, function, args, startTime, "", "object Stat call failed", err)
11982 return
11983 }
11984 if st.Size != int64(bufSize) {
11985 logError(testName, function, args, startTime, "", "Number of bytes in stat does not match: want "+string(bufSize)+", got"+string(st.Size), err)
11986 return
11987 }
11988 if err := r.Close(); err != nil {
11989 logError(testName, function, args, startTime, "", "object Close() call failed", err)
11990 return
11991 }
11992
11993 successLogger(testName, function, args, startTime).Info()
11994}
11995
11996// Test get object with FGetObject with a user provided context
11997func testFGetObjectContext() {
11998 // initialize logging params
11999 startTime := time.Now()
12000 testName := getFuncName()
12001 function := "FGetObject(ctx, bucketName, objectName, fileName)"
12002 args := map[string]interface{}{
12003 "ctx": "",
12004 "bucketName": "",
12005 "objectName": "",
12006 "fileName": "",
12007 }
12008 // Seed random based on current time.
12009 rand.Seed(time.Now().Unix())
12010
12011 // Instantiate new minio client object.
12012 c, err := minio.New(os.Getenv(serverEndpoint),
12013 &minio.Options{
12014 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
12015 Secure: mustParseBool(os.Getenv(enableHTTPS)),
12016 })
12017 if err != nil {
12018 logError(testName, function, args, startTime, "", "MinIO client v4 object creation failed", err)
12019 return
12020 }
12021
12022 // Enable tracing, write to stderr.
12023 // c.TraceOn(os.Stderr)
12024
12025 // Set user agent.
12026 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
12027
12028 // Generate a new random bucket name.
12029 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
12030 args["bucketName"] = bucketName
12031
12032 // Make a new bucket.
12033 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
12034 if err != nil {
12035 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
12036 return
12037 }
12038
12039 defer cleanupBucket(bucketName, c)
12040
12041 bufSize := dataFileMap["datafile-1-MB"]
12042 reader := getDataReader("datafile-1-MB")
12043 defer reader.Close()
12044 // Save the data
12045 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
12046 args["objectName"] = objectName
12047
12048 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
12049 if err != nil {
12050 logError(testName, function, args, startTime, "", "PutObject failed", err)
12051 return
12052 }
12053
12054 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
12055 args["ctx"] = ctx
12056 defer cancel()
12057
12058 fileName := "tempfile-context"
12059 args["fileName"] = fileName
12060 // Read the data back
12061 err = c.FGetObject(ctx, bucketName, objectName, fileName+"-f", minio.GetObjectOptions{})
12062 if err == nil {
12063 logError(testName, function, args, startTime, "", "FGetObject should fail on short timeout", err)
12064 return
12065 }
12066 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
12067 defer cancel()
12068
12069 // Read the data back
12070 err = c.FGetObject(ctx, bucketName, objectName, fileName+"-fcontext", minio.GetObjectOptions{})
12071 if err != nil {
12072 logError(testName, function, args, startTime, "", "FGetObject with long timeout failed", err)
12073 return
12074 }
12075 if err = os.Remove(fileName + "-fcontext"); err != nil {
12076 logError(testName, function, args, startTime, "", "Remove file failed", err)
12077 return
12078 }
12079
12080 successLogger(testName, function, args, startTime).Info()
12081}
12082
12083// Test get object with GetObject with a user provided context
12084func testGetObjectRanges() {
12085 // initialize logging params
12086 startTime := time.Now()
12087 testName := getFuncName()
12088 function := "GetObject(ctx, bucketName, objectName, fileName)"
12089 args := map[string]interface{}{
12090 "ctx": "",
12091 "bucketName": "",
12092 "objectName": "",
12093 "fileName": "",
12094 }
12095 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
12096 defer cancel()
12097
12098 rng := rand.NewSource(time.Now().UnixNano())
12099 // Instantiate new minio client object.
12100 c, err := minio.New(os.Getenv(serverEndpoint),
12101 &minio.Options{
12102 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
12103 Secure: mustParseBool(os.Getenv(enableHTTPS)),
12104 })
12105 if err != nil {
12106 logError(testName, function, args, startTime, "", "MinIO client v4 object creation failed", err)
12107 return
12108 }
12109
12110 // Enable tracing, write to stderr.
12111 // c.TraceOn(os.Stderr)
12112
12113 // Set user agent.
12114 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
12115
12116 // Generate a new random bucket name.
12117 bucketName := randString(60, rng, "minio-go-test-")
12118 args["bucketName"] = bucketName
12119
12120 // Make a new bucket.
12121 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
12122 if err != nil {
12123 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
12124 return
12125 }
12126
12127 defer cleanupBucket(bucketName, c)
12128
12129 bufSize := dataFileMap["datafile-129-MB"]
12130 reader := getDataReader("datafile-129-MB")
12131 defer reader.Close()
12132 // Save the data
12133 objectName := randString(60, rng, "")
12134 args["objectName"] = objectName
12135
12136 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
12137 if err != nil {
12138 logError(testName, function, args, startTime, "", "PutObject failed", err)
12139 return
12140 }
12141
12142 // Read the data back
12143 tests := []struct {
12144 start int64
12145 end int64
12146 }{
12147 {
12148 start: 1024,
12149 end: 1024 + 1<<20,
12150 },
12151 {
12152 start: 20e6,
12153 end: 20e6 + 10000,
12154 },
12155 {
12156 start: 40e6,
12157 end: 40e6 + 10000,
12158 },
12159 {
12160 start: 60e6,
12161 end: 60e6 + 10000,
12162 },
12163 {
12164 start: 80e6,
12165 end: 80e6 + 10000,
12166 },
12167 {
12168 start: 120e6,
12169 end: int64(bufSize),
12170 },
12171 }
12172 for _, test := range tests {
12173 wantRC := getDataReader("datafile-129-MB")
12174 io.CopyN(io.Discard, wantRC, test.start)
12175 want := mustCrcReader(io.LimitReader(wantRC, test.end-test.start+1))
12176 opts := minio.GetObjectOptions{}
12177 opts.SetRange(test.start, test.end)
12178 args["opts"] = fmt.Sprintf("%+v", test)
12179 obj, err := c.GetObject(ctx, bucketName, objectName, opts)
12180 if err != nil {
12181 logError(testName, function, args, startTime, "", "FGetObject with long timeout failed", err)
12182 return
12183 }
12184 err = crcMatches(obj, want)
12185 if err != nil {
12186 logError(testName, function, args, startTime, "", fmt.Sprintf("GetObject offset %d -> %d", test.start, test.end), err)
12187 return
12188 }
12189 }
12190
12191 successLogger(testName, function, args, startTime).Info()
12192}
12193
12194// Test get object ACLs with GetObjectACL with custom provided context
12195func testGetObjectACLContext() {
12196 // initialize logging params
12197 startTime := time.Now()
12198 testName := getFuncName()
12199 function := "GetObjectACL(ctx, bucketName, objectName)"
12200 args := map[string]interface{}{
12201 "ctx": "",
12202 "bucketName": "",
12203 "objectName": "",
12204 }
12205 // Seed random based on current time.
12206 rand.Seed(time.Now().Unix())
12207
12208 // Instantiate new minio client object.
12209 c, err := minio.New(os.Getenv(serverEndpoint),
12210 &minio.Options{
12211 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
12212 Secure: mustParseBool(os.Getenv(enableHTTPS)),
12213 })
12214 if err != nil {
12215 logError(testName, function, args, startTime, "", "MinIO client v4 object creation failed", err)
12216 return
12217 }
12218
12219 // Enable tracing, write to stderr.
12220 // c.TraceOn(os.Stderr)
12221
12222 // Set user agent.
12223 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
12224
12225 // Generate a new random bucket name.
12226 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
12227 args["bucketName"] = bucketName
12228
12229 // Make a new bucket.
12230 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
12231 if err != nil {
12232 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
12233 return
12234 }
12235
12236 defer cleanupBucket(bucketName, c)
12237
12238 bufSize := dataFileMap["datafile-1-MB"]
12239 reader := getDataReader("datafile-1-MB")
12240 defer reader.Close()
12241 // Save the data
12242 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
12243 args["objectName"] = objectName
12244
12245 // Add meta data to add a canned acl
12246 metaData := map[string]string{
12247 "X-Amz-Acl": "public-read-write",
12248 }
12249
12250 _, err = c.PutObject(context.Background(), bucketName,
12251 objectName, reader, int64(bufSize),
12252 minio.PutObjectOptions{
12253 ContentType: "binary/octet-stream",
12254 UserMetadata: metaData,
12255 })
12256
12257 if err != nil {
12258 logError(testName, function, args, startTime, "", "PutObject failed", err)
12259 return
12260 }
12261
12262 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
12263 args["ctx"] = ctx
12264 defer cancel()
12265
12266 // Read the data back
12267 objectInfo, getObjectACLErr := c.GetObjectACL(ctx, bucketName, objectName)
12268 if getObjectACLErr != nil {
12269 logError(testName, function, args, startTime, "", "GetObjectACL failed. ", getObjectACLErr)
12270 return
12271 }
12272
12273 s, ok := objectInfo.Metadata["X-Amz-Acl"]
12274 if !ok {
12275 logError(testName, function, args, startTime, "", "GetObjectACL fail unable to find \"X-Amz-Acl\"", nil)
12276 return
12277 }
12278
12279 if len(s) != 1 {
12280 logError(testName, function, args, startTime, "", "GetObjectACL fail \"X-Amz-Acl\" canned acl expected \"1\" got "+fmt.Sprintf(`"%d"`, len(s)), nil)
12281 return
12282 }
12283
12284 // Do a very limited testing if this is not AWS S3
12285 if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
12286 if s[0] != "private" {
12287 logError(testName, function, args, startTime, "", "GetObjectACL fail \"X-Amz-Acl\" expected \"private\" but got"+fmt.Sprintf("%q", s[0]), nil)
12288 return
12289 }
12290
12291 successLogger(testName, function, args, startTime).Info()
12292 return
12293 }
12294
12295 if s[0] != "public-read-write" {
12296 logError(testName, function, args, startTime, "", "GetObjectACL fail \"X-Amz-Acl\" expected \"public-read-write\" but got"+fmt.Sprintf("%q", s[0]), nil)
12297 return
12298 }
12299
12300 bufSize = dataFileMap["datafile-1-MB"]
12301 reader2 := getDataReader("datafile-1-MB")
12302 defer reader2.Close()
12303 // Save the data
12304 objectName = randString(60, rand.NewSource(time.Now().UnixNano()), "")
12305 args["objectName"] = objectName
12306
12307 // Add meta data to add a canned acl
12308 metaData = map[string]string{
12309 "X-Amz-Grant-Read": "[email protected]",
12310 "X-Amz-Grant-Write": "[email protected]",
12311 }
12312
12313 _, err = c.PutObject(context.Background(), bucketName, objectName, reader2, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream", UserMetadata: metaData})
12314 if err != nil {
12315 logError(testName, function, args, startTime, "", "PutObject failed", err)
12316 return
12317 }
12318
12319 ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
12320 args["ctx"] = ctx
12321 defer cancel()
12322
12323 // Read the data back
12324 objectInfo, getObjectACLErr = c.GetObjectACL(ctx, bucketName, objectName)
12325 if getObjectACLErr == nil {
12326 logError(testName, function, args, startTime, "", "GetObjectACL fail", getObjectACLErr)
12327 return
12328 }
12329
12330 if len(objectInfo.Metadata) != 3 {
12331 logError(testName, function, args, startTime, "", "GetObjectACL fail expected \"3\" ACLs but got "+fmt.Sprintf(`"%d"`, len(objectInfo.Metadata)), nil)
12332 return
12333 }
12334
12335 s, ok = objectInfo.Metadata["X-Amz-Grant-Read"]
12336 if !ok {
12337 logError(testName, function, args, startTime, "", "GetObjectACL fail unable to find \"X-Amz-Grant-Read\"", nil)
12338 return
12339 }
12340
12341 if len(s) != 1 {
12342 logError(testName, function, args, startTime, "", "GetObjectACL fail \"X-Amz-Grant-Read\" acl expected \"1\" got "+fmt.Sprintf(`"%d"`, len(s)), nil)
12343 return
12344 }
12345
12346 if s[0] != "[email protected]" {
12347 logError(testName, function, args, startTime, "", "GetObjectACL fail \"X-Amz-Grant-Read\" acl expected \"[email protected]\" got "+fmt.Sprintf("%q", s), nil)
12348 return
12349 }
12350
12351 s, ok = objectInfo.Metadata["X-Amz-Grant-Write"]
12352 if !ok {
12353 logError(testName, function, args, startTime, "", "GetObjectACL fail unable to find \"X-Amz-Grant-Write\"", nil)
12354 return
12355 }
12356
12357 if len(s) != 1 {
12358 logError(testName, function, args, startTime, "", "GetObjectACL fail \"X-Amz-Grant-Write\" acl expected \"1\" got "+fmt.Sprintf(`"%d"`, len(s)), nil)
12359 return
12360 }
12361
12362 if s[0] != "[email protected]" {
12363 logError(testName, function, args, startTime, "", "GetObjectACL fail \"X-Amz-Grant-Write\" acl expected \"[email protected]\" got "+fmt.Sprintf("%q", s), nil)
12364 return
12365 }
12366
12367 successLogger(testName, function, args, startTime).Info()
12368}
12369
12370// Test validates putObject with context to see if request cancellation is honored for V2.
12371func testPutObjectContextV2() {
12372 // initialize logging params
12373 startTime := time.Now()
12374 testName := getFuncName()
12375 function := "PutObject(ctx, bucketName, objectName, reader, size, opts)"
12376 args := map[string]interface{}{
12377 "ctx": "",
12378 "bucketName": "",
12379 "objectName": "",
12380 "size": "",
12381 "opts": "",
12382 }
12383 // Instantiate new minio client object.
12384 c, err := minio.New(os.Getenv(serverEndpoint),
12385 &minio.Options{
12386 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
12387 Secure: mustParseBool(os.Getenv(enableHTTPS)),
12388 })
12389 if err != nil {
12390 logError(testName, function, args, startTime, "", "MinIO client v2 object creation failed", err)
12391 return
12392 }
12393
12394 // Enable tracing, write to stderr.
12395 // c.TraceOn(os.Stderr)
12396
12397 // Set user agent.
12398 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
12399
12400 // Make a new bucket.
12401 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
12402 args["bucketName"] = bucketName
12403
12404 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
12405 if err != nil {
12406 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
12407 return
12408 }
12409 defer cleanupBucket(bucketName, c)
12410 bufSize := dataFileMap["datatfile-33-kB"]
12411 reader := getDataReader("datafile-33-kB")
12412 defer reader.Close()
12413
12414 objectName := fmt.Sprintf("test-file-%v", rand.Uint32())
12415 args["objectName"] = objectName
12416
12417 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
12418 args["ctx"] = ctx
12419 args["size"] = bufSize
12420 defer cancel()
12421
12422 _, err = c.PutObject(ctx, bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
12423 if err != nil {
12424 logError(testName, function, args, startTime, "", "PutObject with short timeout failed", err)
12425 return
12426 }
12427
12428 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
12429 args["ctx"] = ctx
12430
12431 defer cancel()
12432 reader = getDataReader("datafile-33-kB")
12433 defer reader.Close()
12434 _, err = c.PutObject(ctx, bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
12435 if err != nil {
12436 logError(testName, function, args, startTime, "", "PutObject with long timeout failed", err)
12437 return
12438 }
12439
12440 successLogger(testName, function, args, startTime).Info()
12441}
12442
12443// Test get object with GetObject with custom context
12444func testGetObjectContextV2() {
12445 // initialize logging params
12446 startTime := time.Now()
12447 testName := getFuncName()
12448 function := "GetObject(ctx, bucketName, objectName)"
12449 args := map[string]interface{}{
12450 "ctx": "",
12451 "bucketName": "",
12452 "objectName": "",
12453 }
12454 // Seed random based on current time.
12455 rand.Seed(time.Now().Unix())
12456
12457 // Instantiate new minio client object.
12458 c, err := minio.New(os.Getenv(serverEndpoint),
12459 &minio.Options{
12460 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
12461 Secure: mustParseBool(os.Getenv(enableHTTPS)),
12462 })
12463 if err != nil {
12464 logError(testName, function, args, startTime, "", "MinIO client v2 object creation failed", err)
12465 return
12466 }
12467
12468 // Enable tracing, write to stderr.
12469 // c.TraceOn(os.Stderr)
12470
12471 // Set user agent.
12472 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
12473
12474 // Generate a new random bucket name.
12475 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
12476 args["bucketName"] = bucketName
12477
12478 // Make a new bucket.
12479 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
12480 if err != nil {
12481 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
12482 return
12483 }
12484
12485 defer cleanupBucket(bucketName, c)
12486
12487 bufSize := dataFileMap["datafile-33-kB"]
12488 reader := getDataReader("datafile-33-kB")
12489 defer reader.Close()
12490 // Save the data
12491 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
12492 args["objectName"] = objectName
12493
12494 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
12495 if err != nil {
12496 logError(testName, function, args, startTime, "", "PutObject call failed", err)
12497 return
12498 }
12499
12500 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
12501 args["ctx"] = ctx
12502 cancel()
12503
12504 r, err := c.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
12505 if err != nil {
12506 logError(testName, function, args, startTime, "", "GetObject failed unexpectedly", err)
12507 return
12508 }
12509 if _, err = r.Stat(); err == nil {
12510 logError(testName, function, args, startTime, "", "GetObject should fail on short timeout", err)
12511 return
12512 }
12513 r.Close()
12514
12515 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
12516 defer cancel()
12517
12518 // Read the data back
12519 r, err = c.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
12520 if err != nil {
12521 logError(testName, function, args, startTime, "", "GetObject shouldn't fail on longer timeout", err)
12522 return
12523 }
12524
12525 st, err := r.Stat()
12526 if err != nil {
12527 logError(testName, function, args, startTime, "", "object Stat call failed", err)
12528 return
12529 }
12530 if st.Size != int64(bufSize) {
12531 logError(testName, function, args, startTime, "", "Number of bytes in stat does not match, expected "+string(bufSize)+" got "+string(st.Size), err)
12532 return
12533 }
12534 if err := r.Close(); err != nil {
12535 logError(testName, function, args, startTime, "", " object Close() call failed", err)
12536 return
12537 }
12538
12539 successLogger(testName, function, args, startTime).Info()
12540}
12541
12542// Test get object with FGetObject with custom context
12543func testFGetObjectContextV2() {
12544 // initialize logging params
12545 startTime := time.Now()
12546 testName := getFuncName()
12547 function := "FGetObject(ctx, bucketName, objectName,fileName)"
12548 args := map[string]interface{}{
12549 "ctx": "",
12550 "bucketName": "",
12551 "objectName": "",
12552 "fileName": "",
12553 }
12554 // Seed random based on current time.
12555 rand.Seed(time.Now().Unix())
12556
12557 // Instantiate new minio client object.
12558 c, err := minio.New(os.Getenv(serverEndpoint),
12559 &minio.Options{
12560 Creds: credentials.NewStaticV2(os.Getenv(accessKey), os.Getenv(secretKey), ""),
12561 Secure: mustParseBool(os.Getenv(enableHTTPS)),
12562 })
12563 if err != nil {
12564 logError(testName, function, args, startTime, "", "MinIO client v2 object creation failed", err)
12565 return
12566 }
12567
12568 // Enable tracing, write to stderr.
12569 // c.TraceOn(os.Stderr)
12570
12571 // Set user agent.
12572 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
12573
12574 // Generate a new random bucket name.
12575 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
12576 args["bucketName"] = bucketName
12577
12578 // Make a new bucket.
12579 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
12580 if err != nil {
12581 logError(testName, function, args, startTime, "", "MakeBucket call failed", err)
12582 return
12583 }
12584
12585 defer cleanupBucket(bucketName, c)
12586
12587 bufSize := dataFileMap["datatfile-1-MB"]
12588 reader := getDataReader("datafile-1-MB")
12589 defer reader.Close()
12590 // Save the data
12591 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
12592 args["objectName"] = objectName
12593
12594 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"})
12595 if err != nil {
12596 logError(testName, function, args, startTime, "", "PutObject call failed", err)
12597 return
12598 }
12599
12600 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
12601 args["ctx"] = ctx
12602 defer cancel()
12603
12604 fileName := "tempfile-context"
12605 args["fileName"] = fileName
12606
12607 // Read the data back
12608 err = c.FGetObject(ctx, bucketName, objectName, fileName+"-f", minio.GetObjectOptions{})
12609 if err == nil {
12610 logError(testName, function, args, startTime, "", "FGetObject should fail on short timeout", err)
12611 return
12612 }
12613 ctx, cancel = context.WithTimeout(context.Background(), 1*time.Hour)
12614 defer cancel()
12615
12616 // Read the data back
12617 err = c.FGetObject(ctx, bucketName, objectName, fileName+"-fcontext", minio.GetObjectOptions{})
12618 if err != nil {
12619 logError(testName, function, args, startTime, "", "FGetObject call shouldn't fail on long timeout", err)
12620 return
12621 }
12622
12623 if err = os.Remove(fileName + "-fcontext"); err != nil {
12624 logError(testName, function, args, startTime, "", "Remove file failed", err)
12625 return
12626 }
12627
12628 successLogger(testName, function, args, startTime).Info()
12629}
12630
12631// Test list object v1 and V2
12632func testListObjects() {
12633 // initialize logging params
12634 startTime := time.Now()
12635 testName := getFuncName()
12636 function := "ListObjects(bucketName, objectPrefix, recursive, doneCh)"
12637 args := map[string]interface{}{
12638 "bucketName": "",
12639 "objectPrefix": "",
12640 "recursive": "true",
12641 }
12642 // Seed random based on current time.
12643 rand.Seed(time.Now().Unix())
12644
12645 // Instantiate new minio client object.
12646 c, err := minio.New(os.Getenv(serverEndpoint),
12647 &minio.Options{
12648 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
12649 Secure: mustParseBool(os.Getenv(enableHTTPS)),
12650 })
12651 if err != nil {
12652 logError(testName, function, args, startTime, "", "MinIO client v4 object creation failed", err)
12653 return
12654 }
12655
12656 // Enable tracing, write to stderr.
12657 // c.TraceOn(os.Stderr)
12658
12659 // Set user agent.
12660 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
12661
12662 // Generate a new random bucket name.
12663 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
12664 args["bucketName"] = bucketName
12665
12666 // Make a new bucket.
12667 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"})
12668 if err != nil {
12669 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
12670 return
12671 }
12672
12673 defer cleanupBucket(bucketName, c)
12674
12675 testObjects := []struct {
12676 name string
12677 storageClass string
12678 }{
12679 // Special characters
12680 {"foo bar", "STANDARD"},
12681 {"foo-%", "STANDARD"},
12682 {"random-object-1", "STANDARD"},
12683 {"random-object-2", "REDUCED_REDUNDANCY"},
12684 }
12685
12686 for i, object := range testObjects {
12687 bufSize := dataFileMap["datafile-33-kB"]
12688 reader := getDataReader("datafile-33-kB")
12689 defer reader.Close()
12690 _, err = c.PutObject(context.Background(), bucketName, object.name, reader, int64(bufSize),
12691 minio.PutObjectOptions{ContentType: "binary/octet-stream", StorageClass: object.storageClass})
12692 if err != nil {
12693 logError(testName, function, args, startTime, "", fmt.Sprintf("PutObject %d call failed", i+1), err)
12694 return
12695 }
12696 }
12697
12698 testList := func(listFn func(context.Context, string, minio.ListObjectsOptions) <-chan minio.ObjectInfo, bucket string, opts minio.ListObjectsOptions) {
12699 var objCursor int
12700
12701 // check for object name and storage-class from listing object result
12702 for objInfo := range listFn(context.Background(), bucket, opts) {
12703 if objInfo.Err != nil {
12704 logError(testName, function, args, startTime, "", "ListObjects failed unexpectedly", err)
12705 return
12706 }
12707 if objInfo.Key != testObjects[objCursor].name {
12708 logError(testName, function, args, startTime, "", "ListObjects does not return expected object name", err)
12709 return
12710 }
12711 if objInfo.StorageClass != testObjects[objCursor].storageClass {
12712 // Ignored as Gateways (Azure/GCS etc) wont return storage class
12713 ignoredLog(testName, function, args, startTime, "ListObjects doesn't return expected storage class").Info()
12714 }
12715 objCursor++
12716 }
12717
12718 if objCursor != len(testObjects) {
12719 logError(testName, function, args, startTime, "", "ListObjects returned unexpected number of items", errors.New(""))
12720 return
12721 }
12722 }
12723
12724 testList(c.ListObjects, bucketName, minio.ListObjectsOptions{Recursive: true, UseV1: true})
12725 testList(c.ListObjects, bucketName, minio.ListObjectsOptions{Recursive: true})
12726 testList(c.ListObjects, bucketName, minio.ListObjectsOptions{Recursive: true, WithMetadata: true})
12727
12728 successLogger(testName, function, args, startTime).Info()
12729}
12730
12731// Test deleting multiple objects with object retention set in Governance mode
12732func testRemoveObjects() {
12733 // initialize logging params
12734 startTime := time.Now()
12735 testName := getFuncName()
12736 function := "RemoveObjects(bucketName, objectsCh, opts)"
12737 args := map[string]interface{}{
12738 "bucketName": "",
12739 "objectPrefix": "",
12740 "recursive": "true",
12741 }
12742 // Seed random based on current time.
12743 rand.Seed(time.Now().Unix())
12744
12745 // Instantiate new minio client object.
12746 c, err := minio.New(os.Getenv(serverEndpoint),
12747 &minio.Options{
12748 Creds: credentials.NewStaticV4(os.Getenv(accessKey), os.Getenv(secretKey), ""),
12749 Secure: mustParseBool(os.Getenv(enableHTTPS)),
12750 })
12751 if err != nil {
12752 logError(testName, function, args, startTime, "", "MinIO client v4 object creation failed", err)
12753 return
12754 }
12755
12756 // Enable tracing, write to stderr.
12757 // c.TraceOn(os.Stderr)
12758
12759 // Set user agent.
12760 c.SetAppInfo("MinIO-go-FunctionalTest", "0.1.0")
12761
12762 // Generate a new random bucket name.
12763 bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-")
12764 args["bucketName"] = bucketName
12765 objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
12766 args["objectName"] = objectName
12767
12768 // Make a new bucket.
12769 err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
12770 if err != nil {
12771 logError(testName, function, args, startTime, "", "MakeBucket failed", err)
12772 return
12773 }
12774
12775 bufSize := dataFileMap["datafile-129-MB"]
12776 reader := getDataReader("datafile-129-MB")
12777 defer reader.Close()
12778
12779 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{})
12780 if err != nil {
12781 logError(testName, function, args, startTime, "", "Error uploading object", err)
12782 return
12783 }
12784
12785 // Replace with smaller...
12786 bufSize = dataFileMap["datafile-10-kB"]
12787 reader = getDataReader("datafile-10-kB")
12788 defer reader.Close()
12789
12790 _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{})
12791 if err != nil {
12792 logError(testName, function, args, startTime, "", "Error uploading object", err)
12793 }
12794
12795 t := time.Date(2030, time.April, 25, 14, 0, 0, 0, time.UTC)
12796 m := minio.RetentionMode(minio.Governance)
12797 opts := minio.PutObjectRetentionOptions{
12798 GovernanceBypass: false,
12799 RetainUntilDate: &t,
12800 Mode: &m,
12801 }
12802 err = c.PutObjectRetention(context.Background(), bucketName, objectName, opts)
12803 if err != nil {
12804 logError(testName, function, args, startTime, "", "Error setting retention", err)
12805 return
12806 }
12807
12808 objectsCh := make(chan minio.ObjectInfo)
12809 // Send object names that are needed to be removed to objectsCh
12810 go func() {
12811 defer close(objectsCh)
12812 // List all objects from a bucket-name with a matching prefix.
12813 for object := range c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{UseV1: true, Recursive: true}) {
12814 if object.Err != nil {
12815 logError(testName, function, args, startTime, "", "Error listing objects", object.Err)
12816 return
12817 }
12818 objectsCh <- object
12819 }
12820 }()
12821
12822 for rErr := range c.RemoveObjects(context.Background(), bucketName, objectsCh, minio.RemoveObjectsOptions{}) {
12823 // Error is expected here because Retention is set on the object
12824 // and RemoveObjects is called without Bypass Governance
12825 if rErr.Err == nil {
12826 logError(testName, function, args, startTime, "", "Expected error during deletion", nil)
12827 return
12828 }
12829 }
12830
12831 objectsCh1 := make(chan minio.ObjectInfo)
12832
12833 // Send object names that are needed to be removed to objectsCh
12834 go func() {
12835 defer close(objectsCh1)
12836 // List all objects from a bucket-name with a matching prefix.
12837 for object := range c.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{UseV1: true, Recursive: true}) {
12838 if object.Err != nil {
12839 logError(testName, function, args, startTime, "", "Error listing objects", object.Err)
12840 return
12841 }
12842 objectsCh1 <- object
12843 }
12844 }()
12845
12846 opts1 := minio.RemoveObjectsOptions{
12847 GovernanceBypass: true,
12848 }
12849
12850 for rErr := range c.RemoveObjects(context.Background(), bucketName, objectsCh1, opts1) {
12851 // Error is not expected here because Retention is set on the object
12852 // and RemoveObjects is called with Bypass Governance
12853 logError(testName, function, args, startTime, "", "Error detected during deletion", rErr.Err)
12854 return
12855 }
12856
12857 // Delete all objects and buckets
12858 if err = cleanupVersionedBucket(bucketName, c); err != nil {
12859 logError(testName, function, args, startTime, "", "CleanupBucket failed", err)
12860 return
12861 }
12862
12863 successLogger(testName, function, args, startTime).Info()
12864}
12865
12866// Convert string to bool and always return false if any error
12867func mustParseBool(str string) bool {
12868 b, err := strconv.ParseBool(str)
12869 if err != nil {
12870 return false
12871 }
12872 return b
12873}
12874
12875func main() {
12876 // Output to stdout instead of the default stderr
12877 log.SetOutput(os.Stdout)
12878 // create custom formatter
12879 mintFormatter := mintJSONFormatter{}
12880 // set custom formatter
12881 log.SetFormatter(&mintFormatter)
12882 // log Info or above -- success cases are Info level, failures are Fatal level
12883 log.SetLevel(log.InfoLevel)
12884
12885 tls := mustParseBool(os.Getenv(enableHTTPS))
12886 kms := mustParseBool(os.Getenv(enableKMS))
12887 if os.Getenv(enableKMS) == "" {
12888 // Default to KMS tests.
12889 kms = true
12890 }
12891
12892 // execute tests
12893 if isFullMode() {
12894 testMakeBucketErrorV2()
12895 testGetObjectClosedTwiceV2()
12896 testFPutObjectV2()
12897 testMakeBucketRegionsV2()
12898 testGetObjectReadSeekFunctionalV2()
12899 testGetObjectReadAtFunctionalV2()
12900 testGetObjectRanges()
12901 testCopyObjectV2()
12902 testFunctionalV2()
12903 testComposeObjectErrorCasesV2()
12904 testCompose10KSourcesV2()
12905 testUserMetadataCopyingV2()
12906 testPutObjectWithChecksums()
12907 testPutMultipartObjectWithChecksums()
12908 testPutObject0ByteV2()
12909 testPutObjectNoLengthV2()
12910 testPutObjectsUnknownV2()
12911 testGetObjectContextV2()
12912 testFPutObjectContextV2()
12913 testFGetObjectContextV2()
12914 testPutObjectContextV2()
12915 testPutObjectWithVersioning()
12916 testMakeBucketError()
12917 testMakeBucketRegions()
12918 testPutObjectWithMetadata()
12919 testPutObjectReadAt()
12920 testPutObjectStreaming()
12921 testGetObjectSeekEnd()
12922 testGetObjectClosedTwice()
12923 testGetObjectS3Zip()
12924 testRemoveMultipleObjects()
12925 testRemoveMultipleObjectsWithResult()
12926 testFPutObjectMultipart()
12927 testFPutObject()
12928 testGetObjectReadSeekFunctional()
12929 testGetObjectReadAtFunctional()
12930 testGetObjectReadAtWhenEOFWasReached()
12931 testPresignedPostPolicy()
12932 testCopyObject()
12933 testComposeObjectErrorCases()
12934 testCompose10KSources()
12935 testUserMetadataCopying()
12936 testBucketNotification()
12937 testFunctional()
12938 testGetObjectModified()
12939 testPutObjectUploadSeekedObject()
12940 testGetObjectContext()
12941 testFPutObjectContext()
12942 testFGetObjectContext()
12943 testGetObjectACLContext()
12944 testPutObjectContext()
12945 testStorageClassMetadataPutObject()
12946 testStorageClassInvalidMetadataPutObject()
12947 testStorageClassMetadataCopyObject()
12948 testPutObjectWithContentLanguage()
12949 testListObjects()
12950 testRemoveObjects()
12951 testListObjectVersions()
12952 testStatObjectWithVersioning()
12953 testGetObjectWithVersioning()
12954 testCopyObjectWithVersioning()
12955 testConcurrentCopyObjectWithVersioning()
12956 testComposeObjectWithVersioning()
12957 testRemoveObjectWithVersioning()
12958 testRemoveObjectsWithVersioning()
12959 testObjectTaggingWithVersioning()
12960 testTrailingChecksums()
12961 testPutObjectWithAutomaticChecksums()
12962
12963 // SSE-C tests will only work over TLS connection.
12964 if tls {
12965 testSSECEncryptionPutGet()
12966 testSSECEncryptionFPut()
12967 testSSECEncryptedGetObjectReadAtFunctional()
12968 testSSECEncryptedGetObjectReadSeekFunctional()
12969 testEncryptedCopyObjectV2()
12970 testEncryptedSSECToSSECCopyObject()
12971 testEncryptedSSECToUnencryptedCopyObject()
12972 testUnencryptedToSSECCopyObject()
12973 testUnencryptedToUnencryptedCopyObject()
12974 testEncryptedEmptyObject()
12975 testDecryptedCopyObject()
12976 testSSECEncryptedToSSECCopyObjectPart()
12977 testSSECMultipartEncryptedToSSECCopyObjectPart()
12978 testSSECEncryptedToUnencryptedCopyPart()
12979 testUnencryptedToSSECCopyObjectPart()
12980 testUnencryptedToUnencryptedCopyPart()
12981 testEncryptedSSECToSSES3CopyObject()
12982 testEncryptedSSES3ToSSECCopyObject()
12983 testSSECEncryptedToSSES3CopyObjectPart()
12984 testSSES3EncryptedToSSECCopyObjectPart()
12985 }
12986
12987 // KMS tests
12988 if kms {
12989 testSSES3EncryptionPutGet()
12990 testSSES3EncryptionFPut()
12991 testSSES3EncryptedGetObjectReadAtFunctional()
12992 testSSES3EncryptedGetObjectReadSeekFunctional()
12993 testEncryptedSSES3ToSSES3CopyObject()
12994 testEncryptedSSES3ToUnencryptedCopyObject()
12995 testUnencryptedToSSES3CopyObject()
12996 testUnencryptedToSSES3CopyObjectPart()
12997 testSSES3EncryptedToUnencryptedCopyPart()
12998 testSSES3EncryptedToSSES3CopyObjectPart()
12999 }
13000 } else {
13001 testFunctional()
13002 testFunctionalV2()
13003 }
13004}