diff --git a/fakestorage/object.go b/fakestorage/object.go index 32c0b4fc1e..4b704ecb98 100644 --- a/fakestorage/object.go +++ b/fakestorage/object.go @@ -938,17 +938,37 @@ func (s *Server) patchObject(r *http.Request) jsonResponse { vars := unescapeMuxVars(mux.Vars(r)) bucketName := vars["bucketName"] objectName := vars["objectName"] - var metadata struct { + + type acls struct { + Entity string + Role string + } + + var payload struct { Metadata map[string]string `json:"metadata"` + Acl []acls } - err := json.NewDecoder(r.Body).Decode(&metadata) + err := json.NewDecoder(r.Body).Decode(&payload) if err != nil { return jsonResponse{ status: http.StatusBadRequest, errorMessage: "Metadata in the request couldn't decode", } } - backendObj, err := s.backend.PatchObject(bucketName, objectName, metadata.Metadata) + + var attrsToUpdate backend.ObjectAttrs + + attrsToUpdate.Metadata = payload.Metadata + + if len(payload.Acl) > 0 { + attrsToUpdate.ACL = []storage.ACLRule{} + for _, aclData := range payload.Acl { + newAcl := storage.ACLRule{Entity: storage.ACLEntity(aclData.Entity), Role: storage.ACLRole(aclData.Role)} + attrsToUpdate.ACL = append(attrsToUpdate.ACL, newAcl) + } + } + + backendObj, err := s.backend.PatchObject(bucketName, objectName, attrsToUpdate) if err != nil { return jsonResponse{ status: http.StatusNotFound, diff --git a/internal/backend/fs.go b/internal/backend/fs.go index 27a3a5f003..dc70cfb490 100644 --- a/internal/backend/fs.go +++ b/internal/backend/fs.go @@ -323,24 +323,18 @@ func (s *storageFS) DeleteObject(bucketName, objectName string) error { return os.Remove(path) } -// PatchObject patches the given object metadata. -func (s *storageFS) PatchObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) { +func (s *storageFS) PatchObject(bucketName, objectName string, attrsToUpdate ObjectAttrs) (StreamingObject, error) { obj, err := s.GetObject(bucketName, objectName) if err != nil { return StreamingObject{}, err } defer obj.Close() - if obj.Metadata == nil { - obj.Metadata = map[string]string{} - } - for k, v := range metadata { - obj.Metadata[k] = v - } - obj.Generation = 0 // reset generation id - return s.CreateObject(obj, NoConditions{}) // recreate object + + obj.patch(attrsToUpdate) + obj.Generation = 0 // reset generation id + return s.CreateObject(obj, NoConditions{}) } -// UpdateObject replaces the given object metadata. func (s *storageFS) UpdateObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) { obj, err := s.GetObject(bucketName, objectName) if err != nil { @@ -351,8 +345,8 @@ func (s *storageFS) UpdateObject(bucketName, objectName string, metadata map[str for k, v := range metadata { obj.Metadata[k] = v } - obj.Generation = 0 // reset generation id - return s.CreateObject(obj, NoConditions{}) // recreate object + obj.Generation = 0 // reset generation id + return s.CreateObject(obj, NoConditions{}) } type concatenatedContent struct { diff --git a/internal/backend/memory.go b/internal/backend/memory.go index 80e8bfee90..cc8987f5a4 100644 --- a/internal/backend/memory.go +++ b/internal/backend/memory.go @@ -299,19 +299,14 @@ func (s *storageMemory) DeleteObject(bucketName, objectName string) error { return nil } -// PatchObject updates an object metadata. -func (s *storageMemory) PatchObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) { +func (s *storageMemory) PatchObject(bucketName, objectName string, attrsToUpdate ObjectAttrs) (StreamingObject, error) { obj, err := s.GetObject(bucketName, objectName) if err != nil { return StreamingObject{}, err } - if obj.Metadata == nil { - obj.Metadata = map[string]string{} - } - for k, v := range metadata { - obj.Metadata[k] = v - } - s.CreateObject(obj, NoConditions{}) // recreate object + + obj.patch(attrsToUpdate) + s.CreateObject(obj, NoConditions{}) return obj, nil } @@ -325,7 +320,7 @@ func (s *storageMemory) UpdateObject(bucketName, objectName string, metadata map for k, v := range metadata { obj.Metadata[k] = v } - s.CreateObject(obj, NoConditions{}) // recreate object + s.CreateObject(obj, NoConditions{}) return obj, nil } diff --git a/internal/backend/object.go b/internal/backend/object.go index 1f1e7b9708..e33894b5e4 100644 --- a/internal/backend/object.go +++ b/internal/backend/object.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "io" + "reflect" "cloud.google.com/go/storage" ) @@ -81,3 +82,23 @@ func (o *StreamingObject) BufferedObject() (Object, error) { Content: data, }, err } + +func (o *StreamingObject) patch(attrsToUpdate ObjectAttrs) { + currObjValues := reflect.ValueOf(&(o.ObjectAttrs)).Elem() + currObjType := currObjValues.Type() + newObjValues := reflect.ValueOf(attrsToUpdate) + for i := 0; i < newObjValues.NumField(); i++ { + if reflect.Value.IsZero(newObjValues.Field(i)) { + continue + } else if currObjType.Field(i).Name == "Metadata" { + if o.Metadata == nil { + o.Metadata = map[string]string{} + } + for k, v := range attrsToUpdate.Metadata { + o.Metadata[k] = v + } + } else { + currObjValues.Field(i).Set(newObjValues.Field(i)) + } + } +} diff --git a/internal/backend/storage.go b/internal/backend/storage.go index 596b9cceae..756dd7f177 100644 --- a/internal/backend/storage.go +++ b/internal/backend/storage.go @@ -27,7 +27,7 @@ type Storage interface { GetObject(bucketName, objectName string) (StreamingObject, error) GetObjectWithGeneration(bucketName, objectName string, generation int64) (StreamingObject, error) DeleteObject(bucketName, objectName string) error - PatchObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) + PatchObject(bucketName, objectName string, attrsToUpdate ObjectAttrs) (StreamingObject, error) UpdateObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) ComposeObject(bucketName string, objectNames []string, destinationName string, metadata map[string]string, contentType string) (StreamingObject, error) } diff --git a/main.go b/main.go index df6b63bdc8..0776b235a7 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "path/filepath" "syscall" + "cloud.google.com/go/storage" "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/fsouza/fake-gcs-server/internal/checksum" "github.com/fsouza/fake-gcs-server/internal/config" @@ -104,6 +105,12 @@ func objectsFromBucket(localBucketPath, bucketName string) ([]fakestorage.Object } objects = append(objects, fakestorage.Object{ ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: bucketName, Name: objectKey, ContentType: mime.TypeByExtension(filepath.Ext(path)), diff --git a/main_test.go b/main_test.go index 9592131e8e..c9e0ed783d 100644 --- a/main_test.go +++ b/main_test.go @@ -11,6 +11,7 @@ import ( "os" "testing" + "cloud.google.com/go/storage" "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -48,6 +49,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { expectedObjects: []fakestorage.Object{ { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "sample-bucket", Name: "some_file.txt", ContentType: testContentType, @@ -63,6 +70,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { expectedObjects: []fakestorage.Object{ { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "some-bucket", Name: "a/b/c/d/e/f/object1.txt", ContentType: testContentType, @@ -71,6 +84,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "some-bucket", Name: "a/b/c/d/e/f/object2.txt", ContentType: testContentType, @@ -79,6 +98,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "some-bucket", Name: "root-object.txt", ContentType: testContentType, @@ -101,6 +126,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { expectedObjects: []fakestorage.Object{ { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "bucket1", Name: "object1.txt", ContentType: testContentType, @@ -109,6 +140,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "bucket1", Name: "object2.txt", ContentType: testContentType, @@ -117,6 +154,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "bucket2", Name: "object1.txt", ContentType: testContentType, @@ -125,6 +168,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "bucket2", Name: "object2.txt", ContentType: testContentType,