mirror of
https://github.com/minio/minio.git
synced 2024-12-29 08:33:21 -05:00
1bc32215b9
enable linter using golangci-lint across codebase to run a bunch of linters together, we shall enable new linters as we fix more things the codebase. This PR fixes the first stage of this cleanup.
1067 lines
34 KiB
Go
1067 lines
34 KiB
Go
/*
|
|
*
|
|
* Mint, (C) 2017 Minio, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"net/http"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"github.com/aws/aws-sdk-go/service/s3"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const letterBytes = "abcdefghijklmnopqrstuvwxyz01234569"
|
|
const (
|
|
letterIdxBits = 6 // 6 bits to represent a letter index
|
|
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
|
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
|
)
|
|
|
|
// different kinds of test failures
|
|
const (
|
|
PASS = "PASS" // Indicate that a test passed
|
|
FAIL = "FAIL" // Indicate that a test failed
|
|
)
|
|
|
|
type errorResponse struct {
|
|
XMLName xml.Name `xml:"Error" json:"-"`
|
|
Code string
|
|
Message string
|
|
BucketName string
|
|
Key string
|
|
RequestID string `xml:"RequestId"`
|
|
HostID string `xml:"HostId"`
|
|
|
|
// Region where the bucket is located. This header is returned
|
|
// only in HEAD bucket and ListObjects response.
|
|
Region string
|
|
|
|
// Headers of the returned S3 XML error
|
|
Headers http.Header `xml:"-" json:"-"`
|
|
}
|
|
|
|
type mintJSONFormatter struct {
|
|
}
|
|
|
|
func (f *mintJSONFormatter) Format(entry *log.Entry) ([]byte, error) {
|
|
data := make(log.Fields, len(entry.Data))
|
|
for k, v := range entry.Data {
|
|
switch v := v.(type) {
|
|
case error:
|
|
// Otherwise errors are ignored by `encoding/json`
|
|
// https://github.com/sirupsen/logrus/issues/137
|
|
data[k] = v.Error()
|
|
default:
|
|
data[k] = v
|
|
}
|
|
}
|
|
|
|
serialized, err := json.Marshal(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
|
}
|
|
return append(serialized, '\n'), nil
|
|
}
|
|
|
|
// log successful test runs
|
|
func successLogger(function string, args map[string]interface{}, startTime time.Time) *log.Entry {
|
|
// calculate the test case duration
|
|
duration := time.Since(startTime)
|
|
// log with the fields as per mint
|
|
fields := log.Fields{"name": "aws-sdk-go", "function": function, "args": args, "duration": duration.Nanoseconds() / 1000000, "status": PASS}
|
|
return log.WithFields(fields)
|
|
}
|
|
|
|
// log failed test runs
|
|
func failureLog(function string, args map[string]interface{}, startTime time.Time, alert string, message string, err error) *log.Entry {
|
|
// calculate the test case duration
|
|
duration := time.Since(startTime)
|
|
var fields log.Fields
|
|
// log with the fields as per mint
|
|
if err != nil {
|
|
fields = log.Fields{"name": "aws-sdk-go", "function": function, "args": args,
|
|
"duration": duration.Nanoseconds() / 1000000, "status": FAIL, "alert": alert, "message": message, "error": err}
|
|
} else {
|
|
fields = log.Fields{"name": "aws-sdk-go", "function": function, "args": args,
|
|
"duration": duration.Nanoseconds() / 1000000, "status": FAIL, "alert": alert, "message": message}
|
|
}
|
|
return log.WithFields(fields)
|
|
}
|
|
|
|
func randString(n int, src rand.Source, prefix string) string {
|
|
b := make([]byte, n)
|
|
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
|
|
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
|
|
if remain == 0 {
|
|
cache, remain = src.Int63(), letterIdxMax
|
|
}
|
|
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
|
b[i] = letterBytes[idx]
|
|
i--
|
|
}
|
|
cache >>= letterIdxBits
|
|
remain--
|
|
}
|
|
return prefix + string(b[0:30-len(prefix)])
|
|
}
|
|
|
|
func isObjectTaggingImplemented(s3Client *s3.S3) bool {
|
|
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
object := randString(60, rand.NewSource(time.Now().UnixNano()), "")
|
|
startTime := time.Now()
|
|
function := "isObjectTaggingImplemented"
|
|
args := map[string]interface{}{
|
|
"bucketName": bucket,
|
|
"objectName": object,
|
|
}
|
|
defer cleanup(s3Client, bucket, object, function, args, startTime, true)
|
|
|
|
_, err := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucket),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", err).Fatal()
|
|
return false
|
|
}
|
|
|
|
_, err = s3Client.PutObject(&s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader("testfile")),
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
})
|
|
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to success but got %v", err), err).Fatal()
|
|
return false
|
|
}
|
|
|
|
_, err = s3Client.GetObjectTagging(&s3.GetObjectTaggingInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
})
|
|
if err != nil {
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
|
if awsErr.Code() == "NotImplemented" {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func cleanup(s3Client *s3.S3, bucket string, object string, function string,
|
|
args map[string]interface{}, startTime time.Time, deleteBucket bool) {
|
|
|
|
// Deleting the object, just in case it was created. Will not check for errors.
|
|
s3Client.DeleteObject(&s3.DeleteObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
})
|
|
|
|
if deleteBucket {
|
|
_, err := s3Client.DeleteBucket(&s3.DeleteBucketInput{
|
|
Bucket: aws.String(bucket),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go DeleteBucket Failed", err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func testPresignedPutInvalidHash(s3Client *s3.S3) {
|
|
startTime := time.Now()
|
|
function := "PresignedPut"
|
|
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
object := "presignedTest"
|
|
expiry := 1 * time.Minute
|
|
args := map[string]interface{}{
|
|
"bucketName": bucket,
|
|
"objectName": object,
|
|
"expiry": expiry,
|
|
}
|
|
|
|
_, err := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucket),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", err).Fatal()
|
|
return
|
|
}
|
|
defer cleanup(s3Client, bucket, object, function, args, startTime, true)
|
|
|
|
req, _ := s3Client.PutObjectRequest(&s3.PutObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
ContentType: aws.String("application/octet-stream"),
|
|
})
|
|
|
|
req.HTTPRequest.Header.Set("X-Amz-Content-Sha256", "invalid-sha256")
|
|
url, err := req.Presign(expiry)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go presigned Put request creation failed", err).Fatal()
|
|
return
|
|
}
|
|
|
|
rreq, err := http.NewRequest("PUT", url, bytes.NewReader([]byte("")))
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go presigned PUT request failed", err).Fatal()
|
|
return
|
|
}
|
|
|
|
rreq.Header.Add("X-Amz-Content-Sha256", "invalid-sha256")
|
|
rreq.Header.Add("Content-Type", "application/octet-stream")
|
|
|
|
resp, err := http.DefaultClient.Do(rreq)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go presigned put request failed", err).Fatal()
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
dec := xml.NewDecoder(resp.Body)
|
|
errResp := errorResponse{}
|
|
err = dec.Decode(&errResp)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go unmarshalling xml failed", err).Fatal()
|
|
return
|
|
}
|
|
|
|
if errResp.Code != "XAmzContentSHA256Mismatch" {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go presigned PUT expected to fail with XAmzContentSHA256Mismatch but got %v", errResp.Code), errors.New("AWS S3 error code mismatch")).Fatal()
|
|
return
|
|
}
|
|
|
|
successLogger(function, args, startTime).Info()
|
|
}
|
|
|
|
func testListObjects(s3Client *s3.S3) {
|
|
startTime := time.Now()
|
|
function := "testListObjects"
|
|
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
object1 := "testObject1"
|
|
object2 := "testObject2"
|
|
expiry := 1 * time.Minute
|
|
args := map[string]interface{}{
|
|
"bucketName": bucket,
|
|
"objectName1": object1,
|
|
"objectName2": object2,
|
|
"expiry": expiry,
|
|
}
|
|
|
|
getKeys := func(objects []*s3.Object) []string {
|
|
var rv []string
|
|
for _, obj := range objects {
|
|
rv = append(rv, *obj.Key)
|
|
}
|
|
return rv
|
|
}
|
|
_, err := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucket),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", err).Fatal()
|
|
return
|
|
}
|
|
defer cleanup(s3Client, bucket, object1, function, args, startTime, true)
|
|
defer cleanup(s3Client, bucket, object2, function, args, startTime, false)
|
|
|
|
listInput := &s3.ListObjectsV2Input{
|
|
Bucket: aws.String(bucket),
|
|
MaxKeys: aws.Int64(1000),
|
|
Prefix: aws.String(""),
|
|
}
|
|
result, err := s3Client.ListObjectsV2(listInput)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go listobjects expected to success but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
if *result.KeyCount != 0 {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go listobjects with prefix '' expected 0 key but got %v, %v", result.KeyCount, getKeys(result.Contents)), errors.New("AWS S3 key count mismatch")).Fatal()
|
|
return
|
|
}
|
|
putInput1 := &s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")),
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object1),
|
|
}
|
|
_, err = s3Client.PutObject(putInput1)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to success but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
putInput2 := &s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")),
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object2),
|
|
}
|
|
_, err = s3Client.PutObject(putInput2)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to success but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
result, err = s3Client.ListObjectsV2(listInput)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go listobjects expected to success but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
if *result.KeyCount != 2 {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go listobjects with prefix '' expected 2 key but got %v, %v", *result.KeyCount, getKeys(result.Contents)), errors.New("AWS S3 key count mismatch")).Fatal()
|
|
return
|
|
}
|
|
|
|
successLogger(function, args, startTime).Info()
|
|
}
|
|
|
|
func testSelectObject(s3Client *s3.S3) {
|
|
startTime := time.Now()
|
|
function := "testSelectObject"
|
|
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
object1 := "object1.csv"
|
|
object2 := "object2.csv"
|
|
args := map[string]interface{}{
|
|
"bucketName": bucket,
|
|
"objectName1": object1,
|
|
"objectName2": object2,
|
|
}
|
|
|
|
_, err := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucket),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", err).Fatal()
|
|
return
|
|
}
|
|
|
|
// Test comma field separator
|
|
inputCsv1 := `year,gender,ethnicity,firstname,count,rank
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,SOPHIA,119,1
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,CHLOE,106,2
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,EMILY,93,3
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,OLIVIA,89,4
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,EMMA,75,5
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,ISABELLA,67,6
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,TIFFANY,54,7
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,ASHLEY,52,8
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,FIONA,48,9
|
|
2011,FEMALE,ASIAN AND PACIFIC ISLANDER,ANGELA,47,10
|
|
`
|
|
|
|
outputCSV1 := `2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
`
|
|
|
|
putInput1 := &s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader(inputCsv1)),
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object1),
|
|
}
|
|
_, err = s3Client.PutObject(putInput1)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go Select object failed %v", err), err).Fatal()
|
|
return
|
|
}
|
|
|
|
defer cleanup(s3Client, bucket, object1, function, args, startTime, true)
|
|
|
|
params := &s3.SelectObjectContentInput{
|
|
Bucket: &bucket,
|
|
Key: &object1,
|
|
ExpressionType: aws.String(s3.ExpressionTypeSql),
|
|
Expression: aws.String("SELECT s._1 FROM S3Object s"),
|
|
RequestProgress: &s3.RequestProgress{},
|
|
InputSerialization: &s3.InputSerialization{
|
|
CompressionType: aws.String("NONE"),
|
|
CSV: &s3.CSVInput{
|
|
FileHeaderInfo: aws.String(s3.FileHeaderInfoIgnore),
|
|
FieldDelimiter: aws.String(","),
|
|
RecordDelimiter: aws.String("\n"),
|
|
},
|
|
},
|
|
OutputSerialization: &s3.OutputSerialization{
|
|
CSV: &s3.CSVOutput{},
|
|
},
|
|
}
|
|
|
|
resp, err := s3Client.SelectObjectContent(params)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go Select object failed %v", err), err).Fatal()
|
|
return
|
|
}
|
|
defer resp.EventStream.Close()
|
|
|
|
payload := ""
|
|
for event := range resp.EventStream.Events() {
|
|
switch v := event.(type) {
|
|
case *s3.RecordsEvent:
|
|
// s3.RecordsEvent.Records is a byte slice of select records
|
|
payload = string(v.Payload)
|
|
}
|
|
}
|
|
|
|
if err := resp.EventStream.Err(); err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go Select object failed %v", err), err).Fatal()
|
|
return
|
|
}
|
|
|
|
if payload != outputCSV1 {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go Select object output mismatch %v", payload), errors.New("AWS S3 select object mismatch")).Fatal()
|
|
return
|
|
}
|
|
|
|
// Test unicode field separator
|
|
inputCsv2 := `"year"╦"gender"╦"ethnicity"╦"firstname"╦"count"╦"rank"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"SOPHIA"╦"119"╦"1"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"CHLOE"╦"106"╦"2"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"EMILY"╦"93"╦"3"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"OLIVIA"╦"89"╦"4"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"EMMA"╦"75"╦"5"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"ISABELLA"╦"67"╦"6"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"TIFFANY"╦"54"╦"7"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"ASHLEY"╦"52"╦"8"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"FIONA"╦"48"╦"9"
|
|
"2011"╦"FEMALE"╦"ASIAN AND PACIFIC ISLANDER"╦"ANGELA"╦"47"╦"10"
|
|
`
|
|
|
|
outputCSV2 := `2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
2011
|
|
`
|
|
|
|
putInput2 := &s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader(inputCsv2)),
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object2),
|
|
}
|
|
_, err = s3Client.PutObject(putInput2)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go Select object upload failed: %v", err), err).Fatal()
|
|
return
|
|
}
|
|
|
|
defer cleanup(s3Client, bucket, object2, function, args, startTime, false)
|
|
|
|
params2 := &s3.SelectObjectContentInput{
|
|
Bucket: &bucket,
|
|
Key: &object2,
|
|
ExpressionType: aws.String(s3.ExpressionTypeSql),
|
|
Expression: aws.String("SELECT s._1 FROM S3Object s"),
|
|
RequestProgress: &s3.RequestProgress{},
|
|
InputSerialization: &s3.InputSerialization{
|
|
CompressionType: aws.String("NONE"),
|
|
CSV: &s3.CSVInput{
|
|
FileHeaderInfo: aws.String(s3.FileHeaderInfoIgnore),
|
|
FieldDelimiter: aws.String("╦"),
|
|
RecordDelimiter: aws.String("\n"),
|
|
},
|
|
},
|
|
OutputSerialization: &s3.OutputSerialization{
|
|
CSV: &s3.CSVOutput{},
|
|
},
|
|
}
|
|
|
|
resp, err = s3Client.SelectObjectContent(params2)
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go Select object failed for unicode separator %v", err), err).Fatal()
|
|
return
|
|
}
|
|
defer resp.EventStream.Close()
|
|
|
|
for event := range resp.EventStream.Events() {
|
|
switch v := event.(type) {
|
|
case *s3.RecordsEvent:
|
|
// s3.RecordsEvent.Records is a byte slice of select records
|
|
payload = string(v.Payload)
|
|
}
|
|
}
|
|
|
|
if err := resp.EventStream.Err(); err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go Select object failed for unicode separator %v", err), err).Fatal()
|
|
return
|
|
}
|
|
|
|
if payload != outputCSV2 {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go Select object output mismatch %v", payload), errors.New("AWS S3 select object mismatch")).Fatal()
|
|
return
|
|
}
|
|
|
|
successLogger(function, args, startTime).Info()
|
|
}
|
|
|
|
func testObjectTagging(s3Client *s3.S3) {
|
|
startTime := time.Now()
|
|
function := "testObjectTagging"
|
|
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
object := randString(60, rand.NewSource(time.Now().UnixNano()), "")
|
|
args := map[string]interface{}{
|
|
"bucketName": bucket,
|
|
"objectName": object,
|
|
}
|
|
|
|
_, err := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucket),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", err).Fatal()
|
|
return
|
|
}
|
|
defer cleanup(s3Client, bucket, object, function, args, startTime, true)
|
|
|
|
taginput := "Tag1=Value1"
|
|
tagInputSet := []*s3.Tag{
|
|
{
|
|
Key: aws.String("Tag1"),
|
|
Value: aws.String("Value1"),
|
|
},
|
|
}
|
|
_, err = s3Client.PutObject(&s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader("testfile")),
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
Tagging: &taginput,
|
|
})
|
|
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to success but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
|
|
tagop, err := s3Client.GetObjectTagging(&s3.GetObjectTaggingInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
})
|
|
if err != nil {
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUTObjectTagging expected to success but got %v", awsErr.Code()), err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
if !reflect.DeepEqual(tagop.TagSet, tagInputSet) {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUTObject Tag input did not match with GetObjectTagging output %v", nil), nil).Fatal()
|
|
return
|
|
}
|
|
|
|
taginputSet1 := []*s3.Tag{
|
|
{
|
|
Key: aws.String("Key4"),
|
|
Value: aws.String("Value4"),
|
|
},
|
|
}
|
|
_, err = s3Client.PutObjectTagging(&s3.PutObjectTaggingInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
Tagging: &s3.Tagging{
|
|
TagSet: taginputSet1,
|
|
},
|
|
})
|
|
if err != nil {
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUTObjectTagging expected to success but got %v", awsErr.Code()), err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
|
|
tagop, err = s3Client.GetObjectTagging(&s3.GetObjectTaggingInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
})
|
|
if err != nil {
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUTObjectTagging expected to success but got %v", awsErr.Code()), err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
if !reflect.DeepEqual(tagop.TagSet, taginputSet1) {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUTObjectTagging input did not match with GetObjectTagging output %v", nil), nil).Fatal()
|
|
return
|
|
}
|
|
successLogger(function, args, startTime).Info()
|
|
}
|
|
|
|
func testObjectTaggingErrors(s3Client *s3.S3) {
|
|
startTime := time.Now()
|
|
function := "testObjectTagging"
|
|
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
object := randString(60, rand.NewSource(time.Now().UnixNano()), "")
|
|
args := map[string]interface{}{
|
|
"bucketName": bucket,
|
|
"objectName": object,
|
|
}
|
|
|
|
_, err := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucket),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", err).Fatal()
|
|
return
|
|
}
|
|
defer cleanup(s3Client, bucket, object, function, args, startTime, true)
|
|
|
|
_, err = s3Client.PutObject(&s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader("testfile")),
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
})
|
|
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to success but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
|
|
// case 1 : Too many tags > 10
|
|
input := &s3.PutObjectTaggingInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
Tagging: &s3.Tagging{
|
|
TagSet: []*s3.Tag{
|
|
{
|
|
Key: aws.String("Key1"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key2"),
|
|
Value: aws.String("Value4"),
|
|
},
|
|
{
|
|
Key: aws.String("Key3"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key4"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key5"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key6"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key7"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key8"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key9"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key10"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key11"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err = s3Client.PutObjectTagging(input)
|
|
if err == nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go PUT expected to fail but succeeded", err).Fatal()
|
|
return
|
|
}
|
|
|
|
if aerr, ok := err.(awserr.Error); ok {
|
|
if aerr.Code() != "BadRequest" && aerr.Message() != "BadRequest: Object tags cannot be greater than 10" {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to fail but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
|
|
// case 2 : Duplicate Tag Keys
|
|
input = &s3.PutObjectTaggingInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
Tagging: &s3.Tagging{
|
|
TagSet: []*s3.Tag{
|
|
{
|
|
Key: aws.String("Key1"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key1"),
|
|
Value: aws.String("Value4"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err = s3Client.PutObjectTagging(input)
|
|
if err == nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go PUT expected to fail but succeeded", err).Fatal()
|
|
return
|
|
}
|
|
|
|
if aerr, ok := err.(awserr.Error); ok {
|
|
if aerr.Code() != "InvalidTag" && aerr.Message() != "InvalidTag: Cannot provide multiple Tags with the same key" {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to fail but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
|
|
// case 3 : Too long Tag Key
|
|
input = &s3.PutObjectTaggingInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
Tagging: &s3.Tagging{
|
|
TagSet: []*s3.Tag{
|
|
{
|
|
Key: aws.String("Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1"),
|
|
Value: aws.String("Value3"),
|
|
},
|
|
{
|
|
Key: aws.String("Key1"),
|
|
Value: aws.String("Value4"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err = s3Client.PutObjectTagging(input)
|
|
if err == nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go PUT expected to fail but succeeded", err).Fatal()
|
|
return
|
|
}
|
|
|
|
if aerr, ok := err.(awserr.Error); ok {
|
|
if aerr.Code() != "InvalidTag" && aerr.Message() != "InvalidTag: The TagKey you have provided is invalid" {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to fail but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
|
|
// case 4 : Too long Tag value
|
|
input = &s3.PutObjectTaggingInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
Tagging: &s3.Tagging{
|
|
TagSet: []*s3.Tag{
|
|
{
|
|
Key: aws.String("Key1"),
|
|
Value: aws.String("Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1Key1"),
|
|
},
|
|
{
|
|
Key: aws.String("Key1"),
|
|
Value: aws.String("Value4"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err = s3Client.PutObjectTagging(input)
|
|
if err == nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go PUT expected to fail but succeeded", err).Fatal()
|
|
return
|
|
}
|
|
|
|
if aerr, ok := err.(awserr.Error); ok {
|
|
if aerr.Code() != "InvalidTag" && aerr.Message() != "InvalidTag: The TagValue you have provided is invalid" {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to fail but got %v", err), err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
|
|
successLogger(function, args, startTime).Info()
|
|
}
|
|
|
|
// Tests bucket re-create errors.
|
|
func testCreateBucketError(s3Client *s3.S3) {
|
|
region := s3Client.Config.Region
|
|
// Amazon S3 returns error in all AWS Regions except in the North Virginia Region.
|
|
// More details in https://docs.aws.amazon.com/sdk-for-go/api/service/s3/#S3.CreateBucket
|
|
s3Client.Config.Region = aws.String("us-west-1")
|
|
|
|
// initialize logging params
|
|
startTime := time.Now()
|
|
function := "testMakeBucketError"
|
|
bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
args := map[string]interface{}{
|
|
"bucketName": bucketName,
|
|
}
|
|
_, err := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucketName),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", err).Fatal()
|
|
return
|
|
}
|
|
defer cleanup(s3Client, bucketName, "", function, args, startTime, true)
|
|
|
|
_, errCreating := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucketName),
|
|
})
|
|
if errCreating == nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Should Return Error for Existing bucket", err).Fatal()
|
|
return
|
|
}
|
|
// Verify valid error response from server.
|
|
if errCreating.(s3.RequestFailure).Code() != "BucketAlreadyExists" &&
|
|
errCreating.(s3.RequestFailure).Code() != "BucketAlreadyOwnedByYou" {
|
|
failureLog(function, args, startTime, "", "Invalid error returned by server", err).Fatal()
|
|
return
|
|
}
|
|
|
|
// Restore region in s3Client
|
|
s3Client.Config.Region = region
|
|
successLogger(function, args, startTime).Info()
|
|
}
|
|
|
|
func testListMultipartUploads(s3Client *s3.S3) {
|
|
startTime := time.Now()
|
|
function := "testListMultipartUploads"
|
|
bucket := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
object := randString(60, rand.NewSource(time.Now().UnixNano()), "")
|
|
args := map[string]interface{}{
|
|
"bucketName": bucket,
|
|
"objectName": object,
|
|
}
|
|
_, errCreating := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucket),
|
|
})
|
|
if errCreating != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", errCreating).Fatal()
|
|
return
|
|
}
|
|
defer cleanup(s3Client, bucket, object, function, args, startTime, true)
|
|
|
|
multipartUpload, err := s3Client.CreateMultipartUpload(&s3.CreateMultipartUploadInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go createMultipartupload API failed", err).Fatal()
|
|
return
|
|
}
|
|
parts := make(map[*int64]*string)
|
|
for i := 0; i < 5; i++ {
|
|
result, errUpload := s3Client.UploadPart(&s3.UploadPartInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
UploadId: multipartUpload.UploadId,
|
|
PartNumber: aws.Int64(int64(i + 1)),
|
|
Body: aws.ReadSeekCloser(strings.NewReader("fileToUpload")),
|
|
})
|
|
if errUpload != nil {
|
|
_, _ = s3Client.AbortMultipartUpload(&s3.AbortMultipartUploadInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
UploadId: multipartUpload.UploadId,
|
|
})
|
|
failureLog(function, args, startTime, "", "AWS SDK Go uploadPart API failed for", errUpload).Fatal()
|
|
return
|
|
}
|
|
parts[aws.Int64(int64(i+1))] = result.ETag
|
|
}
|
|
|
|
listParts, errParts := s3Client.ListParts(&s3.ListPartsInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(object),
|
|
UploadId: multipartUpload.UploadId,
|
|
})
|
|
if errParts != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go ListPartsInput API failed for", err).Fatal()
|
|
return
|
|
}
|
|
|
|
if len(parts) != len(listParts.Parts) {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go ListParts.Parts len mismatch want: %v got: %v", len(parts), len(listParts.Parts)), err).Fatal()
|
|
return
|
|
}
|
|
|
|
for _, part := range listParts.Parts {
|
|
if tag, ok := parts[part.PartNumber]; ok {
|
|
if tag != part.ETag {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go ListParts.Parts output mismatch want: %v got: %v", tag, part.ETag), err).Fatal()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
successLogger(function, args, startTime).Info()
|
|
}
|
|
|
|
func testSSECopyObject(s3Client *s3.S3) {
|
|
// initialize logging params
|
|
startTime := time.Now()
|
|
function := "testSSECopyObjectSourceEncrypted"
|
|
bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "aws-sdk-go-test-")
|
|
object := randString(60, rand.NewSource(time.Now().UnixNano()), "")
|
|
args := map[string]interface{}{
|
|
"bucketName": bucketName,
|
|
"objectName": object,
|
|
}
|
|
_, err := s3Client.CreateBucket(&s3.CreateBucketInput{
|
|
Bucket: aws.String(bucketName),
|
|
})
|
|
if err != nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CreateBucket Failed", err).Fatal()
|
|
return
|
|
}
|
|
defer cleanup(s3Client, bucketName, object+"-enc", function, args, startTime, true)
|
|
defer cleanup(s3Client, bucketName, object+"-noenc", function, args, startTime, false)
|
|
var wrongSuccess = errors.New("Succeeded instead of failing. ")
|
|
|
|
// create encrypted object
|
|
sseCustomerKey := aws.String("32byteslongsecretkeymustbegiven2")
|
|
_, errPutEnc := s3Client.PutObject(&s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader("fileToUpload")),
|
|
Bucket: aws.String(bucketName),
|
|
Key: aws.String(object + "-enc"),
|
|
SSECustomerAlgorithm: aws.String("AES256"),
|
|
SSECustomerKey: sseCustomerKey,
|
|
})
|
|
if errPutEnc != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to succeed but got %v", errPutEnc), errPutEnc).Fatal()
|
|
return
|
|
}
|
|
|
|
// copy the encrypted object
|
|
_, errCopyEnc := s3Client.CopyObject(&s3.CopyObjectInput{
|
|
SSECustomerAlgorithm: aws.String("AES256"),
|
|
SSECustomerKey: sseCustomerKey,
|
|
CopySource: aws.String(bucketName + "/" + object + "-enc"),
|
|
Bucket: aws.String(bucketName),
|
|
Key: aws.String(object + "-copy"),
|
|
})
|
|
if errCopyEnc == nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CopyObject expected to fail, but it succeeds ", wrongSuccess).Fatal()
|
|
return
|
|
}
|
|
var invalidSSECustomerAlgorithm = "Requests specifying Server Side Encryption with Customer provided keys must provide a valid encryption algorithm"
|
|
if !strings.Contains(errCopyEnc.Error(), invalidSSECustomerAlgorithm) {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go CopyObject expected error %v got %v", invalidSSECustomerAlgorithm, errCopyEnc), errCopyEnc).Fatal()
|
|
return
|
|
}
|
|
|
|
// create non-encrypted object
|
|
_, errPut := s3Client.PutObject(&s3.PutObjectInput{
|
|
Body: aws.ReadSeekCloser(strings.NewReader("fileToUpload")),
|
|
Bucket: aws.String(bucketName),
|
|
Key: aws.String(object + "-noenc"),
|
|
})
|
|
if errPut != nil {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go PUT expected to succeed but got %v", errPut), errPut).Fatal()
|
|
return
|
|
}
|
|
|
|
// copy the non-encrypted object
|
|
_, errCopy := s3Client.CopyObject(&s3.CopyObjectInput{
|
|
CopySourceSSECustomerAlgorithm: aws.String("AES256"),
|
|
CopySourceSSECustomerKey: sseCustomerKey,
|
|
SSECustomerAlgorithm: aws.String("AES256"),
|
|
SSECustomerKey: sseCustomerKey,
|
|
CopySource: aws.String(bucketName + "/" + object + "-noenc"),
|
|
Bucket: aws.String(bucketName),
|
|
Key: aws.String(object + "-copy"),
|
|
})
|
|
if errCopy == nil {
|
|
failureLog(function, args, startTime, "", "AWS SDK Go CopyObject expected to fail, but it succeeds ", wrongSuccess).Fatal()
|
|
return
|
|
}
|
|
var invalidEncryptionParameters = "The encryption parameters are not applicable to this object."
|
|
if !strings.Contains(errCopy.Error(), invalidEncryptionParameters) {
|
|
failureLog(function, args, startTime, "", fmt.Sprintf("AWS SDK Go CopyObject expected error %v got %v", invalidEncryptionParameters, errCopy), errCopy).Fatal()
|
|
return
|
|
}
|
|
|
|
successLogger(function, args, startTime).Info()
|
|
}
|
|
|
|
func main() {
|
|
endpoint := os.Getenv("SERVER_ENDPOINT")
|
|
accessKey := os.Getenv("ACCESS_KEY")
|
|
secretKey := os.Getenv("SECRET_KEY")
|
|
secure := os.Getenv("ENABLE_HTTPS")
|
|
sdkEndpoint := "http://" + endpoint
|
|
if secure == "1" {
|
|
sdkEndpoint = "https://" + endpoint
|
|
}
|
|
|
|
creds := credentials.NewStaticCredentials(accessKey, secretKey, "")
|
|
newSession := session.New()
|
|
s3Config := &aws.Config{
|
|
Credentials: creds,
|
|
Endpoint: aws.String(sdkEndpoint),
|
|
Region: aws.String("us-east-1"),
|
|
S3ForcePathStyle: aws.Bool(true),
|
|
}
|
|
|
|
// Create an S3 service object in the default region.
|
|
s3Client := s3.New(newSession, s3Config)
|
|
|
|
// Output to stdout instead of the default stderr
|
|
log.SetOutput(os.Stdout)
|
|
// create custom formatter
|
|
mintFormatter := mintJSONFormatter{}
|
|
// set custom formatter
|
|
log.SetFormatter(&mintFormatter)
|
|
// log Info or above -- success cases are Info level, failures are Fatal level
|
|
log.SetLevel(log.InfoLevel)
|
|
// execute tests
|
|
testPresignedPutInvalidHash(s3Client)
|
|
testListObjects(s3Client)
|
|
testSelectObject(s3Client)
|
|
testCreateBucketError(s3Client)
|
|
testListMultipartUploads(s3Client)
|
|
if secure == "1" {
|
|
testSSECopyObject(s3Client)
|
|
}
|
|
if isObjectTaggingImplemented(s3Client) {
|
|
testObjectTagging(s3Client)
|
|
testObjectTaggingErrors(s3Client)
|
|
}
|
|
}
|