Skip to content
Merged
52 changes: 41 additions & 11 deletions dav/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ func NewWithStorageClient(storageClient StorageClient) *DavBlobstore {
func (d *DavBlobstore) Put(sourceFilePath string, dest string) error {
slog.Info("uploading file to webdav", "source", sourceFilePath, "dest", dest)

if err := validateBlobID(dest); err != nil {
return err
}

source, err := os.Open(sourceFilePath)
if err != nil {
return fmt.Errorf("failed to open source file: %w", err)
Expand All @@ -75,6 +79,10 @@ func (d *DavBlobstore) Put(sourceFilePath string, dest string) error {
func (d *DavBlobstore) Get(source string, dest string) error {
slog.Info("downloading file from webdav", "source", source, "dest", dest)

if err := validateBlobID(source); err != nil {
return err
}

destFile, err := os.Create(dest)
if err != nil {
return fmt.Errorf("failed to create destination file: %w", err)
Expand All @@ -98,17 +106,25 @@ func (d *DavBlobstore) Get(source string, dest string) error {

func (d *DavBlobstore) Delete(dest string) error {
slog.Info("deleting file from webdav", "dest", dest)
if err := validateBlobID(dest); err != nil {
return err
}
return d.storageClient.Delete(dest)
}

func (d *DavBlobstore) Exists(dest string) (bool, error) {
slog.Info("checking if file exists on webdav", "dest", dest)
if err := validateBlobID(dest); err != nil {
return false, err
}
return d.storageClient.Exists(dest)
}

func (d *DavBlobstore) Sign(dest string, action string, expiration time.Duration) (string, error) {
slog.Info("signing url for webdav", "dest", dest, "action", action, "expiration", expiration)

if err := validateBlobID(dest); err != nil {
return "", err
}
action = strings.ToUpper(action)
switch action {
case "GET", "PUT":
Expand All @@ -122,27 +138,41 @@ func (d *DavBlobstore) Sign(dest string, action string, expiration time.Duration
}
}

// DeleteRecursive is not yet implemented in this refactoring
func (d *DavBlobstore) DeleteRecursive(prefix string) error {
return fmt.Errorf("DeleteRecursive not yet implemented")
slog.Info("deleting blobs recursively from webdav", "prefix", prefix)
return d.storageClient.DeleteRecursive(prefix)
}

// List is not yet implemented in this refactoring
func (d *DavBlobstore) List(prefix string) ([]string, error) {
return nil, fmt.Errorf("List not yet implemented")
slog.Info("listing blobs on webdav", "prefix", prefix)
if prefix != "" {
if err := validatePrefix(prefix); err != nil {
return nil, err
}
}
return d.storageClient.List(prefix)
}

// Copy is not yet implemented in this refactoring
func (d *DavBlobstore) Copy(srcBlob string, dstBlob string) error {
return fmt.Errorf("Copy not yet implemented")
slog.Info("copying blob on webdav", "src", srcBlob, "dst", dstBlob)
if err := validateBlobID(srcBlob); err != nil {
return fmt.Errorf("invalid source blob ID: %w", err)
}
if err := validateBlobID(dstBlob); err != nil {
return fmt.Errorf("invalid destination blob ID: %w", err)
}
return d.storageClient.Copy(srcBlob, dstBlob)
}

// Properties is not yet implemented in this refactoring
func (d *DavBlobstore) Properties(dest string) error {
return fmt.Errorf("Properties not yet implemented")
slog.Info("fetching blob properties from webdav", "dest", dest)
if err := validateBlobID(dest); err != nil {
return err
}
return d.storageClient.Properties(dest)
}

// EnsureStorageExists is not yet implemented in this refactoring
func (d *DavBlobstore) EnsureStorageExists() error {
return fmt.Errorf("EnsureStorageExists not yet implemented")
slog.Info("ensuring webdav storage root exists")
return d.storageClient.EnsureStorageExists()
}
190 changes: 190 additions & 0 deletions dav/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"os"
"strings"
"time"

"github.com/cloudfoundry/storage-cli/dav/client"
"github.com/cloudfoundry/storage-cli/dav/client/clientfakes"
Expand Down Expand Up @@ -124,4 +125,193 @@ var _ = Describe("Client", func() {
Expect(exists).To(BeFalse())
})
})

Context("Sign", func() {
var expiry = 100 * time.Second

It("returns a signed URL for action 'get'", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.SignReturns("https://the-signed-url", nil)

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
url, err := davBlobstore.Sign("blob/path", "get", expiry)

Expect(err).NotTo(HaveOccurred())
Expect(url).To(Equal("https://the-signed-url"))

Expect(fakeStorageClient.SignCallCount()).To(Equal(1))
object, action, expiration := fakeStorageClient.SignArgsForCall(0)
Expect(object).To(Equal("blob/path"))
Expect(action).To(Equal("GET"))
Expect(expiration).To(Equal(expiry))
})

It("returns a signed URL for action 'put'", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.SignReturns("https://the-signed-url", nil)

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
url, err := davBlobstore.Sign("blob/path", "put", expiry)

Expect(err).NotTo(HaveOccurred())
Expect(url).To(Equal("https://the-signed-url"))

_, action, _ := fakeStorageClient.SignArgsForCall(0)
Expect(action).To(Equal("PUT"))
})

It("fails on unknown action without calling the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
url, err := davBlobstore.Sign("blob/path", "unknown", expiry)

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("action not implemented"))
Expect(url).To(Equal(""))
Expect(fakeStorageClient.SignCallCount()).To(Equal(0))
})

It("propagates errors from the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.SignReturns("", fmt.Errorf("boom"))

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
url, err := davBlobstore.Sign("blob/path", "get", expiry)

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("boom"))
Expect(url).To(Equal(""))
})
})

