diff --git a/pkg/dataplane/container.go b/pkg/dataplane/container.go index 4079bd6..74ce1fc 100644 --- a/pkg/dataplane/container.go +++ b/pkg/dataplane/container.go @@ -61,6 +61,9 @@ type Container interface { // PutObjectSync PutObjectSync(*PutObjectInput) error + // UpdateObjectSync + UpdateObjectSync(*UpdateObjectInput) error + // DeleteObject DeleteObject(*DeleteObjectInput, interface{}, chan *Response) (*Request, error) diff --git a/pkg/dataplane/http/container.go b/pkg/dataplane/http/container.go index 1b421b7..538229f 100644 --- a/pkg/dataplane/http/container.go +++ b/pkg/dataplane/http/container.go @@ -128,6 +128,12 @@ func (c *container) PutObjectSync(putObjectInput *v3io.PutObjectInput) error { return c.session.context.PutObjectSync(putObjectInput) } +// UpdateObjectSync +func (c *container) UpdateObjectSync(updateObjectInput *v3io.UpdateObjectInput) error { + c.populateInputFields(&updateObjectInput.DataPlaneInput) + return c.session.context.UpdateObjectSync(updateObjectInput) +} + // DeleteObject func (c *container) DeleteObject(deleteObjectInput *v3io.DeleteObjectInput, context interface{}, diff --git a/pkg/dataplane/http/context.go b/pkg/dataplane/http/context.go index 5157611..cdf9c2a 100755 --- a/pkg/dataplane/http/context.go +++ b/pkg/dataplane/http/context.go @@ -563,6 +563,28 @@ func (c *context) PutObjectSync(putObjectInput *v3io.PutObjectInput) error { return err } +// UpdateObjectSync +func (c *context) UpdateObjectSync(updateObjectInput *v3io.UpdateObjectInput) error { + headers := map[string]string{ + "X-v3io-function": "DirSetAttr", + } + + marshaledDirAttributes, err := json.Marshal(updateObjectInput.DirAttributes) + if err != nil { + return err + } + + _, err = c.sendRequest(&updateObjectInput.DataPlaneInput, + http.MethodPut, + updateObjectInput.Path, + "", + headers, + marshaledDirAttributes, + true) + + return err +} + // DeleteObject func (c *context) DeleteObject(deleteObjectInput *v3io.DeleteObjectInput, context interface{}, diff --git a/pkg/dataplane/test/sync_test.go b/pkg/dataplane/test/sync_test.go index dbdddf1..be58a41 100644 --- a/pkg/dataplane/test/sync_test.go +++ b/pkg/dataplane/test/sync_test.go @@ -2,6 +2,7 @@ package test import ( "fmt" + "strconv" "testing" "time" @@ -209,6 +210,77 @@ func (suite *syncContainerTestSuite) TestGetContainerContentsDirsWithAllAttrs() } } +func (suite *syncContainerTestSuite) TestSetDirsAttrs() { + path := fmt.Sprintf("tmp/test/sync_test/TestSetDirsAttrs/%d/", time.Now().Unix()) + + // create empty directory + putObjectInput := &v3io.PutObjectInput{} + putObjectInput.Path = fmt.Sprintf("%sdir-test/", path) + putObjectInput.Body = nil + + // when run against a context + suite.populateDataPlaneInput(&putObjectInput.DataPlaneInput) + err := suite.container.PutObjectSync(putObjectInput) + suite.Require().NoError(err, "Failed to create test directory") + + // Update directory's attributes + updateObjectInput := &v3io.UpdateObjectInput{} + updateObjectInput.Path = fmt.Sprintf("%sdir-test/", path) + layout := "2006-01-02T15:04:05.00Z" + atime, err := time.Parse(layout, "2020-11-22T19:27:33.49Z") + suite.Require().NoError(err) + ctime, err := time.Parse(layout, "2020-09-20T15:10:35.08Z") + suite.Require().NoError(err) + mtime, err := time.Parse(layout, "2020-09-24T12:55:35.08Z") + suite.Require().NoError(err) + dirAttributes := &v3io.DirAttributes{ + Mode: 511, + UID: 67, + GID: 68, + AtimeSec: int(atime.Unix()), + AtimeNSec: int(atime.UnixNano() % 1000000000), + CtimeSec: int(ctime.Unix()), + CtimeNSec: int(ctime.UnixNano() % 1000000000), + MtimeSec: int(mtime.Unix()), + MtimeNSec: int(mtime.UnixNano() % 1000000000), + } + updateObjectInput.DirAttributes = dirAttributes + + // when run against a context + suite.populateDataPlaneInput(&updateObjectInput.DataPlaneInput) + err = suite.container.UpdateObjectSync(updateObjectInput) + suite.Require().NoError(err, "Failed to update test directory") + + // Read directory and compare attributes + getContainerContentsInput := v3io.GetContainerContentsInput{ + Path: path, + GetAllAttributes: true, + DirectoriesOnly: true, + Limit: 10, + } + + // when run against a context + suite.populateDataPlaneInput(&getContainerContentsInput.DataPlaneInput) + + // get container contents + response, err := suite.container.GetContainerContentsSync(&getContainerContentsInput) + suite.Require().NoError(err, "Failed to get container contents") + response.Release() + + getContainerContentsOutput := response.Output.(*v3io.GetContainerContentsOutput) + suite.Require().Empty(len(getContainerContentsOutput.Contents)) + suite.Require().Equal(1, len(getContainerContentsOutput.CommonPrefixes)) + suite.Require().Equal(false, getContainerContentsOutput.IsTruncated) + + prefix := getContainerContentsOutput.CommonPrefixes[0] + suite.Require().Equal(atime.Format(layout), prefix.AccessTime) + suite.Require().Equal(mtime.Format(layout), prefix.LastModified) + suite.Require().Equal(ctime.Format(layout), prefix.CreatingTime) + suite.Require().Equal(strconv.FormatInt(int64(dirAttributes.GID), 16), prefix.GID) + suite.Require().Equal(strconv.FormatInt(int64(dirAttributes.UID), 16), prefix.UID) + suite.Require().Equal("040777", string(prefix.Mode)) +} + type syncContextContainerTestSuite struct { syncContainerTestSuite } diff --git a/pkg/dataplane/types.go b/pkg/dataplane/types.go index 9d131f9..ed73df8 100644 --- a/pkg/dataplane/types.go +++ b/pkg/dataplane/types.go @@ -201,6 +201,24 @@ type DeleteObjectInput struct { Path string } +type UpdateObjectInput struct { + DataPlaneInput + Path string + DirAttributes *DirAttributes +} + +type DirAttributes struct { + Mode int `json:"mode,omitempty"` + UID int `json:"uid,omitempty"` + GID int `json:"gid,omitempty"` + AtimeSec int `json:"atime.sec,omitempty"` + AtimeNSec int `json:"atime.nsec,omitempty"` + CtimeSec int `json:"ctime.sec,omitempty"` + CtimeNSec int `json:"ctime.nsec,omitempty"` + MtimeSec int `json:"mtime.sec,omitempty"` + MtimeNSec int `json:"mtime.nsec,omitempty"` +} + // // KV //