From fef9baabf9487e0b6df58e25d077dffb73dadc8a Mon Sep 17 00:00:00 2001 From: Mariana Cardoso Date: Thu, 23 Mar 2017 15:44:41 -0700 Subject: [PATCH] ListBlobs is more complete and easier to use (#31) --- storage/blob.go | 5 +++-- storage/blob_test.go | 8 ++++---- storage/container.go | 28 +++++++++++++++++++++++++--- storage/container_test.go | 26 ++++++++++++++++++++------ 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/storage/blob.go b/storage/blob.go index a487ebd218a9..a0c44adf2ab0 100644 --- a/storage/blob.go +++ b/storage/blob.go @@ -16,6 +16,7 @@ import ( type Blob struct { Container *Container Name string `xml:"Name"` + Snapshot time.Time `xml:"Snapshot"` Properties BlobProperties `xml:"Properties"` Metadata BlobMetadata `xml:"Metadata"` } @@ -246,9 +247,9 @@ type SnapshotOptions struct { RequestID string `header:"x-ms-client-request-id"` } -// Snapshot creates a snapshot for a blob +// CreateSnapshot creates a snapshot for a blob // See https://msdn.microsoft.com/en-us/library/azure/ee691971.aspx -func (b *Blob) Snapshot(options *SnapshotOptions) (snapshotTimestamp *time.Time, err error) { +func (b *Blob) CreateSnapshot(options *SnapshotOptions) (snapshotTimestamp *time.Time, err error) { params := url.Values{"comp": {"snapshot"}} headers := b.Container.bsc.client.getStandardHeaders() headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata) diff --git a/storage/blob_test.go b/storage/blob_test.go index edd340257670..c89652046b9e 100644 --- a/storage/blob_test.go +++ b/storage/blob_test.go @@ -297,7 +297,7 @@ func (s *StorageBlobSuite) TestSnapshotBlob(c *chk.C) { c.Assert(b.putSingleBlockBlob([]byte{}), chk.IsNil) - snapshotTime, err := b.Snapshot(nil) + snapshotTime, err := b.CreateSnapshot(nil) c.Assert(err, chk.IsNil) c.Assert(snapshotTime, chk.NotNil) } @@ -314,7 +314,7 @@ func (s *StorageBlobSuite) TestSnapshotBlobWithTimeout(c *chk.C) { options := SnapshotOptions{ Timeout: 0, } - snapshotTime, err := b.Snapshot(&options) + snapshotTime, err := b.CreateSnapshot(&options) c.Assert(err, chk.IsNil) c.Assert(snapshotTime, chk.NotNil) } @@ -335,7 +335,7 @@ func (s *StorageBlobSuite) TestSnapshotBlobWithValidLease(c *chk.C) { options := SnapshotOptions{ LeaseID: currentLeaseID, } - snapshotTime, err := b.Snapshot(&options) + snapshotTime, err := b.CreateSnapshot(&options) c.Assert(err, chk.IsNil) c.Assert(snapshotTime, chk.NotNil) } @@ -357,7 +357,7 @@ func (s *StorageBlobSuite) TestSnapshotBlobWithInvalidLease(c *chk.C) { options := SnapshotOptions{ LeaseID: "GolangRocksOnAzure", } - snapshotTime, err := b.Snapshot(&options) + snapshotTime, err := b.CreateSnapshot(&options) c.Assert(err, chk.NotNil) c.Assert(snapshotTime, chk.IsNil) } diff --git a/storage/container.go b/storage/container.go index a99ab03bf95a..ae3d6658fc1a 100644 --- a/storage/container.go +++ b/storage/container.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" "strconv" + "strings" "time" ) @@ -70,6 +71,14 @@ type BlobListResponse struct { Delimiter string `xml:"Delimiter"` } +// IncludeBlobDataset has options to include in a list blobs operation +type IncludeBlobDataset struct { + Snapshots bool + Metadata bool + UncommittedBlobs bool + Copy bool +} + // ListBlobsParameters defines the set of customizable // parameters to make a List Blobs call. // @@ -78,7 +87,7 @@ type ListBlobsParameters struct { Prefix string Delimiter string Marker string - Include string + Include *IncludeBlobDataset MaxResults uint Timeout uint RequestID string @@ -96,8 +105,14 @@ func (p ListBlobsParameters) getParameters() url.Values { if p.Marker != "" { out.Set("marker", p.Marker) } - if p.Include != "" { - out.Set("include", p.Include) + if p.Include != nil { + include := []string{} + include = addString(include, p.Include.Snapshots, "snapshots") + include = addString(include, p.Include.Metadata, "metadata") + include = addString(include, p.Include.UncommittedBlobs, "uncommittedblobs") + include = addString(include, p.Include.Copy, "copy") + fullInclude := strings.Join(include, ",") + out.Set("include", fullInclude) } if p.MaxResults != 0 { out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10)) @@ -109,6 +124,13 @@ func (p ListBlobsParameters) getParameters() url.Values { return out } +func addString(datasets []string, include bool, text string) []string { + if include { + datasets = append(datasets, text) + } + return datasets +} + // ContainerAccessType defines the access level to the container from a public // request. // diff --git a/storage/container_test.go b/storage/container_test.go index 886be900af4c..e6913214edf2 100644 --- a/storage/container_test.go +++ b/storage/container_test.go @@ -318,6 +318,8 @@ func (s *ContainerSuite) TestListBlobsWithMetadata(c *chk.C) { "lol": name, "rofl_baz": "Waz Qux", } + _, err := b.CreateSnapshot(nil) + c.Assert(err, chk.IsNil) } // Put one more blob with no metadata @@ -325,20 +327,32 @@ func (s *ContainerSuite) TestListBlobsWithMetadata(c *chk.C) { c.Assert(b.putSingleBlockBlob([]byte("Hello, world!")), chk.IsNil) expectMeta[b.Name] = nil - // Get ListBlobs with include:"metadata" + // Get ListBlobs with include: metadata and snapshots resp, err := cnt.ListBlobs(ListBlobsParameters{ - MaxResults: 5, - Include: "metadata"}) + Include: &IncludeBlobDataset{ + Metadata: true, + Snapshots: true, + }, + }) c.Assert(err, chk.IsNil) - respBlobs := make(map[string]Blob) + originalBlobs := make(map[string]Blob) + snapshotBlobs := make(map[string]Blob) for _, v := range resp.Blobs { - respBlobs[v.Name] = v + if v.Snapshot == (time.Time{}) { + originalBlobs[v.Name] = v + } else { + snapshotBlobs[v.Name] = v + + } } + c.Assert(originalBlobs, chk.HasLen, 5) + c.Assert(snapshotBlobs, chk.HasLen, 4) // Verify the metadata is as expected for name := range expectMeta { - c.Check(respBlobs[name].Metadata, chk.DeepEquals, expectMeta[name]) + c.Check(originalBlobs[name].Metadata, chk.DeepEquals, expectMeta[name]) + c.Check(snapshotBlobs[name].Metadata, chk.DeepEquals, expectMeta[name]) } }