Context("Copy", func() {
It("forwards source and destination to the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.CopyReturns(nil)

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
err := davBlobstore.Copy("src/blob", "dst/blob")

Expect(err).NotTo(HaveOccurred())
Expect(fakeStorageClient.CopyCallCount()).To(Equal(1))

src, dst := fakeStorageClient.CopyArgsForCall(0)
Expect(src).To(Equal("src/blob"))
Expect(dst).To(Equal("dst/blob"))
})

It("propagates errors from the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.CopyReturns(fmt.Errorf("copy failed"))

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
err := davBlobstore.Copy("src/blob", "dst/blob")

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("copy failed"))
})
})

Context("List", func() {
It("returns the blobs reported by the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.ListReturns([]string{"a/b/c", "a/b/d"}, nil)

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
blobs, err := davBlobstore.List("a/b")

Expect(err).NotTo(HaveOccurred())
Expect(blobs).To(ConsistOf("a/b/c", "a/b/d"))

Expect(fakeStorageClient.ListCallCount()).To(Equal(1))
Expect(fakeStorageClient.ListArgsForCall(0)).To(Equal("a/b"))
})

It("propagates errors from the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.ListReturns(nil, fmt.Errorf("list failed"))

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
blobs, err := davBlobstore.List("any/prefix")

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("list failed"))
Expect(blobs).To(BeNil())
})
})

Context("DeleteRecursive", func() {
It("forwards the prefix to the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.DeleteRecursiveReturns(nil)

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
err := davBlobstore.DeleteRecursive("some/prefix")

Expect(err).NotTo(HaveOccurred())
Expect(fakeStorageClient.DeleteRecursiveCallCount()).To(Equal(1))
Expect(fakeStorageClient.DeleteRecursiveArgsForCall(0)).To(Equal("some/prefix"))
})

It("propagates errors from the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.DeleteRecursiveReturns(fmt.Errorf("recursive delete failed"))

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
err := davBlobstore.DeleteRecursive("some/prefix")

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("recursive delete failed"))
})
})

Context("Properties", func() {
It("forwards the destination to the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.PropertiesReturns(nil)

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
err := davBlobstore.Properties("blob/path")

Expect(err).NotTo(HaveOccurred())
Expect(fakeStorageClient.PropertiesCallCount()).To(Equal(1))
Expect(fakeStorageClient.PropertiesArgsForCall(0)).To(Equal("blob/path"))
})

It("propagates errors from the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.PropertiesReturns(fmt.Errorf("properties failed"))

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
err := davBlobstore.Properties("blob/path")

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("properties failed"))
})
})

Context("EnsureStorageExists", func() {
It("delegates to the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.EnsureStorageExistsReturns(nil)

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
err := davBlobstore.EnsureStorageExists()

Expect(err).NotTo(HaveOccurred())
Expect(fakeStorageClient.EnsureStorageExistsCallCount()).To(Equal(1))
})

It("propagates errors from the storage client", func() {
fakeStorageClient := &clientfakes.FakeStorageClient{}
fakeStorageClient.EnsureStorageExistsReturns(fmt.Errorf("ensure failed"))

davBlobstore := client.NewWithStorageClient(fakeStorageClient)
err := davBlobstore.EnsureStorageExists()

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("ensure failed"))
})
})
})
Loading
Loading