Skip to content

Commit

Permalink
Add symlink support
Browse files Browse the repository at this point in the history
Added symlink support with tests. Symlink has been avaialable in Swift
since version 2.17.0 and static links since 2.23.0

This checks /info to make sure symlink is supported before attempting
to run symlink tests
  • Loading branch information
thiagodasilva authored and ncw committed Nov 17, 2019
1 parent 27a552e commit 017f012
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 0 deletions.
16 changes: 16 additions & 0 deletions swift.go
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,22 @@ func (c *Connection) ObjectCreate(container string, objectName string, checkHash
return
}

func (c *Connection) ObjectSymlinkCreate(container string, symlink string, targetAccount string, targetContainer string, targetObject string, targetEtag string) (headers Headers, err error) {

EMPTY_MD5 := "d41d8cd98f00b204e9800998ecf8427e"
symHeaders := Headers{}
contents := bytes.NewBufferString("")
if targetAccount != "" {
symHeaders["X-Symlink-Target-Account"] = targetAccount
}
if targetEtag != "" {
symHeaders["X-Symlink-Target-Etag"] = targetEtag
}
symHeaders["X-Symlink-Target"] = fmt.Sprintf("%s/%s", targetContainer, targetObject)
_, err = c.ObjectPut(container, symlink, contents, true, EMPTY_MD5, "application/symlink", symHeaders)
return
}

func (c *Connection) objectPut(container string, objectName string, contents io.Reader, checkHash bool, Hash string, contentType string, h Headers, parameters url.Values) (headers Headers, err error) {
extraHeaders := objectPutHeaders(objectName, &checkHash, Hash, contentType, h)
hash := md5.New()
Expand Down
125 changes: 125 additions & 0 deletions swift_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@ const (
CURRENT_CONTAINER = "GoSwiftUnitTestCurrent"
OBJECT = "test_object"
OBJECT2 = "test_object2"
SYMLINK_OBJECT = "test_symlink"
SYMLINK_OBJECT2 = "test_symlink2"
EMPTYOBJECT = "empty_test_object"
CONTENTS = "12345"
CONTENTS2 = "54321"
CONTENT_SIZE = int64(len(CONTENTS))
CONTENT_MD5 = "827ccb0eea8a706c4c34a16891f84e7b"
CONTENT2_MD5 = "01cfcd4f6b8770febfb40cb906715822"
EMPTY_MD5 = "d41d8cd98f00b204e9800998ecf8427e"
SECRET_KEY = "b3968d0207b54ece87cccc06515a89d4"
)
Expand Down Expand Up @@ -299,6 +302,12 @@ func isV3Api() bool {
return strings.Contains(AuthUrl, "v3")
}

func getSwinftInfo(t *testing.T) (info swift.SwiftInfo, err error) {
c, rollback := makeConnectionAuth(t)
defer rollback()
return c.QueryInfo()
}

func TestTransport(t *testing.T) {
c, rollback := makeConnection(t)
defer rollback()
Expand Down Expand Up @@ -908,6 +917,122 @@ func TestObjectEmpty(t *testing.T) {
}
}

func TestSymlinkObject(t *testing.T) {
info, err := getSwinftInfo(t)
if err != nil {
t.Fatal(err)
}
if _, ok := info["symlink"]; !ok {
// skip, symlink not supported
t.Skip("skip, symlink not supported")
return
}
c, rollback := makeConnectionWithContainer(t)
defer rollback()

// write target objects
err = c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "text/potato")
if err != nil {
t.Fatal(err)
}
defer func() {
err = c.ObjectDelete(CONTAINER, OBJECT)
if err != nil {
t.Error(err)
}
}()

// test dynamic link
_, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT, "", CONTAINER, OBJECT, "")
if err != nil {
t.Fatal(err)
}
defer func() {
err = c.ObjectDelete(CONTAINER, SYMLINK_OBJECT)
if err != nil {
t.Error(err)
}
}()

md, _, err := c.Object(CONTAINER, SYMLINK_OBJECT)
if err != nil {
t.Error(err)
}
if md.ContentType != "text/potato" {
t.Error("Bad content type", md.ContentType)
}
if md.Bytes != CONTENT_SIZE {
t.Errorf("Bad length want 5 got %v", md.Bytes)
}
if md.Hash != CONTENT_MD5 {
t.Errorf("Bad MD5 want %v got %v", CONTENT_MD5, md.Hash)
}

}

func TestStaticSymlinkObject(t *testing.T) {
info, err := getSwinftInfo(t)
if err != nil {
t.Fatal(err)
}
if sym, ok := info["symlink"].(map[string]interface{}); ok {
if _, ok := sym["static_links"]; !ok {
t.Skip("skip, static symlink not supported")
return
}
} else {
t.Skip("skip, symlink not supported")
return
}

c, rollback := makeConnectionWithContainer(t)
defer rollback()

// write target objects
err = c.ObjectPutBytes(CONTAINER, OBJECT2, []byte(CONTENTS2), "text/tomato")
if err != nil {
t.Fatal(err)
}
defer func() {
err = c.ObjectDelete(CONTAINER, OBJECT2)
if err != nil {
t.Error(err)
}
}()

// test static link
// first with the wrong target etag
_, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT2, "", CONTAINER, OBJECT2, CONTENT_MD5)
if err == nil {
t.Error("Symlink with wrong target etag should have failed")
}

_, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT2, "", CONTAINER, OBJECT2, CONTENT2_MD5)
if err != nil {
t.Fatal(err)
}
defer func() {
err = c.ObjectDelete(CONTAINER, SYMLINK_OBJECT2)
if err != nil {
t.Error(err)
}
}()

md, _, err := c.Object(CONTAINER, SYMLINK_OBJECT2)
if err != nil {
t.Error(err)
}
if md.ContentType != "text/tomato" {
t.Error("Bad content type", md.ContentType)
}
if md.Bytes != CONTENT_SIZE {
t.Errorf("Bad length want 5 got %v", md.Bytes)
}
if md.Hash != CONTENT2_MD5 {
t.Errorf("Bad MD5 want %v got %v", CONTENT2_MD5, md.Hash)
}
}

func TestObjectPutBytes(t *testing.T) {
c, rollback := makeConnectionWithContainer(t)
defer rollback()
Expand Down

0 comments on commit 017f012

Please sign in to comment.