Skip to content

Commit 48f7e59

Browse files
authored
cp,pipe: don't omit some of the metadata flags (#658)
#657 mentioned that `acl` flag was broken in `v2.2.1`. After looking at the issue, it was discovered that not only the `acl` flag was broken, there are couple of more flags that were being omitted during the mentioned commands, which all of them caused by this faulty [PR](#621). Resolves #657.
1 parent 10e4ccf commit 48f7e59

File tree

12 files changed

+246
-83
lines changed

12 files changed

+246
-83
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# Changelog
2+
## v2.2.2 - 13 Sep 2023
23

4+
#### Bugfixes
5+
- Fixed `cp` and `pipe` to not omit some of the metadata flags. ([#657](https://github.com/peak/s5cmd/issues/657))
36
## v2.2.1 - 23 Aug 2023
47

58
#### Bugfixes
69
- Fixed incorrect `s5cmd version` output ([#650](https://github.com/peak/s5cmd/pull/650))
7-
810
## v2.2.0 - 21 Aug 2023
911

1012
#### Features

command/cp.go

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -689,10 +689,17 @@ func (c Copy) doUpload(ctx context.Context, srcurl *url.URL, dsturl *url.URL, ex
689689
if err != nil {
690690
return err
691691
}
692-
metadata := storage.Metadata{UserDefined: extradata}
693692

694-
if c.storageClass != "" {
695-
metadata.StorageClass = string(c.storageClass)
693+
metadata := storage.Metadata{
694+
UserDefined: extradata,
695+
ACL: c.acl,
696+
CacheControl: c.cacheControl,
697+
Expires: c.expires,
698+
StorageClass: string(c.storageClass),
699+
ContentEncoding: c.contentEncoding,
700+
ContentDisposition: c.contentDisposition,
701+
EncryptionMethod: c.encryptionMethod,
702+
EncryptionKeyID: c.encryptionKeyID,
696703
}
697704

698705
if c.contentType != "" {
@@ -701,14 +708,6 @@ func (c Copy) doUpload(ctx context.Context, srcurl *url.URL, dsturl *url.URL, ex
701708
metadata.ContentType = guessContentType(file)
702709
}
703710

704-
if c.contentEncoding != "" {
705-
metadata.ContentEncoding = c.contentEncoding
706-
}
707-
708-
if c.contentDisposition != "" {
709-
metadata.ContentDisposition = c.contentDisposition
710-
}
711-
712711
reader := newCountingReaderWriter(file, c.progressbar)
713712
err = dstClient.Put(ctx, reader, dsturl, metadata, c.concurrency, c.partSize)
714713

@@ -755,20 +754,17 @@ func (c Copy) doCopy(ctx context.Context, srcurl, dsturl *url.URL, extradata map
755754
return err
756755
}
757756

758-
metadata := storage.Metadata{UserDefined: extradata}
759-
if c.storageClass != "" {
760-
metadata.StorageClass = string(c.storageClass)
761-
}
762-
763-
if c.contentType != "" {
764-
metadata.ContentType = c.contentType
765-
}
766-
767-
if c.contentEncoding != "" {
768-
metadata.ContentEncoding = c.contentEncoding
769-
}
770-
if c.contentDisposition != "" {
771-
metadata.ContentDisposition = c.contentDisposition
757+
metadata := storage.Metadata{
758+
UserDefined: extradata,
759+
ACL: c.acl,
760+
CacheControl: c.cacheControl,
761+
Expires: c.expires,
762+
StorageClass: string(c.storageClass),
763+
ContentType: c.contentType,
764+
ContentEncoding: c.contentEncoding,
765+
ContentDisposition: c.contentDisposition,
766+
EncryptionMethod: c.encryptionMethod,
767+
EncryptionKeyID: c.encryptionKeyID,
772768
}
773769

774770
err = c.shouldOverride(ctx, srcurl, dsturl)

command/pipe.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,16 @@ func (c Pipe) Run(ctx context.Context) error {
221221
return err
222222
}
223223

224-
metadata := storage.Metadata{UserDefined: c.metadata}
225-
if c.storageClass != "" {
226-
metadata.StorageClass = string(c.storageClass)
224+
metadata := storage.Metadata{
225+
UserDefined: c.metadata,
226+
ACL: c.acl,
227+
CacheControl: c.cacheControl,
228+
Expires: c.expires,
229+
StorageClass: string(c.storageClass),
230+
ContentEncoding: c.contentEncoding,
231+
ContentDisposition: c.contentDisposition,
232+
EncryptionMethod: c.encryptionMethod,
233+
EncryptionKeyID: c.encryptionKeyID,
227234
}
228235

229236
if c.contentType != "" {
@@ -232,13 +239,6 @@ func (c Pipe) Run(ctx context.Context) error {
232239
metadata.ContentType = guessContentTypeByExtension(c.dst)
233240
}
234241

235-
if c.contentEncoding != "" {
236-
metadata.ContentEncoding = c.contentEncoding
237-
}
238-
if c.contentDisposition != "" {
239-
metadata.ContentDisposition = c.contentDisposition
240-
}
241-
242242
err = client.Put(ctx, &stdin{file: os.Stdin}, c.dst, metadata, c.concurrency, c.partSize)
243243
if err != nil {
244244
return err

e2e/cp_test.go

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ package e2e
2525
import (
2626
"fmt"
2727
"net"
28+
"net/http"
2829
"os"
2930
"path/filepath"
3031
"runtime"
@@ -720,6 +721,75 @@ func TestCopySingleFileToS3(t *testing.T) {
720721
assert.Assert(t, ensureS3Object(s3client, bucket, filename, content, ensureContentType(expectedContentType), ensureContentDisposition(expectedContentDisposition)))
721722
}
722723

724+
func TestCopySingleFileToS3WithAllMetadataFlags(t *testing.T) {
725+
t.Parallel()
726+
727+
s3client, s5cmd := setup(t)
728+
729+
bucket := s3BucketFromTestName(t)
730+
731+
createBucket(t, s3client, bucket)
732+
733+
const (
734+
filename = "index"
735+
content = `testfilecontent`
736+
cacheControl = "public, max-age=3600"
737+
expires = "2025-01-01T00:00:00Z"
738+
storageClass = "STANDARD_IA"
739+
ContentType = "text/html; charset=utf-8"
740+
ContentDisposition = "inline"
741+
ContentEncoding = "utf-8"
742+
EncryptionMethod = "aws:kms"
743+
EncryptionKeyID = "1234abcd-12ab-34cd-56ef-1234567890ab"
744+
)
745+
746+
// expected expires flag is the parsed version of the date in RFC3339 format
747+
parsedTime, err := time.Parse(time.RFC3339, expires)
748+
if err != nil {
749+
t.Fatal(err)
750+
}
751+
752+
expectedExpires := parsedTime.Format(http.TimeFormat)
753+
754+
workdir := fs.NewDir(t, bucket, fs.WithFile(filename, content))
755+
defer workdir.Remove()
756+
757+
srcpath := workdir.Join(filename)
758+
dstpath := fmt.Sprintf("s3://%v/", bucket)
759+
760+
srcpath = filepath.ToSlash(srcpath)
761+
cmd := s5cmd("cp",
762+
"--cache-control", cacheControl,
763+
"--expires", expires,
764+
"--storage-class", storageClass,
765+
"--content-type", ContentType,
766+
"--content-disposition", ContentDisposition,
767+
"--content-encoding", ContentEncoding,
768+
"--sse", EncryptionMethod,
769+
"--sse-kms-key-id", EncryptionKeyID,
770+
srcpath, dstpath,
771+
)
772+
773+
result := icmd.RunCmd(cmd)
774+
775+
result.Assert(t, icmd.Success)
776+
777+
expected := fs.Expected(t, fs.WithFile(filename, content))
778+
assert.Assert(t, fs.Equal(workdir.Path(), expected))
779+
780+
assert.Assert(t, ensureS3Object(s3client, bucket, filename, content,
781+
ensureExpires(expectedExpires),
782+
ensureCacheControl(cacheControl),
783+
ensureStorageClass(storageClass),
784+
ensureContentType(ContentType),
785+
ensureContentDisposition(ContentDisposition),
786+
ensureContentEncoding(ContentEncoding),
787+
ensureEncryptionMethod(EncryptionMethod),
788+
ensureEncryptionKeyID(EncryptionKeyID),
789+
))
790+
791+
}
792+
723793
// cp dir/file s3://bucket/ --metadata key1=val1 --metadata key2=val2 ...
724794
func TestCopySingleFileToS3WithArbitraryMetadata(t *testing.T) {
725795
t.Parallel()
@@ -800,10 +870,11 @@ func TestCopyS3ToS3WithArbitraryMetadata(t *testing.T) {
800870
"Key1": aws.String("foo"),
801871
"Key2": aws.String("bar"),
802872
}
873+
803874
srcpath := fmt.Sprintf("s3://%v/%v", bucket, filename)
804875
dstpath := fmt.Sprintf("s3://%v/%v_cp", bucket, filename)
805876

806-
putFileWithMetadata(t, s3client, bucket, filename, content, srcmetadata)
877+
putFile(t, s3client, bucket, filename, content, putArbitraryMetadata(srcmetadata))
807878
cmd := s5cmd("cp", "--metadata", foo, "--metadata", bar, srcpath, dstpath)
808879
result := icmd.RunCmd(cmd)
809880
result.Assert(t, icmd.Success)

e2e/pipe_test.go

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ package e2e
33
import (
44
"bytes"
55
"fmt"
6+
"net/http"
67
"runtime"
78
"testing"
9+
"time"
810

911
"github.com/aws/aws-sdk-go/aws"
1012
"gotest.tools/v3/assert"
13+
"gotest.tools/v3/fs"
1114
"gotest.tools/v3/icmd"
1215
)
1316

@@ -461,44 +464,68 @@ func TestUploadStdinToS3WithStorageClassGlacier(t *testing.T) {
461464
}
462465

463466
// pipe --content-disposition inline s3://bucket/object
464-
func TestUploadStdinToToS3WithContentDisposition(t *testing.T) {
467+
func TestUploadStdinToToS3WithAllMetadataFlags(t *testing.T) {
465468
t.Parallel()
466469

467470
s3client, s5cmd := setup(t)
468471

469472
bucket := s3BucketFromTestName(t)
473+
470474
createBucket(t, s3client, bucket)
471475

472476
const (
473-
// make sure that Put reads the file header and guess Content-Type correctly.
474-
filename = "index.html"
475-
content = `
476-
<html lang="tr">
477-
<head>
478-
<meta charset="utf-8">
479-
<body>
480-
<header></header>
481-
<main></main>
482-
<footer></footer>
483-
</body>
484-
</html>
485-
`
486-
expectedContentType = "text/html; charset=utf-8"
487-
expectedContentDisposition = "inline"
477+
filename = "index"
478+
content = `testfilecontent`
479+
cacheControl = "public, max-age=3600"
480+
expires = "2025-01-01T00:00:00Z"
481+
storageClass = "STANDARD_IA"
482+
ContentType = "text/html; charset=utf-8"
483+
ContentDisposition = "inline"
484+
ContentEncoding = "utf-8"
485+
EncryptionMethod = "aws:kms"
486+
EncryptionKeyID = "1234abcd-12ab-34cd-56ef-1234567890ab"
488487
)
489488

489+
// expected expires flag is the parsed version of the date in RFC3339 format
490+
parsedTime, err := time.Parse(time.RFC3339, expires)
491+
if err != nil {
492+
t.Fatal(err)
493+
}
494+
495+
expectedExpires := parsedTime.Format(http.TimeFormat)
496+
497+
workdir := fs.NewDir(t, bucket, fs.WithFile(filename, content))
498+
defer workdir.Remove()
499+
490500
dstpath := fmt.Sprintf("s3://%v/%v", bucket, filename)
501+
491502
reader := bytes.NewBufferString(content)
492503

493-
cmd := s5cmd("pipe", "--content-disposition", "inline", dstpath)
504+
cmd := s5cmd("pipe",
505+
"--cache-control", cacheControl,
506+
"--expires", expires,
507+
"--storage-class", storageClass,
508+
"--content-type", ContentType,
509+
"--content-disposition", ContentDisposition,
510+
"--content-encoding", ContentEncoding,
511+
"--sse", EncryptionMethod,
512+
"--sse-kms-key-id", EncryptionKeyID,
513+
dstpath,
514+
)
515+
494516
result := icmd.RunCmd(cmd, icmd.WithStdin(reader))
495517

496518
result.Assert(t, icmd.Success)
497519

498-
assertLines(t, result.Stdout(), map[int]compareFunc{
499-
0: suffix(`pipe %v`, dstpath),
500-
})
501-
502520
// assert S3
503-
assert.Assert(t, ensureS3Object(s3client, bucket, filename, content, ensureContentType(expectedContentType), ensureContentDisposition(expectedContentDisposition)))
521+
assert.Assert(t, ensureS3Object(s3client, bucket, filename, content,
522+
ensureExpires(expectedExpires),
523+
ensureCacheControl(cacheControl),
524+
ensureStorageClass(storageClass),
525+
ensureContentType(ContentType),
526+
ensureContentDisposition(ContentDisposition),
527+
ensureContentEncoding(ContentEncoding),
528+
ensureEncryptionMethod(EncryptionMethod),
529+
ensureEncryptionKeyID(EncryptionKeyID),
530+
))
504531
}

0 commit comments

Comments
 (0)