johannesboyne / gofakes3

A simple fake AWS S3 object storage (used for local test-runs against AWS S3 APIs)
MIT License
358 stars 81 forks source link

PutObjectTagging overwrites metadata #64

Closed elgohr closed 2 years ago

elgohr commented 2 years ago

The following test shows, that Metadata is set after Upload. After executing PutObjectTagging it's gone.

func TestTagging(t *testing.T) {
    ts := httptest.NewServer(gofakes3.New(s3mem.New(), gofakes3.WithGlobalLog()).Server())
    defer ts.Close()

    ctx := context.Background()
    cfg, err := config.LoadDefaultConfig(ctx,
        config.WithRegion("us-east-1"),
        config.WithEndpointResolverWithOptions(aws.EndpointResolverWithOptionsFunc(func(_, _ string, _ ...interface{}) (aws.Endpoint, error) {
            return aws.Endpoint{
                PartitionID:       "aws",
                URL:               ts.URL,
                SigningRegion:     "us-east-1",
                HostnameImmutable: true,
            }, nil
        })),
        config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("dummy", "dummy", "dummy")),
    )
    if err != nil {
        t.Fatal(err)
    }

    client := s3.NewFromConfig(cfg)
    _, err = client.CreateBucket(ctx, &s3.CreateBucketInput{
        Bucket: aws.String("bucket"),
    })
    if err != nil {
        t.Fatal(err)
    }

    _, err = manager.NewUploader(client).Upload(ctx, &s3.PutObjectInput{
        Bucket: aws.String("bucket"),
        Key:    aws.String("TEST"),
        Body:   strings.NewReader("TEST"),
        Metadata: map[string]string{
            "test": "test",
        },
    })
    if err != nil {
        t.Fatal(err)
    }

    head, err := client.HeadObject(ctx, &s3.HeadObjectInput{Bucket: aws.String("bucket"), Key: aws.String("TEST")})
    if err != nil {
        t.Fatal(err)
    }
    if head.Metadata["test"] == "" {
        t.Fatal("no metadata")
    }

    _, err = client.PutObjectTagging(ctx, &s3.PutObjectTaggingInput{
        Bucket: aws.String("bucket"),
        Key:    aws.String("TEST"),
        Tagging: &types.Tagging{
            TagSet: []types.Tag{
                {Key: aws.String("tag-test"), Value: aws.String("test")},
            },
        },
    })
    if err != nil {
        t.Fatal(err)
    }

    head, err = client.HeadObject(ctx, &s3.HeadObjectInput{Bucket: aws.String("bucket"), Key: aws.String("TEST")})
    if err != nil {
        t.Fatal(err)
    }
    if head.Metadata["test"] == "" {
        t.Fatal("no metadata")
    }
}

From the output I would guess, that gofakes3 creates a completly new object instead of updating the existing one

2022/05/10 09:31:17 INFO CREATE BUCKET: bucket
2022/05/10 09:31:17 INFO CREATE OBJECT: bucket TEST
2022/05/10 09:31:17 INFO HEAD OBJECT
2022/05/10 09:31:17 INFO Bucket: bucket
2022/05/10 09:31:17 INFO └── Object: TEST
2022/05/10 09:31:17 INFO CREATE OBJECT: bucket TEST
2022/05/10 09:31:17 INFO HEAD OBJECT
2022/05/10 09:31:17 INFO Bucket: bucket
2022/05/10 09:31:17 INFO └── Object: TEST
johannesboyne commented 2 years ago

Hi @elgohr ,

I just tried to replicate this and added a new test case as seen here https://github.com/johannesboyne/gofakes3/blob/investigate-i64/gofakes3_test.go#L281 I'll start looking into it! Thanks for raising!

https://app.circleci.com/pipelines/github/johannesboyne/gofakes3/181/workflows/965d8ca3-8968-46a5-8ef2-b26a89e6df7d/jobs/191?invite=true#step-103-519